@sunnoy/wecom 2.2.0 → 2.3.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/README.md +2 -1
- package/index.js +13 -2
- package/openclaw.plugin.json +137 -1
- package/package.json +2 -3
- package/skills/wecom-contact-lookup/SKILL.md +167 -0
- package/skills/wecom-doc-manager/SKILL.md +106 -0
- package/skills/wecom-doc-manager/references/api-create-doc.md +56 -0
- package/skills/wecom-doc-manager/references/api-edit-doc-content.md +68 -0
- package/skills/wecom-doc-manager/references/api-export-document.md +88 -0
- package/skills/wecom-edit-todo/SKILL.md +254 -0
- package/skills/wecom-get-todo-detail/SKILL.md +148 -0
- package/skills/wecom-get-todo-list/SKILL.md +132 -0
- package/skills/wecom-meeting-create/SKILL.md +163 -0
- package/skills/wecom-meeting-create/references/example-full.md +30 -0
- package/skills/wecom-meeting-create/references/example-reminder.md +46 -0
- package/skills/wecom-meeting-create/references/example-security.md +22 -0
- package/skills/wecom-meeting-manage/SKILL.md +141 -0
- package/skills/wecom-meeting-query/SKILL.md +335 -0
- package/skills/wecom-preflight/SKILL.md +103 -0
- package/skills/wecom-schedule/SKILL.md +164 -0
- package/skills/wecom-schedule/references/api-check-availability.md +56 -0
- package/skills/wecom-schedule/references/api-create-schedule.md +38 -0
- package/skills/wecom-schedule/references/api-get-schedule-detail.md +81 -0
- package/skills/wecom-schedule/references/api-update-schedule.md +30 -0
- package/skills/wecom-schedule/references/ref-reminders.md +24 -0
- package/skills/wecom-smartsheet-data/SKILL.md +76 -0
- package/skills/wecom-smartsheet-data/references/api-get-records.md +61 -0
- package/skills/wecom-smartsheet-data/references/cell-value-formats.md +120 -0
- package/skills/wecom-smartsheet-schema/SKILL.md +96 -0
- package/skills/wecom-smartsheet-schema/references/field-types.md +43 -0
- package/wecom/accounts.js +2 -0
- package/wecom/callback-inbound.js +9 -5
- package/wecom/channel-plugin.js +65 -1
- package/wecom/constants.js +18 -7
- package/wecom/image-studio-tool.js +764 -0
- package/wecom/mcp-tool.js +660 -0
- package/wecom/parent-resolver.js +26 -0
- package/wecom/plugin-config.js +484 -0
- package/wecom/target.js +3 -2
- package/wecom/welcome-messages-file.js +155 -0
- package/wecom/workspace-template.js +40 -4
- package/wecom/ws-monitor.js +186 -12
- package/skills/wecom-doc/SKILL.md +0 -363
- package/skills/wecom-doc/references/doc-api.md +0 -224
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
const DEFAULT_QWEN_IMAGE_TIMEOUT_MS = 180_000;
|
|
2
|
+
const DEFAULT_QWEN_IMAGE_ENDPOINT = "/services/aigc/multimodal-generation/generation";
|
|
3
|
+
const DEFAULT_WAN_IMAGE_ENDPOINT = "/services/aigc/image-generation/generation";
|
|
4
|
+
const DEFAULT_TASK_ENDPOINT = "/tasks/{task_id}";
|
|
5
|
+
|
|
6
|
+
const DEFAULT_QWEN_SIZE_PRESETS = Object.freeze({
|
|
7
|
+
landscape: "1920*1080",
|
|
8
|
+
square: "1536*1536",
|
|
9
|
+
portrait: "1080*1920",
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
const DEFAULT_WAN_SIZE_PRESETS = Object.freeze({
|
|
13
|
+
landscape: "1280*720",
|
|
14
|
+
square: "1280*1280",
|
|
15
|
+
portrait: "720*1280",
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
export const DEFAULT_QWEN_IMAGE_TOOLS_CONFIG = Object.freeze({
|
|
19
|
+
enabled: false,
|
|
20
|
+
provider: "dashscope",
|
|
21
|
+
route: "auto",
|
|
22
|
+
endpoints: Object.freeze({
|
|
23
|
+
qwen: DEFAULT_QWEN_IMAGE_ENDPOINT,
|
|
24
|
+
wan: DEFAULT_WAN_IMAGE_ENDPOINT,
|
|
25
|
+
task: DEFAULT_TASK_ENDPOINT,
|
|
26
|
+
}),
|
|
27
|
+
timeoutMs: DEFAULT_QWEN_IMAGE_TIMEOUT_MS,
|
|
28
|
+
models: Object.freeze({
|
|
29
|
+
qwen: Object.freeze({
|
|
30
|
+
generate: "qwen-image-2.0-pro",
|
|
31
|
+
edit: "qwen-image-2.0-pro",
|
|
32
|
+
}),
|
|
33
|
+
wan: Object.freeze({
|
|
34
|
+
generate: "wan2.6-image",
|
|
35
|
+
edit: "wan2.6-image",
|
|
36
|
+
}),
|
|
37
|
+
}),
|
|
38
|
+
defaults: Object.freeze({
|
|
39
|
+
aspect: "landscape",
|
|
40
|
+
n: 1,
|
|
41
|
+
watermark: false,
|
|
42
|
+
promptExtend: true,
|
|
43
|
+
qwen: DEFAULT_QWEN_SIZE_PRESETS,
|
|
44
|
+
wan: DEFAULT_WAN_SIZE_PRESETS,
|
|
45
|
+
}),
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
function isPlainObject(value) {
|
|
49
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function createIssue(path, message) {
|
|
53
|
+
return { path, message };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function normalizeString(value, fallback) {
|
|
57
|
+
if (typeof value !== "string") {
|
|
58
|
+
return fallback;
|
|
59
|
+
}
|
|
60
|
+
const trimmed = value.trim();
|
|
61
|
+
return trimmed || fallback;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function normalizeTimeout(value, fallback) {
|
|
65
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
66
|
+
return fallback;
|
|
67
|
+
}
|
|
68
|
+
return Math.max(1_000, Math.trunc(value));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function normalizePositiveInt(value, fallback, minimum = 1) {
|
|
72
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
73
|
+
return fallback;
|
|
74
|
+
}
|
|
75
|
+
return Math.max(minimum, Math.trunc(value));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function normalizeBoolean(value, fallback) {
|
|
79
|
+
return typeof value === "boolean" ? value : fallback;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function normalizeRoute(value, fallback) {
|
|
83
|
+
const normalized = normalizeString(value, fallback).toLowerCase();
|
|
84
|
+
return normalized === "auto" || normalized === "qwen" || normalized === "wan" ? normalized : fallback;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function normalizeAspect(value, fallback) {
|
|
88
|
+
const normalized = normalizeString(value, fallback).toLowerCase();
|
|
89
|
+
return normalized === "landscape" || normalized === "square" || normalized === "portrait" ? normalized : fallback;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function normalizeSizePresets(value, fallback, legacySize = "") {
|
|
93
|
+
const source = isPlainObject(value) ? value : {};
|
|
94
|
+
const fallbackSize = normalizeString(legacySize, "");
|
|
95
|
+
return {
|
|
96
|
+
landscape: normalizeString(source.landscape, fallbackSize || fallback.landscape),
|
|
97
|
+
square: normalizeString(source.square, fallbackSize || fallback.square),
|
|
98
|
+
portrait: normalizeString(source.portrait, fallbackSize || fallback.portrait),
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function resolveQwenImageToolsConfig(pluginConfig) {
|
|
103
|
+
const root = isPlainObject(pluginConfig) ? pluginConfig : {};
|
|
104
|
+
const raw = isPlainObject(root.qwenImageTools) ? root.qwenImageTools : {};
|
|
105
|
+
const rawEndpoints = isPlainObject(raw.endpoints) ? raw.endpoints : {};
|
|
106
|
+
const rawModels = isPlainObject(raw.models) ? raw.models : {};
|
|
107
|
+
const rawDefaults = isPlainObject(raw.defaults) ? raw.defaults : {};
|
|
108
|
+
const rawQwenModels = isPlainObject(rawModels.qwen) ? rawModels.qwen : {};
|
|
109
|
+
const rawWanModels = isPlainObject(rawModels.wan) ? rawModels.wan : {};
|
|
110
|
+
const rawQwenDefaults = isPlainObject(rawDefaults.qwen) ? rawDefaults.qwen : {};
|
|
111
|
+
const rawWanDefaults = isPlainObject(rawDefaults.wan) ? rawDefaults.wan : {};
|
|
112
|
+
const legacySize = normalizeString(rawDefaults.size, "");
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
enabled: raw.enabled === true,
|
|
116
|
+
provider: normalizeString(raw.provider, DEFAULT_QWEN_IMAGE_TOOLS_CONFIG.provider),
|
|
117
|
+
route: normalizeRoute(raw.route, DEFAULT_QWEN_IMAGE_TOOLS_CONFIG.route),
|
|
118
|
+
endpoint: normalizeString(raw.endpoint, DEFAULT_QWEN_IMAGE_TOOLS_CONFIG.endpoints.qwen),
|
|
119
|
+
endpoints: {
|
|
120
|
+
qwen: normalizeString(rawEndpoints.qwen ?? raw.endpoint, DEFAULT_QWEN_IMAGE_TOOLS_CONFIG.endpoints.qwen),
|
|
121
|
+
wan: normalizeString(rawEndpoints.wan, DEFAULT_QWEN_IMAGE_TOOLS_CONFIG.endpoints.wan),
|
|
122
|
+
task: normalizeString(rawEndpoints.task, DEFAULT_QWEN_IMAGE_TOOLS_CONFIG.endpoints.task),
|
|
123
|
+
},
|
|
124
|
+
timeoutMs: normalizeTimeout(raw.timeoutMs, DEFAULT_QWEN_IMAGE_TOOLS_CONFIG.timeoutMs),
|
|
125
|
+
models: {
|
|
126
|
+
qwen: {
|
|
127
|
+
generate: normalizeString(
|
|
128
|
+
rawQwenModels.generate ?? rawModels.generate,
|
|
129
|
+
DEFAULT_QWEN_IMAGE_TOOLS_CONFIG.models.qwen.generate,
|
|
130
|
+
),
|
|
131
|
+
edit: normalizeString(rawQwenModels.edit ?? rawModels.edit, DEFAULT_QWEN_IMAGE_TOOLS_CONFIG.models.qwen.edit),
|
|
132
|
+
},
|
|
133
|
+
wan: {
|
|
134
|
+
generate: normalizeString(rawWanModels.generate, DEFAULT_QWEN_IMAGE_TOOLS_CONFIG.models.wan.generate),
|
|
135
|
+
edit: normalizeString(rawWanModels.edit, DEFAULT_QWEN_IMAGE_TOOLS_CONFIG.models.wan.edit),
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
defaults: {
|
|
139
|
+
size: normalizeString(legacySize, DEFAULT_QWEN_IMAGE_TOOLS_CONFIG.defaults.qwen.square),
|
|
140
|
+
aspect: normalizeAspect(rawDefaults.aspect, DEFAULT_QWEN_IMAGE_TOOLS_CONFIG.defaults.aspect),
|
|
141
|
+
n: normalizePositiveInt(rawDefaults.n, DEFAULT_QWEN_IMAGE_TOOLS_CONFIG.defaults.n),
|
|
142
|
+
watermark: normalizeBoolean(rawDefaults.watermark, DEFAULT_QWEN_IMAGE_TOOLS_CONFIG.defaults.watermark),
|
|
143
|
+
promptExtend: normalizeBoolean(
|
|
144
|
+
rawDefaults.promptExtend,
|
|
145
|
+
DEFAULT_QWEN_IMAGE_TOOLS_CONFIG.defaults.promptExtend,
|
|
146
|
+
),
|
|
147
|
+
qwen: normalizeSizePresets(rawQwenDefaults, DEFAULT_QWEN_IMAGE_TOOLS_CONFIG.defaults.qwen, legacySize),
|
|
148
|
+
wan: normalizeSizePresets(rawWanDefaults, DEFAULT_QWEN_IMAGE_TOOLS_CONFIG.defaults.wan),
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export const wecomPluginConfigSchema = {
|
|
154
|
+
safeParse(value) {
|
|
155
|
+
if (value === undefined) {
|
|
156
|
+
return { success: true, data: undefined };
|
|
157
|
+
}
|
|
158
|
+
if (!isPlainObject(value)) {
|
|
159
|
+
return { success: false, error: { issues: [createIssue([], "expected config object")] } };
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const issues = [];
|
|
163
|
+
if (value.qwenImageTools !== undefined) {
|
|
164
|
+
if (!isPlainObject(value.qwenImageTools)) {
|
|
165
|
+
issues.push(createIssue(["qwenImageTools"], "qwenImageTools must be an object"));
|
|
166
|
+
} else {
|
|
167
|
+
const qwenImageTools = value.qwenImageTools;
|
|
168
|
+
if (qwenImageTools.enabled !== undefined && typeof qwenImageTools.enabled !== "boolean") {
|
|
169
|
+
issues.push(createIssue(["qwenImageTools", "enabled"], "enabled must be a boolean"));
|
|
170
|
+
}
|
|
171
|
+
if (qwenImageTools.provider !== undefined && typeof qwenImageTools.provider !== "string") {
|
|
172
|
+
issues.push(createIssue(["qwenImageTools", "provider"], "provider must be a string"));
|
|
173
|
+
}
|
|
174
|
+
if (qwenImageTools.route !== undefined && typeof qwenImageTools.route !== "string") {
|
|
175
|
+
issues.push(createIssue(["qwenImageTools", "route"], "route must be a string"));
|
|
176
|
+
}
|
|
177
|
+
if (qwenImageTools.endpoint !== undefined && typeof qwenImageTools.endpoint !== "string") {
|
|
178
|
+
issues.push(createIssue(["qwenImageTools", "endpoint"], "endpoint must be a string"));
|
|
179
|
+
}
|
|
180
|
+
if (qwenImageTools.endpoints !== undefined) {
|
|
181
|
+
if (!isPlainObject(qwenImageTools.endpoints)) {
|
|
182
|
+
issues.push(createIssue(["qwenImageTools", "endpoints"], "endpoints must be an object"));
|
|
183
|
+
} else {
|
|
184
|
+
if (qwenImageTools.endpoints.qwen !== undefined && typeof qwenImageTools.endpoints.qwen !== "string") {
|
|
185
|
+
issues.push(createIssue(["qwenImageTools", "endpoints", "qwen"], "qwen must be a string"));
|
|
186
|
+
}
|
|
187
|
+
if (qwenImageTools.endpoints.wan !== undefined && typeof qwenImageTools.endpoints.wan !== "string") {
|
|
188
|
+
issues.push(createIssue(["qwenImageTools", "endpoints", "wan"], "wan must be a string"));
|
|
189
|
+
}
|
|
190
|
+
if (qwenImageTools.endpoints.task !== undefined && typeof qwenImageTools.endpoints.task !== "string") {
|
|
191
|
+
issues.push(createIssue(["qwenImageTools", "endpoints", "task"], "task must be a string"));
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
if (qwenImageTools.timeoutMs !== undefined) {
|
|
196
|
+
if (typeof qwenImageTools.timeoutMs !== "number" || !Number.isFinite(qwenImageTools.timeoutMs)) {
|
|
197
|
+
issues.push(createIssue(["qwenImageTools", "timeoutMs"], "timeoutMs must be a number"));
|
|
198
|
+
} else if (qwenImageTools.timeoutMs < 1_000) {
|
|
199
|
+
issues.push(createIssue(["qwenImageTools", "timeoutMs"], "timeoutMs must be at least 1000"));
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (qwenImageTools.models !== undefined) {
|
|
204
|
+
if (!isPlainObject(qwenImageTools.models)) {
|
|
205
|
+
issues.push(createIssue(["qwenImageTools", "models"], "models must be an object"));
|
|
206
|
+
} else {
|
|
207
|
+
if (
|
|
208
|
+
qwenImageTools.models.generate !== undefined &&
|
|
209
|
+
typeof qwenImageTools.models.generate !== "string"
|
|
210
|
+
) {
|
|
211
|
+
issues.push(createIssue(["qwenImageTools", "models", "generate"], "generate must be a string"));
|
|
212
|
+
}
|
|
213
|
+
if (qwenImageTools.models.edit !== undefined && typeof qwenImageTools.models.edit !== "string") {
|
|
214
|
+
issues.push(createIssue(["qwenImageTools", "models", "edit"], "edit must be a string"));
|
|
215
|
+
}
|
|
216
|
+
if (qwenImageTools.models.qwen !== undefined) {
|
|
217
|
+
if (!isPlainObject(qwenImageTools.models.qwen)) {
|
|
218
|
+
issues.push(createIssue(["qwenImageTools", "models", "qwen"], "qwen must be an object"));
|
|
219
|
+
} else {
|
|
220
|
+
if (
|
|
221
|
+
qwenImageTools.models.qwen.generate !== undefined &&
|
|
222
|
+
typeof qwenImageTools.models.qwen.generate !== "string"
|
|
223
|
+
) {
|
|
224
|
+
issues.push(
|
|
225
|
+
createIssue(["qwenImageTools", "models", "qwen", "generate"], "generate must be a string"),
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
if (
|
|
229
|
+
qwenImageTools.models.qwen.edit !== undefined &&
|
|
230
|
+
typeof qwenImageTools.models.qwen.edit !== "string"
|
|
231
|
+
) {
|
|
232
|
+
issues.push(
|
|
233
|
+
createIssue(["qwenImageTools", "models", "qwen", "edit"], "edit must be a string"),
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
if (qwenImageTools.models.wan !== undefined) {
|
|
239
|
+
if (!isPlainObject(qwenImageTools.models.wan)) {
|
|
240
|
+
issues.push(createIssue(["qwenImageTools", "models", "wan"], "wan must be an object"));
|
|
241
|
+
} else {
|
|
242
|
+
if (
|
|
243
|
+
qwenImageTools.models.wan.generate !== undefined &&
|
|
244
|
+
typeof qwenImageTools.models.wan.generate !== "string"
|
|
245
|
+
) {
|
|
246
|
+
issues.push(
|
|
247
|
+
createIssue(["qwenImageTools", "models", "wan", "generate"], "generate must be a string"),
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
if (
|
|
251
|
+
qwenImageTools.models.wan.edit !== undefined &&
|
|
252
|
+
typeof qwenImageTools.models.wan.edit !== "string"
|
|
253
|
+
) {
|
|
254
|
+
issues.push(
|
|
255
|
+
createIssue(["qwenImageTools", "models", "wan", "edit"], "edit must be a string"),
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (qwenImageTools.defaults !== undefined) {
|
|
264
|
+
if (!isPlainObject(qwenImageTools.defaults)) {
|
|
265
|
+
issues.push(createIssue(["qwenImageTools", "defaults"], "defaults must be an object"));
|
|
266
|
+
} else {
|
|
267
|
+
if (qwenImageTools.defaults.size !== undefined && typeof qwenImageTools.defaults.size !== "string") {
|
|
268
|
+
issues.push(createIssue(["qwenImageTools", "defaults", "size"], "size must be a string"));
|
|
269
|
+
}
|
|
270
|
+
if (qwenImageTools.defaults.aspect !== undefined && typeof qwenImageTools.defaults.aspect !== "string") {
|
|
271
|
+
issues.push(createIssue(["qwenImageTools", "defaults", "aspect"], "aspect must be a string"));
|
|
272
|
+
}
|
|
273
|
+
if (qwenImageTools.defaults.n !== undefined) {
|
|
274
|
+
if (typeof qwenImageTools.defaults.n !== "number" || !Number.isFinite(qwenImageTools.defaults.n)) {
|
|
275
|
+
issues.push(createIssue(["qwenImageTools", "defaults", "n"], "n must be a number"));
|
|
276
|
+
} else if (qwenImageTools.defaults.n < 1) {
|
|
277
|
+
issues.push(createIssue(["qwenImageTools", "defaults", "n"], "n must be at least 1"));
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
if (
|
|
281
|
+
qwenImageTools.defaults.watermark !== undefined &&
|
|
282
|
+
typeof qwenImageTools.defaults.watermark !== "boolean"
|
|
283
|
+
) {
|
|
284
|
+
issues.push(createIssue(["qwenImageTools", "defaults", "watermark"], "watermark must be a boolean"));
|
|
285
|
+
}
|
|
286
|
+
if (
|
|
287
|
+
qwenImageTools.defaults.promptExtend !== undefined &&
|
|
288
|
+
typeof qwenImageTools.defaults.promptExtend !== "boolean"
|
|
289
|
+
) {
|
|
290
|
+
issues.push(
|
|
291
|
+
createIssue(["qwenImageTools", "defaults", "promptExtend"], "promptExtend must be a boolean"),
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
for (const family of ["qwen", "wan"]) {
|
|
295
|
+
const familyDefaults = qwenImageTools.defaults[family];
|
|
296
|
+
if (familyDefaults === undefined) {
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
299
|
+
if (!isPlainObject(familyDefaults)) {
|
|
300
|
+
issues.push(createIssue(["qwenImageTools", "defaults", family], `${family} must be an object`));
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
for (const aspect of ["landscape", "square", "portrait"]) {
|
|
304
|
+
if (familyDefaults[aspect] !== undefined && typeof familyDefaults[aspect] !== "string") {
|
|
305
|
+
issues.push(
|
|
306
|
+
createIssue(["qwenImageTools", "defaults", family, aspect], `${aspect} must be a string`),
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
if (issues.length > 0) {
|
|
317
|
+
return { success: false, error: { issues } };
|
|
318
|
+
}
|
|
319
|
+
return { success: true, data: value };
|
|
320
|
+
},
|
|
321
|
+
jsonSchema: {
|
|
322
|
+
type: "object",
|
|
323
|
+
additionalProperties: true,
|
|
324
|
+
properties: {
|
|
325
|
+
qwenImageTools: {
|
|
326
|
+
type: "object",
|
|
327
|
+
additionalProperties: false,
|
|
328
|
+
properties: {
|
|
329
|
+
enabled: {
|
|
330
|
+
type: "boolean",
|
|
331
|
+
default: DEFAULT_QWEN_IMAGE_TOOLS_CONFIG.enabled,
|
|
332
|
+
description: "Register the image_studio tool backed by DashScope Qwen image APIs.",
|
|
333
|
+
},
|
|
334
|
+
provider: {
|
|
335
|
+
type: "string",
|
|
336
|
+
default: DEFAULT_QWEN_IMAGE_TOOLS_CONFIG.provider,
|
|
337
|
+
description: "Provider alias under models.providers to use for DashScope credentials.",
|
|
338
|
+
},
|
|
339
|
+
route: {
|
|
340
|
+
type: "string",
|
|
341
|
+
default: DEFAULT_QWEN_IMAGE_TOOLS_CONFIG.route,
|
|
342
|
+
enum: ["auto", "qwen", "wan"],
|
|
343
|
+
description: "Default model routing strategy for image_studio.",
|
|
344
|
+
},
|
|
345
|
+
endpoint: {
|
|
346
|
+
type: "string",
|
|
347
|
+
default: DEFAULT_QWEN_IMAGE_TOOLS_CONFIG.endpoints.qwen,
|
|
348
|
+
description: "Legacy alias for the Qwen endpoint path.",
|
|
349
|
+
},
|
|
350
|
+
endpoints: {
|
|
351
|
+
type: "object",
|
|
352
|
+
additionalProperties: false,
|
|
353
|
+
properties: {
|
|
354
|
+
qwen: {
|
|
355
|
+
type: "string",
|
|
356
|
+
default: DEFAULT_QWEN_IMAGE_TOOLS_CONFIG.endpoints.qwen,
|
|
357
|
+
},
|
|
358
|
+
wan: {
|
|
359
|
+
type: "string",
|
|
360
|
+
default: DEFAULT_QWEN_IMAGE_TOOLS_CONFIG.endpoints.wan,
|
|
361
|
+
},
|
|
362
|
+
task: {
|
|
363
|
+
type: "string",
|
|
364
|
+
default: DEFAULT_QWEN_IMAGE_TOOLS_CONFIG.endpoints.task,
|
|
365
|
+
},
|
|
366
|
+
},
|
|
367
|
+
},
|
|
368
|
+
timeoutMs: {
|
|
369
|
+
type: "integer",
|
|
370
|
+
default: DEFAULT_QWEN_IMAGE_TOOLS_CONFIG.timeoutMs,
|
|
371
|
+
minimum: 1000,
|
|
372
|
+
description: "Image generation timeout in milliseconds.",
|
|
373
|
+
},
|
|
374
|
+
models: {
|
|
375
|
+
type: "object",
|
|
376
|
+
additionalProperties: false,
|
|
377
|
+
properties: {
|
|
378
|
+
generate: {
|
|
379
|
+
type: "string",
|
|
380
|
+
default: DEFAULT_QWEN_IMAGE_TOOLS_CONFIG.models.qwen.generate,
|
|
381
|
+
},
|
|
382
|
+
edit: {
|
|
383
|
+
type: "string",
|
|
384
|
+
default: DEFAULT_QWEN_IMAGE_TOOLS_CONFIG.models.qwen.edit,
|
|
385
|
+
},
|
|
386
|
+
qwen: {
|
|
387
|
+
type: "object",
|
|
388
|
+
additionalProperties: false,
|
|
389
|
+
properties: {
|
|
390
|
+
generate: {
|
|
391
|
+
type: "string",
|
|
392
|
+
default: DEFAULT_QWEN_IMAGE_TOOLS_CONFIG.models.qwen.generate,
|
|
393
|
+
},
|
|
394
|
+
edit: {
|
|
395
|
+
type: "string",
|
|
396
|
+
default: DEFAULT_QWEN_IMAGE_TOOLS_CONFIG.models.qwen.edit,
|
|
397
|
+
},
|
|
398
|
+
},
|
|
399
|
+
},
|
|
400
|
+
wan: {
|
|
401
|
+
type: "object",
|
|
402
|
+
additionalProperties: false,
|
|
403
|
+
properties: {
|
|
404
|
+
generate: {
|
|
405
|
+
type: "string",
|
|
406
|
+
default: DEFAULT_QWEN_IMAGE_TOOLS_CONFIG.models.wan.generate,
|
|
407
|
+
},
|
|
408
|
+
edit: {
|
|
409
|
+
type: "string",
|
|
410
|
+
default: DEFAULT_QWEN_IMAGE_TOOLS_CONFIG.models.wan.edit,
|
|
411
|
+
},
|
|
412
|
+
},
|
|
413
|
+
},
|
|
414
|
+
},
|
|
415
|
+
},
|
|
416
|
+
defaults: {
|
|
417
|
+
type: "object",
|
|
418
|
+
additionalProperties: false,
|
|
419
|
+
properties: {
|
|
420
|
+
size: {
|
|
421
|
+
type: "string",
|
|
422
|
+
default: DEFAULT_QWEN_IMAGE_TOOLS_CONFIG.defaults.qwen.square,
|
|
423
|
+
},
|
|
424
|
+
aspect: {
|
|
425
|
+
type: "string",
|
|
426
|
+
default: DEFAULT_QWEN_IMAGE_TOOLS_CONFIG.defaults.aspect,
|
|
427
|
+
enum: ["landscape", "square", "portrait"],
|
|
428
|
+
},
|
|
429
|
+
n: {
|
|
430
|
+
type: "integer",
|
|
431
|
+
default: DEFAULT_QWEN_IMAGE_TOOLS_CONFIG.defaults.n,
|
|
432
|
+
minimum: 1,
|
|
433
|
+
},
|
|
434
|
+
watermark: {
|
|
435
|
+
type: "boolean",
|
|
436
|
+
default: DEFAULT_QWEN_IMAGE_TOOLS_CONFIG.defaults.watermark,
|
|
437
|
+
},
|
|
438
|
+
promptExtend: {
|
|
439
|
+
type: "boolean",
|
|
440
|
+
default: DEFAULT_QWEN_IMAGE_TOOLS_CONFIG.defaults.promptExtend,
|
|
441
|
+
},
|
|
442
|
+
qwen: {
|
|
443
|
+
type: "object",
|
|
444
|
+
additionalProperties: false,
|
|
445
|
+
properties: {
|
|
446
|
+
landscape: {
|
|
447
|
+
type: "string",
|
|
448
|
+
default: DEFAULT_QWEN_IMAGE_TOOLS_CONFIG.defaults.qwen.landscape,
|
|
449
|
+
},
|
|
450
|
+
square: {
|
|
451
|
+
type: "string",
|
|
452
|
+
default: DEFAULT_QWEN_IMAGE_TOOLS_CONFIG.defaults.qwen.square,
|
|
453
|
+
},
|
|
454
|
+
portrait: {
|
|
455
|
+
type: "string",
|
|
456
|
+
default: DEFAULT_QWEN_IMAGE_TOOLS_CONFIG.defaults.qwen.portrait,
|
|
457
|
+
},
|
|
458
|
+
},
|
|
459
|
+
},
|
|
460
|
+
wan: {
|
|
461
|
+
type: "object",
|
|
462
|
+
additionalProperties: false,
|
|
463
|
+
properties: {
|
|
464
|
+
landscape: {
|
|
465
|
+
type: "string",
|
|
466
|
+
default: DEFAULT_QWEN_IMAGE_TOOLS_CONFIG.defaults.wan.landscape,
|
|
467
|
+
},
|
|
468
|
+
square: {
|
|
469
|
+
type: "string",
|
|
470
|
+
default: DEFAULT_QWEN_IMAGE_TOOLS_CONFIG.defaults.wan.square,
|
|
471
|
+
},
|
|
472
|
+
portrait: {
|
|
473
|
+
type: "string",
|
|
474
|
+
default: DEFAULT_QWEN_IMAGE_TOOLS_CONFIG.defaults.wan.portrait,
|
|
475
|
+
},
|
|
476
|
+
},
|
|
477
|
+
},
|
|
478
|
+
},
|
|
479
|
+
},
|
|
480
|
+
},
|
|
481
|
+
},
|
|
482
|
+
},
|
|
483
|
+
},
|
|
484
|
+
};
|
package/wecom/target.js
CHANGED
|
@@ -47,8 +47,9 @@ export function resolveWecomTarget(raw) {
|
|
|
47
47
|
if (/^(wr|wc)/i.test(clean)) {
|
|
48
48
|
return { chatId: clean };
|
|
49
49
|
}
|
|
50
|
-
//
|
|
51
|
-
|
|
50
|
+
// Short pure-digit strings (≤6 digits) are department (party) IDs.
|
|
51
|
+
// Longer digit strings (phone numbers, external IDs) fall through to toUser.
|
|
52
|
+
if (/^\d{1,6}$/.test(clean)) {
|
|
52
53
|
return { toParty: clean };
|
|
53
54
|
}
|
|
54
55
|
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { logger } from "../logger.js";
|
|
5
|
+
|
|
6
|
+
const DEFAULT_STATE_DIRNAME = ".openclaw";
|
|
7
|
+
const LEGACY_STATE_DIRNAMES = [".clawdbot", ".moldbot", ".moltbot"];
|
|
8
|
+
|
|
9
|
+
/** @type {Map<string, { mtimeMs: number; size: number; list: string[] }>} */
|
|
10
|
+
const welcomeMessagesFileCache = new Map();
|
|
11
|
+
|
|
12
|
+
function resolveUserPath(value) {
|
|
13
|
+
const trimmed = String(value ?? "").trim();
|
|
14
|
+
if (!trimmed) {
|
|
15
|
+
return "";
|
|
16
|
+
}
|
|
17
|
+
if (trimmed.startsWith("~")) {
|
|
18
|
+
const homeDir = process.env.OPENCLAW_HOME?.trim() || process.env.HOME || os.homedir();
|
|
19
|
+
return path.resolve(homeDir, trimmed.slice(1).replace(/^\/+/, ""));
|
|
20
|
+
}
|
|
21
|
+
return path.resolve(trimmed);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function resolveOpenclawStateDir() {
|
|
25
|
+
const override = process.env.OPENCLAW_STATE_DIR?.trim() || process.env.CLAWDBOT_STATE_DIR?.trim();
|
|
26
|
+
if (override) {
|
|
27
|
+
return resolveUserPath(override);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const homeDir = process.env.OPENCLAW_HOME?.trim() || process.env.HOME || os.homedir();
|
|
31
|
+
const preferred = path.join(homeDir, DEFAULT_STATE_DIRNAME);
|
|
32
|
+
if (existsSync(preferred)) {
|
|
33
|
+
return preferred;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
for (const legacyName of LEGACY_STATE_DIRNAMES) {
|
|
37
|
+
const candidate = path.join(homeDir, legacyName);
|
|
38
|
+
if (existsSync(candidate)) {
|
|
39
|
+
return candidate;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return preferred;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function resolveWelcomeMessagesFilePath(config) {
|
|
47
|
+
const raw = String(config?.welcomeMessagesFile ?? "").trim();
|
|
48
|
+
if (!raw) {
|
|
49
|
+
return "";
|
|
50
|
+
}
|
|
51
|
+
if (raw.startsWith("~")) {
|
|
52
|
+
return resolveUserPath(raw);
|
|
53
|
+
}
|
|
54
|
+
if (path.isAbsolute(raw)) {
|
|
55
|
+
return path.normalize(raw);
|
|
56
|
+
}
|
|
57
|
+
return path.join(resolveOpenclawStateDir(), raw);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function normalizeWelcomeEntry(item) {
|
|
61
|
+
if (typeof item === "string") {
|
|
62
|
+
const t = item.trim();
|
|
63
|
+
return t || null;
|
|
64
|
+
}
|
|
65
|
+
if (Array.isArray(item)) {
|
|
66
|
+
if (!item.every((line) => line === null || ["string", "number", "boolean"].includes(typeof line))) {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
const joined = item.map((line) => String(line ?? "")).join("\n");
|
|
70
|
+
const t = joined.trim();
|
|
71
|
+
return t || null;
|
|
72
|
+
}
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function parseWelcomeMessagesJson(text) {
|
|
77
|
+
let data;
|
|
78
|
+
try {
|
|
79
|
+
data = JSON.parse(text);
|
|
80
|
+
} catch {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
let list = data;
|
|
85
|
+
if (!Array.isArray(data) && data && typeof data === "object" && Array.isArray(data.messages)) {
|
|
86
|
+
list = data.messages;
|
|
87
|
+
}
|
|
88
|
+
if (!Array.isArray(list)) {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const messages = [];
|
|
93
|
+
for (const item of list) {
|
|
94
|
+
const normalized = normalizeWelcomeEntry(item);
|
|
95
|
+
if (normalized) {
|
|
96
|
+
messages.push(normalized);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return messages.length > 0 ? messages : null;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Load welcome message candidates from welcomeMessagesFile.
|
|
104
|
+
* Accepts: JSON array of strings; array of string arrays (lines joined with \\n); or { "messages": same }.
|
|
105
|
+
* Uses mtime cache so file edits apply without restarting OpenClaw or reloading channel config.
|
|
106
|
+
* @param {Record<string, unknown> | undefined} config
|
|
107
|
+
* @returns {string[] | null}
|
|
108
|
+
*/
|
|
109
|
+
export function loadWelcomeMessagesFromFile(config) {
|
|
110
|
+
const filePath = resolveWelcomeMessagesFilePath(config);
|
|
111
|
+
if (!filePath) {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
let st;
|
|
116
|
+
try {
|
|
117
|
+
st = statSync(filePath);
|
|
118
|
+
} catch {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
if (!st.isFile()) {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const mtimeMs = st.mtimeMs;
|
|
126
|
+
const size = st.size;
|
|
127
|
+
const cached = welcomeMessagesFileCache.get(filePath);
|
|
128
|
+
if (cached && cached.mtimeMs === mtimeMs && cached.size === size) {
|
|
129
|
+
return cached.list;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
let text;
|
|
133
|
+
try {
|
|
134
|
+
text = readFileSync(filePath, "utf8");
|
|
135
|
+
} catch (err) {
|
|
136
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
137
|
+
logger.warn(`[wecom] welcomeMessagesFile read failed (${filePath}): ${message}`);
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const list = parseWelcomeMessagesJson(text);
|
|
142
|
+
if (!list) {
|
|
143
|
+
logger.warn(
|
|
144
|
+
`[wecom] welcomeMessagesFile invalid JSON (expect a non-empty array or { "messages": [...] }): ${filePath}`,
|
|
145
|
+
);
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
welcomeMessagesFileCache.set(filePath, { mtimeMs, size, list });
|
|
150
|
+
return list;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export function clearWelcomeMessagesFileCacheForTesting() {
|
|
154
|
+
welcomeMessagesFileCache.clear();
|
|
155
|
+
}
|