@dsh-overdrive/gateway 0.1.5 → 0.1.7
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/adapters/wechat.d.ts +63 -0
- package/dist/adapters/wechat.js +231 -0
- package/dist/adapters/wechat.js.map +1 -0
- package/dist/commands.d.ts +14 -0
- package/dist/commands.js +26 -0
- package/dist/commands.js.map +1 -1
- package/dist/config.d.ts +2 -0
- package/dist/config.js +6 -0
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.js +81 -6
- package/dist/index.js.map +1 -1
- package/dist/memory.d.ts +24 -0
- package/dist/memory.js +100 -0
- package/dist/memory.js.map +1 -0
- package/package.json +1 -1
- package/src/adapters/wechat.ts +247 -0
- package/src/commands.ts +27 -1
- package/src/config.ts +8 -0
- package/src/index.ts +71 -5
- package/src/memory.ts +108 -0
- package/test/adapters.wechat.test.ts +78 -0
- package/test/commands.test.ts +14 -0
- package/test/memory.test.ts +60 -0
- package/test/multi.test.ts +49 -1
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { Adapter, NormalizedMessage, OutboundButton, OutboundPayload } from '../adapter.js';
|
|
2
|
+
export interface WeChatIncomingMsg {
|
|
3
|
+
from_user_id?: string;
|
|
4
|
+
context_token?: string;
|
|
5
|
+
text_item?: {
|
|
6
|
+
text?: string;
|
|
7
|
+
};
|
|
8
|
+
item_list?: Array<{
|
|
9
|
+
type?: number;
|
|
10
|
+
text_item?: {
|
|
11
|
+
text?: string;
|
|
12
|
+
};
|
|
13
|
+
}>;
|
|
14
|
+
}
|
|
15
|
+
/** 纯函数:从 iLink 消息提取文本(text_item 或 item_list[].text_item,type===1 为文本)。 */
|
|
16
|
+
export declare function extractWeChatText(msg: WeChatIncomingMsg): string | null;
|
|
17
|
+
/** 纯函数:iLink 入站消息 → NormalizedMessage;非文本/缺发送者返回 null。 */
|
|
18
|
+
export declare function parseWeChatUpdate(msg: WeChatIncomingMsg): NormalizedMessage | null;
|
|
19
|
+
/** 纯函数:getupdates 长轮询请求体。base_info.channel_version 缺失时服务器不投递(生态实测)。 */
|
|
20
|
+
export declare function buildGetUpdatesBody(syncBuf: string): Record<string, unknown>;
|
|
21
|
+
/** 纯函数:sendmessage 请求体(msg 包裹 + text_item)。clientId 可注入以便测试。 */
|
|
22
|
+
export declare function buildSendMessageBody(toUserId: string, text: string, contextToken: string, clientId?: string): Record<string, unknown>;
|
|
23
|
+
/** 纯函数:长文本按上限分段(iLink 单条消息长度限制)。 */
|
|
24
|
+
export declare function chunkText(text: string, limit?: number): string[];
|
|
25
|
+
/** 纯函数:审批按钮 → 编号回复文本(原生按钮不支持时的兜底)。 */
|
|
26
|
+
export declare function buildNumberedReplyText(text: string, buttons: OutboundButton[]): string;
|
|
27
|
+
export interface WeChatAdapterOptions {
|
|
28
|
+
/** iLink api-token(扫码登录后自动保存;也可预先配置 WECHAT_TOKEN) */
|
|
29
|
+
token?: string;
|
|
30
|
+
baseUrl?: string;
|
|
31
|
+
stateDir?: string;
|
|
32
|
+
}
|
|
33
|
+
export declare class WeChatAdapter implements Adapter {
|
|
34
|
+
private readonly opts;
|
|
35
|
+
readonly id = "wechat";
|
|
36
|
+
private messageCb?;
|
|
37
|
+
private replyCb?;
|
|
38
|
+
private readonly pendingButtons;
|
|
39
|
+
private readonly stateDir;
|
|
40
|
+
private token;
|
|
41
|
+
private syncBuf;
|
|
42
|
+
private readonly contextTokens;
|
|
43
|
+
private polling;
|
|
44
|
+
private stopped;
|
|
45
|
+
constructor(opts: WeChatAdapterOptions);
|
|
46
|
+
private persistState;
|
|
47
|
+
connect(): Promise<void>;
|
|
48
|
+
private base;
|
|
49
|
+
private apiFetch;
|
|
50
|
+
/** 实验性扫码登录:打印二维码(liteapp URL 渲染为 QR),轮询扫码状态,成功后持久化 token。 */
|
|
51
|
+
private tryLogin;
|
|
52
|
+
private startPolling;
|
|
53
|
+
private pollLoop;
|
|
54
|
+
send(chatId: string, payload: OutboundPayload): Promise<void>;
|
|
55
|
+
onMessage(cb: (msg: NormalizedMessage) => void): void;
|
|
56
|
+
onReply(cb: (buttonId: string, sender: {
|
|
57
|
+
chatId: string;
|
|
58
|
+
userId: string;
|
|
59
|
+
}) => void): void;
|
|
60
|
+
status(): {
|
|
61
|
+
connected: boolean;
|
|
62
|
+
};
|
|
63
|
+
}
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
// v0.2b 实验性:个人微信适配器(腾讯官方 iLink / ClawBot 协议,HTTP/JSON)。
|
|
2
|
+
// 协议形状与 DSH 生态已验证实现(super-wechat-bridge)一致:
|
|
3
|
+
// - 登录:GET /ilink/bot/get_bot_qrcode?bot_type=3 → GET /ilink/bot/get_qrcode_status?qrcode=
|
|
4
|
+
// - 收:POST /ilink/bot/getupdates(长轮询,响应 data.msgs + get_updates_buf 同步游标)
|
|
5
|
+
// - 发:POST /ilink/bot/sendmessage(msg 包裹 + item_list[].text_item,必须带 base_info.channel_version)
|
|
6
|
+
// 使用 iLink 需遵守《微信 ClawBot 功能使用条款》;本适配器标记为实验性,需真实设备验证。
|
|
7
|
+
import { randomUUID } from 'node:crypto';
|
|
8
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
9
|
+
import { join } from 'node:path';
|
|
10
|
+
import * as qrcode from 'qrcode-terminal';
|
|
11
|
+
import { PendingButtons } from '../pending-buttons.js';
|
|
12
|
+
const DEFAULT_BASE = 'https://ilinkai.weixin.qq.com';
|
|
13
|
+
const CHANNEL_VERSION = '1.0.2';
|
|
14
|
+
const TEXT_CHUNK_LIMIT = 800;
|
|
15
|
+
/** 纯函数:从 iLink 消息提取文本(text_item 或 item_list[].text_item,type===1 为文本)。 */
|
|
16
|
+
export function extractWeChatText(msg) {
|
|
17
|
+
if (msg.text_item?.text)
|
|
18
|
+
return msg.text_item.text;
|
|
19
|
+
for (const item of msg.item_list ?? []) {
|
|
20
|
+
if (item.type === 1 && item.text_item?.text)
|
|
21
|
+
return item.text_item.text;
|
|
22
|
+
}
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
/** 纯函数:iLink 入站消息 → NormalizedMessage;非文本/缺发送者返回 null。 */
|
|
26
|
+
export function parseWeChatUpdate(msg) {
|
|
27
|
+
const userId = msg.from_user_id;
|
|
28
|
+
const text = extractWeChatText(msg);
|
|
29
|
+
if (!userId || !text)
|
|
30
|
+
return null;
|
|
31
|
+
return { chatId: userId, userId, text };
|
|
32
|
+
}
|
|
33
|
+
/** 纯函数:getupdates 长轮询请求体。base_info.channel_version 缺失时服务器不投递(生态实测)。 */
|
|
34
|
+
export function buildGetUpdatesBody(syncBuf) {
|
|
35
|
+
return { get_updates_buf: syncBuf, longpolling_timeout: 35000, base_info: { channel_version: CHANNEL_VERSION } };
|
|
36
|
+
}
|
|
37
|
+
/** 纯函数:sendmessage 请求体(msg 包裹 + text_item)。clientId 可注入以便测试。 */
|
|
38
|
+
export function buildSendMessageBody(toUserId, text, contextToken, clientId = `dsh-${randomUUID()}`) {
|
|
39
|
+
return {
|
|
40
|
+
msg: {
|
|
41
|
+
from_user_id: '',
|
|
42
|
+
to_user_id: toUserId,
|
|
43
|
+
client_id: clientId,
|
|
44
|
+
message_type: 2,
|
|
45
|
+
message_state: 2,
|
|
46
|
+
context_token: contextToken,
|
|
47
|
+
item_list: [{ type: 1, text_item: { text } }],
|
|
48
|
+
},
|
|
49
|
+
base_info: { channel_version: CHANNEL_VERSION },
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/** 纯函数:长文本按上限分段(iLink 单条消息长度限制)。 */
|
|
53
|
+
export function chunkText(text, limit = TEXT_CHUNK_LIMIT) {
|
|
54
|
+
const chunks = [];
|
|
55
|
+
for (let i = 0; i < text.length; i += limit)
|
|
56
|
+
chunks.push(text.slice(i, i + limit));
|
|
57
|
+
return chunks;
|
|
58
|
+
}
|
|
59
|
+
/** 纯函数:审批按钮 → 编号回复文本(原生按钮不支持时的兜底)。 */
|
|
60
|
+
export function buildNumberedReplyText(text, buttons) {
|
|
61
|
+
if (buttons.length === 0)
|
|
62
|
+
return text;
|
|
63
|
+
const options = buttons.map((b, i) => `${i + 1}) ${b.label}`).join('\n');
|
|
64
|
+
return `${text}\n\n${options}\n\n回复数字选择。`;
|
|
65
|
+
}
|
|
66
|
+
export class WeChatAdapter {
|
|
67
|
+
opts;
|
|
68
|
+
id = 'wechat';
|
|
69
|
+
messageCb;
|
|
70
|
+
replyCb;
|
|
71
|
+
pendingButtons = new PendingButtons();
|
|
72
|
+
stateDir;
|
|
73
|
+
token;
|
|
74
|
+
syncBuf = '';
|
|
75
|
+
contextTokens = new Map();
|
|
76
|
+
polling = false;
|
|
77
|
+
stopped = false;
|
|
78
|
+
constructor(opts) {
|
|
79
|
+
this.opts = opts;
|
|
80
|
+
this.stateDir = opts.stateDir ?? 'data/wechat';
|
|
81
|
+
this.token = opts.token ?? '';
|
|
82
|
+
// 恢复持久化状态:token / sync 游标 / 上下文 token(回话关联必需)
|
|
83
|
+
try {
|
|
84
|
+
if (existsSync(join(this.stateDir, 'token.txt')))
|
|
85
|
+
this.token = this.token || readFileSync(join(this.stateDir, 'token.txt'), 'utf8').trim();
|
|
86
|
+
if (existsSync(join(this.stateDir, 'sync_buf.txt')))
|
|
87
|
+
this.syncBuf = readFileSync(join(this.stateDir, 'sync_buf.txt'), 'utf8').trim();
|
|
88
|
+
if (existsSync(join(this.stateDir, 'context_tokens.json'))) {
|
|
89
|
+
const tokens = JSON.parse(readFileSync(join(this.stateDir, 'context_tokens.json'), 'utf8'));
|
|
90
|
+
for (const [k, v] of Object.entries(tokens))
|
|
91
|
+
if (typeof v === 'string')
|
|
92
|
+
this.contextTokens.set(k, v);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
/* 状态文件损坏则忽略,走全新登录 */
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
persistState() {
|
|
100
|
+
try {
|
|
101
|
+
mkdirSync(this.stateDir, { recursive: true });
|
|
102
|
+
writeFileSync(join(this.stateDir, 'token.txt'), this.token, 'utf8');
|
|
103
|
+
writeFileSync(join(this.stateDir, 'sync_buf.txt'), this.syncBuf, 'utf8');
|
|
104
|
+
writeFileSync(join(this.stateDir, 'context_tokens.json'), JSON.stringify(Object.fromEntries(this.contextTokens)), 'utf8');
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
/* 持久化失败不阻断 */
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
async connect() {
|
|
111
|
+
if (!this.token) {
|
|
112
|
+
console.log('[wechat] 未配置 WECHAT_TOKEN:尝试实验性扫码登录(v0.2b)…');
|
|
113
|
+
void this.tryLogin().then((ok) => { if (ok)
|
|
114
|
+
this.startPolling(); });
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
this.startPolling();
|
|
118
|
+
}
|
|
119
|
+
base() {
|
|
120
|
+
return (this.opts.baseUrl ?? DEFAULT_BASE).replace(/\/$/, '');
|
|
121
|
+
}
|
|
122
|
+
async apiFetch(endpoint, body, timeoutMs = 45000) {
|
|
123
|
+
const controller = new AbortController();
|
|
124
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
125
|
+
try {
|
|
126
|
+
const res = await fetch(`${this.base()}/${endpoint}`, {
|
|
127
|
+
method: 'POST',
|
|
128
|
+
headers: {
|
|
129
|
+
'content-type': 'application/json',
|
|
130
|
+
AuthorizationType: 'ilink_bot_token',
|
|
131
|
+
Authorization: `Bearer ${this.token}`,
|
|
132
|
+
'X-WECHAT-UIN': String(Math.floor(Math.random() * 0x7fffffff)),
|
|
133
|
+
},
|
|
134
|
+
body: JSON.stringify(body),
|
|
135
|
+
signal: controller.signal,
|
|
136
|
+
});
|
|
137
|
+
if (!res.ok)
|
|
138
|
+
throw new Error(`iLink ${endpoint} HTTP ${res.status}`);
|
|
139
|
+
return (await res.json());
|
|
140
|
+
}
|
|
141
|
+
finally {
|
|
142
|
+
clearTimeout(timer);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/** 实验性扫码登录:打印二维码(liteapp URL 渲染为 QR),轮询扫码状态,成功后持久化 token。 */
|
|
146
|
+
async tryLogin() {
|
|
147
|
+
try {
|
|
148
|
+
const res = await fetch(`${this.base()}/ilink/bot/get_bot_qrcode?bot_type=3`);
|
|
149
|
+
const d = (await res.json().catch(() => null));
|
|
150
|
+
if (!d?.qrcode || !d.qrcode_img_content) {
|
|
151
|
+
console.warn(`[wechat] 获取登录二维码失败: ${JSON.stringify(d).slice(0, 200)}`);
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
console.log('[wechat] 请用微信扫描下方二维码完成 iLink ClawBot 登录:');
|
|
155
|
+
qrcode.generate(d.qrcode_img_content, { small: true });
|
|
156
|
+
for (let i = 0; i < 60 && !this.stopped; i++) {
|
|
157
|
+
await new Promise((r) => setTimeout(r, 3000));
|
|
158
|
+
try {
|
|
159
|
+
const st = (await fetch(`${this.base()}/ilink/bot/get_qrcode_status?qrcode=${encodeURIComponent(d.qrcode)}`).then((r) => r.json()));
|
|
160
|
+
if (st?.bot_token) {
|
|
161
|
+
this.token = st.bot_token;
|
|
162
|
+
this.persistState();
|
|
163
|
+
console.log('[wechat] 扫码成功,已保存 token,开始收消息');
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
/* 状态轮询瞬时失败继续重试 */
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
console.log('[wechat] 扫码超时(3 分钟未确认);重启适配器可重新生成二维码');
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
console.warn(`[wechat] 登录流程异常: ${error instanceof Error ? error.message : String(error)}`);
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
startPolling() {
|
|
180
|
+
if (this.polling)
|
|
181
|
+
return;
|
|
182
|
+
this.polling = true;
|
|
183
|
+
void this.pollLoop();
|
|
184
|
+
}
|
|
185
|
+
async pollLoop() {
|
|
186
|
+
while (!this.stopped && this.token) {
|
|
187
|
+
try {
|
|
188
|
+
const data = await this.apiFetch('ilink/bot/getupdates', buildGetUpdatesBody(this.syncBuf));
|
|
189
|
+
if (typeof data.get_updates_buf === 'string') {
|
|
190
|
+
this.syncBuf = data.get_updates_buf;
|
|
191
|
+
this.persistState();
|
|
192
|
+
}
|
|
193
|
+
for (const msg of data.msgs ?? []) {
|
|
194
|
+
const normalized = parseWeChatUpdate(msg);
|
|
195
|
+
if (!normalized)
|
|
196
|
+
continue;
|
|
197
|
+
if (msg.context_token)
|
|
198
|
+
this.contextTokens.set(normalized.userId, msg.context_token);
|
|
199
|
+
const button = this.pendingButtons.match(normalized.chatId, normalized.text);
|
|
200
|
+
if (button) {
|
|
201
|
+
this.replyCb?.(button.id, { chatId: normalized.chatId, userId: normalized.userId });
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
this.messageCb?.(normalized);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
catch (error) {
|
|
208
|
+
if (this.stopped)
|
|
209
|
+
break;
|
|
210
|
+
console.warn(`[wechat] 轮询失败: ${error instanceof Error ? error.message : String(error)}`);
|
|
211
|
+
await new Promise((r) => setTimeout(r, 5000));
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
this.polling = false;
|
|
215
|
+
}
|
|
216
|
+
async send(chatId, payload) {
|
|
217
|
+
if (!this.token)
|
|
218
|
+
throw new Error('wechat 未登录(无 token),无法发送');
|
|
219
|
+
if (payload.buttons?.length)
|
|
220
|
+
this.pendingButtons.set(chatId, payload.buttons);
|
|
221
|
+
const text = buildNumberedReplyText(payload.text, payload.buttons ?? []);
|
|
222
|
+
const contextToken = this.contextTokens.get(chatId) ?? '';
|
|
223
|
+
for (const chunk of chunkText(text)) {
|
|
224
|
+
await this.apiFetch('ilink/bot/sendmessage', buildSendMessageBody(chatId, chunk, contextToken));
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
onMessage(cb) { this.messageCb = cb; }
|
|
228
|
+
onReply(cb) { this.replyCb = cb; }
|
|
229
|
+
status() { return { connected: Boolean(this.token) }; }
|
|
230
|
+
}
|
|
231
|
+
//# sourceMappingURL=wechat.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wechat.js","sourceRoot":"","sources":["../../src/adapters/wechat.ts"],"names":[],"mappings":"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"}
|
package/dist/commands.d.ts
CHANGED
|
@@ -18,6 +18,20 @@ export type ParsedCommand = {
|
|
|
18
18
|
} | {
|
|
19
19
|
kind: 'cronrm';
|
|
20
20
|
taskId: string;
|
|
21
|
+
} | {
|
|
22
|
+
kind: 'remember';
|
|
23
|
+
text: string;
|
|
24
|
+
} | {
|
|
25
|
+
kind: 'recall';
|
|
26
|
+
query: string;
|
|
27
|
+
} | {
|
|
28
|
+
kind: 'forget';
|
|
29
|
+
memoryId: string;
|
|
30
|
+
} | {
|
|
31
|
+
kind: 'remind';
|
|
32
|
+
text: string;
|
|
33
|
+
inMinutes: number | null;
|
|
34
|
+
atTime: string | null;
|
|
21
35
|
};
|
|
22
36
|
export declare function parseCommand(text: string): ParsedCommand | null;
|
|
23
37
|
export declare const HELP_TEXT: string;
|
package/dist/commands.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
// cron 语法:/cron <分 时 日 月 周> <需求>(schedule 为 5 个空白分隔字段)
|
|
2
2
|
const CRON_RE = /^\/cron\s+(\S+\s+\S+\s+\S+\s+\S+\s+\S+)\s+(.+)$/;
|
|
3
|
+
// /remind in N 分钟/小时/天 <text>(也支持 min/minutes/hour/hours/day/days);或 /remind at HH:MM <text>
|
|
4
|
+
const REMIND_IN_RE = /^\/remind\s+in\s+(\d+)\s+(\S+)\s+(.+)$/i;
|
|
5
|
+
const REMIND_AT_RE = /^\/remind\s+at\s+(\d{1,2}:\d{2})\s+(.+)$/i;
|
|
3
6
|
export function parseCommand(text) {
|
|
4
7
|
const trimmed = text.trim();
|
|
5
8
|
if (trimmed === '/trace')
|
|
@@ -21,6 +24,25 @@ export function parseCommand(text) {
|
|
|
21
24
|
const cronrm = trimmed.match(/^\/cronrm\s+(\S+)$/);
|
|
22
25
|
if (cronrm)
|
|
23
26
|
return { kind: 'cronrm', taskId: cronrm[1] };
|
|
27
|
+
const remember = trimmed.match(/^\/remember\s+(.+)$/);
|
|
28
|
+
if (remember)
|
|
29
|
+
return { kind: 'remember', text: remember[1] };
|
|
30
|
+
const recall = trimmed.match(/^\/recall\s*(.*)$/);
|
|
31
|
+
if (recall)
|
|
32
|
+
return { kind: 'recall', query: recall[1].trim() };
|
|
33
|
+
const forget = trimmed.match(/^\/forget\s+(\S+)$/);
|
|
34
|
+
if (forget)
|
|
35
|
+
return { kind: 'forget', memoryId: forget[1] };
|
|
36
|
+
const remindIn = trimmed.match(REMIND_IN_RE);
|
|
37
|
+
if (remindIn) {
|
|
38
|
+
const unit = (remindIn[2] ?? '').toLowerCase();
|
|
39
|
+
const n = Number(remindIn[1]);
|
|
40
|
+
const minutes = /^(小|h|hour)/.test(unit) ? n * 60 : /^(天|d)/.test(unit) ? n * 1440 : n;
|
|
41
|
+
return { kind: 'remind', text: remindIn[3], inMinutes: minutes, atTime: null };
|
|
42
|
+
}
|
|
43
|
+
const remindAt = trimmed.match(REMIND_AT_RE);
|
|
44
|
+
if (remindAt)
|
|
45
|
+
return { kind: 'remind', text: remindAt[2], inMinutes: null, atTime: remindAt[1] };
|
|
24
46
|
return null;
|
|
25
47
|
}
|
|
26
48
|
export const HELP_TEXT = [
|
|
@@ -30,6 +52,10 @@ export const HELP_TEXT = [
|
|
|
30
52
|
'/cron <分 时 日 月 周> <需求> — 定时任务',
|
|
31
53
|
'/crons — 查看定时任务列表',
|
|
32
54
|
'/cronrm <任务id> — 删除定时任务',
|
|
55
|
+
'/remind in 10 分钟 <提醒内容> — 一次性定时提醒(也支持 at HH:MM)',
|
|
56
|
+
'/remember <事实> — 记住关于我的事',
|
|
57
|
+
'/recall <关键词> — 回忆相关记忆',
|
|
58
|
+
'/forget <记忆id> — 删除一条记忆',
|
|
33
59
|
'/agents — 查看子任务状态',
|
|
34
60
|
'/new — 重置会话',
|
|
35
61
|
].join('\n');
|
package/dist/commands.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"commands.js","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"commands.js","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
CHANGED
package/dist/config.js
CHANGED
|
@@ -6,6 +6,7 @@ import { SlackAdapter } from './adapters/slack.js';
|
|
|
6
6
|
import { FeishuAdapter } from './adapters/feishu.js';
|
|
7
7
|
import { DingTalkAdapter } from './adapters/dingtalk.js';
|
|
8
8
|
import { WeComAdapter } from './adapters/wecom.js';
|
|
9
|
+
import { WeChatAdapter } from './adapters/wechat.js';
|
|
9
10
|
export function parseAdapterIds(raw) {
|
|
10
11
|
const ids = raw.split(',').map((s) => s.trim()).filter(Boolean);
|
|
11
12
|
return ids.length > 0 ? ids : ['cli'];
|
|
@@ -46,6 +47,9 @@ export function createAdapter(id, env) {
|
|
|
46
47
|
token: env.wecomToken, encodingAESKey: env.wecomEncodingAESKey,
|
|
47
48
|
callbackPort: Number(env.wecomCallbackPort ?? 3193),
|
|
48
49
|
});
|
|
50
|
+
case 'wechat':
|
|
51
|
+
// 实验性(v0.2b):token 可缺省——未配置时启动扫码登录(iLink/ClawBot)
|
|
52
|
+
return new WeChatAdapter({ token: env.wechatToken, stateDir: env.wechatStateDir ?? 'data/wechat' });
|
|
49
53
|
default:
|
|
50
54
|
throw new Error(`unknown adapter: ${id}`);
|
|
51
55
|
}
|
|
@@ -68,6 +72,8 @@ export function adapterEnvFromProcess(env = process.env) {
|
|
|
68
72
|
wecomToken: env.WECOM_TOKEN,
|
|
69
73
|
wecomEncodingAESKey: env.WECOM_ENCODING_AES_KEY,
|
|
70
74
|
wecomCallbackPort: env.WECOM_CALLBACK_PORT,
|
|
75
|
+
wechatToken: env.WECHAT_TOKEN,
|
|
76
|
+
wechatStateDir: env.WECHAT_STATE_DIR,
|
|
71
77
|
asrApiKey: env.ASR_API_KEY,
|
|
72
78
|
asrBaseUrl: env.ASR_BASE_URL,
|
|
73
79
|
asrModel: env.ASR_MODEL,
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { GatewayClient, type ServerEvent } from '@dsh-overdrive/sdk';
|
|
|
2
2
|
import type { Adapter, OutboundPayload } from './adapter.js';
|
|
3
3
|
import { CliAdapter } from './adapters/cli.js';
|
|
4
4
|
import { type AsrTranscriber } from './asr.js';
|
|
5
|
+
import { MemoryStore } from './memory.js';
|
|
5
6
|
/**
|
|
6
7
|
* message.delta → 打字指示去重:同一 turn 内首个 delta 触发一次 typing,
|
|
7
8
|
* complete 后复位,下一 turn 可再次触发。纯逻辑,便于单测。
|
|
@@ -22,7 +23,11 @@ export interface WireOptions {
|
|
|
22
23
|
allowAll?: boolean;
|
|
23
24
|
/** ASR 转写器;配置了 API key 时启用,语音消息转成文本再发给 agent。 */
|
|
24
25
|
asr?: AsrTranscriber;
|
|
26
|
+
/** 记忆系统(OpenClaw 式长期记忆);未提供则记忆命令返回不可用。 */
|
|
27
|
+
memory?: MemoryStore;
|
|
25
28
|
}
|
|
29
|
+
/** 纯函数:一次性提醒的时间 → cron 5 字段表达式(分钟精度)。 */
|
|
30
|
+
export declare function remindSchedule(minutes: number, atTime: string | null, now?: Date): string;
|
|
26
31
|
/** 单个适配器的接线:白名单 → 命令面/upsert → sendMessage;按钮 → resolveApproval;事件流经轨迹聚合。 */
|
|
27
32
|
export declare function wireAdapter(adapter: Adapter, client: GatewayClient, opts: WireOptions): Promise<void>;
|
|
28
33
|
export { CliAdapter };
|
package/dist/index.js
CHANGED
|
@@ -6,6 +6,7 @@ import { parseCommand, HELP_TEXT } from './commands.js';
|
|
|
6
6
|
import { TrajectoryAggregator, formatTrajectorySummary } from './trajectory.js';
|
|
7
7
|
import { createStatusServer } from './status.js';
|
|
8
8
|
import { createTranscriber } from './asr.js';
|
|
9
|
+
import { MemoryStore, memoryScope, formatMemories } from './memory.js';
|
|
9
10
|
/**
|
|
10
11
|
* message.delta → 打字指示去重:同一 turn 内首个 delta 触发一次 typing,
|
|
11
12
|
* complete 后复位,下一 turn 可再次触发。纯逻辑,便于单测。
|
|
@@ -58,8 +59,22 @@ export function planOutbound(ev) {
|
|
|
58
59
|
return null;
|
|
59
60
|
}
|
|
60
61
|
}
|
|
61
|
-
/**
|
|
62
|
-
|
|
62
|
+
/** 纯函数:一次性提醒的时间 → cron 5 字段表达式(分钟精度)。 */
|
|
63
|
+
export function remindSchedule(minutes, atTime, now = new Date()) {
|
|
64
|
+
const target = new Date(now);
|
|
65
|
+
if (atTime) {
|
|
66
|
+
const [h, m] = atTime.split(':').map(Number);
|
|
67
|
+
target.setHours(h, m, 0, 0);
|
|
68
|
+
if (target <= now)
|
|
69
|
+
target.setDate(target.getDate() + 1); // 已过则明天
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
target.setMinutes(target.getMinutes() + minutes);
|
|
73
|
+
}
|
|
74
|
+
return `${target.getMinutes()} ${target.getHours()} ${target.getDate()} ${target.getMonth() + 1} *`;
|
|
75
|
+
}
|
|
76
|
+
/** 命令面分发:/trace /new /task /cron /agents /help /remind /remember /recall /forget(M4 + v0.3)。 */
|
|
77
|
+
async function handleCommand(adapter, client, command, sessionId, chatId, aggregator, memory) {
|
|
63
78
|
switch (command.kind) {
|
|
64
79
|
case 'trace': {
|
|
65
80
|
const summary = aggregator.recentSummary(sessionId);
|
|
@@ -84,7 +99,12 @@ async function handleCommand(adapter, client, command, sessionId, chatId, aggreg
|
|
|
84
99
|
case 'crons': {
|
|
85
100
|
const res = await client.listTasks();
|
|
86
101
|
const text = res.tasks.length
|
|
87
|
-
? res.tasks.map((task) =>
|
|
102
|
+
? res.tasks.map((task) => {
|
|
103
|
+
const next = task.nextRunAt
|
|
104
|
+
? new Date(task.nextRunAt).toLocaleString('zh-CN', { hour12: false })
|
|
105
|
+
: '(无下次触发)';
|
|
106
|
+
return `- \`${task.id}\` ${task.schedule} — ${task.prompt}(下次 ${next})`;
|
|
107
|
+
}).join('\n')
|
|
88
108
|
: '暂无定时任务。';
|
|
89
109
|
await adapter.send(chatId, { text: `⏰ 定时任务(${res.tasks.length}):\n${text}` });
|
|
90
110
|
return;
|
|
@@ -100,6 +120,50 @@ async function handleCommand(adapter, client, command, sessionId, chatId, aggreg
|
|
|
100
120
|
await adapter.send(chatId, { text: '(M4 简化)子任务状态由 agent 汇报,/task 派发' });
|
|
101
121
|
return;
|
|
102
122
|
}
|
|
123
|
+
case 'remember': {
|
|
124
|
+
if (!memory) {
|
|
125
|
+
await adapter.send(chatId, { text: '记忆系统未启用。' });
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
const scope = memoryScope(adapter.id, sessionId.split(':')[2] ?? '');
|
|
129
|
+
const entry = memory.add(scope, command.text);
|
|
130
|
+
await adapter.send(chatId, { text: `✅ 已记住(\`${entry.id}\`):${command.text}` });
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
case 'recall': {
|
|
134
|
+
if (!memory) {
|
|
135
|
+
await adapter.send(chatId, { text: '记忆系统未启用。' });
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
const scope = memoryScope(adapter.id, sessionId.split(':')[2] ?? '');
|
|
139
|
+
const hits = memory.search(scope, command.query);
|
|
140
|
+
if (hits.length === 0) {
|
|
141
|
+
await adapter.send(chatId, { text: '没有相关记忆。' });
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
await adapter.send(chatId, {
|
|
145
|
+
text: `🧠 ${hits.length} 条记忆:\n` + hits.map((e) => `- \`${e.id}\` ${e.text}`).join('\n'),
|
|
146
|
+
});
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
case 'forget': {
|
|
150
|
+
if (!memory) {
|
|
151
|
+
await adapter.send(chatId, { text: '记忆系统未启用。' });
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
const scope = memoryScope(adapter.id, sessionId.split(':')[2] ?? '');
|
|
155
|
+
const ok = memory.remove(scope, command.memoryId);
|
|
156
|
+
await adapter.send(chatId, { text: ok ? `🗑️ 已删除记忆 \`${command.memoryId}\`` : `未找到记忆 \`${command.memoryId}\`` });
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
case 'remind': {
|
|
160
|
+
const schedule = remindSchedule(command.inMinutes ?? 0, command.atTime);
|
|
161
|
+
await client.createTask({ sessionId, kind: 'cron', prompt: `⏰ 提醒:${command.text}`, schedule, once: true });
|
|
162
|
+
await adapter.send(chatId, {
|
|
163
|
+
text: `⏰ 已设置提醒「${command.text}」(${command.atTime ? `at ${command.atTime}` : `${command.inMinutes} 分钟后`},一次性)`,
|
|
164
|
+
});
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
103
167
|
case 'help': {
|
|
104
168
|
await adapter.send(chatId, { text: HELP_TEXT });
|
|
105
169
|
return;
|
|
@@ -124,9 +188,18 @@ export async function wireAdapter(adapter, client, opts) {
|
|
|
124
188
|
const command = parseCommand(msg.text);
|
|
125
189
|
if (command) {
|
|
126
190
|
console.log(`[gateway][${adapter.id}] 命令: ${JSON.stringify(command)}`);
|
|
127
|
-
await handleCommand(adapter, client, command, key, msg.chatId, aggregator);
|
|
191
|
+
await handleCommand(adapter, client, command, key, msg.chatId, aggregator, opts.memory);
|
|
128
192
|
return;
|
|
129
193
|
}
|
|
194
|
+
// OpenClaw 式记忆注入:入站消息前检索相关记忆,拼到文本后让 agent「记得你」
|
|
195
|
+
if (opts.memory) {
|
|
196
|
+
const scope = memoryScope(adapter.id, msg.userId);
|
|
197
|
+
const hits = opts.memory.search(scope, msg.text);
|
|
198
|
+
if (hits.length > 0) {
|
|
199
|
+
msg.text = `${msg.text}${formatMemories(hits)}`;
|
|
200
|
+
console.log(`[gateway][${adapter.id}] 注入 ${hits.length} 条相关记忆`);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
130
203
|
// ASR 语音转写:配置了 API key 时把语音消息转成文本;失败/未配置走原降级路径
|
|
131
204
|
if (msg.media?.kind === 'voice' && opts.asr?.enabled) {
|
|
132
205
|
const transcript = await opts.asr.transcribe(msg.media);
|
|
@@ -198,7 +271,7 @@ export async function wireAdapter(adapter, client, opts) {
|
|
|
198
271
|
}
|
|
199
272
|
async function main() {
|
|
200
273
|
const dshBaseUrl = process.env.DSH_BASE_URL ?? 'http://127.0.0.1:3191';
|
|
201
|
-
const dshToken = process.env.DSH_TOKEN ?? 'dev-token';
|
|
274
|
+
const dshToken = process.env.DSH_OVERDRIVE_TOKEN ?? process.env.DSH_TOKEN ?? 'dev-token';
|
|
202
275
|
const allowlist = (process.env.ALLOWLIST ?? '')
|
|
203
276
|
.split(',').map((s) => s.trim()).filter(Boolean);
|
|
204
277
|
const allowAll = process.env.ALLOW_ALL === '1';
|
|
@@ -211,12 +284,14 @@ async function main() {
|
|
|
211
284
|
});
|
|
212
285
|
if (asr.enabled)
|
|
213
286
|
console.log('[gateway] ASR 语音转写已启用');
|
|
287
|
+
const memory = new MemoryStore(process.env.MEMORY_FILE ?? 'data/memory.json');
|
|
288
|
+
console.log(`[gateway] 记忆系统已启用(文件: ${process.env.MEMORY_FILE ?? 'data/memory.json'})`);
|
|
214
289
|
const client = new GatewayClient(dshBaseUrl, dshToken);
|
|
215
290
|
await client.health(); // 确认 DSH 侧(或 mock)活着
|
|
216
291
|
const adapters = adapterIds.map((id) => createAdapter(id, env));
|
|
217
292
|
for (const adapter of adapters) {
|
|
218
293
|
await adapter.connect();
|
|
219
|
-
await wireAdapter(adapter, client, { allowlist, allowAll, asr });
|
|
294
|
+
await wireAdapter(adapter, client, { allowlist, allowAll, asr, memory });
|
|
220
295
|
console.log(`[gateway] ${adapter.id} 适配器已就绪`);
|
|
221
296
|
}
|
|
222
297
|
const consolePort = Number(process.env.GATEWAY_CONSOLE_PORT ?? 3190);
|