volcengine-api-mcp 0.0.0-stage → 0.1.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.
Files changed (48) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +93 -2
  3. package/dist/index.d.ts +2 -0
  4. package/dist/index.js +44 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/lib/ark.d.ts +28 -0
  7. package/dist/lib/ark.js +105 -0
  8. package/dist/lib/ark.js.map +1 -0
  9. package/dist/lib/auc.d.ts +67 -0
  10. package/dist/lib/auc.js +160 -0
  11. package/dist/lib/auc.js.map +1 -0
  12. package/dist/lib/client.d.ts +18 -0
  13. package/dist/lib/client.js +117 -0
  14. package/dist/lib/client.js.map +1 -0
  15. package/dist/lib/index.d.ts +8 -0
  16. package/dist/lib/index.js +9 -0
  17. package/dist/lib/index.js.map +1 -0
  18. package/dist/lib/search.d.ts +41 -0
  19. package/dist/lib/search.js +160 -0
  20. package/dist/lib/search.js.map +1 -0
  21. package/dist/lib/signer.d.ts +17 -0
  22. package/dist/lib/signer.js +58 -0
  23. package/dist/lib/signer.js.map +1 -0
  24. package/dist/lib/tos.d.ts +29 -0
  25. package/dist/lib/tos.js +93 -0
  26. package/dist/lib/tos.js.map +1 -0
  27. package/dist/lib/tts.d.ts +59 -0
  28. package/dist/lib/tts.js +126 -0
  29. package/dist/lib/tts.js.map +1 -0
  30. package/dist/lib/types.d.ts +34 -0
  31. package/dist/lib/types.js +15 -0
  32. package/dist/lib/types.js.map +1 -0
  33. package/dist/tools/auc.d.ts +34 -0
  34. package/dist/tools/auc.js +76 -0
  35. package/dist/tools/auc.js.map +1 -0
  36. package/dist/tools/image.d.ts +37 -0
  37. package/dist/tools/image.js +58 -0
  38. package/dist/tools/image.js.map +1 -0
  39. package/dist/tools/search.d.ts +38 -0
  40. package/dist/tools/search.js +56 -0
  41. package/dist/tools/search.js.map +1 -0
  42. package/dist/tools/tos.d.ts +30 -0
  43. package/dist/tools/tos.js +38 -0
  44. package/dist/tools/tos.js.map +1 -0
  45. package/dist/tools/tts.d.ts +43 -0
  46. package/dist/tools/tts.js +80 -0
  47. package/dist/tools/tts.js.map +1 -0
  48. package/package.json +48 -3
