@openclaw-china/shared 0.1.31 → 0.1.32

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.
@@ -0,0 +1 @@
1
+ export * from "./china-setup.js";
package/src/cron/index.ts CHANGED
@@ -1,115 +1,115 @@
1
- const CRON_HIDDEN_PROMPT = `
2
- When creating or updating a cron task, use the main session's last route as the delivery target.
3
- - Do not ask the user for userId/chatId
4
- - sessionTarget="isolated"
5
- - payload.kind="agentTurn"
6
- - payload.deliver=true
7
- - Do not set payload.channel/to (fall back to last route automatically)
8
- - Do not call the message tool to send`;
9
-
10
- const CRON_TRIGGER_KEYWORDS = [
11
- "定时",
12
- "提醒",
13
- "每分钟",
14
- "每小时",
15
- "每天",
16
- "每周",
17
- "几点",
18
- "早上",
19
- "晚上",
20
- "工作日",
21
- "cron",
22
- "remind",
23
- "reminder",
24
- "schedule",
25
- "scheduled",
26
- "every minute",
27
- "every hour",
28
- "every day",
29
- "daily",
30
- "every week",
31
- "weekly",
32
- "weekday",
33
- "workday",
34
- "morning",
35
- "evening",
36
- ];
37
-
38
- const CRON_TRIGGER_PATTERNS = [
39
- /提醒我/u,
40
- /帮我定时/u,
41
- /每.+提醒/u,
42
- /每天.+发/u,
43
- /remind me/iu,
44
- /set (a )?reminder/iu,
45
- /every .+ remind/iu,
46
- /every day .+ (send|post|notify)/iu,
47
- /schedule .+ (reminder|message|notification)/iu,
48
- ];
49
-
50
- const CRON_EXCLUDE_PATTERNS = [
51
- /是什么意思/u,
52
- /区别/u,
53
- /为什么/u,
54
- /\bhelp\b/iu,
55
- /文档/u,
56
- /怎么用/u,
57
- /what does|what's|meaning of/iu,
58
- /difference/iu,
59
- /why/iu,
60
- /\bdocs?\b/iu,
61
- /documentation/iu,
62
- /how to/iu,
63
- /usage/iu,
64
- ];
65
-
66
- export function shouldInjectCronHiddenPrompt(text: string): boolean {
67
- const normalized = text.trim();
68
- if (!normalized) return false;
69
- const lowered = normalized.toLowerCase();
70
-
71
- for (const pattern of CRON_EXCLUDE_PATTERNS) {
72
- if (pattern.test(lowered)) return false;
73
- }
74
-
75
- for (const keyword of CRON_TRIGGER_KEYWORDS) {
76
- if (lowered.includes(keyword.toLowerCase())) return true;
77
- }
78
-
79
- return CRON_TRIGGER_PATTERNS.some((pattern) => pattern.test(normalized));
80
- }
81
-
82
- export function splitCronHiddenPrompt(text: string): { base: string; prompt?: string } {
83
- const idx = text.indexOf(CRON_HIDDEN_PROMPT);
84
- if (idx === -1) {
85
- return { base: text };
86
- }
87
- const base = text.slice(0, idx).trimEnd();
88
- return { base, prompt: CRON_HIDDEN_PROMPT };
89
- }
90
-
91
- export function appendCronHiddenPrompt(text: string): string {
92
- if (!shouldInjectCronHiddenPrompt(text)) return text;
93
- if (text.includes(CRON_HIDDEN_PROMPT)) return text;
94
- return `${text}\n\n${CRON_HIDDEN_PROMPT}`;
95
- }
96
-
97
- export function applyCronHiddenPromptToContext<
98
- T extends { Body?: string; RawBody?: string; CommandBody?: string }
99
- >(ctx: T): boolean {
100
- const base =
101
- (typeof ctx.RawBody === "string" && ctx.RawBody) ||
102
- (typeof ctx.Body === "string" && ctx.Body) ||
103
- (typeof ctx.CommandBody === "string" && ctx.CommandBody) ||
104
- "";
105
-
106
- if (!base) return false;
107
-
108
- const next = appendCronHiddenPrompt(base);
109
- if (next === base) return false;
110
-
111
- ctx.CommandBody = next;
112
- return true;
113
- }
114
-
115
- export { CRON_HIDDEN_PROMPT };
1
+ const CRON_HIDDEN_PROMPT = `
2
+ When creating or updating a cron task, use the main session's last route as the delivery target.
3
+ - Do not ask the user for userId/chatId
4
+ - sessionTarget="isolated"
5
+ - payload.kind="agentTurn"
6
+ - payload.deliver=true
7
+ - Do not set payload.channel/to (fall back to last route automatically)
8
+ - Do not call the message tool to send`;
9
+
10
+ const CRON_TRIGGER_KEYWORDS = [
11
+ "定时",
12
+ "提醒",
13
+ "每分钟",
14
+ "每小时",
15
+ "每天",
16
+ "每周",
17
+ "几点",
18
+ "早上",
19
+ "晚上",
20
+ "工作日",
21
+ "cron",
22
+ "remind",
23
+ "reminder",
24
+ "schedule",
25
+ "scheduled",
26
+ "every minute",
27
+ "every hour",
28
+ "every day",
29
+ "daily",
30
+ "every week",
31
+ "weekly",
32
+ "weekday",
33
+ "workday",
34
+ "morning",
35
+ "evening",
36
+ ];
37
+
38
+ const CRON_TRIGGER_PATTERNS = [
39
+ /提醒我/u,
40
+ /帮我定时/u,
41
+ /每.+提醒/u,
42
+ /每天.+发/u,
43
+ /remind me/iu,
44
+ /set (a )?reminder/iu,
45
+ /every .+ remind/iu,
46
+ /every day .+ (send|post|notify)/iu,
47
+ /schedule .+ (reminder|message|notification)/iu,
48
+ ];
49
+
50
+ const CRON_EXCLUDE_PATTERNS = [
51
+ /是什么意思/u,
52
+ /区别/u,
53
+ /为什么/u,
54
+ /\bhelp\b/iu,
55
+ /文档/u,
56
+ /怎么用/u,
57
+ /what does|what's|meaning of/iu,
58
+ /difference/iu,
59
+ /why/iu,
60
+ /\bdocs?\b/iu,
61
+ /documentation/iu,
62
+ /how to/iu,
63
+ /usage/iu,
64
+ ];
65
+
66
+ export function shouldInjectCronHiddenPrompt(text: string): boolean {
67
+ const normalized = text.trim();
68
+ if (!normalized) return false;
69
+ const lowered = normalized.toLowerCase();
70
+
71
+ for (const pattern of CRON_EXCLUDE_PATTERNS) {
72
+ if (pattern.test(lowered)) return false;
73
+ }
74
+
75
+ for (const keyword of CRON_TRIGGER_KEYWORDS) {
76
+ if (lowered.includes(keyword.toLowerCase())) return true;
77
+ }
78
+
79
+ return CRON_TRIGGER_PATTERNS.some((pattern) => pattern.test(normalized));
80
+ }
81
+
82
+ export function splitCronHiddenPrompt(text: string): { base: string; prompt?: string } {
83
+ const idx = text.indexOf(CRON_HIDDEN_PROMPT);
84
+ if (idx === -1) {
85
+ return { base: text };
86
+ }
87
+ const base = text.slice(0, idx).trimEnd();
88
+ return { base, prompt: CRON_HIDDEN_PROMPT };
89
+ }
90
+
91
+ export function appendCronHiddenPrompt(text: string): string {
92
+ if (!shouldInjectCronHiddenPrompt(text)) return text;
93
+ if (text.includes(CRON_HIDDEN_PROMPT)) return text;
94
+ return `${text}\n\n${CRON_HIDDEN_PROMPT}`;
95
+ }
96
+
97
+ export function applyCronHiddenPromptToContext<
98
+ T extends { Body?: string; RawBody?: string; CommandBody?: string }
99
+ >(ctx: T): boolean {
100
+ const base =
101
+ (typeof ctx.RawBody === "string" && ctx.RawBody) ||
102
+ (typeof ctx.Body === "string" && ctx.Body) ||
103
+ (typeof ctx.CommandBody === "string" && ctx.CommandBody) ||
104
+ "";
105
+
106
+ if (!base) return false;
107
+
108
+ const next = appendCronHiddenPrompt(base);
109
+ if (next === base) return false;
110
+
111
+ ctx.CommandBody = next;
112
+ return true;
113
+ }
114
+
115
+ export { CRON_HIDDEN_PROMPT };
@@ -1,141 +1,141 @@
1
- /**
2
- * Unit Tests for File Utilities
3
- *
4
- * Feature: dingtalk-media-receive
5
- * Validates: Requirements 5.1-5.8, 6.1-6.6
6
- */
7
-
8
- import { describe, it, expect } from "vitest";
9
- import { resolveFileCategory, resolveExtension } from "./file-utils.js";
10
-
11
- describe("resolveFileCategory", () => {
12
- // Image categorization (Requirement 5.1)
13
- it("should categorize image MIME types", () => {
14
- expect(resolveFileCategory("image/jpeg")).toBe("image");
15
- expect(resolveFileCategory("image/png")).toBe("image");
16
- expect(resolveFileCategory("image/gif")).toBe("image");
17
- expect(resolveFileCategory("image/webp")).toBe("image");
18
- expect(resolveFileCategory("image/bmp")).toBe("image");
19
- });
20
-
21
- // Audio categorization (Requirement 5.2)
22
- it("should categorize audio MIME types", () => {
23
- expect(resolveFileCategory("audio/mpeg")).toBe("audio");
24
- expect(resolveFileCategory("audio/wav")).toBe("audio");
25
- expect(resolveFileCategory("audio/ogg")).toBe("audio");
26
- expect(resolveFileCategory("audio/amr")).toBe("audio");
27
- });
28
-
29
- // Video categorization (Requirement 5.3)
30
- it("should categorize video MIME types", () => {
31
- expect(resolveFileCategory("video/mp4")).toBe("video");
32
- expect(resolveFileCategory("video/quicktime")).toBe("video");
33
- expect(resolveFileCategory("video/webm")).toBe("video");
34
- });
35
-
36
- // Document categorization (Requirement 5.4)
37
- it("should categorize document MIME types", () => {
38
- expect(resolveFileCategory("application/pdf")).toBe("document");
39
- expect(resolveFileCategory("application/msword")).toBe("document");
40
- expect(resolveFileCategory("text/plain")).toBe("document");
41
- expect(resolveFileCategory("text/markdown")).toBe("document");
42
- });
43
-
44
- // Archive categorization (Requirement 5.5)
45
- it("should categorize archive MIME types", () => {
46
- expect(resolveFileCategory("application/zip")).toBe("archive");
47
- expect(resolveFileCategory("application/x-rar-compressed")).toBe("archive");
48
- expect(resolveFileCategory("application/x-7z-compressed")).toBe("archive");
49
- });
50
-
51
- // Code categorization (Requirement 5.6)
52
- it("should categorize code MIME types", () => {
53
- expect(resolveFileCategory("application/json")).toBe("code");
54
- expect(resolveFileCategory("text/html")).toBe("code");
55
- expect(resolveFileCategory("text/css")).toBe("code");
56
- expect(resolveFileCategory("text/javascript")).toBe("code");
57
- });
58
-
59
- // Extension fallback (Requirement 5.8)
60
- it("should use extension fallback when MIME type is unknown", () => {
61
- expect(resolveFileCategory("application/octet-stream", "photo.jpg")).toBe("image");
62
- expect(resolveFileCategory("application/octet-stream", "song.mp3")).toBe("audio");
63
- expect(resolveFileCategory("application/octet-stream", "movie.mp4")).toBe("video");
64
- expect(resolveFileCategory("application/octet-stream", "doc.pdf")).toBe("document");
65
- expect(resolveFileCategory("application/octet-stream", "archive.zip")).toBe("archive");
66
- expect(resolveFileCategory("application/octet-stream", "script.py")).toBe("code");
67
- });
68
-
69
- // Other category (Requirement 5.7)
70
- it("should return 'other' for unknown types", () => {
71
- expect(resolveFileCategory("application/octet-stream")).toBe("other");
72
- expect(resolveFileCategory("application/unknown")).toBe("other");
73
- expect(resolveFileCategory("application/octet-stream", "file.xyz")).toBe("other");
74
- });
75
-
76
- // MIME type normalization
77
- it("should handle MIME types with parameters", () => {
78
- expect(resolveFileCategory("image/jpeg; charset=utf-8")).toBe("image");
79
- expect(resolveFileCategory("text/plain; charset=utf-8")).toBe("document");
80
- });
81
- });
82
-
83
- describe("resolveExtension", () => {
84
- // Image extensions (Requirement 6.1)
85
- it("should resolve image MIME types to extensions", () => {
86
- expect(resolveExtension("image/jpeg")).toBe(".jpg");
87
- expect(resolveExtension("image/png")).toBe(".png");
88
- expect(resolveExtension("image/gif")).toBe(".gif");
89
- expect(resolveExtension("image/webp")).toBe(".webp");
90
- expect(resolveExtension("image/bmp")).toBe(".bmp");
91
- });
92
-
93
- // Audio extensions (Requirement 6.2)
94
- it("should resolve audio MIME types to extensions", () => {
95
- expect(resolveExtension("audio/mpeg")).toBe(".mp3");
96
- expect(resolveExtension("audio/wav")).toBe(".wav");
97
- expect(resolveExtension("audio/ogg")).toBe(".ogg");
98
- expect(resolveExtension("audio/amr")).toBe(".amr");
99
- expect(resolveExtension("audio/x-m4a")).toBe(".m4a");
100
- });
101
-
102
- // Video extensions (Requirement 6.3)
103
- it("should resolve video MIME types to extensions", () => {
104
- expect(resolveExtension("video/mp4")).toBe(".mp4");
105
- expect(resolveExtension("video/quicktime")).toBe(".mov");
106
- expect(resolveExtension("video/x-msvideo")).toBe(".avi");
107
- expect(resolveExtension("video/webm")).toBe(".webm");
108
- });
109
-
110
- // Document extensions (Requirement 6.4)
111
- it("should resolve document MIME types to extensions", () => {
112
- expect(resolveExtension("application/pdf")).toBe(".pdf");
113
- expect(resolveExtension("application/msword")).toBe(".doc");
114
- expect(resolveExtension("application/vnd.openxmlformats-officedocument.wordprocessingml.document")).toBe(".docx");
115
- });
116
-
117
- // Default extension (Requirement 6.5)
118
- it("should return .bin for unknown MIME types", () => {
119
- expect(resolveExtension("application/unknown")).toBe(".bin");
120
- expect(resolveExtension("application/octet-stream")).toBe(".bin");
121
- });
122
-
123
- // fileName precedence (Requirement 6.6)
124
- it("should use fileName extension when provided", () => {
125
- expect(resolveExtension("application/octet-stream", "photo.jpg")).toBe(".jpg");
126
- expect(resolveExtension("image/png", "custom.jpeg")).toBe(".jpeg");
127
- expect(resolveExtension("application/unknown", "document.pdf")).toBe(".pdf");
128
- });
129
-
130
- // MIME type normalization
131
- it("should handle MIME types with parameters", () => {
132
- expect(resolveExtension("image/jpeg; charset=utf-8")).toBe(".jpg");
133
- expect(resolveExtension("audio/mpeg; bitrate=320")).toBe(".mp3");
134
- });
135
-
136
- // Edge cases
137
- it("should handle fileName without extension", () => {
138
- expect(resolveExtension("image/jpeg", "photo")).toBe(".jpg");
139
- expect(resolveExtension("application/unknown", "noext")).toBe(".bin");
140
- });
141
- });
1
+ /**
2
+ * Unit Tests for File Utilities
3
+ *
4
+ * Feature: dingtalk-media-receive
5
+ * Validates: Requirements 5.1-5.8, 6.1-6.6
6
+ */
7
+
8
+ import { describe, it, expect } from "vitest";
9
+ import { resolveFileCategory, resolveExtension } from "./file-utils.js";
10
+
11
+ describe("resolveFileCategory", () => {
12
+ // Image categorization (Requirement 5.1)
13
+ it("should categorize image MIME types", () => {
14
+ expect(resolveFileCategory("image/jpeg")).toBe("image");
15
+ expect(resolveFileCategory("image/png")).toBe("image");
16
+ expect(resolveFileCategory("image/gif")).toBe("image");
17
+ expect(resolveFileCategory("image/webp")).toBe("image");
18
+ expect(resolveFileCategory("image/bmp")).toBe("image");
19
+ });
20
+
21
+ // Audio categorization (Requirement 5.2)
22
+ it("should categorize audio MIME types", () => {
23
+ expect(resolveFileCategory("audio/mpeg")).toBe("audio");
24
+ expect(resolveFileCategory("audio/wav")).toBe("audio");
25
+ expect(resolveFileCategory("audio/ogg")).toBe("audio");
26
+ expect(resolveFileCategory("audio/amr")).toBe("audio");
27
+ });
28
+
29
+ // Video categorization (Requirement 5.3)
30
+ it("should categorize video MIME types", () => {
31
+ expect(resolveFileCategory("video/mp4")).toBe("video");
32
+ expect(resolveFileCategory("video/quicktime")).toBe("video");
33
+ expect(resolveFileCategory("video/webm")).toBe("video");
34
+ });
35
+
36
+ // Document categorization (Requirement 5.4)
37
+ it("should categorize document MIME types", () => {
38
+ expect(resolveFileCategory("application/pdf")).toBe("document");
39
+ expect(resolveFileCategory("application/msword")).toBe("document");
40
+ expect(resolveFileCategory("text/plain")).toBe("document");
41
+ expect(resolveFileCategory("text/markdown")).toBe("document");
42
+ });
43
+
44
+ // Archive categorization (Requirement 5.5)
45
+ it("should categorize archive MIME types", () => {
46
+ expect(resolveFileCategory("application/zip")).toBe("archive");
47
+ expect(resolveFileCategory("application/x-rar-compressed")).toBe("archive");
48
+ expect(resolveFileCategory("application/x-7z-compressed")).toBe("archive");
49
+ });
50
+
51
+ // Code categorization (Requirement 5.6)
52
+ it("should categorize code MIME types", () => {
53
+ expect(resolveFileCategory("application/json")).toBe("code");
54
+ expect(resolveFileCategory("text/html")).toBe("code");
55
+ expect(resolveFileCategory("text/css")).toBe("code");
56
+ expect(resolveFileCategory("text/javascript")).toBe("code");
57
+ });
58
+
59
+ // Extension fallback (Requirement 5.8)
60
+ it("should use extension fallback when MIME type is unknown", () => {
61
+ expect(resolveFileCategory("application/octet-stream", "photo.jpg")).toBe("image");
62
+ expect(resolveFileCategory("application/octet-stream", "song.mp3")).toBe("audio");
63
+ expect(resolveFileCategory("application/octet-stream", "movie.mp4")).toBe("video");
64
+ expect(resolveFileCategory("application/octet-stream", "doc.pdf")).toBe("document");
65
+ expect(resolveFileCategory("application/octet-stream", "archive.zip")).toBe("archive");
66
+ expect(resolveFileCategory("application/octet-stream", "script.py")).toBe("code");
67
+ });
68
+
69
+ // Other category (Requirement 5.7)
70
+ it("should return 'other' for unknown types", () => {
71
+ expect(resolveFileCategory("application/octet-stream")).toBe("other");
72
+ expect(resolveFileCategory("application/unknown")).toBe("other");
73
+ expect(resolveFileCategory("application/octet-stream", "file.xyz")).toBe("other");
74
+ });
75
+
76
+ // MIME type normalization
77
+ it("should handle MIME types with parameters", () => {
78
+ expect(resolveFileCategory("image/jpeg; charset=utf-8")).toBe("image");
79
+ expect(resolveFileCategory("text/plain; charset=utf-8")).toBe("document");
80
+ });
81
+ });
82
+
83
+ describe("resolveExtension", () => {
84
+ // Image extensions (Requirement 6.1)
85
+ it("should resolve image MIME types to extensions", () => {
86
+ expect(resolveExtension("image/jpeg")).toBe(".jpg");
87
+ expect(resolveExtension("image/png")).toBe(".png");
88
+ expect(resolveExtension("image/gif")).toBe(".gif");
89
+ expect(resolveExtension("image/webp")).toBe(".webp");
90
+ expect(resolveExtension("image/bmp")).toBe(".bmp");
91
+ });
92
+
93
+ // Audio extensions (Requirement 6.2)
94
+ it("should resolve audio MIME types to extensions", () => {
95
+ expect(resolveExtension("audio/mpeg")).toBe(".mp3");
96
+ expect(resolveExtension("audio/wav")).toBe(".wav");
97
+ expect(resolveExtension("audio/ogg")).toBe(".ogg");
98
+ expect(resolveExtension("audio/amr")).toBe(".amr");
99
+ expect(resolveExtension("audio/x-m4a")).toBe(".m4a");
100
+ });
101
+
102
+ // Video extensions (Requirement 6.3)
103
+ it("should resolve video MIME types to extensions", () => {
104
+ expect(resolveExtension("video/mp4")).toBe(".mp4");
105
+ expect(resolveExtension("video/quicktime")).toBe(".mov");
106
+ expect(resolveExtension("video/x-msvideo")).toBe(".avi");
107
+ expect(resolveExtension("video/webm")).toBe(".webm");
108
+ });
109
+
110
+ // Document extensions (Requirement 6.4)
111
+ it("should resolve document MIME types to extensions", () => {
112
+ expect(resolveExtension("application/pdf")).toBe(".pdf");
113
+ expect(resolveExtension("application/msword")).toBe(".doc");
114
+ expect(resolveExtension("application/vnd.openxmlformats-officedocument.wordprocessingml.document")).toBe(".docx");
115
+ });
116
+
117
+ // Default extension (Requirement 6.5)
118
+ it("should return .bin for unknown MIME types", () => {
119
+ expect(resolveExtension("application/unknown")).toBe(".bin");
120
+ expect(resolveExtension("application/octet-stream")).toBe(".bin");
121
+ });
122
+
123
+ // fileName precedence (Requirement 6.6)
124
+ it("should use fileName extension when provided", () => {
125
+ expect(resolveExtension("application/octet-stream", "photo.jpg")).toBe(".jpg");
126
+ expect(resolveExtension("image/png", "custom.jpeg")).toBe(".jpeg");
127
+ expect(resolveExtension("application/unknown", "document.pdf")).toBe(".pdf");
128
+ });
129
+
130
+ // MIME type normalization
131
+ it("should handle MIME types with parameters", () => {
132
+ expect(resolveExtension("image/jpeg; charset=utf-8")).toBe(".jpg");
133
+ expect(resolveExtension("audio/mpeg; bitrate=320")).toBe(".mp3");
134
+ });
135
+
136
+ // Edge cases
137
+ it("should handle fileName without extension", () => {
138
+ expect(resolveExtension("image/jpeg", "photo")).toBe(".jpg");
139
+ expect(resolveExtension("application/unknown", "noext")).toBe(".bin");
140
+ });
141
+ });