@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.
- package/package.json +4 -1
- package/src/asr/README.md +100 -100
- package/src/asr/errors.ts +61 -61
- package/src/asr/index.ts +11 -11
- package/src/asr/tencent-flash.ts +165 -165
- package/src/cli/china-setup.ts +783 -0
- package/src/cli/index.ts +1 -0
- package/src/cron/index.ts +115 -115
- 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/http/client.ts +141 -141
- package/src/http/index.ts +2 -2
- package/src/http/retry.ts +110 -110
- package/src/index.ts +10 -9
- package/src/logger/index.ts +1 -1
- package/src/logger/logger.ts +51 -51
- package/src/media/index.ts +65 -65
- package/src/media/media-io.ts +732 -732
- package/src/media/media-parser.ts +722 -722
- package/src/policy/dm-policy.ts +82 -82
- package/src/policy/group-policy.ts +93 -93
- package/src/policy/index.ts +2 -2
- package/src/types/common.ts +24 -24
- package/tsconfig.json +8 -8
- package/vitest.config.ts +8 -8
package/package.json
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw-china/shared",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.32",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./src/index.ts"
|
|
7
7
|
},
|
|
8
|
+
"dependencies": {
|
|
9
|
+
"@clack/prompts": "^1.0.0"
|
|
10
|
+
},
|
|
8
11
|
"scripts": {
|
|
9
12
|
"build": "tsc",
|
|
10
13
|
"dev": "tsc --watch",
|
package/src/asr/README.md
CHANGED
|
@@ -1,100 +1,100 @@
|
|
|
1
|
-
# ASR 接入说明(供各插件复用)
|
|
2
|
-
|
|
3
|
-
本文说明如何在任意渠道插件中复用 `@openclaw-china/shared` 的语音转文本能力。
|
|
4
|
-
|
|
5
|
-
当前已提供:
|
|
6
|
-
- 腾讯云录音文件识别极速版(`asr/flash/v1`)
|
|
7
|
-
- 统一错误类型(便于插件侧日志和降级)
|
|
8
|
-
|
|
9
|
-
## 职责边界
|
|
10
|
-
|
|
11
|
-
`shared` 负责:
|
|
12
|
-
- 调用 ASR 服务(签名、请求、响应解析、超时)
|
|
13
|
-
- 抛出结构化错误(`ASRError` 及子类)
|
|
14
|
-
|
|
15
|
-
单个插件负责:
|
|
16
|
-
- 平台事件解析(如何识别语音消息)
|
|
17
|
-
- 媒体下载(URL/鉴权/大小限制)
|
|
18
|
-
- 业务策略(失败是否降级、回复文案、是否中断本次处理)
|
|
19
|
-
- 密钥配置(建议保留在插件配置内)
|
|
20
|
-
|
|
21
|
-
## 可直接使用的导出
|
|
22
|
-
|
|
23
|
-
```ts
|
|
24
|
-
import {
|
|
25
|
-
transcribeTencentFlash,
|
|
26
|
-
ASRError,
|
|
27
|
-
ASRTimeoutError,
|
|
28
|
-
ASRAuthError,
|
|
29
|
-
ASRRequestError,
|
|
30
|
-
ASRResponseParseError,
|
|
31
|
-
ASRServiceError,
|
|
32
|
-
ASREmptyResultError,
|
|
33
|
-
} from "@openclaw-china/shared";
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
## 插件接入步骤
|
|
37
|
-
|
|
38
|
-
1. 在插件配置中增加 ASR 字段(示例)
|
|
39
|
-
```ts
|
|
40
|
-
asr: {
|
|
41
|
-
enabled?: boolean;
|
|
42
|
-
appId?: string;
|
|
43
|
-
secretId?: string;
|
|
44
|
-
secretKey?: string;
|
|
45
|
-
}
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
2. 在入站消息里识别语音附件,并下载到 `Buffer`
|
|
49
|
-
- 推荐复用 `shared/media` 的下载能力:
|
|
50
|
-
```ts
|
|
51
|
-
import { fetchMediaFromUrl } from "@openclaw-china/shared";
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
3. 调用转写
|
|
55
|
-
```ts
|
|
56
|
-
const transcript = await transcribeTencentFlash({
|
|
57
|
-
audio: media.buffer,
|
|
58
|
-
config: {
|
|
59
|
-
appId: asr.appId,
|
|
60
|
-
secretId: asr.secretId,
|
|
61
|
-
secretKey: asr.secretKey,
|
|
62
|
-
// 可选,默认如下:
|
|
63
|
-
// engineType: "16k_zh",
|
|
64
|
-
// voiceFormat: "silk",
|
|
65
|
-
// timeoutMs: 30000,
|
|
66
|
-
},
|
|
67
|
-
});
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
4. 按错误类型做日志与降级
|
|
71
|
-
```ts
|
|
72
|
-
try {
|
|
73
|
-
// transcribeTencentFlash(...)
|
|
74
|
-
} catch (err) {
|
|
75
|
-
if (err instanceof ASRError) {
|
|
76
|
-
logger.warn(
|
|
77
|
-
`asr failed kind=${err.kind} provider=${err.provider} retryable=${err.retryable} msg=${err.message}`
|
|
78
|
-
);
|
|
79
|
-
} else {
|
|
80
|
-
logger.warn(`asr failed: ${String(err)}`);
|
|
81
|
-
}
|
|
82
|
-
// 在这里决定是否回退文本、提示用户、或继续其他链路
|
|
83
|
-
}
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
## 错误类型语义
|
|
87
|
-
|
|
88
|
-
- `ASRTimeoutError`:请求超时,通常可重试
|
|
89
|
-
- `ASRAuthError`:鉴权失败(密钥错误/权限问题),通常不可重试
|
|
90
|
-
- `ASRRequestError`:请求失败(网络或 HTTP 失败)
|
|
91
|
-
- `ASRResponseParseError`:服务返回非 JSON 或格式异常
|
|
92
|
-
- `ASRServiceError`:服务端返回业务错误码(`code != 0`)
|
|
93
|
-
- `ASREmptyResultError`:识别成功但无文本
|
|
94
|
-
|
|
95
|
-
## 最佳实践
|
|
96
|
-
|
|
97
|
-
- 不要把密钥硬编码到仓库;仅通过配置注入。
|
|
98
|
-
- 给下载和 ASR 都设置超时与大小上限。
|
|
99
|
-
- 将“ASR 失败文案”放在插件侧,不放到 `shared`。
|
|
100
|
-
- 先记录结构化日志,再决定业务降级策略。
|
|
1
|
+
# ASR 接入说明(供各插件复用)
|
|
2
|
+
|
|
3
|
+
本文说明如何在任意渠道插件中复用 `@openclaw-china/shared` 的语音转文本能力。
|
|
4
|
+
|
|
5
|
+
当前已提供:
|
|
6
|
+
- 腾讯云录音文件识别极速版(`asr/flash/v1`)
|
|
7
|
+
- 统一错误类型(便于插件侧日志和降级)
|
|
8
|
+
|
|
9
|
+
## 职责边界
|
|
10
|
+
|
|
11
|
+
`shared` 负责:
|
|
12
|
+
- 调用 ASR 服务(签名、请求、响应解析、超时)
|
|
13
|
+
- 抛出结构化错误(`ASRError` 及子类)
|
|
14
|
+
|
|
15
|
+
单个插件负责:
|
|
16
|
+
- 平台事件解析(如何识别语音消息)
|
|
17
|
+
- 媒体下载(URL/鉴权/大小限制)
|
|
18
|
+
- 业务策略(失败是否降级、回复文案、是否中断本次处理)
|
|
19
|
+
- 密钥配置(建议保留在插件配置内)
|
|
20
|
+
|
|
21
|
+
## 可直接使用的导出
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import {
|
|
25
|
+
transcribeTencentFlash,
|
|
26
|
+
ASRError,
|
|
27
|
+
ASRTimeoutError,
|
|
28
|
+
ASRAuthError,
|
|
29
|
+
ASRRequestError,
|
|
30
|
+
ASRResponseParseError,
|
|
31
|
+
ASRServiceError,
|
|
32
|
+
ASREmptyResultError,
|
|
33
|
+
} from "@openclaw-china/shared";
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## 插件接入步骤
|
|
37
|
+
|
|
38
|
+
1. 在插件配置中增加 ASR 字段(示例)
|
|
39
|
+
```ts
|
|
40
|
+
asr: {
|
|
41
|
+
enabled?: boolean;
|
|
42
|
+
appId?: string;
|
|
43
|
+
secretId?: string;
|
|
44
|
+
secretKey?: string;
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
2. 在入站消息里识别语音附件,并下载到 `Buffer`
|
|
49
|
+
- 推荐复用 `shared/media` 的下载能力:
|
|
50
|
+
```ts
|
|
51
|
+
import { fetchMediaFromUrl } from "@openclaw-china/shared";
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
3. 调用转写
|
|
55
|
+
```ts
|
|
56
|
+
const transcript = await transcribeTencentFlash({
|
|
57
|
+
audio: media.buffer,
|
|
58
|
+
config: {
|
|
59
|
+
appId: asr.appId,
|
|
60
|
+
secretId: asr.secretId,
|
|
61
|
+
secretKey: asr.secretKey,
|
|
62
|
+
// 可选,默认如下:
|
|
63
|
+
// engineType: "16k_zh",
|
|
64
|
+
// voiceFormat: "silk",
|
|
65
|
+
// timeoutMs: 30000,
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
4. 按错误类型做日志与降级
|
|
71
|
+
```ts
|
|
72
|
+
try {
|
|
73
|
+
// transcribeTencentFlash(...)
|
|
74
|
+
} catch (err) {
|
|
75
|
+
if (err instanceof ASRError) {
|
|
76
|
+
logger.warn(
|
|
77
|
+
`asr failed kind=${err.kind} provider=${err.provider} retryable=${err.retryable} msg=${err.message}`
|
|
78
|
+
);
|
|
79
|
+
} else {
|
|
80
|
+
logger.warn(`asr failed: ${String(err)}`);
|
|
81
|
+
}
|
|
82
|
+
// 在这里决定是否回退文本、提示用户、或继续其他链路
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## 错误类型语义
|
|
87
|
+
|
|
88
|
+
- `ASRTimeoutError`:请求超时,通常可重试
|
|
89
|
+
- `ASRAuthError`:鉴权失败(密钥错误/权限问题),通常不可重试
|
|
90
|
+
- `ASRRequestError`:请求失败(网络或 HTTP 失败)
|
|
91
|
+
- `ASRResponseParseError`:服务返回非 JSON 或格式异常
|
|
92
|
+
- `ASRServiceError`:服务端返回业务错误码(`code != 0`)
|
|
93
|
+
- `ASREmptyResultError`:识别成功但无文本
|
|
94
|
+
|
|
95
|
+
## 最佳实践
|
|
96
|
+
|
|
97
|
+
- 不要把密钥硬编码到仓库;仅通过配置注入。
|
|
98
|
+
- 给下载和 ASR 都设置超时与大小上限。
|
|
99
|
+
- 将“ASR 失败文案”放在插件侧,不放到 `shared`。
|
|
100
|
+
- 先记录结构化日志,再决定业务降级策略。
|
package/src/asr/errors.ts
CHANGED
|
@@ -1,61 +1,61 @@
|
|
|
1
|
-
export type ASRErrorKind =
|
|
2
|
-
| "timeout"
|
|
3
|
-
| "auth"
|
|
4
|
-
| "request"
|
|
5
|
-
| "response_parse"
|
|
6
|
-
| "service"
|
|
7
|
-
| "empty_result";
|
|
8
|
-
|
|
9
|
-
export class ASRError extends Error {
|
|
10
|
-
constructor(
|
|
11
|
-
message: string,
|
|
12
|
-
public readonly kind: ASRErrorKind,
|
|
13
|
-
public readonly provider: string,
|
|
14
|
-
public readonly retryable: boolean = false
|
|
15
|
-
) {
|
|
16
|
-
super(message);
|
|
17
|
-
this.name = "ASRError";
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export class ASRTimeoutError extends ASRError {
|
|
22
|
-
constructor(provider: string, public readonly timeoutMs: number) {
|
|
23
|
-
super(`ASR request timeout after ${timeoutMs}ms`, "timeout", provider, true);
|
|
24
|
-
this.name = "ASRTimeoutError";
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export class ASRAuthError extends ASRError {
|
|
29
|
-
constructor(provider: string, message: string, public readonly status?: number) {
|
|
30
|
-
super(message, "auth", provider, false);
|
|
31
|
-
this.name = "ASRAuthError";
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export class ASRRequestError extends ASRError {
|
|
36
|
-
constructor(provider: string, message: string, public readonly status?: number) {
|
|
37
|
-
super(message, "request", provider, true);
|
|
38
|
-
this.name = "ASRRequestError";
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export class ASRResponseParseError extends ASRError {
|
|
43
|
-
constructor(provider: string, public readonly bodySnippet: string) {
|
|
44
|
-
super("ASR response is not valid JSON", "response_parse", provider, false);
|
|
45
|
-
this.name = "ASRResponseParseError";
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export class ASRServiceError extends ASRError {
|
|
50
|
-
constructor(provider: string, message: string, public readonly serviceCode?: number) {
|
|
51
|
-
super(message, "service", provider, false);
|
|
52
|
-
this.name = "ASRServiceError";
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export class ASREmptyResultError extends ASRError {
|
|
57
|
-
constructor(provider: string) {
|
|
58
|
-
super("ASR returned empty transcript", "empty_result", provider, false);
|
|
59
|
-
this.name = "ASREmptyResultError";
|
|
60
|
-
}
|
|
61
|
-
}
|
|
1
|
+
export type ASRErrorKind =
|
|
2
|
+
| "timeout"
|
|
3
|
+
| "auth"
|
|
4
|
+
| "request"
|
|
5
|
+
| "response_parse"
|
|
6
|
+
| "service"
|
|
7
|
+
| "empty_result";
|
|
8
|
+
|
|
9
|
+
export class ASRError extends Error {
|
|
10
|
+
constructor(
|
|
11
|
+
message: string,
|
|
12
|
+
public readonly kind: ASRErrorKind,
|
|
13
|
+
public readonly provider: string,
|
|
14
|
+
public readonly retryable: boolean = false
|
|
15
|
+
) {
|
|
16
|
+
super(message);
|
|
17
|
+
this.name = "ASRError";
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export class ASRTimeoutError extends ASRError {
|
|
22
|
+
constructor(provider: string, public readonly timeoutMs: number) {
|
|
23
|
+
super(`ASR request timeout after ${timeoutMs}ms`, "timeout", provider, true);
|
|
24
|
+
this.name = "ASRTimeoutError";
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export class ASRAuthError extends ASRError {
|
|
29
|
+
constructor(provider: string, message: string, public readonly status?: number) {
|
|
30
|
+
super(message, "auth", provider, false);
|
|
31
|
+
this.name = "ASRAuthError";
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export class ASRRequestError extends ASRError {
|
|
36
|
+
constructor(provider: string, message: string, public readonly status?: number) {
|
|
37
|
+
super(message, "request", provider, true);
|
|
38
|
+
this.name = "ASRRequestError";
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export class ASRResponseParseError extends ASRError {
|
|
43
|
+
constructor(provider: string, public readonly bodySnippet: string) {
|
|
44
|
+
super("ASR response is not valid JSON", "response_parse", provider, false);
|
|
45
|
+
this.name = "ASRResponseParseError";
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export class ASRServiceError extends ASRError {
|
|
50
|
+
constructor(provider: string, message: string, public readonly serviceCode?: number) {
|
|
51
|
+
super(message, "service", provider, false);
|
|
52
|
+
this.name = "ASRServiceError";
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export class ASREmptyResultError extends ASRError {
|
|
57
|
+
constructor(provider: string) {
|
|
58
|
+
super("ASR returned empty transcript", "empty_result", provider, false);
|
|
59
|
+
this.name = "ASREmptyResultError";
|
|
60
|
+
}
|
|
61
|
+
}
|
package/src/asr/index.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
export { transcribeTencentFlash, type TencentFlashASRConfig } from "./tencent-flash.js";
|
|
2
|
-
export {
|
|
3
|
-
ASRError,
|
|
4
|
-
ASRTimeoutError,
|
|
5
|
-
ASRAuthError,
|
|
6
|
-
ASRRequestError,
|
|
7
|
-
ASRResponseParseError,
|
|
8
|
-
ASRServiceError,
|
|
9
|
-
ASREmptyResultError,
|
|
10
|
-
type ASRErrorKind,
|
|
11
|
-
} from "./errors.js";
|
|
1
|
+
export { transcribeTencentFlash, type TencentFlashASRConfig } from "./tencent-flash.js";
|
|
2
|
+
export {
|
|
3
|
+
ASRError,
|
|
4
|
+
ASRTimeoutError,
|
|
5
|
+
ASRAuthError,
|
|
6
|
+
ASRRequestError,
|
|
7
|
+
ASRResponseParseError,
|
|
8
|
+
ASRServiceError,
|
|
9
|
+
ASREmptyResultError,
|
|
10
|
+
type ASRErrorKind,
|
|
11
|
+
} from "./errors.js";
|