openclaw-openagent 1.0.12 → 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 (132) hide show
  1. package/dist/index.js +2 -0
  2. package/dist/src/config/config-schema.d.ts +9 -0
  3. package/dist/src/config/config-schema.js +6 -0
  4. package/dist/src/messaging/executor.d.ts +2 -3
  5. package/dist/src/messaging/executor.js +4 -31
  6. package/dist/src/plugin-ui/assets/openagent-override.js +124 -18
  7. package/dist/src/proxy/auth-proxy.js +5 -0
  8. package/dist/src/runtime/update-checker.d.ts +18 -0
  9. package/dist/src/runtime/update-checker.js +216 -0
  10. package/index.ts +3 -0
  11. package/package.json +1 -3
  12. package/src/app/channel-tools.ts +0 -249
  13. package/src/app/discovery-tools.ts +0 -273
  14. package/src/app/download-file-tool.ts +0 -133
  15. package/src/app/hooks.ts +0 -144
  16. package/src/app/index.ts +0 -84
  17. package/src/app/messaging-tools.ts +0 -79
  18. package/src/app/ops-tools.ts +0 -155
  19. package/src/app/remote-agent-tool.ts +0 -762
  20. package/src/app/types.ts +0 -68
  21. package/src/app/upload-file-tool.ts +0 -411
  22. package/src/app/verbose-preflight.ts +0 -190
  23. package/src/auth/config.ts +0 -266
  24. package/src/auth/credential-manager.ts +0 -146
  25. package/src/auth/index.ts +0 -24
  26. package/src/auth/verify.ts +0 -99
  27. package/src/channel.ts +0 -566
  28. package/src/compat.ts +0 -94
  29. package/src/config/config-schema.ts +0 -39
  30. package/src/messaging/aggregator.ts +0 -120
  31. package/src/messaging/collector.ts +0 -89
  32. package/src/messaging/executor.ts +0 -72
  33. package/src/messaging/inbound.ts +0 -150
  34. package/src/messaging/index.ts +0 -11
  35. package/src/messaging/mention-protocol.ts +0 -94
  36. package/src/messaging/process-c2c-request.ts +0 -564
  37. package/src/messaging/process-message.ts +0 -373
  38. package/src/messaging/scheduler.ts +0 -55
  39. package/src/messaging/types.ts +0 -38
  40. package/src/plugin-ui/adapters/oc-2026-04.js +0 -103
  41. package/src/plugin-ui/adapters/oc-2026-05.js +0 -125
  42. package/src/plugin-ui/adapters/oc-2026-06.js +0 -125
  43. package/src/plugin-ui/adapters/oc-unknown.js +0 -48
  44. package/src/plugin-ui/assets/agentbook-icon.svg +0 -5
  45. package/src/plugin-ui/assets/bg.png +0 -0
  46. package/src/plugin-ui/assets/icon.png +0 -0
  47. package/src/plugin-ui/assets/magic.svg +0 -5
  48. package/src/plugin-ui/assets/openagent-override.js +0 -11156
  49. package/src/plugin-ui/build.cjs +0 -463
  50. package/src/plugin-ui/index.ts +0 -18
  51. package/src/plugin-ui/modules/agent-book/panel/agent-book.js +0 -570
  52. package/src/plugin-ui/modules/agent-book/panel/agent-card.js +0 -170
  53. package/src/plugin-ui/modules/agent-book/panel/agent-data.js +0 -643
  54. package/src/plugin-ui/modules/agent-book/panel/inject-ui.js +0 -665
  55. package/src/plugin-ui/modules/agent-book/panel/mention-state.js +0 -206
  56. package/src/plugin-ui/modules/agent-book/panel/styles.js +0 -833
  57. package/src/plugin-ui/modules/agent-book/panel/theme-adapter.js +0 -86
  58. package/src/plugin-ui/modules/agent-book/remote-agent-tool/components-core.js +0 -295
  59. package/src/plugin-ui/modules/agent-book/remote-agent-tool/thought-chain-card.js +0 -211
  60. package/src/plugin-ui/modules/agent-book/scanner.js +0 -131
  61. package/src/plugin-ui/modules/agent-book/travelcard/travel-cards.js +0 -500
  62. package/src/plugin-ui/modules/agent-book/travelcard/travel-engine.js +0 -652
  63. package/src/plugin-ui/modules/agent-book/travelcard/travel-styles.js +0 -256
  64. package/src/plugin-ui/modules/loader/bootstrap.js +0 -38
  65. package/src/plugin-ui/modules/loader/shared-state.js +0 -838
  66. package/src/plugin-ui/modules/loader/ws-intercept.js +0 -199
  67. package/src/plugin-ui/modules/remote-agent/chunk-parser.js +0 -161
  68. package/src/plugin-ui/modules/remote-agent/execution-card.js +0 -530
  69. package/src/plugin-ui/modules/remote-agent/markdown-renderer.js +0 -256
  70. package/src/plugin-ui/modules/remote-agent/media-links.js +0 -151
  71. package/src/plugin-ui/modules/remote-agent/native-style-adapter.js +0 -134
  72. package/src/plugin-ui/modules/remote-agent/output-card.js +0 -342
  73. package/src/plugin-ui/modules/remote-agent/progress-store.js +0 -363
  74. package/src/plugin-ui/modules/remote-agent/render-hooks.js +0 -1700
  75. package/src/plugin-ui/modules/remote-agent/styles.js +0 -896
  76. package/src/plugin-ui/modules/remote-agent/tool-card-model.js +0 -130
  77. package/src/plugin-ui/postinstall-deploy.cjs +0 -52
  78. package/src/plugin-ui/ui-extension-loader/backup.ts +0 -92
  79. package/src/plugin-ui/ui-extension-loader/index.ts +0 -276
  80. package/src/plugin-ui/ui-extension-loader/locator.ts +0 -152
  81. package/src/plugin-ui/ui-extension-loader/manifest.ts +0 -57
  82. package/src/plugin-ui/ui-extension-loader/registry-regex.ts +0 -944
  83. package/src/plugin-ui/ui-extension-loader/removed-extensions.ts +0 -70
  84. package/src/plugin-ui/ui-extension-loader/types.ts +0 -72
  85. package/src/proxy/auth-proxy.ts +0 -409
  86. package/src/runtime/account.ts +0 -549
  87. package/src/runtime/index.ts +0 -7
  88. package/src/runtime/plugin-runtime.ts +0 -94
  89. package/src/runtime/registry.ts +0 -71
  90. package/src/sdk/CLASS_MAP.md +0 -143
  91. package/src/sdk/index.d.ts +0 -126
  92. package/src/sdk/index.js +0 -23990
  93. package/src/sdk/modules/cloud-search-module.js +0 -1117
  94. package/src/sdk/modules/follow-module.js +0 -1069
  95. package/src/sdk/modules/group-module.js +0 -7397
  96. package/src/sdk/modules/relationship-module.js +0 -2269
  97. package/src/sdk/modules/signaling-module.js +0 -1468
  98. package/src/sdk/modules/tim-upload-plugin.js +0 -730
  99. package/src/sdk/node-env/http-request.js +0 -90
  100. package/src/sdk/node-env/index.js +0 -57
  101. package/src/sdk/node-env/storage.js +0 -114
  102. package/src/sdk/package.json +0 -10
  103. package/src/sdk/tsconfig.json +0 -16
  104. package/src/state/pending-invocation-store.ts +0 -43
  105. package/src/state/store.ts +0 -756
  106. package/src/tim/c2c.ts +0 -451
  107. package/src/tim/channels.ts +0 -364
  108. package/src/tim/client.ts +0 -330
  109. package/src/tim/index.ts +0 -18
  110. package/src/tim/messages.ts +0 -166
  111. package/src/tim/sdk-logger-init.ts +0 -50
  112. package/src/tools.ts +0 -10
  113. package/src/transport/factory.ts +0 -95
  114. package/src/transport/oasn/index.ts +0 -17
  115. package/src/transport/oasn/oasn-agent-card.ts +0 -111
  116. package/src/transport/oasn/oasn-discovery.ts +0 -108
  117. package/src/transport/oasn/oasn-files.ts +0 -210
  118. package/src/transport/oasn/oasn-http.ts +0 -529
  119. package/src/transport/oasn/oasn-invocation.ts +0 -604
  120. package/src/transport/oasn/oasn-normalize.ts +0 -133
  121. package/src/transport/oasn/oasn-register.ts +0 -33
  122. package/src/transport/oasn/oasn-transport.ts +0 -318
  123. package/src/transport/oasn/oasn-types.ts +0 -404
  124. package/src/transport/tim/index.ts +0 -8
  125. package/src/transport/tim/tim-transport.ts +0 -515
  126. package/src/transport/types.ts +0 -538
  127. package/src/types/openclaw-plugin-sdk-media-store.d.ts +0 -9
  128. package/src/types/openclaw.d.ts +0 -97
  129. package/src/types/tencentcloud-chat.d.ts +0 -15
  130. package/src/util/http.ts +0 -113
  131. package/src/util/logger.ts +0 -131
  132. 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';