@dcrays/dcgchat-test 0.1.9 → 0.1.10
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/README.md +83 -83
- package/index.ts +24 -24
- package/openclaw.plugin.json +9 -9
- package/package.json +58 -58
- package/src/api.ts +62 -62
- package/src/bot.ts +272 -272
- package/src/channel.ts +192 -192
- package/src/connection.ts +11 -11
- package/src/log.ts +46 -46
- package/src/monitor.ts +191 -190
- package/src/oss.ts +72 -72
- package/src/request.ts +194 -194
- package/src/runtime.ts +38 -38
- package/src/skill.ts +110 -110
- package/src/tool.ts +74 -64
- package/src/types.ts +103 -103
- package/src/userInfo.ts +97 -97
package/src/request.ts
CHANGED
|
@@ -1,194 +1,194 @@
|
|
|
1
|
-
import axios from "axios";
|
|
2
|
-
import md5 from "md5";
|
|
3
|
-
import type { IResponse } from "./types.js";
|
|
4
|
-
import { getUserTokenCache } from "./userInfo.js";
|
|
5
|
-
|
|
6
|
-
export const apiUrlMap = {
|
|
7
|
-
production: "https://api-gateway.shuwenda.com",
|
|
8
|
-
test: "https://api-gateway.shuwenda.icu",
|
|
9
|
-
develop: "https://shenyu-dev.shuwenda.icu",
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
export const appKey = {
|
|
13
|
-
production: "2A1C74D315CB4A01BF3DA8983695AFE2",
|
|
14
|
-
test: "7374A073CCBD4C8CA84FAD33896F0B69",
|
|
15
|
-
develop: "7374A073CCBD4C8CA84FAD33896F0B69",
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
export const signKey = {
|
|
19
|
-
production: "34E9023008EA445AAE6CC075CC954F46",
|
|
20
|
-
test: "FE93D3322CB94E978CE95BD4AA2A37D7",
|
|
21
|
-
develop: "FE93D3322CB94E978CE95BD4AA2A37D7",
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
const env = "test";
|
|
25
|
-
export const version = "1.0.0";
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* 根据 axios 请求配置生成等价 curl,便于复制给后端排查
|
|
29
|
-
*/
|
|
30
|
-
function toCurl(config: {
|
|
31
|
-
baseURL?: string;
|
|
32
|
-
url?: string;
|
|
33
|
-
method?: string;
|
|
34
|
-
headers?: Record<string, string | number | undefined>;
|
|
35
|
-
data?: unknown;
|
|
36
|
-
}): string {
|
|
37
|
-
const base = config.baseURL ?? "";
|
|
38
|
-
const path = config.url ?? "";
|
|
39
|
-
const url = path.startsWith("http")
|
|
40
|
-
? path
|
|
41
|
-
: `${base.replace(/\/$/, "")}/${path.replace(/^\//, "")}`;
|
|
42
|
-
const method = (config.method ?? "GET").toUpperCase();
|
|
43
|
-
const headers = config.headers ?? {};
|
|
44
|
-
const parts = ["curl", "-X", method, `'${url}'`];
|
|
45
|
-
for (const [k, v] of Object.entries(headers)) {
|
|
46
|
-
if (v !== undefined && v !== "") {
|
|
47
|
-
parts.push("-H", `'${k}: ${v}'`);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
if (method !== "GET" && config.data !== undefined) {
|
|
51
|
-
const body = typeof config.data === "string" ? config.data : JSON.stringify(config.data);
|
|
52
|
-
parts.push("-d", `'${body.replace(/'/g, "'\\''")}'`);
|
|
53
|
-
}
|
|
54
|
-
return parts.join(" ");
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* 生成签名
|
|
59
|
-
* @param {Object} body 请求体
|
|
60
|
-
* @param {number} timestamp 时间戳
|
|
61
|
-
* @param {string} path 请求地址
|
|
62
|
-
* @param {'production' | 'test' | 'develop'} env 请求环境
|
|
63
|
-
* @param {string} version 版本号
|
|
64
|
-
* @returns {string} 大写 MD5 签名
|
|
65
|
-
*/
|
|
66
|
-
export function getSignature(
|
|
67
|
-
body: Record<string, unknown>,
|
|
68
|
-
timestamp: number,
|
|
69
|
-
path: string,
|
|
70
|
-
env: "production" | "test" | "develop",
|
|
71
|
-
version: string = "1.0.0",
|
|
72
|
-
) {
|
|
73
|
-
// 1. 构造 map
|
|
74
|
-
const map = {
|
|
75
|
-
timestamp,
|
|
76
|
-
path,
|
|
77
|
-
version,
|
|
78
|
-
...body,
|
|
79
|
-
};
|
|
80
|
-
// 2. 按 key 进行自然排序
|
|
81
|
-
const sortedKeys = Object.keys(map).sort();
|
|
82
|
-
|
|
83
|
-
// 3. 拼接 key + value
|
|
84
|
-
const signStr =
|
|
85
|
-
sortedKeys
|
|
86
|
-
.map((key) => {
|
|
87
|
-
const val = map[key as keyof typeof map];
|
|
88
|
-
return val === undefined
|
|
89
|
-
? ""
|
|
90
|
-
: `${key}${typeof val === "object" ? JSON.stringify(val) : val}`;
|
|
91
|
-
})
|
|
92
|
-
.join("") + signKey[env];
|
|
93
|
-
|
|
94
|
-
// 4. MD5 加密并转大写
|
|
95
|
-
return md5(signStr).toUpperCase();
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function buildHeaders(data: Record<string, unknown>, url: string, userToken?: string) {
|
|
99
|
-
const timestamp = Date.now();
|
|
100
|
-
|
|
101
|
-
const headers: Record<string, string | number> = {
|
|
102
|
-
"Content-Type": "application/json",
|
|
103
|
-
appKey: appKey[env],
|
|
104
|
-
sign: getSignature(data, timestamp, url, env, version),
|
|
105
|
-
timestamp,
|
|
106
|
-
version,
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
// 如果提供了 userToken,添加到 headers
|
|
110
|
-
if (userToken) {
|
|
111
|
-
headers.authorization = userToken;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
return headers;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
const axiosInstance = axios.create({
|
|
118
|
-
baseURL: apiUrlMap[env],
|
|
119
|
-
timeout: 10000,
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
// 请求拦截器:自动注入 userToken
|
|
123
|
-
axiosInstance.interceptors.request.use(
|
|
124
|
-
(config) => {
|
|
125
|
-
// 如果请求配置中已经有 authorization,优先使用
|
|
126
|
-
if (config.headers?.authorization) {
|
|
127
|
-
return config;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// 从请求上下文中获取 botToken(需要在调用时设置)
|
|
131
|
-
const botToken = (config as any).__botToken as string | undefined;
|
|
132
|
-
if (botToken) {
|
|
133
|
-
const cachedToken = getUserTokenCache(botToken);
|
|
134
|
-
if (cachedToken) {
|
|
135
|
-
config.headers = config.headers || {};
|
|
136
|
-
config.headers.authorization = cachedToken;
|
|
137
|
-
console.log(`[request] auto-injected userToken from cache for botToken=${botToken.slice(0, 10)}...`);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
return config;
|
|
142
|
-
},
|
|
143
|
-
(error) => {
|
|
144
|
-
return Promise.reject(error);
|
|
145
|
-
},
|
|
146
|
-
);
|
|
147
|
-
|
|
148
|
-
// 响应拦截器:打印 curl 便于调试
|
|
149
|
-
axiosInstance.interceptors.response.use(
|
|
150
|
-
(response) => {
|
|
151
|
-
const curl = toCurl(response.config);
|
|
152
|
-
console.log("[request] curl for backend:", curl);
|
|
153
|
-
return response.data;
|
|
154
|
-
},
|
|
155
|
-
(error) => {
|
|
156
|
-
const config = error.config ?? {};
|
|
157
|
-
const curl = toCurl(config);
|
|
158
|
-
console.log("[request] curl for backend (failed request):", curl);
|
|
159
|
-
return Promise.reject(error);
|
|
160
|
-
},
|
|
161
|
-
);
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* POST 请求(支持可选的 userToken 和 botToken)
|
|
167
|
-
* @param url 请求路径
|
|
168
|
-
* @param data 请求体
|
|
169
|
-
* @param options 可选配置
|
|
170
|
-
* @param options.userToken 直接提供的 userToken(优先级最高)
|
|
171
|
-
* @param options.botToken 用于从缓存获取 userToken 的 botToken
|
|
172
|
-
*/
|
|
173
|
-
export function post<T = Record<string, unknown>, R = unknown>(
|
|
174
|
-
url: string,
|
|
175
|
-
data: T,
|
|
176
|
-
options?: {
|
|
177
|
-
userToken?: string;
|
|
178
|
-
botToken?: string;
|
|
179
|
-
},
|
|
180
|
-
): Promise<IResponse<R>> {
|
|
181
|
-
const config: any = {
|
|
182
|
-
method: "POST",
|
|
183
|
-
url,
|
|
184
|
-
data,
|
|
185
|
-
headers: buildHeaders(data as Record<string, unknown>, url, options?.userToken),
|
|
186
|
-
};
|
|
187
|
-
|
|
188
|
-
// 将 botToken 附加到配置中,供请求拦截器使用
|
|
189
|
-
if (options?.botToken) {
|
|
190
|
-
config.__botToken = options.botToken;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
return axiosInstance.request(config);
|
|
194
|
-
}
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
import md5 from "md5";
|
|
3
|
+
import type { IResponse } from "./types.js";
|
|
4
|
+
import { getUserTokenCache } from "./userInfo.js";
|
|
5
|
+
|
|
6
|
+
export const apiUrlMap = {
|
|
7
|
+
production: "https://api-gateway.shuwenda.com",
|
|
8
|
+
test: "https://api-gateway.shuwenda.icu",
|
|
9
|
+
develop: "https://shenyu-dev.shuwenda.icu",
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const appKey = {
|
|
13
|
+
production: "2A1C74D315CB4A01BF3DA8983695AFE2",
|
|
14
|
+
test: "7374A073CCBD4C8CA84FAD33896F0B69",
|
|
15
|
+
develop: "7374A073CCBD4C8CA84FAD33896F0B69",
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const signKey = {
|
|
19
|
+
production: "34E9023008EA445AAE6CC075CC954F46",
|
|
20
|
+
test: "FE93D3322CB94E978CE95BD4AA2A37D7",
|
|
21
|
+
develop: "FE93D3322CB94E978CE95BD4AA2A37D7",
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const env = "test";
|
|
25
|
+
export const version = "1.0.0";
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 根据 axios 请求配置生成等价 curl,便于复制给后端排查
|
|
29
|
+
*/
|
|
30
|
+
function toCurl(config: {
|
|
31
|
+
baseURL?: string;
|
|
32
|
+
url?: string;
|
|
33
|
+
method?: string;
|
|
34
|
+
headers?: Record<string, string | number | undefined>;
|
|
35
|
+
data?: unknown;
|
|
36
|
+
}): string {
|
|
37
|
+
const base = config.baseURL ?? "";
|
|
38
|
+
const path = config.url ?? "";
|
|
39
|
+
const url = path.startsWith("http")
|
|
40
|
+
? path
|
|
41
|
+
: `${base.replace(/\/$/, "")}/${path.replace(/^\//, "")}`;
|
|
42
|
+
const method = (config.method ?? "GET").toUpperCase();
|
|
43
|
+
const headers = config.headers ?? {};
|
|
44
|
+
const parts = ["curl", "-X", method, `'${url}'`];
|
|
45
|
+
for (const [k, v] of Object.entries(headers)) {
|
|
46
|
+
if (v !== undefined && v !== "") {
|
|
47
|
+
parts.push("-H", `'${k}: ${v}'`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (method !== "GET" && config.data !== undefined) {
|
|
51
|
+
const body = typeof config.data === "string" ? config.data : JSON.stringify(config.data);
|
|
52
|
+
parts.push("-d", `'${body.replace(/'/g, "'\\''")}'`);
|
|
53
|
+
}
|
|
54
|
+
return parts.join(" ");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* 生成签名
|
|
59
|
+
* @param {Object} body 请求体
|
|
60
|
+
* @param {number} timestamp 时间戳
|
|
61
|
+
* @param {string} path 请求地址
|
|
62
|
+
* @param {'production' | 'test' | 'develop'} env 请求环境
|
|
63
|
+
* @param {string} version 版本号
|
|
64
|
+
* @returns {string} 大写 MD5 签名
|
|
65
|
+
*/
|
|
66
|
+
export function getSignature(
|
|
67
|
+
body: Record<string, unknown>,
|
|
68
|
+
timestamp: number,
|
|
69
|
+
path: string,
|
|
70
|
+
env: "production" | "test" | "develop",
|
|
71
|
+
version: string = "1.0.0",
|
|
72
|
+
) {
|
|
73
|
+
// 1. 构造 map
|
|
74
|
+
const map = {
|
|
75
|
+
timestamp,
|
|
76
|
+
path,
|
|
77
|
+
version,
|
|
78
|
+
...body,
|
|
79
|
+
};
|
|
80
|
+
// 2. 按 key 进行自然排序
|
|
81
|
+
const sortedKeys = Object.keys(map).sort();
|
|
82
|
+
|
|
83
|
+
// 3. 拼接 key + value
|
|
84
|
+
const signStr =
|
|
85
|
+
sortedKeys
|
|
86
|
+
.map((key) => {
|
|
87
|
+
const val = map[key as keyof typeof map];
|
|
88
|
+
return val === undefined
|
|
89
|
+
? ""
|
|
90
|
+
: `${key}${typeof val === "object" ? JSON.stringify(val) : val}`;
|
|
91
|
+
})
|
|
92
|
+
.join("") + signKey[env];
|
|
93
|
+
|
|
94
|
+
// 4. MD5 加密并转大写
|
|
95
|
+
return md5(signStr).toUpperCase();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function buildHeaders(data: Record<string, unknown>, url: string, userToken?: string) {
|
|
99
|
+
const timestamp = Date.now();
|
|
100
|
+
|
|
101
|
+
const headers: Record<string, string | number> = {
|
|
102
|
+
"Content-Type": "application/json",
|
|
103
|
+
appKey: appKey[env],
|
|
104
|
+
sign: getSignature(data, timestamp, url, env, version),
|
|
105
|
+
timestamp,
|
|
106
|
+
version,
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
// 如果提供了 userToken,添加到 headers
|
|
110
|
+
if (userToken) {
|
|
111
|
+
headers.authorization = userToken;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return headers;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const axiosInstance = axios.create({
|
|
118
|
+
baseURL: apiUrlMap[env],
|
|
119
|
+
timeout: 10000,
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// 请求拦截器:自动注入 userToken
|
|
123
|
+
axiosInstance.interceptors.request.use(
|
|
124
|
+
(config) => {
|
|
125
|
+
// 如果请求配置中已经有 authorization,优先使用
|
|
126
|
+
if (config.headers?.authorization) {
|
|
127
|
+
return config;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// 从请求上下文中获取 botToken(需要在调用时设置)
|
|
131
|
+
const botToken = (config as any).__botToken as string | undefined;
|
|
132
|
+
if (botToken) {
|
|
133
|
+
const cachedToken = getUserTokenCache(botToken);
|
|
134
|
+
if (cachedToken) {
|
|
135
|
+
config.headers = config.headers || {};
|
|
136
|
+
config.headers.authorization = cachedToken;
|
|
137
|
+
console.log(`[request] auto-injected userToken from cache for botToken=${botToken.slice(0, 10)}...`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return config;
|
|
142
|
+
},
|
|
143
|
+
(error) => {
|
|
144
|
+
return Promise.reject(error);
|
|
145
|
+
},
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
// 响应拦截器:打印 curl 便于调试
|
|
149
|
+
axiosInstance.interceptors.response.use(
|
|
150
|
+
(response) => {
|
|
151
|
+
const curl = toCurl(response.config);
|
|
152
|
+
console.log("[request] curl for backend:", curl);
|
|
153
|
+
return response.data;
|
|
154
|
+
},
|
|
155
|
+
(error) => {
|
|
156
|
+
const config = error.config ?? {};
|
|
157
|
+
const curl = toCurl(config);
|
|
158
|
+
console.log("[request] curl for backend (failed request):", curl);
|
|
159
|
+
return Promise.reject(error);
|
|
160
|
+
},
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* POST 请求(支持可选的 userToken 和 botToken)
|
|
167
|
+
* @param url 请求路径
|
|
168
|
+
* @param data 请求体
|
|
169
|
+
* @param options 可选配置
|
|
170
|
+
* @param options.userToken 直接提供的 userToken(优先级最高)
|
|
171
|
+
* @param options.botToken 用于从缓存获取 userToken 的 botToken
|
|
172
|
+
*/
|
|
173
|
+
export function post<T = Record<string, unknown>, R = unknown>(
|
|
174
|
+
url: string,
|
|
175
|
+
data: T,
|
|
176
|
+
options?: {
|
|
177
|
+
userToken?: string;
|
|
178
|
+
botToken?: string;
|
|
179
|
+
},
|
|
180
|
+
): Promise<IResponse<R>> {
|
|
181
|
+
const config: any = {
|
|
182
|
+
method: "POST",
|
|
183
|
+
url,
|
|
184
|
+
data,
|
|
185
|
+
headers: buildHeaders(data as Record<string, unknown>, url, options?.userToken),
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
// 将 botToken 附加到配置中,供请求拦截器使用
|
|
189
|
+
if (options?.botToken) {
|
|
190
|
+
config.__botToken = options.botToken;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return axiosInstance.request(config);
|
|
194
|
+
}
|
package/src/runtime.ts
CHANGED
|
@@ -1,38 +1,38 @@
|
|
|
1
|
-
import type { PluginRuntime } from "openclaw/plugin-sdk";
|
|
2
|
-
|
|
3
|
-
const path = require('path');
|
|
4
|
-
const fs = require('fs');
|
|
5
|
-
function getWorkspacePath() {
|
|
6
|
-
const current = process.cwd();
|
|
7
|
-
const workspacePath = path.join(current, '.openclaw/workspace');
|
|
8
|
-
if (fs.existsSync(workspacePath)) {
|
|
9
|
-
return workspacePath;
|
|
10
|
-
}
|
|
11
|
-
return null;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
let runtime: PluginRuntime | null = null;
|
|
15
|
-
let workspaceDir: string = getWorkspacePath();
|
|
16
|
-
|
|
17
|
-
export function setWorkspaceDir(dir?: string) {
|
|
18
|
-
if (dir) {
|
|
19
|
-
workspaceDir = dir;
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
export function getWorkspaceDir(): string {
|
|
23
|
-
if (!workspaceDir) {
|
|
24
|
-
throw new Error("Workspace directory not initialized");
|
|
25
|
-
}
|
|
26
|
-
return workspaceDir;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export function setDcgchatRuntime(next: PluginRuntime) {
|
|
30
|
-
runtime = next;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export function getDcgchatRuntime(): PluginRuntime {
|
|
34
|
-
if (!runtime) {
|
|
35
|
-
throw new Error("DCG Chat runtime not initialized");
|
|
36
|
-
}
|
|
37
|
-
return runtime;
|
|
38
|
-
}
|
|
1
|
+
import type { PluginRuntime } from "openclaw/plugin-sdk";
|
|
2
|
+
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
function getWorkspacePath() {
|
|
6
|
+
const current = process.cwd();
|
|
7
|
+
const workspacePath = path.join(current, '.openclaw/workspace');
|
|
8
|
+
if (fs.existsSync(workspacePath)) {
|
|
9
|
+
return workspacePath;
|
|
10
|
+
}
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
let runtime: PluginRuntime | null = null;
|
|
15
|
+
let workspaceDir: string = getWorkspacePath();
|
|
16
|
+
|
|
17
|
+
export function setWorkspaceDir(dir?: string) {
|
|
18
|
+
if (dir) {
|
|
19
|
+
workspaceDir = dir;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export function getWorkspaceDir(): string {
|
|
23
|
+
if (!workspaceDir) {
|
|
24
|
+
throw new Error("Workspace directory not initialized");
|
|
25
|
+
}
|
|
26
|
+
return workspaceDir;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function setDcgchatRuntime(next: PluginRuntime) {
|
|
30
|
+
runtime = next;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function getDcgchatRuntime(): PluginRuntime {
|
|
34
|
+
if (!runtime) {
|
|
35
|
+
throw new Error("DCG Chat runtime not initialized");
|
|
36
|
+
}
|
|
37
|
+
return runtime;
|
|
38
|
+
}
|