openclaw-openagent 1.0.13 → 1.0.14

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 (128) hide show
  1. package/dist/src/messaging/executor.d.ts +2 -3
  2. package/dist/src/messaging/executor.js +4 -31
  3. package/dist/src/plugin-ui/assets/openagent-override.js +4 -5
  4. package/dist/src/runtime/update-checker.d.ts +1 -1
  5. package/dist/src/runtime/update-checker.js +9 -46
  6. package/package.json +1 -3
  7. package/src/app/channel-tools.ts +0 -249
  8. package/src/app/discovery-tools.ts +0 -273
  9. package/src/app/download-file-tool.ts +0 -133
  10. package/src/app/hooks.ts +0 -144
  11. package/src/app/index.ts +0 -84
  12. package/src/app/messaging-tools.ts +0 -79
  13. package/src/app/ops-tools.ts +0 -155
  14. package/src/app/remote-agent-tool.ts +0 -762
  15. package/src/app/types.ts +0 -68
  16. package/src/app/upload-file-tool.ts +0 -411
  17. package/src/app/verbose-preflight.ts +0 -190
  18. package/src/auth/config.ts +0 -266
  19. package/src/auth/credential-manager.ts +0 -146
  20. package/src/auth/index.ts +0 -24
  21. package/src/auth/verify.ts +0 -99
  22. package/src/channel.ts +0 -566
  23. package/src/compat.ts +0 -94
  24. package/src/config/config-schema.ts +0 -45
  25. package/src/messaging/aggregator.ts +0 -120
  26. package/src/messaging/collector.ts +0 -89
  27. package/src/messaging/executor.ts +0 -72
  28. package/src/messaging/inbound.ts +0 -150
  29. package/src/messaging/index.ts +0 -11
  30. package/src/messaging/mention-protocol.ts +0 -94
  31. package/src/messaging/process-c2c-request.ts +0 -564
  32. package/src/messaging/process-message.ts +0 -373
  33. package/src/messaging/scheduler.ts +0 -55
  34. package/src/messaging/types.ts +0 -38
  35. package/src/plugin-ui/adapters/oc-2026-04.js +0 -103
  36. package/src/plugin-ui/adapters/oc-2026-05.js +0 -125
  37. package/src/plugin-ui/adapters/oc-2026-06.js +0 -125
  38. package/src/plugin-ui/adapters/oc-unknown.js +0 -48
  39. package/src/plugin-ui/assets/agentbook-icon.svg +0 -5
  40. package/src/plugin-ui/assets/bg.png +0 -0
  41. package/src/plugin-ui/assets/icon.png +0 -0
  42. package/src/plugin-ui/assets/magic.svg +0 -5
  43. package/src/plugin-ui/assets/openagent-override.js +0 -11263
  44. package/src/plugin-ui/build.cjs +0 -463
  45. package/src/plugin-ui/index.ts +0 -18
  46. package/src/plugin-ui/modules/agent-book/panel/agent-book.js +0 -570
  47. package/src/plugin-ui/modules/agent-book/panel/agent-card.js +0 -170
  48. package/src/plugin-ui/modules/agent-book/panel/agent-data.js +0 -643
  49. package/src/plugin-ui/modules/agent-book/panel/inject-ui.js +0 -665
  50. package/src/plugin-ui/modules/agent-book/panel/mention-state.js +0 -206
  51. package/src/plugin-ui/modules/agent-book/panel/styles.js +0 -832
  52. package/src/plugin-ui/modules/agent-book/panel/theme-adapter.js +0 -86
  53. package/src/plugin-ui/modules/agent-book/remote-agent-tool/components-core.js +0 -295
  54. package/src/plugin-ui/modules/agent-book/remote-agent-tool/thought-chain-card.js +0 -211
  55. package/src/plugin-ui/modules/agent-book/scanner.js +0 -131
  56. package/src/plugin-ui/modules/agent-book/travelcard/travel-cards.js +0 -500
  57. package/src/plugin-ui/modules/agent-book/travelcard/travel-engine.js +0 -652
  58. package/src/plugin-ui/modules/agent-book/travelcard/travel-styles.js +0 -262
  59. package/src/plugin-ui/modules/loader/bootstrap.js +0 -133
  60. package/src/plugin-ui/modules/loader/shared-state.js +0 -838
  61. package/src/plugin-ui/modules/loader/ws-intercept.js +0 -199
  62. package/src/plugin-ui/modules/remote-agent/chunk-parser.js +0 -161
  63. package/src/plugin-ui/modules/remote-agent/execution-card.js +0 -530
  64. package/src/plugin-ui/modules/remote-agent/markdown-renderer.js +0 -256
  65. package/src/plugin-ui/modules/remote-agent/media-links.js +0 -151
  66. package/src/plugin-ui/modules/remote-agent/native-style-adapter.js +0 -134
  67. package/src/plugin-ui/modules/remote-agent/output-card.js +0 -342
  68. package/src/plugin-ui/modules/remote-agent/progress-store.js +0 -363
  69. package/src/plugin-ui/modules/remote-agent/render-hooks.js +0 -1700
  70. package/src/plugin-ui/modules/remote-agent/styles.js +0 -903
  71. package/src/plugin-ui/modules/remote-agent/tool-card-model.js +0 -130
  72. package/src/plugin-ui/postinstall-deploy.cjs +0 -52
  73. package/src/plugin-ui/ui-extension-loader/backup.ts +0 -92
  74. package/src/plugin-ui/ui-extension-loader/index.ts +0 -276
  75. package/src/plugin-ui/ui-extension-loader/locator.ts +0 -152
  76. package/src/plugin-ui/ui-extension-loader/manifest.ts +0 -57
  77. package/src/plugin-ui/ui-extension-loader/registry-regex.ts +0 -944
  78. package/src/plugin-ui/ui-extension-loader/removed-extensions.ts +0 -70
  79. package/src/plugin-ui/ui-extension-loader/types.ts +0 -72
  80. package/src/proxy/auth-proxy.ts +0 -414
  81. package/src/runtime/account.ts +0 -549
  82. package/src/runtime/index.ts +0 -7
  83. package/src/runtime/plugin-runtime.ts +0 -94
  84. package/src/runtime/registry.ts +0 -71
  85. package/src/runtime/update-checker.ts +0 -286
  86. package/src/sdk/CLASS_MAP.md +0 -143
  87. package/src/sdk/index.d.ts +0 -126
  88. package/src/sdk/index.js +0 -23990
  89. package/src/sdk/modules/cloud-search-module.js +0 -1117
  90. package/src/sdk/modules/follow-module.js +0 -1069
  91. package/src/sdk/modules/group-module.js +0 -7397
  92. package/src/sdk/modules/relationship-module.js +0 -2269
  93. package/src/sdk/modules/signaling-module.js +0 -1468
  94. package/src/sdk/modules/tim-upload-plugin.js +0 -730
  95. package/src/sdk/node-env/http-request.js +0 -90
  96. package/src/sdk/node-env/index.js +0 -57
  97. package/src/sdk/node-env/storage.js +0 -114
  98. package/src/sdk/package.json +0 -10
  99. package/src/sdk/tsconfig.json +0 -16
  100. package/src/state/pending-invocation-store.ts +0 -43
  101. package/src/state/store.ts +0 -756
  102. package/src/tim/c2c.ts +0 -451
  103. package/src/tim/channels.ts +0 -364
  104. package/src/tim/client.ts +0 -330
  105. package/src/tim/index.ts +0 -18
  106. package/src/tim/messages.ts +0 -166
  107. package/src/tim/sdk-logger-init.ts +0 -50
  108. package/src/tools.ts +0 -10
  109. package/src/transport/factory.ts +0 -95
  110. package/src/transport/oasn/index.ts +0 -17
  111. package/src/transport/oasn/oasn-agent-card.ts +0 -111
  112. package/src/transport/oasn/oasn-discovery.ts +0 -108
  113. package/src/transport/oasn/oasn-files.ts +0 -210
  114. package/src/transport/oasn/oasn-http.ts +0 -529
  115. package/src/transport/oasn/oasn-invocation.ts +0 -604
  116. package/src/transport/oasn/oasn-normalize.ts +0 -133
  117. package/src/transport/oasn/oasn-register.ts +0 -33
  118. package/src/transport/oasn/oasn-transport.ts +0 -318
  119. package/src/transport/oasn/oasn-types.ts +0 -404
  120. package/src/transport/tim/index.ts +0 -8
  121. package/src/transport/tim/tim-transport.ts +0 -515
  122. package/src/transport/types.ts +0 -538
  123. package/src/types/openclaw-plugin-sdk-media-store.d.ts +0 -9
  124. package/src/types/openclaw.d.ts +0 -97
  125. package/src/types/tencentcloud-chat.d.ts +0 -15
  126. package/src/util/http.ts +0 -113
  127. package/src/util/logger.ts +0 -131
  128. package/src/util/url-resolver.ts +0 -77