@@ -0,0 +1,117 @@
1
+ import { signRequest } from './signer.js';
2
+ import { VolcengineApiError, } from './types.js';
3
+ /** 常见服务的默认 Host(不在此表中的服务回退为 <service>.<region>.volcengineapi.com) */
4
+ export const SERVICE_HOSTS = {
5
+ ecs: 'ecs.cn-north-1.volcengineapi.com',
6
+ vpc: 'vpc.cn-north-1.volcengineapi.com',
7
+ iam: 'iam.volcengineapi.com',
8
+ vod: 'vod.volcengineapi.com',
9
+ live: 'live.volcengineapi.com',
10
+ tls: 'tls-cn-beijing.volces.com',
11
+ ces: 'ces.cn-north-1.volcengineapi.com',
12
+ };
13
+ export class VolcengineClient {
14
+ opts;
15
+ constructor(opts) {
16
+ this.opts = opts;
17
+ }
18
+ resolveHost(service, host, region) {
19
+ if (host)
20
+ return host;
21
+ return SERVICE_HOSTS[service] ?? `${service}.${region ?? this.opts.region ?? 'cn-north-1'}.volcengineapi.com`;
22
+ }
23
+ async request(req) {
24
+ const method = req.method ?? 'GET';
25
+ const region = req.region ?? this.opts.region ?? 'cn-north-1';
26
+ const service = req.service;
27
+ const host = this.resolveHost(service, req.host, region);
28
+ const credentials = this.opts.credentials;
29
+ if (!credentials?.accessKeyId || !credentials?.secretAccessKey) {
30
+ throw new VolcengineApiError('缺少火山引擎 AK/SK,请通过环境变量 VOLCENGINE_ACCESS_KEY / VOLCENGINE_SECRET_KEY 配置', 0, 'MissingCredentials', undefined);
31
+ }
32
+ const bodyStr = method === 'POST' || method === 'PUT'
33
+ ? req.params
34
+ ? JSON.stringify(req.params)
35
+ : ''
36
+ : '';
37
+ const query = method === 'POST' || method === 'PUT'
38
+ ? { Action: req.action, Version: req.version }
39
+ : {
40
+ Action: req.action,
41
+ Version: req.version,
42
+ ...flattenParams(req.params ?? {}),
43
+ };
44
+ const headers = {
45
+ Host: host,
46
+ 'Content-Type': 'application/json',
47
+ ...(bodyStr ? { 'Content-Length': String(Buffer.byteLength(bodyStr)) } : {}),
48
+ ...(req.headers ?? {}),
49
+ };
50
+ const signed = signRequest({
51
+ method,
52
+ path: '/',
53
+ query,
54
+ headers,
55
+ body: bodyStr,
56
+ accessKeyId: credentials.accessKeyId,
57
+ secretAccessKey: credentials.secretAccessKey,
58
+ region,
59
+ service,
60
+ });
61
+ const url = `https://${host}/?${new URLSearchParams(query)}`;
62
+ const controller = new AbortController();
63
+ const timer = setTimeout(() => controller.abort(), this.opts.timeoutMs ?? 30_000);
64
+ let res;
65
+ try {
66
+ res = await fetch(url, {
67
+ method,
68
+ headers: { ...headers, ...signed },
69
+ body: bodyStr || undefined,
70
+ signal: controller.signal,
71
+ });
72
+ }
73
+ finally {
74
+ clearTimeout(timer);
75
+ }
76
+ const resHeaders = {};
77
+ res.headers.forEach((v, k) => {
78
+ resHeaders[k] = v;
79
+ });
80
+ const text = await res.text();
81
+ let parsed;
82
+ try {
83
+ parsed = text ? JSON.parse(text) : {};
84
+ }
85
+ catch {
86
+ parsed = text;
87
+ }
88
+ if (!res.ok) {
89
+ const errObj = (parsed ?? {});
90
+ const data = errObj.ResponseMetadata ?? errObj;
91
+ throw new VolcengineApiError(`${req.action} 调用失败: ${data?.Error?.Message ?? data?.Message ?? res.statusText}`, res.status, data?.Error?.Code ?? data?.Code, data?.RequestId, parsed);
92
+ }
93
+ return { status: res.status, headers: resHeaders, body: parsed };
94
+ }
95
+ }
96
+ /**
97
+ * 把嵌套参数展开成火山 API 需要的扁平 query(如 List.1.Name=a&List.1.Id=b)。
98
+ */
99
+ export function flattenParams(params, prefix = '') {
100
+ const result = {};
101
+ for (const [key, value] of Object.entries(params)) {
102
+ const fullKey = prefix ? `${prefix}.${key}` : key;
103
+ if (value === null || value === undefined)
104
+ continue;
105
+ if (Array.isArray(value)) {
106
+ Object.assign(result, flattenParams(Object.fromEntries(value.map((v, i) => [String(i + 1), v])), fullKey));
107
+ }
108
+ else if (typeof value === 'object') {
109
+ Object.assign(result, flattenParams(value, fullKey));
110
+ }
111
+ else {
112
+ result[fullKey] = String(value);
113
+ }
114
+ }
115
+ return result;
116
+ }
117
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/lib/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export * from './types.js';
2
+ export * from './signer.js';
3
+ export * from './client.js';
4
+ export * from './ark.js';
5
+ export * from './tos.js';
6
+ export * from './auc.js';
7
+ export * from './tts.js';
8
+ export * from './search.js';
@@ -0,0 +1,9 @@
1
+ export * from './types.js';
2
+ export * from './signer.js';
3
+ export * from './client.js';
4
+ export * from './ark.js';
5
+ export * from './tos.js';
6
+ export * from './auc.js';
7
+ export * from './tts.js';
8
+ export * from './search.js';
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/lib/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * 火山引擎联网搜索(豆包搜索 / WebSearch)。
3
+ * 移植自 mcp-server/server/mcp_server_askecho_search_infinity(Python)。
4
+ * 两条调用路径:
5
+ * - API Key 模式: POST https://open.feedcoopapi.com/search_api/web_search(Bearer)
6
+ * - AK/SK 模式: POST https://mercury.volcengineapi.com/?Action=WebSearch&Version=2025-01-01(V4 签名)
7
+ */
8
+ import type { VolcengineCredentials } from './types.js';
9
+ /** API Key 模式网关 */
10
+ export declare const SEARCH_API_HOST = "open.feedcoopapi.com";
11
+ /** AK/SK 模式(火山 mercury 网关) */
12
+ export declare const SEARCH_V4_HOST = "mercury.volcengineapi.com";
13
+ export declare const SEARCH_V4_SERVICE = "volc_torchlight_api";
14
+ export declare const SEARCH_V4_VERSION = "2025-01-01";
15
+ export declare const SEARCH_V4_REGION = "cn-beijing";
16
+ export interface WebSearchParams {
17
+ query: string;
18
+ count?: number;
19
+ searchType?: 'web' | 'image';
20
+ /** OneDay/OneWeek/OneMonth/OneYear 或 YYYY-MM-DD..YYYY-MM-DD */
21
+ timeRange?: string;
22
+ /** 权威等级过滤,0 默认 / 1 非常权威 */
23
+ authLevel?: 0 | 1;
24
+ needContent?: boolean;
25
+ needUrl?: boolean;
26
+ /** 仅搜索指定站点,最多 20 个 */
27
+ sites?: string[];
28
+ /** 排除指定站点,最多 5 个 */
29
+ blockHosts?: string[];
30
+ /** 行业搜索 finance / game / gov */
31
+ industry?: 'finance' | 'game' | 'gov';
32
+ queryRewrite?: boolean;
33
+ /** 网页正文格式 text / markdown */
34
+ contentFormats?: 'text' | 'markdown';
35
+ }
36
+ /** 参数校验并组装请求 payload(结构与官方 WebSearch 接口一致,大写字段名) */
37
+ export declare function buildWebSearchPayload(params: WebSearchParams): Record<string, unknown>;
38
+ /** API Key 模式:feedcoopapi 网关,Bearer 鉴权 */
39
+ export declare function webSearchByApiKey(apiKey: string, payload: Record<string, unknown>): Promise<unknown>;
40
+ /** AK/SK 模式:mercury 网关,V4 签名(Action=WebSearch) */
41
+ export declare function webSearchByAkSk(credentials: VolcengineCredentials, payload: Record<string, unknown>): Promise<unknown>;
@@ -0,0 +1,160 @@
1
+ /**
2
+ * 火山引擎联网搜索(豆包搜索 / WebSearch)。
3
+ * 移植自 mcp-server/server/mcp_server_askecho_search_infinity(Python)。
4
+ * 两条调用路径:
5
+ * - API Key 模式: POST https://open.feedcoopapi.com/search_api/web_search(Bearer)
6
+ * - AK/SK 模式: POST https://mercury.volcengineapi.com/?Action=WebSearch&Version=2025-01-01(V4 签名)
7
+ */
8
+ import { VolcengineClient } from './client.js';
9
+ /** API Key 模式网关 */
10
+ export const SEARCH_API_HOST = 'open.feedcoopapi.com';
11
+ /** AK/SK 模式(火山 mercury 网关) */
12
+ export const SEARCH_V4_HOST = 'mercury.volcengineapi.com';
13
+ export const SEARCH_V4_SERVICE = 'volc_torchlight_api';
14
+ export const SEARCH_V4_VERSION = '2025-01-01';
15
+ export const SEARCH_V4_REGION = 'cn-beijing';
16
+ const TIME_RANGE_SHORTCUTS = new Set(['OneDay', 'OneWeek', 'OneMonth', 'OneYear']);
17
+ const SUPPORTED_INDUSTRIES = new Set(['finance', 'game', 'gov']);
18
+ const SUPPORTED_CONTENT_FORMATS = new Set(['text', 'markdown']);
19
+ function validateTimeRange(timeRange) {
20
+ if (!timeRange)
21
+ return undefined;
22
+ if (TIME_RANGE_SHORTCUTS.has(timeRange))
23
+ return timeRange;
24
+ const match = /^(\d{4}-\d{2}-\d{2})\.\.(\d{4}-\d{2}-\d{2})$/.exec(timeRange);
25
+ if (!match) {
26
+ throw new Error('timeRange 需为 OneDay/OneWeek/OneMonth/OneYear,或日期区间 YYYY-MM-DD..YYYY-MM-DD');
27
+ }
28
+ const [startText, endText] = match.slice(1);
29
+ const start = new Date(startText);
30
+ const end = new Date(endText);
31
+ if (Number.isNaN(start.getTime()) || Number.isNaN(end.getTime())) {
32
+ throw new Error('timeRange 中的日期需为有效的 YYYY-MM-DD');
33
+ }
34
+ if (start > end)
35
+ throw new Error('timeRange 的开始日期不能晚于结束日期');
36
+ return timeRange;
37
+ }
38
+ function validateHostList(value, fieldName, maxCount) {
39
+ if (!value || value.length === 0)
40
+ return undefined;
41
+ const items = value.map((item) => item.trim()).filter((item) => item.length > 0);
42
+ if (items.length === 0)
43
+ throw new Error(`${fieldName} 需为非空域名列表`);
44
+ if (items.length > maxCount)
45
+ throw new Error(`${fieldName} 最多支持 ${maxCount} 个域名`);
46
+ return items.join('|');
47
+ }
48
+ /** 参数校验并组装请求 payload(结构与官方 WebSearch 接口一致,大写字段名) */
49
+ export function buildWebSearchPayload(params) {
50
+ const query = (params.query ?? '').trim();
51
+ if (!query)
52
+ throw new Error('query 不能为空');
53
+ if (query.length > 100)
54
+ throw new Error('query 长度需为 1~100 个字符');
55
+ const searchType = (params.searchType ?? 'web').toLowerCase();
56
+ if (searchType !== 'web' && searchType !== 'image') {
57
+ throw new Error('searchType 仅支持 web 或 image');
58
+ }
59
+ const count = params.count ?? (searchType === 'web' ? 10 : 5);
60
+ if (count < 1)
61
+ throw new Error('count 需大于等于 1');
62
+ const maxCount = searchType === 'web' ? 50 : 5;
63
+ if (count > maxCount)
64
+ throw new Error(`${searchType} 类型最多返回 ${maxCount} 条`);
65
+ const authLevel = params.authLevel ?? 0;
66
+ if (authLevel !== 0 && authLevel !== 1)
67
+ throw new Error('authLevel 仅支持 0 或 1');
68
+ const industry = params.industry?.toLowerCase();
69
+ if (industry && !SUPPORTED_INDUSTRIES.has(industry)) {
70
+ throw new Error('industry 仅支持 finance、game 或 gov');
71
+ }
72
+ const contentFormats = params.contentFormats?.toLowerCase();
73
+ if (contentFormats && !SUPPORTED_CONTENT_FORMATS.has(contentFormats)) {
74
+ throw new Error('contentFormats 仅支持 text 或 markdown');
75
+ }
76
+ const queryControl = params.queryRewrite !== undefined ? { QueryRewrite: params.queryRewrite } : undefined;
77
+ if (searchType === 'image') {
78
+ const webOnly = [
79
+ ['timeRange', params.timeRange],
80
+ ['authLevel', authLevel || undefined],
81
+ ['needContent', params.needContent],
82
+ ['needUrl', params.needUrl],
83
+ ['sites', params.sites?.length ? params.sites : undefined],
84
+ ['blockHosts', params.blockHosts?.length ? params.blockHosts : undefined],
85
+ ['industry', industry],
86
+ ['contentFormats', contentFormats],
87
+ ];
88
+ const unsupported = webOnly.filter(([, v]) => v !== undefined).map(([k]) => k);
89
+ if (unsupported.length > 0) {
90
+ throw new Error(`${unsupported.join(', ')} 仅支持 web 搜索`);
91
+ }
92
+ const payload = { Query: query, SearchType: searchType, Count: count };
93
+ if (queryControl)
94
+ payload.QueryControl = queryControl;
95
+ return payload;
96
+ }
97
+ const filter = {};
98
+ if (authLevel > 0)
99
+ filter.AuthInfoLevel = authLevel;
100
+ if (params.needContent !== undefined)
101
+ filter.NeedContent = params.needContent;
102
+ if (params.needUrl !== undefined)
103
+ filter.NeedUrl = params.needUrl;
104
+ const sites = validateHostList(params.sites, 'sites', 20);
105
+ if (sites)
106
+ filter.Sites = sites;
107
+ const blockHosts = validateHostList(params.blockHosts, 'blockHosts', 5);
108
+ if (blockHosts)
109
+ filter.BlockHosts = blockHosts;
110
+ if (industry)
111
+ filter.Industry = industry;
112
+ const payload = { Query: query, SearchType: searchType, Count: count };
113
+ if (Object.keys(filter).length > 0)
114
+ payload.Filter = filter;
115
+ const timeRange = validateTimeRange(params.timeRange);
116
+ if (timeRange)
117
+ payload.TimeRange = timeRange;
118
+ if (queryControl)
119
+ payload.QueryControl = queryControl;
120
+ if (contentFormats)
121
+ payload.ContentFormats = contentFormats;
122
+ return payload;
123
+ }
124
+ const REQUEST_TIMEOUT_MS = 30_000;
125
+ /** API Key 模式:feedcoopapi 网关,Bearer 鉴权 */
126
+ export async function webSearchByApiKey(apiKey, payload) {
127
+ const res = await fetch(`https://${SEARCH_API_HOST}/search_api/web_search`, {
128
+ method: 'POST',
129
+ headers: {
130
+ 'Content-Type': 'application/json',
131
+ Authorization: `Bearer ${apiKey}`,
132
+ 'X-Traffic-Tag': 'volcengine-api-mcp_web_search',
133
+ },
134
+ body: JSON.stringify(payload),
135
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
136
+ });
137
+ const text = await res.text();
138
+ if (!res.ok)
139
+ throw new Error(`搜索接口返回 ${res.status}:${text.slice(0, 300)}`);
140
+ return JSON.parse(text);
141
+ }
142
+ /** AK/SK 模式:mercury 网关,V4 签名(Action=WebSearch) */
143
+ export async function webSearchByAkSk(credentials, payload) {
144
+ const client = new VolcengineClient({
145
+ credentials,
146
+ region: SEARCH_V4_REGION,
147
+ timeoutMs: REQUEST_TIMEOUT_MS,
148
+ });
149
+ const res = await client.request({
150
+ method: 'POST',
151
+ service: SEARCH_V4_SERVICE,
152
+ action: 'WebSearch',
153
+ version: SEARCH_V4_VERSION,
154
+ host: SEARCH_V4_HOST,
155
+ params: payload,
156
+ headers: { 'X-Traffic-Tag': 'volcengine-api-mcp_web_search' },
157
+ });
158
+ return res.body;
159
+ }
160
+ //# sourceMappingURL=search.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.js","sourceRoot":"","sources":["../../src/lib/search.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ export interface SignOptions {
2
+ method: string;
3
+ path: string;
4
+ query: Record<string, string>;
5
+ headers: Record<string, string>;
6
+ /** 已序列化的 body(GET 时为空字符串) */
7
+ body: string;
8
+ accessKeyId: string;
9
+ secretAccessKey: string;
10
+ region: string;
11
+ service: string;
12
+ }
13
+ /**
14
+ * 火山引擎 Signature V4 签名。
15
+ * 返回需要附加到请求的 header(Authorization / X-Date / X-Content-Sha256)。
16
+ */
17
+ export declare function signRequest(opts: SignOptions): Record<string, string>;
@@ -0,0 +1,58 @@
1
+ import { createHash, createHmac } from 'node:crypto';
2
+ const SHA256_HEX = (data) => createHash('sha256').update(data).digest('hex');
3
+ const HMAC_SHA256_HEX = (key, data) => createHmac('sha256', key).update(data).digest('hex');
4
+ const HMAC_SHA256_RAW = (key, data) => createHmac('sha256', key).update(data).digest();
5
+ /** RFC3986 编码(火山签名要求,空格编码为 %20 而非 +) */
6
+ function uriEncode(value) {
7
+ return encodeURIComponent(value).replace(/[!'()*]/g, (ch) => `%${ch.charCodeAt(0).toString(16).toUpperCase()}`);
8
+ }
9
+ /**
10
+ * 火山引擎 Signature V4 签名。
11
+ * 返回需要附加到请求的 header(Authorization / X-Date / X-Content-Sha256)。
12
+ */
13
+ export function signRequest(opts) {
14
+ const xDate = opts.headers['X-Date'] ?? new Date().toISOString().replace(/\.\d{3}Z$/, 'Z');
15
+ const date = xDate.slice(0, 10);
16
+ const payloadHash = SHA256_HEX(opts.body);
17
+ const headers = {
18
+ ...opts.headers,
19
+ 'X-Date': xDate,
20
+ 'X-Content-Sha256': payloadHash,
21
+ };
22
+ // 1. Canonical Request
23
+ const sortedHeaders = Object.keys(headers)
24
+ .map((k) => [k.toLowerCase(), String(headers[k]).trim()])
25
+ .sort(([a], [b]) => (a < b ? -1 : 1));
26
+ const canonicalHeaders = sortedHeaders.map(([k, v]) => `${k}:${v}\n`).join('');
27
+ const signedHeaders = sortedHeaders.map(([k]) => k).join(';');
28
+ const canonicalQuery = Object.entries(opts.query)
29
+ .map(([k, v]) => [uriEncode(k), uriEncode(v)])
30
+ .sort(([a], [b]) => (a < b ? -1 : 1))
31
+ .map(([k, v]) => `${k}=${v}`)
32
+ .join('&');
33
+ const canonicalRequest = [
34
+ opts.method.toUpperCase(),
35
+ opts.path,
36
+ canonicalQuery,
37
+ canonicalHeaders,
38
+ signedHeaders,
39
+ payloadHash,
40
+ ].join('\n');
41
+ // 2. String to Sign
42
+ const credentialScope = `${date}/${opts.region}/${opts.service}/request`;
43
+ const stringToSign = [
44
+ 'HMAC-SHA256',
45
+ xDate,
46
+ credentialScope,
47
+ SHA256_HEX(canonicalRequest),
48
+ ].join('\n');
49
+ // 3. Signature
50
+ let signingKey = HMAC_SHA256_RAW(opts.secretAccessKey, date);
51
+ for (const part of [opts.region, opts.service, 'request']) {
52
+ signingKey = HMAC_SHA256_RAW(signingKey, part);
53
+ }
54
+ const signature = HMAC_SHA256_HEX(signingKey, stringToSign);
55
+ const authorization = `HMAC-SHA256 Credential=${opts.accessKeyId}/${credentialScope}, SignedHeaders=${signedHeaders}, Signature=${signature}`;
56
+ return { 'X-Date': xDate, 'X-Content-Sha256': payloadHash, Authorization: authorization };
57
+ }
58
+ //# sourceMappingURL=signer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signer.js","sourceRoot":"","sources":["../../src/lib/signer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ /** 按扩展名映射 Content-Type(覆盖常见音视频/图片/文档类型) */
2
+ export declare const CONTENT_TYPES: Record<string, string>;
3
+ export declare const TOS_ENV_NAMES: Record<string, string>;
4
+ export interface TosOptions {
5
+ accessKeyId: string;
6
+ accessKeySecret: string;
7
+ bucket: string;
8
+ region: string;
9
+ endpoint?: string;
10
+ }
11
+ export interface TosResolveInput extends Partial<TosOptions> {
12
+ prefix?: string;
13
+ }
14
+ /** 读取 VOLC_TOS_* 环境变量(可被显式参数覆盖);配置不全时抛出缺失变量清单 */
15
+ export declare function resolveTosOptions(overrides?: TosResolveInput): TosOptions & {
16
+ prefix: string;
17
+ };
18
+ /** Object key:<prefix>/<date>/<basename>(空格转连字符) */
19
+ export declare function buildObjectKey(prefix: string, date: string, filePath: string): string;
20
+ /**
21
+ * 上传本地文件到 TOS 并返回预签名下载 URL。
22
+ * expires 默认 24h(86400s);contentType 不传时按扩展名推导。
23
+ */
24
+ export declare function uploadToTos(opts: TosOptions, filePath: string, key: string, expires?: number, contentType?: string): Promise<{
25
+ url: string;
26
+ key: string;
27
+ bytes: number;
28
+ bucket: string;
29
+ }>;
@@ -0,0 +1,93 @@
1
+ /**
2
+ * 火山引擎 TOS 上传:putObject + 预签名下载 URL(bucket 保持私有)。
3
+ * 凭据来自 VOLC_TOS_* 环境变量。
4
+ */
5
+ import fs from 'node:fs';
6
+ import path from 'node:path';
7
+ import { TosClient } from '@volcengine/tos-sdk';
8
+ /** 按扩展名映射 Content-Type(覆盖常见音视频/图片/文档类型) */
9
+ export const CONTENT_TYPES = {
10
+ wav: 'audio/wav',
11
+ mp3: 'audio/mpeg',
12
+ ogg: 'audio/ogg',
13
+ spx: 'audio/ogg',
14
+ amr: 'audio/amr',
15
+ aac: 'audio/aac',
16
+ m4a: 'audio/mp4',
17
+ mp4: 'video/mp4',
18
+ mov: 'video/quicktime',
19
+ webm: 'video/webm',
20
+ png: 'image/png',
21
+ jpg: 'image/jpeg',
22
+ jpeg: 'image/jpeg',
23
+ gif: 'image/gif',
24
+ webp: 'image/webp',
25
+ svg: 'image/svg+xml',
26
+ pdf: 'application/pdf',
27
+ txt: 'text/plain',
28
+ json: 'application/json',
29
+ zip: 'application/zip',
30
+ };
31
+ export const TOS_ENV_NAMES = {
32
+ accessKeyId: 'VOLC_TOS_ACCESS_KEY',
33
+ accessKeySecret: 'VOLC_TOS_SECRET_KEY',
34
+ bucket: 'VOLC_TOS_BUCKET',
35
+ region: 'VOLC_TOS_REGION',
36
+ endpoint: 'VOLC_TOS_ENDPOINT',
37
+ };
38
+ /** 读取 VOLC_TOS_* 环境变量(可被显式参数覆盖);配置不全时抛出缺失变量清单 */
39
+ export function resolveTosOptions(overrides) {
40
+ const resolved = {
41
+ accessKeyId: overrides?.accessKeyId ?? process.env.VOLC_TOS_ACCESS_KEY,
42
+ accessKeySecret: overrides?.accessKeySecret ?? process.env.VOLC_TOS_SECRET_KEY,
43
+ bucket: overrides?.bucket ?? process.env.VOLC_TOS_BUCKET,
44
+ region: overrides?.region ?? process.env.VOLC_TOS_REGION,
45
+ endpoint: overrides?.endpoint ?? process.env.VOLC_TOS_ENDPOINT,
46
+ };
47
+ const missing = ['accessKeyId', 'accessKeySecret', 'bucket', 'region']
48
+ .filter((k) => !resolved[k])
49
+ .map((k) => TOS_ENV_NAMES[k]);
50
+ if (missing.length > 0) {
51
+ throw new Error(`缺少 TOS 配置,请设置环境变量:${missing.join(', ')}`);
52
+ }
53
+ return {
54
+ accessKeyId: resolved.accessKeyId,
55
+ accessKeySecret: resolved.accessKeySecret,
56
+ bucket: resolved.bucket,
57
+ region: resolved.region,
58
+ endpoint: resolved.endpoint,
59
+ prefix: overrides?.prefix ?? process.env.VOLC_TOS_PREFIX ?? 'volcengine-api-mcp',
60
+ };
61
+ }
62
+ /** Object key:<prefix>/<date>/<basename>(空格转连字符) */
63
+ export function buildObjectKey(prefix, date, filePath) {
64
+ const base = path.basename(filePath).replace(/\s+/g, '-');
65
+ return `${prefix}/${date}/${base}`;
66
+ }
67
+ /**
68
+ * 上传本地文件到 TOS 并返回预签名下载 URL。
69
+ * expires 默认 24h(86400s);contentType 不传时按扩展名推导。
70
+ */
71
+ export async function uploadToTos(opts, filePath, key, expires = 86_400, contentType) {
72
+ if (!fs.existsSync(filePath)) {
73
+ throw new Error(`本地文件不存在:${filePath}`);
74
+ }
75
+ const size = fs.statSync(filePath).size;
76
+ const client = new TosClient({
77
+ accessKeyId: opts.accessKeyId,
78
+ accessKeySecret: opts.accessKeySecret,
79
+ region: opts.region,
80
+ ...(opts.endpoint ? { endpoint: opts.endpoint } : {}),
81
+ });
82
+ const ext = path.extname(filePath).replace(/^\./, '').toLowerCase();
83
+ await client.putObject({
84
+ bucket: opts.bucket,
85
+ key,
86
+ body: fs.createReadStream(filePath),
87
+ contentLength: size,
88
+ contentType: contentType ?? CONTENT_TYPES[ext] ?? 'application/octet-stream',
89
+ });
90
+ const url = client.getPreSignedUrl({ bucket: opts.bucket, key, method: 'GET', expires });
91
+ return { url, key, bytes: size, bucket: opts.bucket };
92
+ }
93
+ //# sourceMappingURL=tos.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tos.js","sourceRoot":"","sources":["../../src/lib/tos.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAEhD,2CAA2C;AAC3C,MAAM,CAAC,MAAM,aAAa,GAA2B;IACnD,GAAG,EAAE,WAAW;IAChB,GAAG,EAAE,YAAY;IACjB,GAAG,EAAE,WAAW;IAChB,GAAG,EAAE,WAAW;IAChB,GAAG,EAAE,WAAW;IAChB,GAAG,EAAE,WAAW;IAChB,GAAG,EAAE,WAAW;IAChB,GAAG,EAAE,WAAW;IAChB,GAAG,EAAE,iBAAiB;IACtB,IAAI,EAAE,YAAY;IAClB,GAAG,EAAE,WAAW;IAChB,GAAG,EAAE,YAAY;IACjB,IAAI,EAAE,YAAY;IAClB,GAAG,EAAE,WAAW;IAChB,IAAI,EAAE,YAAY;IAClB,GAAG,EAAE,eAAe;IACpB,GAAG,EAAE,iBAAiB;IACtB,GAAG,EAAE,YAAY;IACjB,IAAI,EAAE,kBAAkB;IACxB,GAAG,EAAE,iBAAiB;CACvB,CAAC;AAEF,MAAM,CAAC,MAAM,aAAa,GAA2B;IACnD,WAAW,EAAE,qBAAqB;IAClC,eAAe,EAAE,qBAAqB;IACtC,MAAM,EAAE,iBAAiB;IACzB,MAAM,EAAE,iBAAiB;IACzB,QAAQ,EAAE,mBAAmB;CAC9B,CAAC;AAcF,iDAAiD;AACjD,MAAM,UAAU,iBAAiB,CAAC,SAA2B;IAC3D,MAAM,QAAQ,GAAG;QACf,WAAW,EAAE,SAAS,EAAE,WAAW,IAAI,OAAO,CAAC,GAAG,CAAC,mBAAmB;QACtE,eAAe,EAAE,SAAS,EAAE,eAAe,IAAI,OAAO,CAAC,GAAG,CAAC,mBAAmB;QAC9E,MAAM,EAAE,SAAS,EAAE,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,eAAe;QACxD,MAAM,EAAE,SAAS,EAAE,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,eAAe;QACxD,QAAQ,EAAE,SAAS,EAAE,QAAQ,IAAI,OAAO,CAAC,GAAG,CAAC,iBAAiB;KAC/D,CAAC;IACF,MAAM,OAAO,GAAI,CAAC,aAAa,EAAE,iBAAiB,EAAE,QAAQ,EAAE,QAAQ,CAAW;SAC9E,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;SAC3B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;IAChC,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,qBAAqB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC7D,CAAC;IACD,OAAO;QACL,WAAW,EAAE,QAAQ,CAAC,WAAY;QAClC,eAAe,EAAE,QAAQ,CAAC,eAAgB;QAC1C,MAAM,EAAE,QAAQ,CAAC,MAAO;QACxB,MAAM,EAAE,QAAQ,CAAC,MAAO;QACxB,QAAQ,EAAE,QAAQ,CAAC,QAAQ;QAC3B,MAAM,EAAE,SAAS,EAAE,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,oBAAoB;KACjF,CAAC;AACJ,CAAC;AAED,oDAAoD;AACpD,MAAM,UAAU,cAAc,CAAC,MAAc,EAAE,IAAY,EAAE,QAAgB;IAC3E,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC1D,OAAO,GAAG,MAAM,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC;AACrC,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,IAAgB,EAChB,QAAgB,EAChB,GAAW,EACX,OAAO,GAAG,MAAM,EAChB,WAAoB;IAEpB,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,WAAW,QAAQ,EAAE,CAAC,CAAC;IACzC,CAAC;IACD,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC;IACxC,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;QAC3B,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,eAAe,EAAE,IAAI,CAAC,eAAe;QACrC,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACtD,CAAC,CAAC;IACH,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;IACpE,MAAM,MAAM,CAAC,SAAS,CAAC;QACrB,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,GAAG;QACH,IAAI,EAAE,EAAE,CAAC,gBAAgB,CAAC,QAAQ,CAAC;QACnC,aAAa,EAAE,IAAI;QACnB,WAAW,EAAE,WAAW,IAAI,aAAa,CAAC,GAAG,CAAC,IAAI,0BAA0B;KAC7E,CAAC,CAAC;IACH,MAAM,GAAG,GAAW,MAAM,CAAC,eAAe,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;IACjG,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;AACxD,CAAC"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * 豆包语音合成大模型 - 单向流式 HTTP 接口(HTTP Chunked)。
3
+ * 文档: https://www.volcengine.com/docs/6561/1327536 (单向流式语音合成 HTTP)
4
+ * - Agent Plan 网关: POST https://openspeech.bytedance.com/api/v3/plan/tts/unidirectional(ark- 密钥)
5
+ * - 语音控制台: POST https://openspeech.bytedance.com/api/v3/tts/unidirectional(X-Api-Key)
6
+ * 响应为流式 JSON 分片,每片 {code, message, data: base64音频, sentence?, usage?},拼接 data 直到流结束。
7
+ */
8
+ /** 语音控制台密钥使用的官方端点(Agent Plan 之外的接入方式,暂未暴露为工具参数) */
9
+ export declare const CONSOLE_TTS_URL = "https://openspeech.bytedance.com/api/v3/tts/unidirectional";
10
+ export declare const TTS_DEFAULT_SPEAKER = "zh_female_vv_uranus_bigtts";
11
+ export declare const TTS_DEFAULT_RESOURCE_ID = "seed-tts-2.0";
12
+ export declare const TTS_DEFAULT_SAMPLE_RATE = 24000;
13
+ export type TtsAudioFormat = 'mp3' | 'pcm' | 'ogg_opus' | 'wav';
14
+ export interface TtsOptions {
15
+ apiKey: string;
16
+ text: string;
17
+ speaker?: string;
18
+ /** 模型版本资源:seed-tts-2.0 / seed-icl-2.0(复刻音色) */
19
+ resourceId?: string;
20
+ /** 复刻音色时需指定,如 seed-tts-2.0-standard */
21
+ model?: string;
22
+ format?: TtsAudioFormat;
23
+ sampleRate?: number;
24
+ /** 语速 [-50, 100],100 = 2.0 倍速 */
25
+ speechRate?: number;
26
+ /** 音量 [-50, 100],100 = 2.0 倍音量 */
27
+ loudnessRate?: number;
28
+ enableSubtitle?: boolean;
29
+ /** 显式朗读语种,如 zh-cn / en / ja */
30
+ explicitLanguage?: string;
31
+ /** 覆盖默认端点 */
32
+ endpoint?: string;
33
+ timeoutMs?: number;
34
+ }
35
+ export interface TtsResult {
36
+ audio: Buffer;
37
+ format: TtsAudioFormat;
38
+ sampleRate: number;
39
+ /** 最后一个分片的句级信息(含字幕时间戳,enableSubtitle 开启时) */
40
+ sentence?: {
41
+ text: string;
42
+ words?: Array<{
43
+ word: string;
44
+ startTime: number;
45
+ endTime: number;
46
+ confidence?: number;
47
+ }>;
48
+ };
49
+ usage?: {
50
+ text_words?: number;
51
+ };
52
+ }
53
+ /** 从流式 buffer 中提取顶层 JSON 对象(兼容换行分隔与直接拼接两种分片方式) */
54
+ export declare function extractJsonObjects(buffer: string): {
55
+ objects: unknown[];
56
+ rest: string;
57
+ };
58
+ /** 提交单向流式合成并收集完整音频。 */
59
+ export declare function unidirectionalTts(opts: TtsOptions): Promise<TtsResult>;