@openclaw-china/shared 0.1.38 → 2026.3.2
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/package.json +1 -1
- package/src/cli/china-setup.ts +15 -24
- package/src/cli/index.ts +2 -2
- package/src/file/file-utils.test.ts +141 -141
- package/src/file/file-utils.ts +284 -284
- package/src/file/index.ts +10 -10
- package/src/index.ts +3 -3
- package/src/logger/index.ts +1 -1
- package/src/logger/logger.ts +51 -51
- package/src/media/index.ts +22 -22
- package/src/media/media-io.ts +328 -328
- package/vitest.config.ts +8 -8
package/src/file/file-utils.ts
CHANGED
|
@@ -1,284 +1,284 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* File utilities for categorizing and resolving file extensions
|
|
3
|
-
* @module @openclaw-china/shared/file
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* File category for processing strategy
|
|
8
|
-
*/
|
|
9
|
-
export type FileCategory =
|
|
10
|
-
| "image"
|
|
11
|
-
| "audio"
|
|
12
|
-
| "video"
|
|
13
|
-
| "document"
|
|
14
|
-
| "archive"
|
|
15
|
-
| "code"
|
|
16
|
-
| "other";
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* MIME type to extension mapping
|
|
20
|
-
*/
|
|
21
|
-
const MIME_TO_EXTENSION: Record<string, string> = {
|
|
22
|
-
// Images
|
|
23
|
-
"image/jpeg": ".jpg",
|
|
24
|
-
"image/png": ".png",
|
|
25
|
-
"image/gif": ".gif",
|
|
26
|
-
"image/webp": ".webp",
|
|
27
|
-
"image/bmp": ".bmp",
|
|
28
|
-
|
|
29
|
-
// Audio
|
|
30
|
-
"audio/mpeg": ".mp3",
|
|
31
|
-
"audio/wav": ".wav",
|
|
32
|
-
"audio/ogg": ".ogg",
|
|
33
|
-
"audio/amr": ".amr",
|
|
34
|
-
"audio/x-m4a": ".m4a",
|
|
35
|
-
|
|
36
|
-
// Video
|
|
37
|
-
"video/mp4": ".mp4",
|
|
38
|
-
"video/quicktime": ".mov",
|
|
39
|
-
"video/x-msvideo": ".avi",
|
|
40
|
-
"video/webm": ".webm",
|
|
41
|
-
|
|
42
|
-
// Documents
|
|
43
|
-
"application/pdf": ".pdf",
|
|
44
|
-
"application/msword": ".doc",
|
|
45
|
-
"application/vnd.openxmlformats-officedocument.wordprocessingml.document":
|
|
46
|
-
".docx",
|
|
47
|
-
"application/vnd.ms-excel": ".xls",
|
|
48
|
-
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": ".xlsx",
|
|
49
|
-
"application/vnd.ms-powerpoint": ".ppt",
|
|
50
|
-
"application/vnd.openxmlformats-officedocument.presentationml.presentation":
|
|
51
|
-
".pptx",
|
|
52
|
-
"application/rtf": ".rtf",
|
|
53
|
-
"application/vnd.oasis.opendocument.text": ".odt",
|
|
54
|
-
"application/vnd.oasis.opendocument.spreadsheet": ".ods",
|
|
55
|
-
"text/plain": ".txt",
|
|
56
|
-
"text/markdown": ".md",
|
|
57
|
-
"text/csv": ".csv",
|
|
58
|
-
|
|
59
|
-
// Archives
|
|
60
|
-
"application/zip": ".zip",
|
|
61
|
-
"application/x-rar-compressed": ".rar",
|
|
62
|
-
"application/vnd.rar": ".rar",
|
|
63
|
-
"application/x-7z-compressed": ".7z",
|
|
64
|
-
"application/x-tar": ".tar",
|
|
65
|
-
"application/gzip": ".gz",
|
|
66
|
-
"application/x-gzip": ".gz",
|
|
67
|
-
"application/x-bzip2": ".bz2",
|
|
68
|
-
|
|
69
|
-
// Code
|
|
70
|
-
"application/json": ".json",
|
|
71
|
-
"application/xml": ".xml",
|
|
72
|
-
"text/xml": ".xml",
|
|
73
|
-
"text/html": ".html",
|
|
74
|
-
"text/css": ".css",
|
|
75
|
-
"text/javascript": ".js",
|
|
76
|
-
"application/javascript": ".js",
|
|
77
|
-
"text/x-python": ".py",
|
|
78
|
-
"text/x-java-source": ".java",
|
|
79
|
-
"text/x-c": ".c",
|
|
80
|
-
"text/x-yaml": ".yaml",
|
|
81
|
-
"application/x-yaml": ".yaml",
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* MIME type to category mapping for non-prefix-based types
|
|
86
|
-
*/
|
|
87
|
-
const CATEGORY_BY_MIME: Record<string, FileCategory> = {
|
|
88
|
-
// Documents
|
|
89
|
-
"application/pdf": "document",
|
|
90
|
-
"application/msword": "document",
|
|
91
|
-
"application/vnd.openxmlformats-officedocument.wordprocessingml.document":
|
|
92
|
-
"document",
|
|
93
|
-
"application/vnd.ms-excel": "document",
|
|
94
|
-
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":
|
|
95
|
-
"document",
|
|
96
|
-
"application/vnd.ms-powerpoint": "document",
|
|
97
|
-
"application/vnd.openxmlformats-officedocument.presentationml.presentation":
|
|
98
|
-
"document",
|
|
99
|
-
"application/rtf": "document",
|
|
100
|
-
"application/vnd.oasis.opendocument.text": "document",
|
|
101
|
-
"application/vnd.oasis.opendocument.spreadsheet": "document",
|
|
102
|
-
"text/plain": "document",
|
|
103
|
-
"text/markdown": "document",
|
|
104
|
-
"text/csv": "document",
|
|
105
|
-
// Archives
|
|
106
|
-
"application/zip": "archive",
|
|
107
|
-
"application/x-rar-compressed": "archive",
|
|
108
|
-
"application/vnd.rar": "archive",
|
|
109
|
-
"application/x-7z-compressed": "archive",
|
|
110
|
-
"application/x-tar": "archive",
|
|
111
|
-
"application/gzip": "archive",
|
|
112
|
-
"application/x-gzip": "archive",
|
|
113
|
-
"application/x-bzip2": "archive",
|
|
114
|
-
// Code
|
|
115
|
-
"application/json": "code",
|
|
116
|
-
"application/xml": "code",
|
|
117
|
-
"text/xml": "code",
|
|
118
|
-
"text/html": "code",
|
|
119
|
-
"text/css": "code",
|
|
120
|
-
"text/javascript": "code",
|
|
121
|
-
"application/javascript": "code",
|
|
122
|
-
"text/x-python": "code",
|
|
123
|
-
"text/x-java-source": "code",
|
|
124
|
-
"text/x-c": "code",
|
|
125
|
-
"text/x-yaml": "code",
|
|
126
|
-
"application/x-yaml": "code",
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Extension to category mapping
|
|
131
|
-
*/
|
|
132
|
-
const CATEGORY_BY_EXTENSION: Record<string, FileCategory> = {
|
|
133
|
-
// Images
|
|
134
|
-
".jpg": "image",
|
|
135
|
-
".jpeg": "image",
|
|
136
|
-
".png": "image",
|
|
137
|
-
".gif": "image",
|
|
138
|
-
".webp": "image",
|
|
139
|
-
".bmp": "image",
|
|
140
|
-
// Audio
|
|
141
|
-
".mp3": "audio",
|
|
142
|
-
".wav": "audio",
|
|
143
|
-
".ogg": "audio",
|
|
144
|
-
".m4a": "audio",
|
|
145
|
-
".amr": "audio",
|
|
146
|
-
// Video
|
|
147
|
-
".mp4": "video",
|
|
148
|
-
".mov": "video",
|
|
149
|
-
".avi": "video",
|
|
150
|
-
".mkv": "video",
|
|
151
|
-
".webm": "video",
|
|
152
|
-
// Documents
|
|
153
|
-
".pdf": "document",
|
|
154
|
-
".doc": "document",
|
|
155
|
-
".docx": "document",
|
|
156
|
-
".txt": "document",
|
|
157
|
-
".md": "document",
|
|
158
|
-
".rtf": "document",
|
|
159
|
-
".odt": "document",
|
|
160
|
-
".xls": "document",
|
|
161
|
-
".xlsx": "document",
|
|
162
|
-
".csv": "document",
|
|
163
|
-
".ods": "document",
|
|
164
|
-
".ppt": "document",
|
|
165
|
-
".pptx": "document",
|
|
166
|
-
// Archives
|
|
167
|
-
".zip": "archive",
|
|
168
|
-
".rar": "archive",
|
|
169
|
-
".7z": "archive",
|
|
170
|
-
".tar": "archive",
|
|
171
|
-
".gz": "archive",
|
|
172
|
-
".bz2": "archive",
|
|
173
|
-
// Code
|
|
174
|
-
".py": "code",
|
|
175
|
-
".js": "code",
|
|
176
|
-
".ts": "code",
|
|
177
|
-
".jsx": "code",
|
|
178
|
-
".tsx": "code",
|
|
179
|
-
".java": "code",
|
|
180
|
-
".cpp": "code",
|
|
181
|
-
".c": "code",
|
|
182
|
-
".go": "code",
|
|
183
|
-
".rs": "code",
|
|
184
|
-
".json": "code",
|
|
185
|
-
".xml": "code",
|
|
186
|
-
".yaml": "code",
|
|
187
|
-
".yml": "code",
|
|
188
|
-
".html": "code",
|
|
189
|
-
".css": "code",
|
|
190
|
-
};
|
|
191
|
-
|
|
192
|
-
/**
|
|
193
|
-
* Extract file extension from a file name
|
|
194
|
-
* @param fileName - The file name to extract extension from
|
|
195
|
-
* @returns The extension with leading dot (e.g., ".jpg") or empty string if none
|
|
196
|
-
*/
|
|
197
|
-
function extractExtension(fileName: string): string {
|
|
198
|
-
const lastDot = fileName.lastIndexOf(".");
|
|
199
|
-
if (lastDot === -1 || lastDot === fileName.length - 1) {
|
|
200
|
-
return "";
|
|
201
|
-
}
|
|
202
|
-
return fileName.slice(lastDot).toLowerCase();
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
* Categorize a file based on MIME type and extension
|
|
207
|
-
*
|
|
208
|
-
* Priority:
|
|
209
|
-
* 1. Check MIME type prefix (image/, audio/, video/)
|
|
210
|
-
* 2. Check exact MIME type mapping (document, archive, code)
|
|
211
|
-
* 3. Check file extension from fileName
|
|
212
|
-
* 4. Return 'other' if no match
|
|
213
|
-
*
|
|
214
|
-
* @param contentType - MIME type string
|
|
215
|
-
* @param fileName - Optional file name for extension-based fallback
|
|
216
|
-
* @returns File category
|
|
217
|
-
*/
|
|
218
|
-
export function resolveFileCategory(
|
|
219
|
-
contentType: string,
|
|
220
|
-
fileName?: string
|
|
221
|
-
): FileCategory {
|
|
222
|
-
// Normalize content type (remove parameters like charset)
|
|
223
|
-
const mimeType = contentType.split(";")[0].trim().toLowerCase();
|
|
224
|
-
|
|
225
|
-
// Check MIME type prefix first (image/, audio/, video/)
|
|
226
|
-
if (mimeType.startsWith("image/")) {
|
|
227
|
-
return "image";
|
|
228
|
-
}
|
|
229
|
-
if (mimeType.startsWith("audio/")) {
|
|
230
|
-
return "audio";
|
|
231
|
-
}
|
|
232
|
-
if (mimeType.startsWith("video/")) {
|
|
233
|
-
return "video";
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
// Check exact MIME type mapping (document, archive, code)
|
|
237
|
-
if (mimeType in CATEGORY_BY_MIME) {
|
|
238
|
-
return CATEGORY_BY_MIME[mimeType];
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
// Check file extension if fileName is provided
|
|
242
|
-
if (fileName) {
|
|
243
|
-
const ext = extractExtension(fileName);
|
|
244
|
-
if (ext && ext in CATEGORY_BY_EXTENSION) {
|
|
245
|
-
return CATEGORY_BY_EXTENSION[ext];
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
return "other";
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
/**
|
|
253
|
-
* Resolve file extension from MIME type or fileName
|
|
254
|
-
*
|
|
255
|
-
* Priority:
|
|
256
|
-
* 1. fileName extension (if provided and has extension)
|
|
257
|
-
* 2. MIME type mapping
|
|
258
|
-
* 3. ".bin" default
|
|
259
|
-
*
|
|
260
|
-
* @param contentType - MIME type string
|
|
261
|
-
* @param fileName - Optional file name to extract extension from (takes precedence)
|
|
262
|
-
* @returns Extension with leading dot (e.g., ".jpg") or ".bin" if unknown
|
|
263
|
-
*/
|
|
264
|
-
export function resolveExtension(
|
|
265
|
-
contentType: string,
|
|
266
|
-
fileName?: string
|
|
267
|
-
): string {
|
|
268
|
-
// Priority 1: fileName extension
|
|
269
|
-
if (fileName) {
|
|
270
|
-
const ext = extractExtension(fileName);
|
|
271
|
-
if (ext) {
|
|
272
|
-
return ext;
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
// Priority 2: MIME type mapping
|
|
277
|
-
const mimeType = contentType.split(";")[0].trim().toLowerCase();
|
|
278
|
-
if (mimeType in MIME_TO_EXTENSION) {
|
|
279
|
-
return MIME_TO_EXTENSION[mimeType];
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
// Priority 3: Default
|
|
283
|
-
return ".bin";
|
|
284
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* File utilities for categorizing and resolving file extensions
|
|
3
|
+
* @module @openclaw-china/shared/file
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* File category for processing strategy
|
|
8
|
+
*/
|
|
9
|
+
export type FileCategory =
|
|
10
|
+
| "image"
|
|
11
|
+
| "audio"
|
|
12
|
+
| "video"
|
|
13
|
+
| "document"
|
|
14
|
+
| "archive"
|
|
15
|
+
| "code"
|
|
16
|
+
| "other";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* MIME type to extension mapping
|
|
20
|
+
*/
|
|
21
|
+
const MIME_TO_EXTENSION: Record<string, string> = {
|
|
22
|
+
// Images
|
|
23
|
+
"image/jpeg": ".jpg",
|
|
24
|
+
"image/png": ".png",
|
|
25
|
+
"image/gif": ".gif",
|
|
26
|
+
"image/webp": ".webp",
|
|
27
|
+
"image/bmp": ".bmp",
|
|
28
|
+
|
|
29
|
+
// Audio
|
|
30
|
+
"audio/mpeg": ".mp3",
|
|
31
|
+
"audio/wav": ".wav",
|
|
32
|
+
"audio/ogg": ".ogg",
|
|
33
|
+
"audio/amr": ".amr",
|
|
34
|
+
"audio/x-m4a": ".m4a",
|
|
35
|
+
|
|
36
|
+
// Video
|
|
37
|
+
"video/mp4": ".mp4",
|
|
38
|
+
"video/quicktime": ".mov",
|
|
39
|
+
"video/x-msvideo": ".avi",
|
|
40
|
+
"video/webm": ".webm",
|
|
41
|
+
|
|
42
|
+
// Documents
|
|
43
|
+
"application/pdf": ".pdf",
|
|
44
|
+
"application/msword": ".doc",
|
|
45
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document":
|
|
46
|
+
".docx",
|
|
47
|
+
"application/vnd.ms-excel": ".xls",
|
|
48
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": ".xlsx",
|
|
49
|
+
"application/vnd.ms-powerpoint": ".ppt",
|
|
50
|
+
"application/vnd.openxmlformats-officedocument.presentationml.presentation":
|
|
51
|
+
".pptx",
|
|
52
|
+
"application/rtf": ".rtf",
|
|
53
|
+
"application/vnd.oasis.opendocument.text": ".odt",
|
|
54
|
+
"application/vnd.oasis.opendocument.spreadsheet": ".ods",
|
|
55
|
+
"text/plain": ".txt",
|
|
56
|
+
"text/markdown": ".md",
|
|
57
|
+
"text/csv": ".csv",
|
|
58
|
+
|
|
59
|
+
// Archives
|
|
60
|
+
"application/zip": ".zip",
|
|
61
|
+
"application/x-rar-compressed": ".rar",
|
|
62
|
+
"application/vnd.rar": ".rar",
|
|
63
|
+
"application/x-7z-compressed": ".7z",
|
|
64
|
+
"application/x-tar": ".tar",
|
|
65
|
+
"application/gzip": ".gz",
|
|
66
|
+
"application/x-gzip": ".gz",
|
|
67
|
+
"application/x-bzip2": ".bz2",
|
|
68
|
+
|
|
69
|
+
// Code
|
|
70
|
+
"application/json": ".json",
|
|
71
|
+
"application/xml": ".xml",
|
|
72
|
+
"text/xml": ".xml",
|
|
73
|
+
"text/html": ".html",
|
|
74
|
+
"text/css": ".css",
|
|
75
|
+
"text/javascript": ".js",
|
|
76
|
+
"application/javascript": ".js",
|
|
77
|
+
"text/x-python": ".py",
|
|
78
|
+
"text/x-java-source": ".java",
|
|
79
|
+
"text/x-c": ".c",
|
|
80
|
+
"text/x-yaml": ".yaml",
|
|
81
|
+
"application/x-yaml": ".yaml",
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* MIME type to category mapping for non-prefix-based types
|
|
86
|
+
*/
|
|
87
|
+
const CATEGORY_BY_MIME: Record<string, FileCategory> = {
|
|
88
|
+
// Documents
|
|
89
|
+
"application/pdf": "document",
|
|
90
|
+
"application/msword": "document",
|
|
91
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document":
|
|
92
|
+
"document",
|
|
93
|
+
"application/vnd.ms-excel": "document",
|
|
94
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":
|
|
95
|
+
"document",
|
|
96
|
+
"application/vnd.ms-powerpoint": "document",
|
|
97
|
+
"application/vnd.openxmlformats-officedocument.presentationml.presentation":
|
|
98
|
+
"document",
|
|
99
|
+
"application/rtf": "document",
|
|
100
|
+
"application/vnd.oasis.opendocument.text": "document",
|
|
101
|
+
"application/vnd.oasis.opendocument.spreadsheet": "document",
|
|
102
|
+
"text/plain": "document",
|
|
103
|
+
"text/markdown": "document",
|
|
104
|
+
"text/csv": "document",
|
|
105
|
+
// Archives
|
|
106
|
+
"application/zip": "archive",
|
|
107
|
+
"application/x-rar-compressed": "archive",
|
|
108
|
+
"application/vnd.rar": "archive",
|
|
109
|
+
"application/x-7z-compressed": "archive",
|
|
110
|
+
"application/x-tar": "archive",
|
|
111
|
+
"application/gzip": "archive",
|
|
112
|
+
"application/x-gzip": "archive",
|
|
113
|
+
"application/x-bzip2": "archive",
|
|
114
|
+
// Code
|
|
115
|
+
"application/json": "code",
|
|
116
|
+
"application/xml": "code",
|
|
117
|
+
"text/xml": "code",
|
|
118
|
+
"text/html": "code",
|
|
119
|
+
"text/css": "code",
|
|
120
|
+
"text/javascript": "code",
|
|
121
|
+
"application/javascript": "code",
|
|
122
|
+
"text/x-python": "code",
|
|
123
|
+
"text/x-java-source": "code",
|
|
124
|
+
"text/x-c": "code",
|
|
125
|
+
"text/x-yaml": "code",
|
|
126
|
+
"application/x-yaml": "code",
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Extension to category mapping
|
|
131
|
+
*/
|
|
132
|
+
const CATEGORY_BY_EXTENSION: Record<string, FileCategory> = {
|
|
133
|
+
// Images
|
|
134
|
+
".jpg": "image",
|
|
135
|
+
".jpeg": "image",
|
|
136
|
+
".png": "image",
|
|
137
|
+
".gif": "image",
|
|
138
|
+
".webp": "image",
|
|
139
|
+
".bmp": "image",
|
|
140
|
+
// Audio
|
|
141
|
+
".mp3": "audio",
|
|
142
|
+
".wav": "audio",
|
|
143
|
+
".ogg": "audio",
|
|
144
|
+
".m4a": "audio",
|
|
145
|
+
".amr": "audio",
|
|
146
|
+
// Video
|
|
147
|
+
".mp4": "video",
|
|
148
|
+
".mov": "video",
|
|
149
|
+
".avi": "video",
|
|
150
|
+
".mkv": "video",
|
|
151
|
+
".webm": "video",
|
|
152
|
+
// Documents
|
|
153
|
+
".pdf": "document",
|
|
154
|
+
".doc": "document",
|
|
155
|
+
".docx": "document",
|
|
156
|
+
".txt": "document",
|
|
157
|
+
".md": "document",
|
|
158
|
+
".rtf": "document",
|
|
159
|
+
".odt": "document",
|
|
160
|
+
".xls": "document",
|
|
161
|
+
".xlsx": "document",
|
|
162
|
+
".csv": "document",
|
|
163
|
+
".ods": "document",
|
|
164
|
+
".ppt": "document",
|
|
165
|
+
".pptx": "document",
|
|
166
|
+
// Archives
|
|
167
|
+
".zip": "archive",
|
|
168
|
+
".rar": "archive",
|
|
169
|
+
".7z": "archive",
|
|
170
|
+
".tar": "archive",
|
|
171
|
+
".gz": "archive",
|
|
172
|
+
".bz2": "archive",
|
|
173
|
+
// Code
|
|
174
|
+
".py": "code",
|
|
175
|
+
".js": "code",
|
|
176
|
+
".ts": "code",
|
|
177
|
+
".jsx": "code",
|
|
178
|
+
".tsx": "code",
|
|
179
|
+
".java": "code",
|
|
180
|
+
".cpp": "code",
|
|
181
|
+
".c": "code",
|
|
182
|
+
".go": "code",
|
|
183
|
+
".rs": "code",
|
|
184
|
+
".json": "code",
|
|
185
|
+
".xml": "code",
|
|
186
|
+
".yaml": "code",
|
|
187
|
+
".yml": "code",
|
|
188
|
+
".html": "code",
|
|
189
|
+
".css": "code",
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Extract file extension from a file name
|
|
194
|
+
* @param fileName - The file name to extract extension from
|
|
195
|
+
* @returns The extension with leading dot (e.g., ".jpg") or empty string if none
|
|
196
|
+
*/
|
|
197
|
+
function extractExtension(fileName: string): string {
|
|
198
|
+
const lastDot = fileName.lastIndexOf(".");
|
|
199
|
+
if (lastDot === -1 || lastDot === fileName.length - 1) {
|
|
200
|
+
return "";
|
|
201
|
+
}
|
|
202
|
+
return fileName.slice(lastDot).toLowerCase();
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Categorize a file based on MIME type and extension
|
|
207
|
+
*
|
|
208
|
+
* Priority:
|
|
209
|
+
* 1. Check MIME type prefix (image/, audio/, video/)
|
|
210
|
+
* 2. Check exact MIME type mapping (document, archive, code)
|
|
211
|
+
* 3. Check file extension from fileName
|
|
212
|
+
* 4. Return 'other' if no match
|
|
213
|
+
*
|
|
214
|
+
* @param contentType - MIME type string
|
|
215
|
+
* @param fileName - Optional file name for extension-based fallback
|
|
216
|
+
* @returns File category
|
|
217
|
+
*/
|
|
218
|
+
export function resolveFileCategory(
|
|
219
|
+
contentType: string,
|
|
220
|
+
fileName?: string
|
|
221
|
+
): FileCategory {
|
|
222
|
+
// Normalize content type (remove parameters like charset)
|
|
223
|
+
const mimeType = contentType.split(";")[0].trim().toLowerCase();
|
|
224
|
+
|
|
225
|
+
// Check MIME type prefix first (image/, audio/, video/)
|
|
226
|
+
if (mimeType.startsWith("image/")) {
|
|
227
|
+
return "image";
|
|
228
|
+
}
|
|
229
|
+
if (mimeType.startsWith("audio/")) {
|
|
230
|
+
return "audio";
|
|
231
|
+
}
|
|
232
|
+
if (mimeType.startsWith("video/")) {
|
|
233
|
+
return "video";
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Check exact MIME type mapping (document, archive, code)
|
|
237
|
+
if (mimeType in CATEGORY_BY_MIME) {
|
|
238
|
+
return CATEGORY_BY_MIME[mimeType];
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Check file extension if fileName is provided
|
|
242
|
+
if (fileName) {
|
|
243
|
+
const ext = extractExtension(fileName);
|
|
244
|
+
if (ext && ext in CATEGORY_BY_EXTENSION) {
|
|
245
|
+
return CATEGORY_BY_EXTENSION[ext];
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return "other";
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Resolve file extension from MIME type or fileName
|
|
254
|
+
*
|
|
255
|
+
* Priority:
|
|
256
|
+
* 1. fileName extension (if provided and has extension)
|
|
257
|
+
* 2. MIME type mapping
|
|
258
|
+
* 3. ".bin" default
|
|
259
|
+
*
|
|
260
|
+
* @param contentType - MIME type string
|
|
261
|
+
* @param fileName - Optional file name to extract extension from (takes precedence)
|
|
262
|
+
* @returns Extension with leading dot (e.g., ".jpg") or ".bin" if unknown
|
|
263
|
+
*/
|
|
264
|
+
export function resolveExtension(
|
|
265
|
+
contentType: string,
|
|
266
|
+
fileName?: string
|
|
267
|
+
): string {
|
|
268
|
+
// Priority 1: fileName extension
|
|
269
|
+
if (fileName) {
|
|
270
|
+
const ext = extractExtension(fileName);
|
|
271
|
+
if (ext) {
|
|
272
|
+
return ext;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Priority 2: MIME type mapping
|
|
277
|
+
const mimeType = contentType.split(";")[0].trim().toLowerCase();
|
|
278
|
+
if (mimeType in MIME_TO_EXTENSION) {
|
|
279
|
+
return MIME_TO_EXTENSION[mimeType];
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Priority 3: Default
|
|
283
|
+
return ".bin";
|
|
284
|
+
}
|
package/src/file/index.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* File utilities module
|
|
3
|
-
* @module @openclaw-china/shared/file
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
export {
|
|
7
|
-
type FileCategory,
|
|
8
|
-
resolveFileCategory,
|
|
9
|
-
resolveExtension,
|
|
10
|
-
} from "./file-utils.js";
|
|
1
|
+
/**
|
|
2
|
+
* File utilities module
|
|
3
|
+
* @module @openclaw-china/shared/file
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export {
|
|
7
|
+
type FileCategory,
|
|
8
|
+
resolveFileCategory,
|
|
9
|
+
resolveExtension,
|
|
10
|
+
} from "./file-utils.js";
|
package/src/index.ts
CHANGED
|
@@ -7,6 +7,6 @@ export * from "./http/index.js";
|
|
|
7
7
|
export * from "./types/common.js";
|
|
8
8
|
export * from "./file/index.js";
|
|
9
9
|
export * from "./media/index.js";
|
|
10
|
-
export * from "./cron/index.js";
|
|
11
|
-
export * from "./asr/index.js";
|
|
12
|
-
export * from "./cli/index.js";
|
|
10
|
+
export * from "./cron/index.js";
|
|
11
|
+
export * from "./asr/index.js";
|
|
12
|
+
export * from "./cli/index.js";
|
package/src/logger/index.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from "./logger.js";
|
|
1
|
+
export * from "./logger.js";
|