@openclaw/twitch 2026.2.21 → 2026.5.2-beta.1

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 (45) hide show
  1. package/README.md +2 -2
  2. package/api.ts +21 -0
  3. package/channel-plugin-api.ts +1 -0
  4. package/index.test.ts +13 -0
  5. package/index.ts +12 -16
  6. package/openclaw.plugin.json +224 -1
  7. package/package.json +36 -7
  8. package/runtime-api.ts +22 -0
  9. package/setup-entry.ts +9 -0
  10. package/setup-plugin-api.ts +3 -0
  11. package/src/access-control.test.ts +136 -239
  12. package/src/access-control.ts +11 -4
  13. package/src/actions.test.ts +74 -0
  14. package/src/actions.ts +7 -6
  15. package/src/client-manager-registry.ts +0 -28
  16. package/src/config-schema.test.ts +46 -0
  17. package/src/config-schema.ts +25 -21
  18. package/src/config.test.ts +147 -1
  19. package/src/config.ts +76 -15
  20. package/src/monitor.ts +126 -85
  21. package/src/outbound.test.ts +140 -75
  22. package/src/outbound.ts +4 -5
  23. package/src/plugin.test.ts +39 -1
  24. package/src/plugin.ts +176 -242
  25. package/src/probe.test.ts +1 -1
  26. package/src/probe.ts +16 -5
  27. package/src/resolver.ts +5 -3
  28. package/src/runtime.ts +8 -13
  29. package/src/send.test.ts +92 -59
  30. package/src/send.ts +18 -15
  31. package/src/setup-surface.test.ts +511 -0
  32. package/src/setup-surface.ts +520 -0
  33. package/src/status.test.ts +120 -153
  34. package/src/status.ts +1 -1
  35. package/src/test-fixtures.ts +1 -1
  36. package/src/token.test.ts +22 -1
  37. package/src/token.ts +8 -6
  38. package/src/twitch-client.test.ts +18 -25
  39. package/src/twitch-client.ts +5 -6
  40. package/src/types.ts +7 -46
  41. package/src/utils/twitch.ts +8 -2
  42. package/tsconfig.json +16 -0
  43. package/CHANGELOG.md +0 -21
  44. package/src/onboarding.test.ts +0 -316
  45. package/src/onboarding.ts +0 -417
