gewe-openclaw 2026.3.13 → 2026.3.23

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 (44) hide show
  1. package/README.md +455 -3
  2. package/index.ts +39 -1
  3. package/package.json +12 -1
  4. package/skills/gewe-agent-tools/SKILL.md +113 -0
  5. package/skills/gewe-channel-rules/SKILL.md +7 -0
  6. package/src/accounts.ts +51 -5
  7. package/src/api-tools.ts +1264 -0
  8. package/src/api.ts +37 -2
  9. package/src/binary-command.ts +65 -0
  10. package/src/channel-actions.ts +536 -0
  11. package/src/channel-allowlist.ts +150 -0
  12. package/src/channel-directory.ts +419 -0
  13. package/src/channel-status.ts +186 -0
  14. package/src/channel.ts +155 -58
  15. package/src/config-edit.ts +94 -0
  16. package/src/config-schema.ts +78 -3
  17. package/src/contacts-api.ts +113 -0
  18. package/src/delivery.ts +502 -62
  19. package/src/directory-cache.ts +164 -0
  20. package/src/gewe-account-api.ts +27 -0
  21. package/src/group-allowlist-tool.ts +242 -0
  22. package/src/group-binding-tool.ts +154 -0
  23. package/src/group-binding.ts +405 -0
  24. package/src/groups-api.ts +146 -0
  25. package/src/inbound-batch.ts +5 -2
  26. package/src/inbound.ts +248 -41
  27. package/src/media-server.ts +73 -93
  28. package/src/moments-api.ts +138 -0
  29. package/src/monitor.ts +81 -24
  30. package/src/onboarding.ts +9 -4
  31. package/src/openclaw-compat.ts +1070 -0
  32. package/src/pairing-store.ts +478 -0
  33. package/src/personal-api.ts +45 -0
  34. package/src/policy.ts +130 -22
  35. package/src/quote-context-cache.ts +97 -0
  36. package/src/reply-options.ts +101 -2
  37. package/src/s3.ts +1 -1
  38. package/src/send.ts +235 -16
  39. package/src/setup-wizard-types.ts +162 -0
  40. package/src/setup-wizard.ts +464 -0
  41. package/src/silk.ts +2 -1
  42. package/src/state-paths.ts +55 -14
  43. package/src/types.ts +66 -7
  44. package/src/xml.ts +158 -0