@@ -1,364 +0,0 @@
1
- /**
2
- * TIM Channels — Channel management operations via SDK
3
- *
4
- * All operations use the TIM SDK (not REST API).
5
- * listChannels uses the auth service endpoint.
6
- */
7
-
8
- import { logger } from '../util/logger.js';
9
- import { httpGet } from '../util/http.js';
10
- import type { TIMClient } from './client.js';
11
-
12
- const AUTH_BASE = 'https://auth.ai-talk.live';
13
-
14
- // ── Types ──
15
-
16
- export interface ChannelInfo {
17
- id: string;
18
- name: string;
19
- desc: string;
20
- memberCount?: number;
21
- }
22
-
23
- export interface MemberInfo {
24
- id: string;
25
- nick: string;
26
- role: string;
27
- avatar?: string;
28
- }
29
-
30
- // ── Functions ──
31
-
32
- /**
33
- * List all available channels from the auth service.
34
- */
35
- export async function listChannels(client: TIMClient): Promise<ChannelInfo[]> {
36
- logger.info('[tim/channels] listChannels: fetching from auth service');
37
- try {
38
- const headers: Record<string, string> = {};
39
- if (client.userSig) {
40
- headers['Authorization'] = `Bearer ${client.userSig}`;
41
- }
42
-
43
- const data = await httpGet<
44
- | ChannelInfo[]
45
- | { data?: ChannelInfo[]; channels?: ChannelInfo[] }
46
- >(`${AUTH_BASE}/api/channels`, { headers });
47
-
48
- const rawList: unknown[] = Array.isArray(data) ? data : (
49
- (data as { data?: unknown[] }).data
50
- || (data as { channels?: unknown[] }).channels
51
- || []
52
- );
53
-
54
- logger.info(`[tim/channels] listChannels: found ${rawList.length} channels`);
55
- return rawList.map((item) => {
56
- const ch = item as Record<string, unknown>;
57
- return {
58
- id: (ch['channel_id'] || ch['id'] || ch['channelId'] || ch['GroupId'] || '') as string,
59
- name: (ch['name'] || ch['Name'] || '') as string,
60
- desc: (ch['description'] || ch['desc'] || ch['Introduction'] || '') as string,
61
- memberCount: (ch['member_count'] || ch['memberCount'] || 0) as number,
62
- };
63
- });
64
- } catch (err) {
65
- logger.error(`[tim/channels] listChannels error: ${(err as Error).message}`);
66
- throw err;
67
- }
68
- }
69
-
70
- /**
71
- * Search channels by keyword (client-side filter of listChannels).
72
- */
73
- export async function searchChannels(
74
- client: TIMClient,
75
- keyword: string,
76
- ): Promise<ChannelInfo[]> {
77
- logger.info(`[tim/channels] searchChannels: keyword="${keyword}"`);
78
- const all = await listChannels(client);
79
- const lower = keyword.toLowerCase();
80
- return all.filter(ch =>
81
- ch.name.toLowerCase().includes(lower) ||
82
- ch.desc.toLowerCase().includes(lower) ||
83
- ch.id.toLowerCase().includes(lower),
84
- );
85
- }
86
-
87
- /**
88
- * TIM SDK error code for "already a group member".
89
- * @see https://cloud.tencent.com/document/product/269/75394
90
- */
91
- const ERR_ALREADY_GROUP_MEMBER = 10013;
92
-
93
- /**
94
- * Join a group channel.
95
- *
96
- * Per TIM Web SDK docs, joinGroup may:
97
- * - Resolve with status JOIN_STATUS_SUCCESS / ALREADY_IN_GROUP / WAIT_APPROVAL
98
- * - Reject with error code 10013 ("already group member")
99
- *
100
- * We handle both paths and always refresh the SDK conversation cache
101
- * afterwards to prevent sendMessage error 2801.
102
- *
103
- * @see https://cloud.tencent.com/document/product/269/75395
104
- */
105
- export async function joinChannel(
106
- client: TIMClient,
107
- channelId: string,
108
- ): Promise<{ success: boolean; message: string }> {
109
- const chat = client._chat;
110
- if (!chat || !client.isReady) throw new Error('Not connected');
111
-
112
- logger.info(`[tim/channels] joinChannel: ${channelId}`);
113
-
114
- try {
115
- await chat.joinGroup({ groupID: channelId });
116
- logger.info(`[tim/channels] joinChannel: joined ${channelId}`);
117
- } catch (err) {
118
- const code = (err as { code?: number })?.code;
119
- if (code === ERR_ALREADY_GROUP_MEMBER) {
120
- logger.info(`[tim/channels] joinChannel: already a member of ${channelId} (code=${code})`);
121
- } else {
122
- logger.error(`[tim/channels] joinChannel failed: code=${code} msg=${(err as Error).message}`);
123
- throw err;
124
- }
125
- }
126
-
127
- // Force SDK to cache group conversation profile.
128
- // Required because: (1) ALREADY_IN_GROUP does not refresh cache,
129
- // and (2) even new joins may not populate the conversation list.
130
- // Without this, sendMessage fails with error 2801 (group profile cache miss).
131
- try {
132
- await chat.getConversationProfile(`GROUP${channelId}`);
133
- logger.info(`[tim/channels] joinChannel: conversation profile cached for ${channelId}`);
134
- } catch (cacheErr) {
135
- const cacheCode = (cacheErr as { code?: number })?.code;
136
- logger.warn(`[tim/channels] joinChannel: failed to cache conversation profile for ${channelId} (code=${cacheCode})`);
137
- }
138
-
139
- return { success: true, message: `Joined ${channelId}` };
140
- }
141
-
142
- /**
143
- * Leave a group channel.
144
- */
145
- export async function leaveChannel(
146
- client: TIMClient,
147
- channelId: string,
148
- ): Promise<{ success: boolean; message: string }> {
149
- const chat = client._chat;
150
- if (!chat || !client.isReady) throw new Error('Not connected');
151
-
152
- logger.info(`[tim/channels] leaveChannel: ${channelId}`);
153
- try {
154
- await chat.quitGroup(channelId);
155
- return { success: true, message: `Left ${channelId}` };
156
- } catch (err) {
157
- const code = (err as { code?: number })?.code;
158
- logger.error(`[tim/channels] leaveChannel failed: code=${code} msg=${(err as Error).message} ch=${channelId}`);
159
- throw err;
160
- }
161
- }
162
-
163
- /**
164
- * Get members of a group channel.
165
- */
166
- export async function getMembers(
167
- client: TIMClient,
168
- channelId: string,
169
- ): Promise<MemberInfo[]> {
170
- const chat = client._chat;
171
- if (!chat || !client.isReady) throw new Error('Not connected');
172
-
173
- logger.info(`[tim/channels] getMembers: ${channelId}`);
174
- try {
175
- const res = await chat.getGroupMemberList({
176
- groupID: channelId,
177
- count: 100,
178
- offset: 0,
179
- }) as {
180
- data?: {
181
- memberList?: Array<{
182
- userID: string;
183
- nick?: string;
184
- role?: string;
185
- avatar?: string;
186
- }>;
187
- };
188
- };
189
-
190
- const members = (res.data?.memberList || []).map(m => ({
191
- id: m.userID,
192
- nick: m.nick || m.userID,
193
- role: m.role || 'Member',
194
- avatar: m.avatar || '',
195
- }));
196
- logger.info(`[tim/channels] getMembers: ${channelId} found ${members.length} members`);
197
- return members;
198
- } catch (err) {
199
- const code = (err as { code?: number })?.code;
200
- logger.error(`[tim/channels] getMembers failed: code=${code} msg=${(err as Error).message} ch=${channelId}`);
201
- throw err;
202
- }
203
- }
204
-
205
- /**
206
- * Create a new Community group channel.
207
- *
208
- * Uses GRP_COMMUNITY (社群) — supports large-scale membership, topics,
209
- * and free join/leave. Requires Tencent IM flagship plan with Community
210
- * feature enabled in console.
211
- *
212
- * groupID is always auto-generated by the SDK with @TGS#_ prefix.
213
- * Community groups reject custom groupIDs that don't start with @TGS#_
214
- * (SDK error ILLEGAL_GRP_ID / 2602).
215
- *
216
- * @see https://cloud.tencent.com/document/product/269/75394
217
- * @see https://cloud.tencent.com/document/product/269/1502 (群组类型)
218
- */
219
- export async function createChannel(
220
- client: TIMClient,
221
- name: string,
222
- description = '',
223
- skill = '',
224
- ): Promise<{ success: boolean; channelId: string }> {
225
- const chat = client._chat;
226
- if (!chat || !client.isReady) throw new Error('Not connected');
227
-
228
- logger.info(`[tim/channels] createChannel: type=Community name="${name}"`);
229
-
230
- try {
231
- // Community groups require groupID to start with @TGS#_ or be empty.
232
- // SDK auto-generates @TGS#_ prefixed ID when groupID is omitted.
233
- const result = await chat.createGroup({
234
- type: client._types.GRP_COMMUNITY,
235
- name,
236
- // groupID intentionally omitted — SDK auto-generates @TGS#_ prefixed ID
237
- introduction: description,
238
- notification: skill,
239
- isSupportTopic: false,
240
- });
241
-
242
- const actualId = result?.data?.group?.groupID;
243
- if (!actualId) throw new Error('SDK returned no groupID');
244
- logger.info(`[tim/channels] Channel created: ${actualId} (type=Community)`);
245
- return { success: true, channelId: actualId };
246
- } catch (err) {
247
- const code = (err as { code?: number })?.code;
248
- const msg = (err as { message?: string })?.message || String(err);
249
- logger.error(`[tim/channels] createChannel failed: code=${code} msg=${msg}`);
250
- throw err;
251
- }
252
- }
253
-
254
- /**
255
- * Get channel skill (stored in group notification field).
256
- */
257
- export async function getChannelSkill(
258
- client: TIMClient,
259
- channelId: string,
260
- ): Promise<string | null> {
261
- const chat = client._chat;
262
- if (!chat || !client.isReady) return null;
263
-
264
- try {
265
- const res = await chat.getGroupProfile({
266
- groupID: channelId,
267
- groupCustomFieldFilter: [],
268
- }) as { data?: { group?: { notification?: string } } };
269
-
270
- return res.data?.group?.notification || null;
271
- } catch (err) {
272
- const code = (err as { code?: number })?.code;
273
- logger.warn(`[tim/channels] getChannelSkill failed: code=${code} msg=${(err as Error).message} ch=${channelId}`);
274
- return null;
275
- }
276
- }
277
-
278
- /**
279
- * Set channel skill (stored in group notification field).
280
- */
281
- export async function setChannelSkill(
282
- client: TIMClient,
283
- channelId: string,
284
- skill: string,
285
- ): Promise<{ success: boolean }> {
286
- const chat = client._chat;
287
- if (!chat || !client.isReady) throw new Error('Not connected');
288
-
289
- logger.info(`[tim/channels] setChannelSkill: ${channelId}`);
290
- try {
291
- await chat.updateGroupProfile({
292
- groupID: channelId,
293
- notification: skill,
294
- });
295
- return { success: true };
296
- } catch (err) {
297
- const code = (err as { code?: number })?.code;
298
- logger.error(`[tim/channels] setChannelSkill failed: code=${code} msg=${(err as Error).message} ch=${channelId}`);
299
- throw err;
300
- }
301
- }
302
-
303
- /**
304
- * Get channel display name from group profile.
305
- */
306
- export async function getGroupName(
307
- client: TIMClient,
308
- channelId: string,
309
- ): Promise<string | null> {
310
- const chat = client._chat;
311
- if (!chat || !client.isReady) return null;
312
-
313
- try {
314
- const res = await chat.getGroupProfile({
315
- groupID: channelId,
316
- groupCustomFieldFilter: [],
317
- }) as { data?: { group?: { name?: string } } };
318
-
319
- return res.data?.group?.name || null;
320
- } catch (err) {
321
- const code = (err as { code?: number })?.code;
322
- logger.warn(`[tim/channels] getGroupName failed: code=${code} msg=${(err as Error).message} ch=${channelId}`);
323
- return null;
324
- }
325
- }
326
-
327
- /**
328
- * Read a single group-level custom field (AppDefinedData).
329
- *
330
- * Uses getGroupProfile with groupCustomFieldFilter to fetch only the
331
- * requested field. Returns null if field not set or fetch fails.
332
- *
333
- * @param fieldKey - The custom field key (e.g. 'gm_req_mention')
334
- */
335
- export async function getGroupCustomField(
336
- client: TIMClient,
337
- channelId: string,
338
- fieldKey: string,
339
- ): Promise<string | null> {
340
- const chat = client._chat;
341
- if (!chat || !client.isReady) return null;
342
-
343
- try {
344
- const res = await chat.getGroupProfile({
345
- groupID: channelId,
346
- groupCustomFieldFilter: [fieldKey],
347
- }) as {
348
- data?: {
349
- group?: {
350
- groupCustomField?: Array<{ key: string; value: string }>;
351
- };
352
- };
353
- };
354
-
355
- const fields = res.data?.group?.groupCustomField ?? [];
356
- const target = fields.find((f: { key: string; value: string }) => f.key === fieldKey);
357
- return target?.value ?? null;
358
- } catch (err) {
359
- const code = (err as { code?: number })?.code;
360
- logger.warn(`[tim/channels] getGroupCustomField(${fieldKey}) failed: code=${code} ch=${channelId}`);
361
- return null;
362
- }
363
- }
364
-
package/src/tim/client.ts DELETED
@@ -1,330 +0,0 @@
1
- /**
2
- * TIM Client — SDK connection core
3
- *
4
- * Manages TIM SDK lifecycle: connect, disconnect, auto-reconnect.
5
- * Emits 'message' events for incoming group chat messages.
6
- * Emits 'reconnected' when auto-reconnect succeeds.
7
- *
8
- * Internal noise containment: all SDK calls wrapped in try/catch,
9
- * known TIM noise errors logged at debug level.
10
- */
11
-
12
- import './sdk-logger-init.js'; // Must be before @tencentcloud/chat — redirects SDK logs to file
13
- // @ts-ignore — JS side-effect module, no types needed
14
- import '../sdk/node-env/index.js'; // Must be before @tencentcloud/chat — installs ws (no permessage-deflate), localStorage, rejection handler
15
- import TencentCloudChat from '../sdk/index.js';
16
- // @ts-ignore — vendor JS module, typed as unknown
17
- import GroupModule from '../sdk/modules/group-module.js';
18
- // @ts-ignore — vendor JS module, patched for Node.js
19
- import TIMUploadPlugin from '../sdk/modules/tim-upload-plugin.js';
20
- import { EventEmitter } from 'node:events';
21
-
22
- import { logger } from '../util/logger.js';
23
- import type { TIMCredentials } from '../auth/verify.js';
24
-
25
- // ── Types ──
26
-
27
- export interface RawPushMessage {
28
- id: string;
29
- channelId: string;
30
- seq: number;
31
- from: string;
32
- nick: string;
33
- text: string;
34
- time: number;
35
- mentionsMe: boolean;
36
- source: 'push';
37
- }
38
-
39
- // ── TIMClient ──
40
-
41
- export class TIMClient extends EventEmitter {
42
- private chat: ReturnType<typeof TencentCloudChat.create> | null = null;
43
- private _agentId: string = '';
44
- private _credentials: TIMCredentials | null = null;
45
- private _reconnecting = false;
46
- private _ready = false;
47
-
48
- get agentId(): string { return this._agentId; }
49
- get isReady(): boolean { return this._ready; }
50
- get userSig(): string | null { return this._credentials?.userSig ?? null; }
51
-
52
- /**
53
- * Connect to TIM SDK using verified credentials.
54
- */
55
- async connect(credentials: TIMCredentials): Promise<void> {
56
- this._agentId = credentials.userId;
57
- this._credentials = credentials;
58
-
59
- logger.info(`[tim/client] Connecting agentId=${this._agentId}`);
60
-
61
- // Create SDK instance
62
- this.chat = TencentCloudChat.create({
63
- SDKAppID: credentials.sdkAppId,
64
- modules: { 'group-module': GroupModule },
65
- });
66
- this.chat.setLogLevel(0); // ALL logs — WebSocket heartbeat, transport, message push (P0-A diagnosis)
67
-
68
- // Register tim-upload-plugin — enables createFileMessage/sendMessage for T4 file transfer
69
- // Must be after create(), before login(). SDK_READY triggers UploadModule._init() which looks up this plugin.
70
- // @see docs/audit/023-tim-upload-plugin-v1.md §4.7
71
- this.chat.registerPlugin({ 'tim-upload-plugin': TIMUploadPlugin });
72
- logger.info('[tim/client] tim-upload-plugin registered');
73
-
74
- // Wait for SDK_READY with guaranteed timer cleanup
75
- let readyTimeout: ReturnType<typeof setTimeout>;
76
- const sdkReadyPromise = new Promise<void>((resolve, reject) => {
77
- readyTimeout = setTimeout(
78
- () => reject(new Error('SDK_READY timeout (15s)')),
79
- 15_000,
80
- );
81
- this.chat!.on(TencentCloudChat.EVENT.SDK_READY, () => {
82
- clearTimeout(readyTimeout);
83
- this._ready = true;
84
- resolve();
85
- });
86
- });
87
-
88
- // Register event listeners
89
- this._registerListeners();
90
-
91
- logger.info('[tim/client] Calling TIM SDK login...');
92
- try {
93
- await this.chat.login({
94
- userID: credentials.userId,
95
- userSig: credentials.userSig,
96
- });
97
- await sdkReadyPromise;
98
- } catch (err) {
99
- clearTimeout(readyTimeout!);
100
- this._ready = false;
101
- throw err;
102
- }
103
-
104
- logger.info('[tim/client] SDK_READY — connected');
105
- }
106
-
107
- /**
108
- * Gracefully disconnect from TIM SDK.
109
- */
110
- async disconnect(): Promise<void> {
111
- this._reconnecting = false;
112
- if (this.chat) {
113
- try { await this.chat.logout(); } catch { /* ignore */ }
114
- this._ready = false;
115
- this._removeListeners();
116
- this.chat = null;
117
- }
118
- }
119
-
120
- // ── Event Listeners ──
121
-
122
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
123
- private _messageHandler: ((...args: any[]) => void) | null = null;
124
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
125
- private _notReadyHandler: ((...args: any[]) => void) | null = null;
126
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
127
- private _kickedHandler: ((...args: any[]) => void) | null = null;
128
-
129
- private _registerListeners(): void {
130
- if (!this.chat) {
131
- logger.error('[tim/client] _registerListeners: chat instance is null, cannot register');
132
- return;
133
- }
134
- logger.info('[tim/client] _registerListeners: installing SDK event handlers');
135
-
136
- // Disconnect detection
137
- this._notReadyHandler = () => {
138
- logger.warn('[tim/client] SDK_NOT_READY — connection lost');
139
- this._ready = false;
140
- void this._autoReconnect();
141
- };
142
- this.chat.on(TencentCloudChat.EVENT.SDK_NOT_READY, this._notReadyHandler);
143
-
144
- this._kickedHandler = (event: unknown) => {
145
- const ev = event as { data?: { type?: string } } | undefined;
146
- const reason = ev?.data?.type || 'unknown';
147
- logger.warn(`[tim/client] KICKED_OUT (reason=${reason})`);
148
- this._ready = false;
149
-
150
- switch (reason) {
151
- case 'multipleDevice':
152
- case 'multipleAccount':
153
- // Multi-device/page kick: do NOT reconnect to avoid kick loop
154
- logger.warn('[tim/client] Kicked by multi-device/account login, will not reconnect');
155
- break;
156
-
157
- case 'userSigExpired':
158
- // UserSig expired: old credentials are useless, notify upper layer
159
- logger.warn('[tim/client] UserSig expired, emitting credential_expired event');
160
- this.emit('credential_expired');
161
- break;
162
-
163
- case 'REST_API_Kick':
164
- default:
165
- // REST API kick or unknown: reconnect with existing credentials
166
- void this._autoReconnect();
167
- break;
168
- }
169
- };
170
- this.chat.on(TencentCloudChat.EVENT.KICKED_OUT, this._kickedHandler);
171
-
172
- // Push message listener
173
- this._messageHandler = (event: unknown) => {
174
- const ev = event as { data: unknown[] };
175
- const messages = ev.data as Array<{
176
- conversationType: string;
177
- type?: string;
178
- from: string;
179
- to: string;
180
- sequence: number;
181
- nick?: string;
182
- time: number;
183
- payload?: { text?: string; data?: string; description?: string; extension?: string };
184
- atUserList?: string[];
185
- }>;
186
-
187
- logger.info(`[tim/client] MESSAGE_RECEIVED fired: ${messages?.length ?? 0} message(s)`);
188
-
189
- if (!messages || messages.length === 0) {
190
- logger.warn('[tim/client] MESSAGE_RECEIVED: empty data array, nothing to process');
191
- return;
192
- }
193
-
194
- for (const msg of messages) {
195
- try {
196
- // ── C2C messages → emit for c2c.ts handler, skip group logic ──
197
- if (msg.conversationType === TencentCloudChat.TYPES.CONV_C2C) {
198
- logger.info(`[tim/client] C2C msg from=${msg.from}`);
199
- this.emit('c2c_message', {
200
- from: msg.from,
201
- payload: msg.payload,
202
- type: msg.type,
203
- time: msg.time,
204
- });
205
- continue;
206
- }
207
-
208
- // Only group messages
209
- if (msg.conversationType !== TencentCloudChat.TYPES.CONV_GROUP) {
210
- logger.debug(`[tim/client] skip non-group msg type=${msg.conversationType} from=${msg.from}`);
211
- continue;
212
- }
213
- // Skip own messages
214
- if (msg.from === this._agentId) {
215
- logger.debug(`[tim/client] skip self msg from=${msg.from} to=${msg.to}`);
216
- continue;
217
- }
218
-
219
- const text = msg.payload?.text || '';
220
- if (!text) {
221
- logger.debug(`[tim/client] skip empty text msg from=${msg.from} to=${msg.to}`);
222
- continue;
223
- }
224
-
225
- const channelId = msg.to;
226
- const seq = msg.sequence;
227
-
228
- const rawMessage: RawPushMessage = {
229
- id: `${channelId}:${seq}`,
230
- channelId,
231
- seq,
232
- from: msg.from,
233
- nick: msg.nick || msg.from,
234
- text,
235
- time: msg.time * 1000,
236
- mentionsMe: Array.isArray(msg.atUserList)
237
- && (msg.atUserList.includes(this._agentId)
238
- || msg.atUserList.includes(TencentCloudChat.TYPES.MSG_AT_ALL)),
239
- source: 'push',
240
- };
241
-
242
- logger.debug(`[tim/client] PUSH msg id=${rawMessage.id} ch=${channelId} from=${rawMessage.nick}`);
243
- this.emit('message', rawMessage);
244
- } catch (err) {
245
- logger.debug(`[tim/client] Error processing push message: ${(err as Error).message}`);
246
- }
247
- }
248
- };
249
- this.chat.on(TencentCloudChat.EVENT.MESSAGE_RECEIVED, this._messageHandler);
250
- }
251
-
252
- private _removeListeners(): void {
253
- if (!this.chat) return;
254
- if (this._notReadyHandler) {
255
- this.chat.off(TencentCloudChat.EVENT.SDK_NOT_READY, this._notReadyHandler);
256
- this._notReadyHandler = null;
257
- }
258
- if (this._kickedHandler) {
259
- this.chat.off(TencentCloudChat.EVENT.KICKED_OUT, this._kickedHandler);
260
- this._kickedHandler = null;
261
- }
262
- if (this._messageHandler) {
263
- this.chat.off(TencentCloudChat.EVENT.MESSAGE_RECEIVED, this._messageHandler);
264
- this._messageHandler = null;
265
- }
266
- }
267
-
268
- // ── Auto-reconnect ──
269
-
270
- private async _autoReconnect(maxRetries = 5): Promise<void> {
271
- if (this._reconnecting) {
272
- logger.debug('[tim/client] Reconnect already in progress');
273
- return;
274
- }
275
- if (!this._credentials) {
276
- logger.error('[tim/client] Cannot reconnect: missing credentials');
277
- return;
278
- }
279
-
280
- this._reconnecting = true;
281
- logger.warn('[tim/client] Starting auto-reconnect...');
282
-
283
- for (let attempt = 1; attempt <= maxRetries; attempt++) {
284
- if (!this._reconnecting) {
285
- logger.info('[tim/client] Reconnect cancelled');
286
- return;
287
- }
288
-
289
- const delay = Math.min(5000 * attempt, 25_000);
290
- logger.info(`[tim/client] Reconnect attempt ${attempt}/${maxRetries} in ${delay / 1000}s...`);
291
- await new Promise(r => setTimeout(r, delay));
292
-
293
- try {
294
- // Teardown old SDK instance
295
- if (this.chat) {
296
- this._removeListeners();
297
- try { await this.chat.logout(); } catch { /* ignore */ }
298
- this.chat = null;
299
- }
300
- this._ready = false;
301
-
302
- await this.connect(this._credentials);
303
-
304
- if (this._ready) {
305
- logger.info(`[tim/client] Auto-reconnect successful on attempt ${attempt}`);
306
- this._reconnecting = false;
307
- this.emit('reconnected');
308
- return;
309
- }
310
- } catch (err) {
311
- logger.error(`[tim/client] Reconnect attempt ${attempt}/${maxRetries} failed: ${(err as Error).message}`);
312
- }
313
- }
314
-
315
- this._reconnecting = false;
316
- logger.error(`[tim/client] All ${maxRetries} reconnect attempts failed`);
317
- }
318
-
319
- // ── Internal SDK access (for messages.ts and channels.ts) ──
320
-
321
- /** @internal */
322
- get _chat(): ReturnType<typeof TencentCloudChat.create> | null {
323
- return this.chat;
324
- }
325
-
326
- /** @internal */
327
- get _types(): typeof TencentCloudChat.TYPES {
328
- return TencentCloudChat.TYPES;
329
- }
330
- }
package/src/tim/index.ts DELETED
@@ -1,18 +0,0 @@
1
- /**
2
- * TIM — Module barrel export
3
- */
4
-
5
- export { TIMClient, type RawPushMessage } from './client.js';
6
- export { sendMessage, fetchMessages, mapToHistoryMessage, type HistoryMessage, type SendResult } from './messages.js';
7
- export {
8
- listChannels,
9
- searchChannels,
10
- joinChannel,
11
- leaveChannel,
12
- getMembers,
13
- createChannel,
14
- getChannelSkill,
15
- setChannelSkill,
16
- type ChannelInfo,
17
- type MemberInfo,
18
- } from './channels.js';