@qwen-code/channel-dingtalk 0.21.0-nightly.20260729.0c0ca5fed
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/dist/DingtalkAdapter.d.ts +105 -0
- package/dist/DingtalkAdapter.d.ts.map +1 -0
- package/dist/DingtalkAdapter.js +1078 -0
- package/dist/DingtalkAdapter.js.map +1 -0
- package/dist/DingtalkConnectionManager.d.ts +55 -0
- package/dist/DingtalkConnectionManager.d.ts.map +1 -0
- package/dist/DingtalkConnectionManager.js +317 -0
- package/dist/DingtalkConnectionManager.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -0
- package/dist/markdown.d.ts +13 -0
- package/dist/markdown.d.ts.map +1 -0
- package/dist/markdown.js +93 -0
- package/dist/markdown.js.map +1 -0
- package/dist/media.d.ts +21 -0
- package/dist/media.d.ts.map +1 -0
- package/dist/media.js +93 -0
- package/dist/media.js.map +1 -0
- package/dist/outbound-image.d.ts +22 -0
- package/dist/outbound-image.d.ts.map +1 -0
- package/dist/outbound-image.js +205 -0
- package/dist/outbound-image.js.map +1 -0
- package/package.json +29 -0
package/dist/media.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DingTalk media download helpers.
|
|
3
|
+
*
|
|
4
|
+
* Two-step flow:
|
|
5
|
+
* 1. POST downloadCode to DingTalk API → get a temporary downloadUrl
|
|
6
|
+
* 2. GET the downloadUrl → arraybuffer
|
|
7
|
+
*/
|
|
8
|
+
export interface MediaFile {
|
|
9
|
+
buffer: Buffer;
|
|
10
|
+
mimeType: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Download a media file from DingTalk using a downloadCode.
|
|
14
|
+
*
|
|
15
|
+
* @param downloadCode - The code from incoming message richText/content
|
|
16
|
+
* @param robotCode - The bot's clientId (appKey)
|
|
17
|
+
* @param accessToken - A valid DingTalk access token
|
|
18
|
+
* @returns MediaFile with buffer and mimeType, or null on failure
|
|
19
|
+
*/
|
|
20
|
+
export declare function downloadMedia(downloadCode: string, robotCode: string, accessToken: string): Promise<MediaFile | null>;
|
|
21
|
+
//# sourceMappingURL=media.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media.d.ts","sourceRoot":"","sources":["../src/media.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;GAOG;AACH,wBAAsB,aAAa,CACjC,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAgG3B"}
|
package/dist/media.js
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DingTalk media download helpers.
|
|
3
|
+
*
|
|
4
|
+
* Two-step flow:
|
|
5
|
+
* 1. POST downloadCode to DingTalk API → get a temporary downloadUrl
|
|
6
|
+
* 2. GET the downloadUrl → arraybuffer
|
|
7
|
+
*/
|
|
8
|
+
const DOWNLOAD_API = 'https://api.dingtalk.com/v1.0/robot/messageFiles/download';
|
|
9
|
+
/**
|
|
10
|
+
* Download a media file from DingTalk using a downloadCode.
|
|
11
|
+
*
|
|
12
|
+
* @param downloadCode - The code from incoming message richText/content
|
|
13
|
+
* @param robotCode - The bot's clientId (appKey)
|
|
14
|
+
* @param accessToken - A valid DingTalk access token
|
|
15
|
+
* @returns MediaFile with buffer and mimeType, or null on failure
|
|
16
|
+
*/
|
|
17
|
+
export async function downloadMedia(downloadCode, robotCode, accessToken) {
|
|
18
|
+
if (!downloadCode || !robotCode || !accessToken) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
try {
|
|
22
|
+
// Step 1: Get downloadUrl from DingTalk API
|
|
23
|
+
const apiResp = await fetch(DOWNLOAD_API, {
|
|
24
|
+
method: 'POST',
|
|
25
|
+
headers: {
|
|
26
|
+
'x-acs-dingtalk-access-token': accessToken,
|
|
27
|
+
'Content-Type': 'application/json',
|
|
28
|
+
},
|
|
29
|
+
body: JSON.stringify({ downloadCode, robotCode }),
|
|
30
|
+
});
|
|
31
|
+
if (!apiResp.ok) {
|
|
32
|
+
const detail = await apiResp.text().catch(() => '');
|
|
33
|
+
process.stderr.write(`[DingTalk] downloadMedia API failed: HTTP ${apiResp.status} ${detail}\n`);
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
const payload = (await apiResp.json());
|
|
37
|
+
const downloadUrl = payload['downloadUrl'] ??
|
|
38
|
+
payload['data']?.['downloadUrl'];
|
|
39
|
+
if (!downloadUrl) {
|
|
40
|
+
process.stderr.write(`[DingTalk] downloadMedia: no downloadUrl in response\n`);
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
// Step 2: Download the actual file. Bound the request so a stalled CDN
|
|
44
|
+
// connection can't hang the streaming loop below indefinitely (matches
|
|
45
|
+
// the Feishu adapter).
|
|
46
|
+
const fileResp = await fetch(downloadUrl, {
|
|
47
|
+
signal: AbortSignal.timeout(30_000),
|
|
48
|
+
});
|
|
49
|
+
if (!fileResp.ok) {
|
|
50
|
+
process.stderr.write(`[DingTalk] downloadMedia file fetch failed: HTTP ${fileResp.status}\n`);
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
const MAX_DOWNLOAD_BYTES = 50 * 1024 * 1024; // 50 MB
|
|
54
|
+
const contentLength = fileResp.headers.get('content-length');
|
|
55
|
+
if (contentLength && parseInt(contentLength, 10) > MAX_DOWNLOAD_BYTES) {
|
|
56
|
+
// Release the connection instead of letting the unread body pin it
|
|
57
|
+
// until GC.
|
|
58
|
+
await fileResp.body?.cancel();
|
|
59
|
+
process.stderr.write(`[DingTalk] downloadMedia rejected: size ${contentLength} exceeds ${MAX_DOWNLOAD_BYTES} byte limit\n`);
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
const mimeType = fileResp.headers.get('content-type') || 'application/octet-stream';
|
|
63
|
+
// Stream-read with size enforcement (handles chunked transfer without Content-Length)
|
|
64
|
+
const reader = fileResp.body?.getReader();
|
|
65
|
+
if (!reader) {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
const chunks = [];
|
|
69
|
+
let totalSize = 0;
|
|
70
|
+
while (true) {
|
|
71
|
+
const { done, value } = await reader.read();
|
|
72
|
+
if (done)
|
|
73
|
+
break;
|
|
74
|
+
totalSize += value.byteLength;
|
|
75
|
+
if (totalSize > MAX_DOWNLOAD_BYTES) {
|
|
76
|
+
// Await the cancel so a stream error during teardown surfaces here
|
|
77
|
+
// instead of becoming an unhandled rejection (matches the body.cancel
|
|
78
|
+
// above).
|
|
79
|
+
await reader.cancel();
|
|
80
|
+
process.stderr.write(`[DingTalk] downloadMedia rejected: actual size exceeds ${MAX_DOWNLOAD_BYTES} byte limit\n`);
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
chunks.push(Buffer.from(value));
|
|
84
|
+
}
|
|
85
|
+
const buffer = Buffer.concat(chunks);
|
|
86
|
+
return { buffer, mimeType };
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
process.stderr.write(`[DingTalk] downloadMedia error: ${err instanceof Error ? err.message : err}\n`);
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=media.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media.js","sourceRoot":"","sources":["../src/media.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface ImageMarker {
|
|
2
|
+
start: number;
|
|
3
|
+
end: number;
|
|
4
|
+
path: string;
|
|
5
|
+
}
|
|
6
|
+
export interface ValidatedImage {
|
|
7
|
+
data: Buffer;
|
|
8
|
+
fileName: string;
|
|
9
|
+
mimeType: string;
|
|
10
|
+
}
|
|
11
|
+
export declare class DingTalkMediaUploadError extends Error {
|
|
12
|
+
readonly authFailure: boolean;
|
|
13
|
+
constructor(message: string, authFailure: boolean);
|
|
14
|
+
}
|
|
15
|
+
export declare function findImageMarkers(text: string): ImageMarker[];
|
|
16
|
+
export declare function replaceImageMarkers(text: string, markers: readonly ImageMarker[], replacements: readonly string[]): string;
|
|
17
|
+
export declare function readValidatedImage(imagePath: string, options: {
|
|
18
|
+
workspaceDir: string;
|
|
19
|
+
temporaryDir?: string;
|
|
20
|
+
}): ValidatedImage;
|
|
21
|
+
export declare function uploadDingTalkImage(image: ValidatedImage, accessToken: string): Promise<string>;
|
|
22
|
+
//# sourceMappingURL=outbound-image.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"outbound-image.d.ts","sourceRoot":"","sources":["../src/outbound-image.ts"],"names":[],"mappings":"AAiBA,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,wBAAyB,SAAQ,KAAK;IAG/C,QAAQ,CAAC,WAAW,EAAE,OAAO;gBAD7B,OAAO,EAAE,MAAM,EACN,WAAW,EAAE,OAAO;CAKhC;AAoCD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,EAAE,CAgB5D;AAED,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,SAAS,WAAW,EAAE,EAC/B,YAAY,EAAE,SAAS,MAAM,EAAE,GAC9B,MAAM,CAcR;AA+BD,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE;IACP,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,GACA,cAAc,CA4DhB;AASD,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,cAAc,EACrB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,MAAM,CAAC,CAiEjB"}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { closeSync, fstatSync, openSync, readFileSync, readSync, realpathSync, } from 'node:fs';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import { basename, extname, isAbsolute, relative } from 'node:path';
|
|
4
|
+
const MEDIA_UPLOAD_API = 'https://oapi.dingtalk.com/media/upload';
|
|
5
|
+
const MEDIA_UPLOAD_TIMEOUT_MS = 30_000;
|
|
6
|
+
const MAX_IMAGE_BYTES = 20 * 1024 * 1024;
|
|
7
|
+
const IMAGE_EXTENSIONS = new Set(['.png', '.jpg', '.jpeg', '.gif', '.bmp']);
|
|
8
|
+
const AUTH_ERROR_CODES = new Set([40014, 42001]);
|
|
9
|
+
export class DingTalkMediaUploadError extends Error {
|
|
10
|
+
authFailure;
|
|
11
|
+
constructor(message, authFailure) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.authFailure = authFailure;
|
|
14
|
+
this.name = 'DingTalkMediaUploadError';
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
function maskCode(text) {
|
|
18
|
+
const masked = text.split('');
|
|
19
|
+
const blank = (start, end) => {
|
|
20
|
+
for (let i = start; i < end; i++) {
|
|
21
|
+
if (masked[i] !== '\n')
|
|
22
|
+
masked[i] = ' ';
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
let offset = 0;
|
|
26
|
+
while (offset < text.length) {
|
|
27
|
+
if (text[offset] === '`') {
|
|
28
|
+
let runLength = 1;
|
|
29
|
+
while (text[offset + runLength] === '`')
|
|
30
|
+
runLength++;
|
|
31
|
+
const delimiter = '`'.repeat(runLength);
|
|
32
|
+
const closing = text.indexOf(delimiter, offset + runLength);
|
|
33
|
+
const newline = runLength >= 3 ? -1 : text.indexOf('\n', offset + runLength);
|
|
34
|
+
const closesBeforeNewline = closing !== -1 && (newline === -1 || closing < newline);
|
|
35
|
+
const end = closesBeforeNewline
|
|
36
|
+
? closing + runLength
|
|
37
|
+
: newline === -1
|
|
38
|
+
? text.length
|
|
39
|
+
: newline;
|
|
40
|
+
blank(offset, end);
|
|
41
|
+
offset = end;
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
offset++;
|
|
45
|
+
}
|
|
46
|
+
return masked.join('');
|
|
47
|
+
}
|
|
48
|
+
export function findImageMarkers(text) {
|
|
49
|
+
const visibleText = maskCode(text);
|
|
50
|
+
const markerPattern = /\[IMAGE:\s*([^\]\r\n]+)\]/gi;
|
|
51
|
+
const markers = [];
|
|
52
|
+
for (const match of visibleText.matchAll(markerPattern)) {
|
|
53
|
+
const path = match[1]?.trim();
|
|
54
|
+
if (!path || match.index === undefined)
|
|
55
|
+
continue;
|
|
56
|
+
markers.push({
|
|
57
|
+
start: match.index,
|
|
58
|
+
end: match.index + match[0].length,
|
|
59
|
+
path,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
return markers;
|
|
63
|
+
}
|
|
64
|
+
export function replaceImageMarkers(text, markers, replacements) {
|
|
65
|
+
if (markers.length !== replacements.length) {
|
|
66
|
+
throw new Error('Image marker replacement count mismatch');
|
|
67
|
+
}
|
|
68
|
+
let result = text;
|
|
69
|
+
for (let i = markers.length - 1; i >= 0; i--) {
|
|
70
|
+
const marker = markers[i];
|
|
71
|
+
result =
|
|
72
|
+
result.slice(0, marker.start) +
|
|
73
|
+
replacements[i] +
|
|
74
|
+
result.slice(marker.end);
|
|
75
|
+
}
|
|
76
|
+
return result;
|
|
77
|
+
}
|
|
78
|
+
function isInside(realPath, directory) {
|
|
79
|
+
const pathFromDirectory = relative(directory, realPath);
|
|
80
|
+
return (pathFromDirectory === '' ||
|
|
81
|
+
(!pathFromDirectory.startsWith('..') && !isAbsolute(pathFromDirectory)));
|
|
82
|
+
}
|
|
83
|
+
function detectImageMime(data) {
|
|
84
|
+
if (data[0] === 0x89 &&
|
|
85
|
+
data[1] === 0x50 &&
|
|
86
|
+
data[2] === 0x4e &&
|
|
87
|
+
data[3] === 0x47) {
|
|
88
|
+
return 'image/png';
|
|
89
|
+
}
|
|
90
|
+
if (data[0] === 0xff && data[1] === 0xd8 && data[2] === 0xff) {
|
|
91
|
+
return 'image/jpeg';
|
|
92
|
+
}
|
|
93
|
+
if (data[0] === 0x47 && data[1] === 0x49 && data[2] === 0x46) {
|
|
94
|
+
return 'image/gif';
|
|
95
|
+
}
|
|
96
|
+
if (data[0] === 0x42 && data[1] === 0x4d) {
|
|
97
|
+
return 'image/bmp';
|
|
98
|
+
}
|
|
99
|
+
throw new Error('Unrecognized image format');
|
|
100
|
+
}
|
|
101
|
+
export function readValidatedImage(imagePath, options) {
|
|
102
|
+
if (!isAbsolute(imagePath)) {
|
|
103
|
+
throw new Error(`Image path must be absolute: ${imagePath}`);
|
|
104
|
+
}
|
|
105
|
+
const extension = extname(imagePath).toLowerCase();
|
|
106
|
+
if (!IMAGE_EXTENSIONS.has(extension)) {
|
|
107
|
+
throw new Error(`Image extension not allowed: ${extension}`);
|
|
108
|
+
}
|
|
109
|
+
let realPath;
|
|
110
|
+
try {
|
|
111
|
+
realPath = realpathSync(imagePath);
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
throw new Error(`Image file not found: ${imagePath}`);
|
|
115
|
+
}
|
|
116
|
+
const allowedDirectories = [
|
|
117
|
+
realpathSync(options.workspaceDir),
|
|
118
|
+
realpathSync(options.temporaryDir ?? tmpdir()),
|
|
119
|
+
];
|
|
120
|
+
if (!allowedDirectories.some((directory) => isInside(realPath, directory))) {
|
|
121
|
+
throw new Error(`Image path outside allowed directories: ${realPath}`);
|
|
122
|
+
}
|
|
123
|
+
const descriptor = openSync(realPath, 'r');
|
|
124
|
+
try {
|
|
125
|
+
const stats = fstatSync(descriptor);
|
|
126
|
+
if (!stats.isFile()) {
|
|
127
|
+
throw new Error(`Not a regular file: ${realPath}`);
|
|
128
|
+
}
|
|
129
|
+
if (stats.size > MAX_IMAGE_BYTES) {
|
|
130
|
+
throw new Error(`Image too large: ${stats.size} bytes (max ${MAX_IMAGE_BYTES})`);
|
|
131
|
+
}
|
|
132
|
+
const header = Buffer.alloc(16);
|
|
133
|
+
const bytesRead = readSync(descriptor, header, 0, header.length, 0);
|
|
134
|
+
const mimeType = detectImageMime(header.subarray(0, bytesRead));
|
|
135
|
+
const expectedMime = {
|
|
136
|
+
'.png': 'image/png',
|
|
137
|
+
'.jpg': 'image/jpeg',
|
|
138
|
+
'.jpeg': 'image/jpeg',
|
|
139
|
+
'.gif': 'image/gif',
|
|
140
|
+
'.bmp': 'image/bmp',
|
|
141
|
+
};
|
|
142
|
+
if (mimeType !== expectedMime[extension]) {
|
|
143
|
+
throw new Error(`Image type mismatch: ${extension} expects ${expectedMime[extension]} but got ${mimeType}`);
|
|
144
|
+
}
|
|
145
|
+
return {
|
|
146
|
+
data: readFileSync(descriptor),
|
|
147
|
+
fileName: basename(realPath),
|
|
148
|
+
mimeType,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
finally {
|
|
152
|
+
closeSync(descriptor);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
function sanitizeApiMessage(message, accessToken) {
|
|
156
|
+
const value = String(message ?? '');
|
|
157
|
+
return (accessToken ? value.replaceAll(accessToken, '[redacted]') : value)
|
|
158
|
+
.replace(/[\r\n\t]+/g, ' ')
|
|
159
|
+
.slice(0, 200);
|
|
160
|
+
}
|
|
161
|
+
export async function uploadDingTalkImage(image, accessToken) {
|
|
162
|
+
const form = new FormData();
|
|
163
|
+
form.append('media', new Blob([image.data], { type: image.mimeType }), image.fileName);
|
|
164
|
+
let response;
|
|
165
|
+
try {
|
|
166
|
+
const url = new URL(MEDIA_UPLOAD_API);
|
|
167
|
+
url.searchParams.set('access_token', accessToken);
|
|
168
|
+
url.searchParams.set('type', 'image');
|
|
169
|
+
response = await fetch(url, {
|
|
170
|
+
method: 'POST',
|
|
171
|
+
body: form,
|
|
172
|
+
signal: AbortSignal.timeout(MEDIA_UPLOAD_TIMEOUT_MS),
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
throw new DingTalkMediaUploadError('DingTalk media upload failed: network request failed', false);
|
|
177
|
+
}
|
|
178
|
+
let payload;
|
|
179
|
+
try {
|
|
180
|
+
const parsed = (await response.json());
|
|
181
|
+
payload =
|
|
182
|
+
parsed && typeof parsed === 'object' && !Array.isArray(parsed)
|
|
183
|
+
? parsed
|
|
184
|
+
: {};
|
|
185
|
+
}
|
|
186
|
+
catch {
|
|
187
|
+
throw new DingTalkMediaUploadError(`DingTalk media upload failed: HTTP ${response.status} invalid JSON response`, response.status === 401);
|
|
188
|
+
}
|
|
189
|
+
const errcode = typeof payload['errcode'] === 'number' ? payload['errcode'] : undefined;
|
|
190
|
+
if (!response.ok || (errcode !== undefined && errcode !== 0)) {
|
|
191
|
+
const detail = sanitizeApiMessage(payload['errmsg'], accessToken);
|
|
192
|
+
throw new DingTalkMediaUploadError(`DingTalk media upload failed: HTTP ${response.status}${errcode === undefined ? '' : ` errcode=${errcode}`}${detail ? ` ${detail}` : ''}`, response.status === 401 ||
|
|
193
|
+
(errcode !== undefined && AUTH_ERROR_CODES.has(errcode)));
|
|
194
|
+
}
|
|
195
|
+
const mediaId = typeof payload['media_id'] === 'string'
|
|
196
|
+
? payload['media_id']
|
|
197
|
+
: typeof payload['mediaId'] === 'string'
|
|
198
|
+
? payload['mediaId']
|
|
199
|
+
: undefined;
|
|
200
|
+
if (!mediaId) {
|
|
201
|
+
throw new DingTalkMediaUploadError('DingTalk media upload failed: response did not include a MediaID', false);
|
|
202
|
+
}
|
|
203
|
+
return mediaId;
|
|
204
|
+
}
|
|
205
|
+
//# sourceMappingURL=outbound-image.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"outbound-image.js","sourceRoot":"","sources":["../src/outbound-image.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@qwen-code/channel-dingtalk",
|
|
3
|
+
"version": "0.21.0-nightly.20260729.0c0ca5fed",
|
|
4
|
+
"description": "DingTalk channel adapter for Qwen Code",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"default": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsc --build",
|
|
19
|
+
"test": "vitest run",
|
|
20
|
+
"test:ci": "vitest run"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@qwen-code/channel-base": "0.21.0-nightly.20260729.0c0ca5fed",
|
|
24
|
+
"dingtalk-stream-sdk-nodejs": "^2.0.4"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"typescript": "^5.0.0"
|
|
28
|
+
}
|
|
29
|
+
}
|