package/src/xml.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import crypto from "node:crypto";
2
+
1
3
  function decodeEntities(value: string): string {
2
4
  return value
3
5
  .replace(/&/g, "&")
@@ -23,6 +25,93 @@ function stripCdata(value: string): string {
23
25
  return trimmed;
24
26
  }
25
27
 
28
+ export type GeweQuoteDetails = {
29
+ title?: string;
30
+ referType?: number;
31
+ svrid?: string;
32
+ fromUsr?: string;
33
+ chatUsr?: string;
34
+ displayName?: string;
35
+ content?: string;
36
+ msgSource?: string;
37
+ partialText?: GeweQuotePartialText;
38
+ };
39
+
40
+ export type GeweQuotePartialText = {
41
+ start?: string;
42
+ end?: string;
43
+ startIndex?: number;
44
+ endIndex?: number;
45
+ quoteMd5?: string;
46
+ text?: string;
47
+ };
48
+
49
+ function parseOptionalNumber(value?: string): number | undefined {
50
+ if (!value) return undefined;
51
+ const parsed = Number.parseInt(value, 10);
52
+ return Number.isFinite(parsed) ? parsed : undefined;
53
+ }
54
+
55
+ function mapQuoteTypeLabel(type?: number): string {
56
+ switch (type) {
57
+ case 1:
58
+ return "文本";
59
+ case 3:
60
+ return "图片";
61
+ case 6:
62
+ return "文件";
63
+ case 43:
64
+ return "视频";
65
+ case 49:
66
+ return "卡片";
67
+ default:
68
+ return "消息";
69
+ }
70
+ }
71
+
72
+ function looksLikeXmlFragment(value?: string): boolean {
73
+ const trimmed = value?.trim();
74
+ if (!trimmed) return false;
75
+ return trimmed.startsWith("<") && trimmed.endsWith(">");
76
+ }
77
+
78
+ function md5Hex(value: string): string {
79
+ return crypto.createHash("md5").update(value, "utf8").digest("hex");
80
+ }
81
+
82
+ function resolvePartialQuoteText(params: {
83
+ content?: string;
84
+ partialText?: GeweQuotePartialText;
85
+ }): string | undefined {
86
+ const content = params.content?.trim();
87
+ const partialText = params.partialText;
88
+ if (!content || !partialText) return undefined;
89
+ if (looksLikeXmlFragment(content) || /<img[\s>]/i.test(content)) return undefined;
90
+
91
+ const start = partialText.start?.trim();
92
+ const end = partialText.end?.trim();
93
+ const quoteMd5 = partialText.quoteMd5?.trim().toLowerCase();
94
+ const rawText = partialText.text?.trim();
95
+ if (rawText && (!quoteMd5 || md5Hex(rawText) === quoteMd5)) {
96
+ return rawText;
97
+ }
98
+ if (!start || !end) return undefined;
99
+
100
+ const step = Math.max(start.length, 1);
101
+ for (let startPos = content.indexOf(start); startPos >= 0; startPos = content.indexOf(start, startPos + step)) {
102
+ const endStep = Math.max(end.length, 1);
103
+ for (let endPos = content.indexOf(end, startPos); endPos >= 0; endPos = content.indexOf(end, endPos + endStep)) {
104
+ const candidate = content.slice(startPos, endPos + end.length);
105
+ if (!candidate) continue;
106
+ if (!quoteMd5 || md5Hex(candidate) === quoteMd5) {
107
+ return candidate;
108
+ }
109
+ }
110
+ }
111
+
112
+ return undefined;
113
+ }
114
+
26
115
  export function extractXmlTag(xml: string, tag: string): string | undefined {
27
116
  const re = new RegExp(`<${tag}>([\\s\\S]*?)<\\/${tag}>`, "i");
28
117
  const match = re.exec(xml);
@@ -57,3 +146,72 @@ export function extractFileName(xml: string): string | undefined {
57
146
  if (title) return title.trim();
58
147
  return undefined;
59
148
  }
149
+
150
+ export function extractQuoteDetails(xml: string): GeweQuoteDetails | undefined {
151
+ if (!/<refermsg>/i.test(xml)) return undefined;
152
+ const referXml = extractXmlTag(xml, "refermsg");
153
+ if (!referXml) return undefined;
154
+ const partialTextXml = extractXmlTag(referXml, "partialtext");
155
+ const partialText = partialTextXml
156
+ ? ({
157
+ start: extractXmlTag(partialTextXml, "start"),
158
+ end: extractXmlTag(partialTextXml, "end"),
159
+ startIndex: parseOptionalNumber(extractXmlTag(partialTextXml, "startindex")),
160
+ endIndex: parseOptionalNumber(extractXmlTag(partialTextXml, "endindex")),
161
+ quoteMd5: extractXmlTag(partialTextXml, "quotemd5")?.toLowerCase(),
162
+ } as GeweQuotePartialText)
163
+ : undefined;
164
+ const details = {
165
+ title: extractXmlTag(xml, "title"),
166
+ referType: parseOptionalNumber(extractXmlTag(referXml, "type")),
167
+ svrid: extractXmlTag(referXml, "svrid"),
168
+ fromUsr: extractXmlTag(referXml, "fromusr"),
169
+ chatUsr: extractXmlTag(referXml, "chatusr"),
170
+ displayName: extractXmlTag(referXml, "displayname"),
171
+ content: extractXmlTag(referXml, "content"),
172
+ msgSource: extractXmlTag(referXml, "msgsource"),
173
+ partialText:
174
+ partialText &&
175
+ Object.fromEntries(
176
+ Object.entries({
177
+ ...partialText,
178
+ text: resolvePartialQuoteText({
179
+ content: extractXmlTag(referXml, "content"),
180
+ partialText,
181
+ }),
182
+ }).filter(([, value]) => value !== undefined),
183
+ ),
184
+ };
185
+ return Object.fromEntries(
186
+ Object.entries(details).filter(([, value]) => value !== undefined),
187
+ ) as GeweQuoteDetails;
188
+ }
189
+
190
+ export function extractQuoteSummary(xml: string):
191
+ | {
192
+ body: string;
193
+ quoteLabel: string;
194
+ }
195
+ | undefined {
196
+ const details = extractQuoteDetails(xml);
197
+ if (!details) return undefined;
198
+ const quoteLabel = mapQuoteTypeLabel(details.referType);
199
+ const parts = [];
200
+ const partialReferContent = details.partialText?.text?.trim();
201
+ const referContent = details.content?.trim();
202
+ const safeReferContent =
203
+ partialReferContent ||
204
+ (referContent && !looksLikeXmlFragment(referContent) && !/<img[\s>]/i.test(referContent)
205
+ ? referContent
206
+ : undefined);
207
+ parts.push(
208
+ safeReferContent ? `[引用:${quoteLabel}] ${safeReferContent}` : `[引用:${quoteLabel}]`,
209
+ );
210
+ if (details.title?.trim()) {
211
+ parts.push(details.title.trim());
212
+ }
213
+ return {
214
+ body: parts.join("\n").trim(),
215
+ quoteLabel,
216
+ };
217
+ }