@@ -17,16 +17,79 @@ describe("checkTwitchAccessControl", () => {
17
17
  channel: "testchannel",
18
18
  };
19
19
 
20
- describe("when no restrictions are configured", () => {
21
- it("allows messages that mention the bot (default requireMention)", () => {
22
- const message: TwitchChatMessage = {
20
+ function runAccessCheck(params: {
21
+ account?: Partial<TwitchAccountConfig>;
22
+ message?: Partial<TwitchChatMessage>;
23
+ }) {
24
+ return checkTwitchAccessControl({
25
+ message: {
23
26
  ...mockMessage,
27
+ ...params.message,
28
+ },
29
+ account: {
30
+ ...mockAccount,
31
+ ...params.account,
32
+ },
33
+ botUsername: "testbot",
34
+ });
35
+ }
36
+
37
+ function expectSingleRoleAllowed(params: {
38
+ role: NonNullable<TwitchAccountConfig["allowedRoles"]>[number];
39
+ message: Partial<TwitchChatMessage>;
40
+ }) {
41
+ const result = runAccessCheck({
42
+ account: { allowedRoles: [params.role] },
43
+ message: {
24
44
  message: "@testbot hello",
25
- };
26
- const result = checkTwitchAccessControl({
27
- message,
28
- account: mockAccount,
29
- botUsername: "testbot",
45
+ ...params.message,
46
+ },
47
+ });
48
+ expect(result.allowed).toBe(true);
49
+ return result;
50
+ }
51
+
52
+ function expectAllowedAccessCheck(params: {
53
+ account?: Partial<TwitchAccountConfig>;
54
+ message?: Partial<TwitchChatMessage>;
55
+ }) {
56
+ const result = runAccessCheck({
57
+ account: params.account,
58
+ message: {
59
+ message: "@testbot hello",
60
+ ...params.message,
61
+ },
62
+ });
63
+ expect(result.allowed).toBe(true);
64
+ return result;
65
+ }
66
+
67
+ function expectAllowFromBlocked(params: {
68
+ allowFrom: string[];
69
+ allowedRoles?: NonNullable<TwitchAccountConfig["allowedRoles"]>;
70
+ message?: Partial<TwitchChatMessage>;
71
+ reason: string;
72
+ }) {
73
+ const result = runAccessCheck({
74
+ account: {
75
+ allowFrom: params.allowFrom,
76
+ allowedRoles: params.allowedRoles,
77
+ },
78
+ message: {
79
+ message: "@testbot hello",
80
+ ...params.message,
81
+ },
82
+ });
83
+ expect(result.allowed).toBe(false);
84
+ expect(result.reason).toContain(params.reason);
85
+ }
86
+
87
+ describe("when no restrictions are configured", () => {
88
+ it("allows messages that mention the bot (default requireMention)", () => {
89
+ const result = runAccessCheck({
90
+ message: {
91
+ message: "@testbot hello",
92
+ },
30
93
  });
31
94
  expect(result.allowed).toBe(true);
32
95
  });
@@ -34,30 +97,20 @@ describe("checkTwitchAccessControl", () => {
34
97
 
35
98
  describe("requireMention default", () => {
36
99
  it("defaults to true when undefined", () => {
37
- const message: TwitchChatMessage = {
38
- ...mockMessage,
39
- message: "hello bot",
40
- };
41
-
42
- const result = checkTwitchAccessControl({
43
- message,
44
- account: mockAccount,
45
- botUsername: "testbot",
100
+ const result = runAccessCheck({
101
+ message: {
102
+ message: "hello bot",
103
+ },
46
104
  });
47
105
  expect(result.allowed).toBe(false);
48
106
  expect(result.reason).toContain("does not mention the bot");
49
107
  });
50
108
 
51
109
  it("allows mention when requireMention is undefined", () => {
52
- const message: TwitchChatMessage = {
53
- ...mockMessage,
54
- message: "@testbot hello",
55
- };
56
-
57
- const result = checkTwitchAccessControl({
58
- message,
59
- account: mockAccount,
60
- botUsername: "testbot",
110
+ const result = runAccessCheck({
111
+ message: {
112
+ message: "@testbot hello",
113
+ },
61
114
  });
62
115
  expect(result.allowed).toBe(true);
63
116
  });
@@ -65,52 +118,25 @@ describe("checkTwitchAccessControl", () => {
65
118
 
66
119
  describe("requireMention", () => {
67
120
  it("allows messages that mention the bot", () => {
68
- const account: TwitchAccountConfig = {
69
- ...mockAccount,
70
- requireMention: true,
71
- };
72
- const message: TwitchChatMessage = {
73
- ...mockMessage,
74
- message: "@testbot hello",
75
- };
76
-
77
- const result = checkTwitchAccessControl({
78
- message,
79
- account,
80
- botUsername: "testbot",
121
+ const result = runAccessCheck({
122
+ account: { requireMention: true },
123
+ message: { message: "@testbot hello" },
81
124
  });
82
125
  expect(result.allowed).toBe(true);
83
126
  });
84
127
 
85
128
  it("blocks messages that don't mention the bot", () => {
86
- const account: TwitchAccountConfig = {
87
- ...mockAccount,
88
- requireMention: true,
89
- };
90
-
91
- const result = checkTwitchAccessControl({
92
- message: mockMessage,
93
- account,
94
- botUsername: "testbot",
129
+ const result = runAccessCheck({
130
+ account: { requireMention: true },
95
131
  });
96
132
  expect(result.allowed).toBe(false);
97
133
  expect(result.reason).toContain("does not mention the bot");
98
134
  });
99
135
 
100
136
  it("is case-insensitive for bot username", () => {
101
- const account: TwitchAccountConfig = {
102
- ...mockAccount,
103
- requireMention: true,
104
- };
105
- const message: TwitchChatMessage = {
106
- ...mockMessage,
107
- message: "@TestBot hello",
108
- };
109
-
110
- const result = checkTwitchAccessControl({
111
- message,
112
- account,
113
- botUsername: "testbot",
137
+ const result = runAccessCheck({
138
+ account: { requireMention: true },
139
+ message: { message: "@TestBot hello" },
114
140
  });
115
141
  expect(result.allowed).toBe(true);
116
142
  });
@@ -118,62 +144,35 @@ describe("checkTwitchAccessControl", () => {
118
144
 
119
145
  describe("allowFrom allowlist", () => {
120
146
  it("allows users in the allowlist", () => {
121
- const account: TwitchAccountConfig = {
122
- ...mockAccount,
123
- allowFrom: ["123456", "789012"],
124
- };
125
- const message: TwitchChatMessage = {
126
- ...mockMessage,
127
- message: "@testbot hello",
128
- };
129
-
130
- const result = checkTwitchAccessControl({
131
- message,
132
- account,
133
- botUsername: "testbot",
147
+ const result = expectAllowedAccessCheck({
148
+ account: {
149
+ allowFrom: ["123456", "789012"],
150
+ },
134
151
  });
135
- expect(result.allowed).toBe(true);
136
152
  expect(result.matchKey).toBe("123456");
137
153
  expect(result.matchSource).toBe("allowlist");
138
154
  });
139
155
 
140
156
  it("blocks users not in allowlist when allowFrom is set", () => {
141
- const account: TwitchAccountConfig = {
142
- ...mockAccount,
157
+ expectAllowFromBlocked({
143
158
  allowFrom: ["789012"],
144
- };
145
- const message: TwitchChatMessage = {
146
- ...mockMessage,
147
- message: "@testbot hello",
148
- };
159
+ reason: "allowFrom",
160
+ });
161
+ });
149
162
 
150
- const result = checkTwitchAccessControl({
151
- message,
152
- account,
153
- botUsername: "testbot",
163
+ it("blocks everyone when allowFrom is explicitly empty", () => {
164
+ expectAllowFromBlocked({
165
+ allowFrom: [],
166
+ reason: "allowFrom",
154
167
  });
155
- expect(result.allowed).toBe(false);
156
- expect(result.reason).toContain("allowFrom");
157
168
  });
158
169
 
159
170
  it("blocks messages without userId", () => {
160
- const account: TwitchAccountConfig = {
161
- ...mockAccount,
171
+ expectAllowFromBlocked({
162
172
  allowFrom: ["123456"],
163
- };
164
- const message: TwitchChatMessage = {
165
- ...mockMessage,
166
- message: "@testbot hello",
167
- userId: undefined,
168
- };
169
-
170
- const result = checkTwitchAccessControl({
171
- message,
172
- account,
173
- botUsername: "testbot",
173
+ message: { userId: undefined },
174
+ reason: "user ID not available",
174
175
  });
175
- expect(result.allowed).toBe(false);
176
- expect(result.reason).toContain("user ID not available");
177
176
  });
178
177
 
179
178
  it("bypasses role checks when user is in allowlist", () => {
@@ -197,68 +196,30 @@ describe("checkTwitchAccessControl", () => {
197
196
  });
198
197
 
199
198
  it("blocks user with role when not in allowlist", () => {
200
- const account: TwitchAccountConfig = {
201
- ...mockAccount,
199
+ expectAllowFromBlocked({
202
200
  allowFrom: ["789012"],
203
201
  allowedRoles: ["moderator"],
204
- };
205
- const message: TwitchChatMessage = {
206
- ...mockMessage,
207
- message: "@testbot hello",
208
- userId: "123456",
209
- isMod: true,
210
- };
211
-
212
- const result = checkTwitchAccessControl({
213
- message,
214
- account,
215
- botUsername: "testbot",
202
+ message: { userId: "123456", isMod: true },
203
+ reason: "allowFrom",
216
204
  });
217
- expect(result.allowed).toBe(false);
218
- expect(result.reason).toContain("allowFrom");
219
205
  });
220
206
 
221
207
  it("blocks user not in allowlist even when roles configured", () => {
222
- const account: TwitchAccountConfig = {
223
- ...mockAccount,
208
+ expectAllowFromBlocked({
224
209
  allowFrom: ["789012"],
225
210
  allowedRoles: ["moderator"],
226
- };
227
- const message: TwitchChatMessage = {
228
- ...mockMessage,
229
- message: "@testbot hello",
230
- userId: "123456",
231
- isMod: false,
232
- };
233
-
234
- const result = checkTwitchAccessControl({
235
- message,
236
- account,
237
- botUsername: "testbot",
211
+ message: { userId: "123456", isMod: false },
212
+ reason: "allowFrom",
238
213
  });
239
- expect(result.allowed).toBe(false);
240
- expect(result.reason).toContain("allowFrom");
241
214
  });
242
215
  });
243
216
 
244
217
  describe("allowedRoles", () => {
245
218
  it("allows users with matching role", () => {
246
- const account: TwitchAccountConfig = {
247
- ...mockAccount,
248
- allowedRoles: ["moderator"],
249
- };
250
- const message: TwitchChatMessage = {
251
- ...mockMessage,
252
- message: "@testbot hello",
253
- isMod: true,
254
- };
255
-
256
- const result = checkTwitchAccessControl({
257
- message,
258
- account,
259
- botUsername: "testbot",
219
+ const result = expectSingleRoleAllowed({
220
+ role: "moderator",
221
+ message: { isMod: true },
260
222
  });
261
- expect(result.allowed).toBe(true);
262
223
  expect(result.matchSource).toBe("role");
263
224
  });
264
225
 
@@ -304,98 +265,40 @@ describe("checkTwitchAccessControl", () => {
304
265
  });
305
266
 
306
267
  it("allows all users when role is 'all'", () => {
307
- const account: TwitchAccountConfig = {
308
- ...mockAccount,
309
- allowedRoles: ["all"],
310
- };
311
- const message: TwitchChatMessage = {
312
- ...mockMessage,
313
- message: "@testbot hello",
314
- };
315
-
316
- const result = checkTwitchAccessControl({
317
- message,
318
- account,
319
- botUsername: "testbot",
268
+ const result = expectAllowedAccessCheck({
269
+ account: {
270
+ allowedRoles: ["all"],
271
+ },
320
272
  });
321
- expect(result.allowed).toBe(true);
322
273
  expect(result.matchKey).toBe("all");
323
274
  });
324
275
 
325
276
  it("handles moderator role", () => {
326
- const account: TwitchAccountConfig = {
327
- ...mockAccount,
328
- allowedRoles: ["moderator"],
329
- };
330
- const message: TwitchChatMessage = {
331
- ...mockMessage,
332
- message: "@testbot hello",
333
- isMod: true,
334
- };
335
-
336
- const result = checkTwitchAccessControl({
337
- message,
338
- account,
339
- botUsername: "testbot",
277
+ expectSingleRoleAllowed({
278
+ role: "moderator",
279
+ message: { isMod: true },
340
280
  });
341
- expect(result.allowed).toBe(true);
342
281
  });
343
282
 
344
283
  it("handles subscriber role", () => {
345
- const account: TwitchAccountConfig = {
346
- ...mockAccount,
347
- allowedRoles: ["subscriber"],
348
- };
349
- const message: TwitchChatMessage = {
350
- ...mockMessage,
351
- message: "@testbot hello",
352
- isSub: true,
353
- };
354
-
355
- const result = checkTwitchAccessControl({
356
- message,
357
- account,
358
- botUsername: "testbot",
284
+ expectSingleRoleAllowed({
285
+ role: "subscriber",
286
+ message: { isSub: true },
359
287
  });
360
- expect(result.allowed).toBe(true);
361
288
  });
362
289
 
363
290
  it("handles owner role", () => {
364
- const account: TwitchAccountConfig = {
365
- ...mockAccount,
366
- allowedRoles: ["owner"],
367
- };
368
- const message: TwitchChatMessage = {
369
- ...mockMessage,
370
- message: "@testbot hello",
371
- isOwner: true,
372
- };
373
-
374
- const result = checkTwitchAccessControl({
375
- message,
376
- account,
377
- botUsername: "testbot",
291
+ expectSingleRoleAllowed({
292
+ role: "owner",
293
+ message: { isOwner: true },
378
294
  });
379
- expect(result.allowed).toBe(true);
380
295
  });
381
296
 
382
297
  it("handles vip role", () => {
383
- const account: TwitchAccountConfig = {
384
- ...mockAccount,
385
- allowedRoles: ["vip"],
386
- };
387
- const message: TwitchChatMessage = {
388
- ...mockMessage,
389
- message: "@testbot hello",
390
- isVip: true,
391
- };
392
-
393
- const result = checkTwitchAccessControl({
394
- message,
395
- account,
396
- botUsername: "testbot",
298
+ expectSingleRoleAllowed({
299
+ role: "vip",
300
+ message: { isVip: true },
397
301
  });
398
- expect(result.allowed).toBe(true);
399
302
  });
400
303
  });
401
304
 
@@ -421,21 +324,15 @@ describe("checkTwitchAccessControl", () => {
421
324
  });
422
325
 
423
326
  it("checks allowlist before allowedRoles", () => {
424
- const account: TwitchAccountConfig = {
425
- ...mockAccount,
426
- allowFrom: ["123456"],
427
- allowedRoles: ["owner"],
428
- };
429
- const message: TwitchChatMessage = {
430
- ...mockMessage,
431
- message: "@testbot hello",
432
- isOwner: false,
433
- };
434
-
435
- const result = checkTwitchAccessControl({
436
- message,
437
- account,
438
- botUsername: "testbot",
327
+ const result = runAccessCheck({
328
+ account: {
329
+ allowFrom: ["123456"],
330
+ allowedRoles: ["owner"],
331
+ },
332
+ message: {
333
+ message: "@testbot hello",
334
+ isOwner: false,
335
+ },
439
336
  });
440
337
  expect(result.allowed).toBe(true);
441
338
  expect(result.matchSource).toBe("allowlist");
@@ -1,9 +1,10 @@
1
+ import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
1
2
  import type { TwitchAccountConfig, TwitchChatMessage } from "./types.js";
2
3
 
3
4
  /**
4
5
  * Result of checking access control for a Twitch message
5
6
  */
6
- export type TwitchAccessControlResult = {
7
+ type TwitchAccessControlResult = {
7
8
  allowed: boolean;
8
9
  reason?: string;
9
10
  matchKey?: string;
@@ -40,7 +41,7 @@ export function checkTwitchAccessControl(params: {
40
41
 
41
42
  if (account.requireMention ?? true) {
42
43
  const mentions = extractMentions(message.message);
43
- if (!mentions.includes(botUsername.toLowerCase())) {
44
+ if (!mentions.includes(normalizeLowercaseStringOrEmpty(botUsername))) {
44
45
  return {
45
46
  allowed: false,
46
47
  reason: "message does not mention the bot (requireMention is enabled)",
@@ -48,8 +49,14 @@ export function checkTwitchAccessControl(params: {
48
49
  }
49
50
  }
50
51
 
51
- if (account.allowFrom && account.allowFrom.length > 0) {
52
+ if (account.allowFrom !== undefined) {
52
53
  const allowFrom = account.allowFrom;
54
+ if (allowFrom.length === 0) {
55
+ return {
56
+ allowed: false,
57
+ reason: "sender is not in allowFrom allowlist",
58
+ };
59
+ }
53
60
  const senderId = message.userId;
54
61
 
55
62
  if (!senderId) {
@@ -158,7 +165,7 @@ export function extractMentions(message: string): string[] {
158
165
  while ((match = mentionRegex.exec(message)) !== null) {
159
166
  const username = match[1];
160
167
  if (username) {
161
- mentions.push(username.toLowerCase());
168
+ mentions.push(normalizeLowercaseStringOrEmpty(username));
162
169
  }
163
170
  }
164
171
 
@@ -0,0 +1,74 @@
1
+ import { describe, expect, it, vi, beforeEach } from "vitest";
2
+ import { twitchMessageActions } from "./actions.js";
3
+ import type { ResolvedTwitchAccountContext } from "./config.js";
4
+ import { resolveTwitchAccountContext } from "./config.js";
5
+ import { twitchOutbound } from "./outbound.js";
6
+
7
+ vi.mock("./config.js", () => ({
8
+ DEFAULT_ACCOUNT_ID: "default",
9
+ resolveTwitchAccountContext: vi.fn(),
10
+ }));
11
+
12
+ vi.mock("./outbound.js", () => ({
13
+ twitchOutbound: {
14
+ sendText: vi.fn(),
15
+ },
16
+ }));
17
+
18
+ function createSecondaryAccountContext(accountId = "secondary"): ResolvedTwitchAccountContext {
19
+ return {
20
+ accountId,
21
+ account: {
22
+ channel: "secondary-channel",
23
+ username: "secondary",
24
+ accessToken: "oauth:secondary-token",
25
+ clientId: "secondary-client",
26
+ enabled: true,
27
+ },
28
+ tokenResolution: { source: "config", token: "oauth:secondary-token" },
29
+ configured: true,
30
+ availableAccountIds: ["default", "secondary"],
31
+ };
32
+ }
33
+
34
+ describe("twitchMessageActions", () => {
35
+ beforeEach(() => {
36
+ vi.clearAllMocks();
37
+ });
38
+
39
+ it("uses configured defaultAccount when action accountId is omitted", async () => {
40
+ vi.mocked(resolveTwitchAccountContext)
41
+ .mockImplementationOnce(() => createSecondaryAccountContext())
42
+ .mockImplementation((_cfg, accountId) =>
43
+ createSecondaryAccountContext(accountId?.trim() || "secondary"),
44
+ );
45
+ const sendText = twitchOutbound.sendText;
46
+ if (!sendText) {
47
+ throw new Error("twitchOutbound.sendText is unavailable");
48
+ }
49
+ vi.mocked(sendText).mockResolvedValue({
50
+ channel: "twitch",
51
+ messageId: "msg-1",
52
+ timestamp: 1,
53
+ });
54
+
55
+ await twitchMessageActions.handleAction!({
56
+ action: "send",
57
+ params: { message: "Hello!" },
58
+ cfg: {
59
+ channels: {
60
+ twitch: {
61
+ defaultAccount: "secondary",
62
+ },
63
+ },
64
+ },
65
+ } as never);
66
+
67
+ expect(twitchOutbound.sendText).toHaveBeenCalledWith(
68
+ expect.objectContaining({
69
+ accountId: "secondary",
70
+ to: "secondary-channel",
71
+ }),
72
+ );
73
+ });
74
+ });
package/src/actions.ts CHANGED
@@ -4,7 +4,8 @@
4
4
  * Handles tool-based actions for Twitch, such as sending messages.
5
5
  */
6
6
 
7
- import { DEFAULT_ACCOUNT_ID, getAccountConfig } from "./config.js";
7
+ import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
8
+ import { resolveTwitchAccountContext } from "./config.js";
8
9
  import { twitchOutbound } from "./outbound.js";
9
10
  import type { ChannelMessageActionAdapter, ChannelMessageActionContext } from "./types.js";
10
11
 
@@ -68,7 +69,7 @@ export const twitchMessageActions: ChannelMessageActionAdapter = {
68
69
  /**
69
70
  * List available actions for this channel.
70
71
  */
71
- listActions: () => [...TWITCH_ACTIONS],
72
+ describeMessageTool: () => ({ actions: [...TWITCH_ACTIONS] }),
72
73
 
73
74
  /**
74
75
  * Check if an action is supported.
@@ -130,12 +131,12 @@ export const twitchMessageActions: ChannelMessageActionAdapter = {
130
131
 
131
132
  const message = readStringParam(ctx.params, "message", { required: true });
132
133
  const to = readStringParam(ctx.params, "to", { required: false });
133
- const accountId = ctx.accountId ?? DEFAULT_ACCOUNT_ID;
134
+ const accountId = ctx.accountId ?? resolveTwitchAccountContext(ctx.cfg).accountId;
134
135
 
135
- const account = getAccountConfig(ctx.cfg, accountId);
136
+ const { account, availableAccountIds } = resolveTwitchAccountContext(ctx.cfg, accountId);
136
137
  if (!account) {
137
138
  return errorResponse(
138
- `Account not found: ${accountId}. Available accounts: ${Object.keys(ctx.cfg.channels?.twitch?.accounts ?? {}).join(", ") || "none"}`,
139
+ `Account not found: ${accountId}. Available accounts: ${availableAccountIds.join(", ") || "none"}`,
139
140
  );
140
141
  }
141
142
 
@@ -167,7 +168,7 @@ export const twitchMessageActions: ChannelMessageActionAdapter = {
167
168
  details: { ok: true },
168
169
  };
169
170
  } catch (error) {
170
- const errorMsg = error instanceof Error ? error.message : String(error);
171
+ const errorMsg = formatErrorMessage(error);
171
172
  return errorResponse(errorMsg);
172
173
  }
173
174
  },
@@ -85,31 +85,3 @@ export async function removeClientManager(accountId: string): Promise<void> {
85
85
  registry.delete(accountId);
86
86
  entry.logger.info(`Unregistered client manager for account: ${accountId}`);
87
87
  }
88
-
89
- /**
90
- * Disconnect and remove all client managers from the registry.
91
- *
92
- * @returns Promise that resolves when all cleanup is complete
93
- */
94
- export async function removeAllClientManagers(): Promise<void> {
95
- const promises = [...registry.keys()].map((accountId) => removeClientManager(accountId));
96
- await Promise.all(promises);
97
- }
98
-
99
- /**
100
- * Get the number of registered client managers.
101
- *
102
- * @returns The count of registered managers
103
- */
104
- export function getRegisteredClientManagerCount(): number {
105
- return registry.size;
106
- }
107
-
108
- /**
109
- * Clear all client managers without disconnecting.
110
- *
111
- * This is primarily for testing purposes.
112
- */
113
- export function _clearAllClientManagersForTest(): void {
114
- registry.clear();
115
- }