@shadowob/sdk 1.1.0 → 1.1.3

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/index.js CHANGED
@@ -1,10 +1,46 @@
1
1
  // src/client.ts
2
+ function sanitizeErrorBody(body) {
3
+ if (!body) return "(empty response)";
4
+ if (!/<[^>]+>/.test(body)) return body.slice(0, 500);
5
+ const titleMatch = /<title>([^<]+)<\/title>/i.exec(body);
6
+ if (titleMatch?.[1]) return titleMatch[1].trim();
7
+ const text = body.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
8
+ return text.slice(0, 200) || "(HTML error page)";
9
+ }
10
+ function contentDispositionFilename(header) {
11
+ if (!header) return null;
12
+ const utf8Match = /filename\*=UTF-8''([^;]+)/i.exec(header);
13
+ if (utf8Match?.[1]) {
14
+ try {
15
+ return decodeURIComponent(utf8Match[1].trim());
16
+ } catch {
17
+ return utf8Match[1].trim();
18
+ }
19
+ }
20
+ const quotedMatch = /filename="([^"]+)"/i.exec(header);
21
+ if (quotedMatch?.[1]) return quotedMatch[1];
22
+ const bareMatch = /filename=([^;]+)/i.exec(header);
23
+ return bareMatch?.[1]?.trim() ?? null;
24
+ }
2
25
  var ShadowClient = class {
3
26
  constructor(baseUrl, token) {
4
27
  this.token = token;
5
28
  this.baseUrl = baseUrl.replace(/\/api\/?$/, "");
6
29
  }
7
30
  baseUrl;
31
+ isShadowPrivateMediaUrl(value) {
32
+ if (value.startsWith("/shadow/uploads/") || value.startsWith("/api/media/signed/")) {
33
+ return true;
34
+ }
35
+ if (!/^https?:\/\//.test(value)) return false;
36
+ try {
37
+ const url = new URL(value);
38
+ const base = new URL(this.baseUrl);
39
+ return url.origin === base.origin && (url.pathname.startsWith("/shadow/uploads/") || url.pathname.startsWith("/api/media/signed/"));
40
+ } catch {
41
+ return false;
42
+ }
43
+ }
8
44
  async request(path, init) {
9
45
  const url = `${this.baseUrl}${path}`;
10
46
  const controller = new AbortController();
@@ -21,11 +57,19 @@ var ShadowClient = class {
21
57
  });
22
58
  if (!res.ok) {
23
59
  const body = await res.text().catch(() => "");
60
+ const message = sanitizeErrorBody(body);
24
61
  throw new Error(
25
- `Shadow API ${init?.method ?? "GET"} ${path} failed (${res.status}): ${body}`
62
+ `Shadow API ${init?.method ?? "GET"} ${path} failed (${res.status}): ${message}`
26
63
  );
27
64
  }
28
- return res.json();
65
+ const payload = await res.json();
66
+ if (payload && typeof payload === "object" && !Array.isArray(payload) && "ok" in payload && !("success" in payload)) {
67
+ return {
68
+ ...payload,
69
+ success: Boolean(payload.ok)
70
+ };
71
+ }
72
+ return payload;
29
73
  } finally {
30
74
  clearTimeout(timeout);
31
75
  }
@@ -41,7 +85,10 @@ var ShadowClient = class {
41
85
  });
42
86
  if (!res.ok) {
43
87
  const body = await res.text().catch(() => "");
44
- throw new Error(`Shadow API ${init?.method ?? "GET"} ${path} failed (${res.status}): ${body}`);
88
+ const message = sanitizeErrorBody(body);
89
+ throw new Error(
90
+ `Shadow API ${init?.method ?? "GET"} ${path} failed (${res.status}): ${message}`
91
+ );
45
92
  }
46
93
  return res;
47
94
  }
@@ -58,8 +105,23 @@ var ShadowClient = class {
58
105
  body: JSON.stringify(data)
59
106
  });
60
107
  }
61
- async refreshToken() {
62
- return this.request("/api/auth/refresh", { method: "POST" });
108
+ async startEmailLogin(data) {
109
+ return this.request("/api/auth/email/start", {
110
+ method: "POST",
111
+ body: JSON.stringify(data)
112
+ });
113
+ }
114
+ async verifyEmailLogin(data) {
115
+ return this.request("/api/auth/email/verify", {
116
+ method: "POST",
117
+ body: JSON.stringify(data)
118
+ });
119
+ }
120
+ async refreshToken(refreshToken) {
121
+ return this.request("/api/auth/refresh", {
122
+ method: "POST",
123
+ body: JSON.stringify({ refreshToken })
124
+ });
63
125
  }
64
126
  async getMe() {
65
127
  return this.request("/api/auth/me");
@@ -73,6 +135,45 @@ var ShadowClient = class {
73
135
  async disconnect() {
74
136
  return this.request("/api/auth/disconnect", { method: "POST" });
75
137
  }
138
+ async getMembership() {
139
+ return this.request("/api/membership/me");
140
+ }
141
+ async redeemInviteCode(code) {
142
+ return this.request("/api/membership/redeem-invite", {
143
+ method: "POST",
144
+ body: JSON.stringify({ code })
145
+ });
146
+ }
147
+ async launchPlay(data) {
148
+ return this.request("/api/play/launch", {
149
+ method: "POST",
150
+ body: JSON.stringify(data)
151
+ });
152
+ }
153
+ async getPlayCatalog() {
154
+ const response = await this.request("/api/play/catalog");
155
+ return response.plays;
156
+ }
157
+ // ── Official Model Proxy ──────────────────────────────────────────────
158
+ async listOfficialModelProxyModels() {
159
+ return this.request("/api/ai/v1/models");
160
+ }
161
+ async getOfficialModelProxyBilling() {
162
+ return this.request("/api/ai/v1/billing");
163
+ }
164
+ async createOfficialChatCompletion(data) {
165
+ return this.request("/api/ai/v1/chat/completions", {
166
+ method: "POST",
167
+ body: JSON.stringify(data)
168
+ });
169
+ }
170
+ async createOfficialChatCompletionStream(data) {
171
+ return this.requestRaw("/api/ai/v1/chat/completions", {
172
+ method: "POST",
173
+ headers: { "Content-Type": "application/json" },
174
+ body: JSON.stringify({ ...data, stream: true })
175
+ });
176
+ }
76
177
  // ── Agents ────────────────────────────────────────────────────────────
77
178
  async listAgents() {
78
179
  return this.request("/api/agents");
@@ -110,21 +211,62 @@ var ShadowClient = class {
110
211
  body: JSON.stringify({})
111
212
  });
112
213
  }
214
+ async reportAgentUsageSnapshot(agentId, snapshot) {
215
+ return this.request(`/api/agents/${agentId}/usage-snapshot`, {
216
+ method: "POST",
217
+ body: JSON.stringify(snapshot)
218
+ });
219
+ }
113
220
  async getAgentConfig(agentId) {
114
221
  return this.request(`/api/agents/${agentId}/config`);
115
222
  }
223
+ async updateAgentSlashCommands(agentId, commands) {
224
+ return this.request(`/api/agents/${agentId}/slash-commands`, {
225
+ method: "PUT",
226
+ body: JSON.stringify({ commands })
227
+ });
228
+ }
229
+ async getAgentSlashCommands(agentId) {
230
+ return this.request(`/api/agents/${agentId}/slash-commands`);
231
+ }
232
+ async listChannelSlashCommands(channelId) {
233
+ return this.request(
234
+ `/api/channels/${channelId}/slash-commands`
235
+ );
236
+ }
116
237
  // ── Agent Policies ────────────────────────────────────────────────────
117
238
  async listPolicies(agentId, serverId) {
118
- return this.request(`/api/agents/${agentId}/servers/${serverId}/policies`);
239
+ const policies = await this.request(`/api/agents/${agentId}/policies`);
240
+ if (!serverId) return policies;
241
+ return policies.filter((policy) => policy.serverId === serverId);
119
242
  }
120
243
  async upsertPolicy(agentId, serverId, data) {
121
- return this.request(`/api/agents/${agentId}/servers/${serverId}/policies`, {
244
+ const policy = {
245
+ serverId,
246
+ ...data.channelId !== void 0 ? { channelId: data.channelId } : {},
247
+ ...data.mentionOnly !== void 0 ? { mentionOnly: data.mentionOnly } : {},
248
+ ...data.reply !== void 0 ? { reply: data.reply } : {},
249
+ ...data.config !== void 0 ? { config: data.config } : {}
250
+ };
251
+ const results = await this.request(`/api/agents/${agentId}/policies`, {
122
252
  method: "PUT",
123
- body: JSON.stringify(data)
253
+ body: JSON.stringify({ policies: [policy] })
124
254
  });
255
+ const [result] = results;
256
+ if (!result) {
257
+ throw new Error(`Shadow API PUT /api/agents/${agentId}/policies returned no policy result`);
258
+ }
259
+ return result;
125
260
  }
126
261
  async deletePolicy(agentId, serverId, channelId) {
127
- return this.request(`/api/agents/${agentId}/servers/${serverId}/policies/${channelId}`, {
262
+ const policies = await this.listPolicies(agentId, serverId);
263
+ const policy = policies.find((entry) => entry.channelId === channelId);
264
+ if (!policy?.id) {
265
+ throw new Error(
266
+ `Shadow policy not found for agent ${agentId} in server ${serverId} channel ${channelId}`
267
+ );
268
+ }
269
+ return this.request(`/api/agents/${agentId}/policies/${policy.id}`, {
128
270
  method: "DELETE"
129
271
  });
130
272
  }
@@ -147,6 +289,9 @@ var ShadowClient = class {
147
289
  async getServer(serverIdOrSlug) {
148
290
  return this.request(`/api/servers/${serverIdOrSlug}`);
149
291
  }
292
+ async getServerAccess(serverIdOrSlug) {
293
+ return this.request(`/api/servers/${serverIdOrSlug}/access`);
294
+ }
150
295
  async updateServer(serverIdOrSlug, data) {
151
296
  return this.request(`/api/servers/${serverIdOrSlug}`, {
152
297
  method: "PATCH",
@@ -165,6 +310,20 @@ var ShadowClient = class {
165
310
  body: JSON.stringify(inviteCode ? { inviteCode } : {})
166
311
  });
167
312
  }
313
+ async requestServerAccess(serverIdOrSlug) {
314
+ return this.request(
315
+ `/api/servers/${serverIdOrSlug}/join-requests`,
316
+ {
317
+ method: "POST"
318
+ }
319
+ );
320
+ }
321
+ async reviewServerJoinRequest(requestId, status) {
322
+ return this.request(`/api/servers/join-requests/${requestId}`, {
323
+ method: "PATCH",
324
+ body: JSON.stringify({ status })
325
+ });
326
+ }
168
327
  async leaveServer(serverId) {
169
328
  return this.request(`/api/servers/${serverId}/leave`, { method: "POST" });
170
329
  }
@@ -181,7 +340,7 @@ var ShadowClient = class {
181
340
  return this.request(`/api/servers/${serverId}/members/${userId}`, { method: "DELETE" });
182
341
  }
183
342
  async regenerateInviteCode(serverId) {
184
- return this.request(`/api/servers/${serverId}/invite`, { method: "POST" });
343
+ return this.request(`/api/servers/${serverId}/invite/regenerate`, { method: "POST" });
185
344
  }
186
345
  async addAgentsToServer(serverId, agentIds) {
187
346
  return this.request(`/api/servers/${serverId}/agents`, {
@@ -206,6 +365,9 @@ var ShadowClient = class {
206
365
  const ch = await this.request(`/api/channels/${channelId}`);
207
366
  return { ...ch, description: ch.topic };
208
367
  }
368
+ async getChannelAccess(channelId) {
369
+ return this.request(`/api/channels/${channelId}/access`);
370
+ }
209
371
  async getChannelMembers(channelId) {
210
372
  return this.request(`/api/channels/${channelId}/members`);
211
373
  }
@@ -222,8 +384,8 @@ var ShadowClient = class {
222
384
  return this.request(`/api/channels/${channelId}`, { method: "DELETE" });
223
385
  }
224
386
  async reorderChannels(serverId, channelIds) {
225
- return this.request(`/api/servers/${serverId}/channels/reorder`, {
226
- method: "PUT",
387
+ return this.request(`/api/servers/${serverId}/channels/positions`, {
388
+ method: "PATCH",
227
389
  body: JSON.stringify({ channelIds })
228
390
  });
229
391
  }
@@ -233,18 +395,32 @@ var ShadowClient = class {
233
395
  body: JSON.stringify({ userId })
234
396
  });
235
397
  }
398
+ async requestChannelAccess(channelId) {
399
+ return this.request(
400
+ `/api/channels/${channelId}/join-requests`,
401
+ {
402
+ method: "POST"
403
+ }
404
+ );
405
+ }
406
+ async reviewChannelJoinRequest(requestId, status) {
407
+ return this.request(`/api/channel-join-requests/${requestId}`, {
408
+ method: "PATCH",
409
+ body: JSON.stringify({ status })
410
+ });
411
+ }
236
412
  async removeChannelMember(channelId, userId) {
237
413
  return this.request(`/api/channels/${channelId}/members/${userId}`, { method: "DELETE" });
238
414
  }
239
415
  // ── Channel Buddy Policy ─────────────────────────────────────────────
240
- async setBuddyPolicy(channelId, data) {
241
- return this.request(`/api/channels/${channelId}/buddy-policy`, {
416
+ async setBuddyPolicy(channelId, agentId, data) {
417
+ return this.request(`/api/channels/${channelId}/agents/${agentId}/policy`, {
242
418
  method: "PUT",
243
419
  body: JSON.stringify(data)
244
420
  });
245
421
  }
246
- async getBuddyPolicy(channelId) {
247
- return this.request(`/api/channels/${channelId}/buddy-policy`);
422
+ async getBuddyPolicy(channelId, agentId) {
423
+ return this.request(`/api/channels/${channelId}/agents/${agentId}/policy`);
248
424
  }
249
425
  // ── Messages ──────────────────────────────────────────────────────────
250
426
  async sendMessage(channelId, content, opts) {
@@ -254,10 +430,27 @@ var ShadowClient = class {
254
430
  content,
255
431
  ...opts?.threadId ? { threadId: opts.threadId } : {},
256
432
  ...opts?.replyToId ? { replyToId: opts.replyToId } : {},
257
- ...opts?.metadata ? { metadata: opts.metadata } : {}
433
+ ...opts?.mentions ? { mentions: opts.mentions } : {},
434
+ ...opts?.metadata ? { metadata: opts.metadata } : {},
435
+ ...opts?.attachments ? { attachments: opts.attachments } : {}
258
436
  })
259
437
  });
260
438
  }
439
+ async suggestMentions(input) {
440
+ const params = new URLSearchParams({
441
+ channelId: input.channelId,
442
+ trigger: input.trigger
443
+ });
444
+ if (input.query) params.set("q", input.query);
445
+ if (input.limit) params.set("limit", String(input.limit));
446
+ return this.request(`/api/mentions/suggest?${params}`);
447
+ }
448
+ async resolveMentions(input) {
449
+ return this.request("/api/mentions/resolve", {
450
+ method: "POST",
451
+ body: JSON.stringify(input)
452
+ });
453
+ }
261
454
  async getMessages(channelId, limit = 50, cursor) {
262
455
  const params = new URLSearchParams({ limit: String(limit) });
263
456
  if (cursor) params.set("cursor", cursor);
@@ -268,6 +461,20 @@ var ShadowClient = class {
268
461
  async getMessage(messageId) {
269
462
  return this.request(`/api/messages/${messageId}`);
270
463
  }
464
+ async submitInteractiveAction(messageId, input) {
465
+ return this.request(`/api/messages/${messageId}/interactive`, {
466
+ method: "POST",
467
+ body: JSON.stringify(input)
468
+ });
469
+ }
470
+ async getInteractiveState(messageId, blockId) {
471
+ const params = new URLSearchParams();
472
+ if (blockId) params.set("blockId", blockId);
473
+ const query = params.toString();
474
+ return this.request(
475
+ `/api/messages/${messageId}/interactive-state${query ? `?${query}` : ""}`
476
+ );
477
+ }
271
478
  async editMessage(messageId, content) {
272
479
  return this.request(`/api/messages/${messageId}`, {
273
480
  method: "PATCH",
@@ -281,16 +488,10 @@ var ShadowClient = class {
281
488
  }
282
489
  // ── Pins ──────────────────────────────────────────────────────────────
283
490
  async pinMessage(messageId, channelId) {
284
- if (channelId) {
285
- return this.request(`/api/channels/${channelId}/pins/${messageId}`, { method: "PUT" });
286
- }
287
- return this.request(`/api/messages/${messageId}/pin`, { method: "POST" });
491
+ return this.request(`/api/channels/${channelId}/pins/${messageId}`, { method: "PUT" });
288
492
  }
289
493
  async unpinMessage(messageId, channelId) {
290
- if (channelId) {
291
- return this.request(`/api/channels/${channelId}/pins/${messageId}`, { method: "DELETE" });
292
- }
293
- return this.request(`/api/messages/${messageId}/pin`, { method: "DELETE" });
494
+ return this.request(`/api/channels/${channelId}/pins/${messageId}`, { method: "DELETE" });
294
495
  }
295
496
  async getPinnedMessages(channelId) {
296
497
  return this.request(`/api/channels/${channelId}/pins`);
@@ -337,36 +538,26 @@ var ShadowClient = class {
337
538
  if (cursor) params.set("cursor", cursor);
338
539
  return this.request(`/api/threads/${threadId}/messages?${params}`);
339
540
  }
340
- async sendToThread(threadId, content) {
541
+ async sendToThread(threadId, content, options) {
341
542
  return this.request(`/api/threads/${threadId}/messages`, {
342
543
  method: "POST",
343
- body: JSON.stringify({ content })
544
+ body: JSON.stringify({
545
+ content,
546
+ ...options?.replyToId ? { replyToId: options.replyToId } : {},
547
+ ...options?.mentions ? { mentions: options.mentions } : {},
548
+ ...options?.metadata ? { metadata: options.metadata } : {}
549
+ })
344
550
  });
345
551
  }
346
- // ── DMs ───────────────────────────────────────────────────────────────
347
- async createDmChannel(userId) {
348
- return this.request("/api/dm/channels", {
552
+ // ── Direct channels ──────────────────────────────────────────────────
553
+ async createDirectChannel(userId) {
554
+ return this.request("/api/channels/dm", {
349
555
  method: "POST",
350
556
  body: JSON.stringify({ userId })
351
557
  });
352
558
  }
353
- async listDmChannels() {
354
- return this.request("/api/dm/channels");
355
- }
356
- async getDmMessages(channelId, limit = 50, cursor) {
357
- const params = new URLSearchParams({ limit: String(limit) });
358
- if (cursor) params.set("cursor", cursor);
359
- return this.request(`/api/dm/channels/${channelId}/messages?${params}`);
360
- }
361
- async sendDmMessage(channelId, content, options) {
362
- return this.request(`/api/dm/channels/${channelId}/messages`, {
363
- method: "POST",
364
- body: JSON.stringify({
365
- content,
366
- replyToId: options?.replyToId,
367
- ...options?.metadata ? { metadata: options.metadata } : {}
368
- })
369
- });
559
+ async listDirectChannels() {
560
+ return this.request("/api/channels/dm");
370
561
  }
371
562
  // ── Notifications ─────────────────────────────────────────────────────
372
563
  async listNotifications(limit = 50, offset = 0) {
@@ -417,8 +608,10 @@ var ShadowClient = class {
417
608
  const formData = new FormData();
418
609
  const blob = file instanceof Blob ? file : new Blob([file], { type: contentType });
419
610
  formData.append("file", blob, filename);
420
- if (messageId) {
611
+ if (typeof messageId === "string") {
421
612
  formData.append("messageId", messageId);
613
+ } else if (messageId) {
614
+ if (messageId.messageId) formData.append("messageId", messageId.messageId);
422
615
  }
423
616
  const url = `${this.baseUrl}/api/media/upload`;
424
617
  const res = await fetch(url, {
@@ -434,6 +627,12 @@ var ShadowClient = class {
434
627
  }
435
628
  return res.json();
436
629
  }
630
+ async resolveAttachmentMediaUrl(attachmentId, options) {
631
+ const disposition = options?.disposition ?? "inline";
632
+ return this.request(
633
+ `/api/attachments/${attachmentId}/media-url?disposition=${disposition}`
634
+ );
635
+ }
437
636
  /**
438
637
  * Download a file from a URL and upload it to the Shadow media service.
439
638
  * Supports local filesystem paths, file:// URLs, tilde paths, and HTTP(S) URLs.
@@ -449,6 +648,15 @@ var ShadowClient = class {
449
648
  if (normalizedUrl.startsWith("~")) {
450
649
  normalizedUrl = normalizedUrl.replace(/^~/, homedir());
451
650
  }
651
+ if (this.isShadowPrivateMediaUrl(normalizedUrl)) {
652
+ const downloaded = await this.downloadFile(normalizedUrl);
653
+ return this.uploadMedia(
654
+ downloaded.buffer,
655
+ downloaded.filename,
656
+ downloaded.contentType,
657
+ messageId
658
+ );
659
+ }
452
660
  if (!normalizedUrl.startsWith("/") && !normalizedUrl.startsWith("http://") && !normalizedUrl.startsWith("https://") && !normalizedUrl.startsWith("//")) {
453
661
  const { existsSync } = await import("fs");
454
662
  const { resolve } = await import("path");
@@ -523,7 +731,7 @@ var ShadowClient = class {
523
731
  const buffer = await res.arrayBuffer();
524
732
  const contentType = res.headers.get("content-type") ?? "application/octet-stream";
525
733
  const urlPath = new URL(fullUrl).pathname;
526
- const filename = decodeURIComponent(urlPath.split("/").pop() ?? "file");
734
+ const filename = contentDispositionFilename(res.headers.get("content-disposition")) ?? decodeURIComponent(urlPath.split("/").pop() ?? "file");
527
735
  return { buffer, contentType, filename };
528
736
  }
529
737
  // ── Workspace ─────────────────────────────────────────────────────────
@@ -651,6 +859,21 @@ var ShadowClient = class {
651
859
  async unlinkOAuthAccount(accountId) {
652
860
  return this.request(`/api/auth/oauth/accounts/${accountId}`, { method: "DELETE" });
653
861
  }
862
+ async changePassword(data) {
863
+ return this.request("/api/auth/password", {
864
+ method: "PUT",
865
+ body: JSON.stringify(data)
866
+ });
867
+ }
868
+ async getDashboard() {
869
+ return this.request("/api/auth/dashboard");
870
+ }
871
+ async loginWithGoogleIdToken(idToken) {
872
+ return this.request("/api/auth/google/id-token", {
873
+ method: "POST",
874
+ body: JSON.stringify({ idToken })
875
+ });
876
+ }
654
877
  // ── Friendships ───────────────────────────────────────────────────────
655
878
  async sendFriendRequest(username) {
656
879
  return this.request("/api/friends/request", {
@@ -695,6 +918,27 @@ var ShadowClient = class {
695
918
  body: JSON.stringify(data)
696
919
  });
697
920
  }
921
+ async getNotificationChannelPreferences() {
922
+ return this.request("/api/notifications/channel-preferences");
923
+ }
924
+ async updateNotificationChannelPreference(data) {
925
+ return this.request("/api/notifications/channel-preferences", {
926
+ method: "PATCH",
927
+ body: JSON.stringify(data)
928
+ });
929
+ }
930
+ async registerPushToken(data) {
931
+ return this.request("/api/notifications/push-tokens", {
932
+ method: "POST",
933
+ body: JSON.stringify(data)
934
+ });
935
+ }
936
+ async registerWebPushSubscription(data) {
937
+ return this.request("/api/notifications/web-push-subscriptions", {
938
+ method: "POST",
939
+ body: JSON.stringify(data)
940
+ });
941
+ }
698
942
  // ── OAuth Apps ────────────────────────────────────────────────────────
699
943
  async createOAuthApp(data) {
700
944
  return this.request("/api/oauth/apps", {
@@ -815,6 +1059,133 @@ var ShadowClient = class {
815
1059
  async getShop(serverId) {
816
1060
  return this.request(`/api/servers/${serverId}/shop`);
817
1061
  }
1062
+ async getMyShop() {
1063
+ return this.request("/api/me/shop");
1064
+ }
1065
+ async upsertMyShop(data) {
1066
+ return this.request("/api/me/shop", {
1067
+ method: "POST",
1068
+ body: JSON.stringify(data)
1069
+ });
1070
+ }
1071
+ async getUserShop(userId) {
1072
+ return this.request(`/api/users/${userId}/shop`);
1073
+ }
1074
+ async getManagedUserShop(userId) {
1075
+ return this.request(`/api/users/${userId}/shop/manage`);
1076
+ }
1077
+ async upsertManagedUserShop(userId, data) {
1078
+ return this.request(`/api/users/${userId}/shop/manage`, {
1079
+ method: "POST",
1080
+ body: JSON.stringify(data)
1081
+ });
1082
+ }
1083
+ async getShopById(shopId) {
1084
+ return this.request(`/api/shops/${shopId}`);
1085
+ }
1086
+ async listShopProducts(shopId, params) {
1087
+ const qs = new URLSearchParams();
1088
+ if (params?.keyword) qs.set("keyword", params.keyword);
1089
+ if (params?.limit) qs.set("limit", String(params.limit));
1090
+ if (params?.offset) qs.set("offset", String(params.offset));
1091
+ return this.request(`/api/shops/${shopId}/products?${qs}`);
1092
+ }
1093
+ async getScopeNeutralProduct(productId) {
1094
+ return this.request(`/api/products/${productId}`);
1095
+ }
1096
+ async getShopProduct(shopId, productId) {
1097
+ return this.request(`/api/shops/${shopId}/products/${productId}`);
1098
+ }
1099
+ async createShopProduct(shopId, data) {
1100
+ return this.request(`/api/shops/${shopId}/products`, {
1101
+ method: "POST",
1102
+ body: JSON.stringify(data)
1103
+ });
1104
+ }
1105
+ async updateShopProduct(shopId, productId, data) {
1106
+ return this.request(`/api/shops/${shopId}/products/${productId}`, {
1107
+ method: "PUT",
1108
+ body: JSON.stringify(data)
1109
+ });
1110
+ }
1111
+ async deleteShopProduct(shopId, productId) {
1112
+ return this.request(`/api/shops/${shopId}/products/${productId}`, { method: "DELETE" });
1113
+ }
1114
+ async purchaseShopProduct(shopId, productId, data) {
1115
+ return this.request(`/api/shops/${shopId}/products/${productId}/purchase`, {
1116
+ method: "POST",
1117
+ body: JSON.stringify(data)
1118
+ });
1119
+ }
1120
+ async purchaseCommerceOffer(offerId, data) {
1121
+ return this.request(`/api/commerce/offers/${offerId}/purchase`, {
1122
+ method: "POST",
1123
+ body: JSON.stringify(data)
1124
+ });
1125
+ }
1126
+ async getCommerceOfferCheckoutPreview(offerId, params) {
1127
+ const qs = new URLSearchParams();
1128
+ if (params?.skuId) qs.set("skuId", params.skuId);
1129
+ if (params?.viewerUserId) qs.set("viewerUserId", params.viewerUserId);
1130
+ const suffix = qs.toString() ? `?${qs}` : "";
1131
+ return this.request(`/api/commerce/offers/${offerId}/checkout-preview${suffix}`);
1132
+ }
1133
+ async createCommerceOffer(shopId, data) {
1134
+ return this.request(`/api/shops/${shopId}/offers`, {
1135
+ method: "POST",
1136
+ body: JSON.stringify(data)
1137
+ });
1138
+ }
1139
+ async listCommerceOffers(shopId, params) {
1140
+ const qs = new URLSearchParams();
1141
+ if (params?.keyword) qs.set("keyword", params.keyword);
1142
+ if (params?.limit) qs.set("limit", String(params.limit));
1143
+ return this.request(`/api/shops/${shopId}/offers?${qs}`);
1144
+ }
1145
+ async createCommerceDeliverable(shopId, offerId, data) {
1146
+ return this.request(`/api/shops/${shopId}/offers/${offerId}/deliverables`, {
1147
+ method: "POST",
1148
+ body: JSON.stringify(data)
1149
+ });
1150
+ }
1151
+ async listShopAssetDefinitions(shopId) {
1152
+ return this.request(`/api/shops/${shopId}/assets`);
1153
+ }
1154
+ async createShopAssetDefinition(shopId, data) {
1155
+ return this.request(`/api/shops/${shopId}/assets`, {
1156
+ method: "POST",
1157
+ body: JSON.stringify(data)
1158
+ });
1159
+ }
1160
+ async updateShopAssetDefinition(shopId, assetDefinitionId, data) {
1161
+ return this.request(`/api/shops/${shopId}/assets/${assetDefinitionId}`, {
1162
+ method: "PATCH",
1163
+ body: JSON.stringify(data)
1164
+ });
1165
+ }
1166
+ async purchaseMessageCommerceCard(messageId, cardId, data) {
1167
+ return this.request(`/api/messages/${messageId}/commerce-cards/${cardId}/purchase`, {
1168
+ method: "POST",
1169
+ body: JSON.stringify(data)
1170
+ });
1171
+ }
1172
+ async listCommerceProductCards(params) {
1173
+ const qs = new URLSearchParams();
1174
+ qs.set("target", params.target);
1175
+ qs.set("channelId", params.channelId);
1176
+ if (params.keyword) qs.set("keyword", params.keyword);
1177
+ if (params.limit) qs.set("limit", String(params.limit));
1178
+ return this.request(`/api/commerce/product-picker?${qs}`);
1179
+ }
1180
+ async openPaidFile(fileId) {
1181
+ return this.request(`/api/paid-files/${fileId}/open`, { method: "POST" });
1182
+ }
1183
+ async listShopEntitlements(shopId, params) {
1184
+ const qs = new URLSearchParams();
1185
+ if (params?.limit) qs.set("limit", String(params.limit));
1186
+ if (params?.offset) qs.set("offset", String(params.offset));
1187
+ return this.request(`/api/shops/${shopId}/entitlements?${qs}`);
1188
+ }
818
1189
  async updateShop(serverId, data) {
819
1190
  return this.request(`/api/servers/${serverId}/shop`, {
820
1191
  method: "PUT",
@@ -889,7 +1260,7 @@ var ShadowClient = class {
889
1260
  async createOrder(serverId, data) {
890
1261
  return this.request(`/api/servers/${serverId}/shop/orders`, {
891
1262
  method: "POST",
892
- body: JSON.stringify(data ?? {})
1263
+ body: JSON.stringify(data)
893
1264
  });
894
1265
  }
895
1266
  async listOrders(serverId) {
@@ -930,18 +1301,232 @@ var ShadowClient = class {
930
1301
  async getWallet() {
931
1302
  return this.request("/api/wallet");
932
1303
  }
933
- async topUpWallet(amount) {
934
- return this.request("/api/wallet/topup", {
1304
+ async topUpWallet(_amount) {
1305
+ throw new Error(
1306
+ "Public wallet top-up is disabled. Use a verified payment flow, refund, settlement, or admin grant."
1307
+ );
1308
+ }
1309
+ async getWalletTransactions(params) {
1310
+ const qs = new URLSearchParams();
1311
+ if (params?.audience) qs.set("audience", params.audience);
1312
+ if (params?.direction) qs.set("direction", params.direction);
1313
+ if (params?.limit != null) qs.set("limit", String(params.limit));
1314
+ if (params?.offset != null) qs.set("offset", String(params.offset));
1315
+ const suffix = qs.toString() ? `?${qs}` : "";
1316
+ return this.request(`/api/wallet/transactions${suffix}`);
1317
+ }
1318
+ // ── Community Economy ────────────────────────────────────────────────
1319
+ async listCommunityAssets() {
1320
+ return this.request("/api/economy/assets");
1321
+ }
1322
+ async getCommunityAsset(grantId) {
1323
+ return this.request(`/api/economy/assets/${grantId}`);
1324
+ }
1325
+ async consumeCommunityAsset(grantId, data) {
1326
+ return this.request(`/api/economy/assets/${grantId}/consume`, {
1327
+ method: "POST",
1328
+ body: JSON.stringify(data)
1329
+ });
1330
+ }
1331
+ async lockCommunityAsset(grantId, data) {
1332
+ return this.request(`/api/economy/assets/${grantId}/lock`, {
1333
+ method: "POST",
1334
+ body: JSON.stringify(data)
1335
+ });
1336
+ }
1337
+ async unlockCommunityAsset(grantId, data) {
1338
+ return this.request(`/api/economy/assets/${grantId}/unlock`, {
1339
+ method: "POST",
1340
+ body: JSON.stringify(data)
1341
+ });
1342
+ }
1343
+ async revokeCommunityAsset(grantId, data) {
1344
+ return this.request(`/api/economy/assets/${grantId}/revoke`, {
1345
+ method: "POST",
1346
+ body: JSON.stringify(data)
1347
+ });
1348
+ }
1349
+ async sendTip(data) {
1350
+ return this.request("/api/economy/tips", {
1351
+ method: "POST",
1352
+ body: JSON.stringify(data)
1353
+ });
1354
+ }
1355
+ async listTips() {
1356
+ return this.request("/api/economy/tips");
1357
+ }
1358
+ async sendGift(data) {
1359
+ return this.request("/api/economy/gifts", {
1360
+ method: "POST",
1361
+ body: JSON.stringify(data)
1362
+ });
1363
+ }
1364
+ async listGifts() {
1365
+ return this.request("/api/economy/gifts");
1366
+ }
1367
+ async listSettlements(params) {
1368
+ const qs = new URLSearchParams();
1369
+ if (params?.limit != null) qs.set("limit", String(params.limit));
1370
+ if (params?.offset != null) qs.set("offset", String(params.offset));
1371
+ const suffix = qs.toString() ? `?${qs}` : "";
1372
+ return this.request(`/api/economy/settlements${suffix}`);
1373
+ }
1374
+ async settleAvailableSettlements() {
1375
+ return this.request("/api/economy/settlements/settle", { method: "POST" });
1376
+ }
1377
+ // ── Cloud SaaS DIY Generation ───────────────────────────────────────
1378
+ async createDiyCloudRun(data) {
1379
+ return this.request("/api/cloud-saas/diy/runs", {
1380
+ method: "POST",
1381
+ body: JSON.stringify(data)
1382
+ });
1383
+ }
1384
+ async getDiyCloudRun(runId) {
1385
+ return this.request(`/api/cloud-saas/diy/runs/${encodeURIComponent(runId)}`);
1386
+ }
1387
+ async createDiyCloudFeedbackRun(runId, data) {
1388
+ return this.request(`/api/cloud-saas/diy/runs/${encodeURIComponent(runId)}/feedback`, {
1389
+ method: "POST",
1390
+ body: JSON.stringify(data)
1391
+ });
1392
+ }
1393
+ async streamDiyCloudRun(runId, options = {}) {
1394
+ const qs = new URLSearchParams();
1395
+ if (options.afterSeq != null) qs.set("afterSeq", String(options.afterSeq));
1396
+ const suffix = qs.toString() ? `?${qs}` : "";
1397
+ return this.requestRaw(
1398
+ `/api/cloud-saas/diy/runs/${encodeURIComponent(runId)}/stream${suffix}`,
1399
+ {
1400
+ headers: { Accept: "text/event-stream" }
1401
+ }
1402
+ );
1403
+ }
1404
+ async cancelDiyCloudRun(runId) {
1405
+ return this.request(`/api/cloud-saas/diy/runs/${encodeURIComponent(runId)}/cancel`, {
1406
+ method: "POST"
1407
+ });
1408
+ }
1409
+ // ── Cloud SaaS Deployment Runtime ──────────────────────────────────
1410
+ async getCloudDeploymentManifest(deploymentId) {
1411
+ return this.request(`/api/cloud-saas/deployments/${encodeURIComponent(deploymentId)}/manifest`);
1412
+ }
1413
+ async syncCloudDeploymentTemplate(deploymentId, data = {}) {
1414
+ return this.request(
1415
+ `/api/cloud-saas/deployments/${encodeURIComponent(deploymentId)}/template`,
1416
+ {
1417
+ method: "POST",
1418
+ body: JSON.stringify(data)
1419
+ }
1420
+ );
1421
+ }
1422
+ async redeployCloudDeployment(deploymentId, data = {}) {
1423
+ return this.request(
1424
+ `/api/cloud-saas/deployments/${encodeURIComponent(deploymentId)}/redeploy`,
1425
+ {
1426
+ method: "POST",
1427
+ body: JSON.stringify(data)
1428
+ }
1429
+ );
1430
+ }
1431
+ async pauseCloudDeployment(deploymentId, data = {}) {
1432
+ return this.request(`/api/cloud-saas/deployments/${encodeURIComponent(deploymentId)}/pause`, {
1433
+ method: "POST",
1434
+ body: JSON.stringify(data)
1435
+ });
1436
+ }
1437
+ async resumeCloudDeployment(deploymentId, data = {}) {
1438
+ return this.request(`/api/cloud-saas/deployments/${encodeURIComponent(deploymentId)}/resume`, {
1439
+ method: "POST",
1440
+ body: JSON.stringify(data)
1441
+ });
1442
+ }
1443
+ async listCloudDeploymentBackups(deploymentId, params = {}) {
1444
+ const qs = new URLSearchParams();
1445
+ if (params.agentId) qs.set("agentId", params.agentId);
1446
+ const suffix = qs.toString() ? `?${qs}` : "";
1447
+ return this.request(
1448
+ `/api/cloud-saas/deployments/${encodeURIComponent(deploymentId)}/backups${suffix}`
1449
+ );
1450
+ }
1451
+ async createCloudDeploymentBackup(deploymentId, data = {}) {
1452
+ return this.request(`/api/cloud-saas/deployments/${encodeURIComponent(deploymentId)}/backups`, {
935
1453
  method: "POST",
936
- body: JSON.stringify({ amount })
1454
+ body: JSON.stringify(data)
1455
+ });
1456
+ }
1457
+ async restoreCloudDeploymentBackup(deploymentId, data = {}) {
1458
+ return this.request(`/api/cloud-saas/deployments/${encodeURIComponent(deploymentId)}/restore`, {
1459
+ method: "POST",
1460
+ body: JSON.stringify(data)
937
1461
  });
938
1462
  }
939
- async getWalletTransactions() {
940
- return this.request("/api/wallet/transactions");
1463
+ // ── Cloud SaaS Provider Gateway ─────────────────────────────────────
1464
+ async listCloudProviderCatalogs() {
1465
+ return this.request("/api/cloud-saas/provider-catalogs");
1466
+ }
1467
+ async listCloudProviderProfiles() {
1468
+ return this.request("/api/cloud-saas/provider-profiles");
1469
+ }
1470
+ async upsertCloudProviderProfile(data) {
1471
+ return this.request("/api/cloud-saas/provider-profiles", {
1472
+ method: "PUT",
1473
+ body: JSON.stringify(data)
1474
+ });
1475
+ }
1476
+ async testCloudProviderProfile(profileId) {
1477
+ return this.request(`/api/cloud-saas/provider-profiles/${encodeURIComponent(profileId)}/test`, {
1478
+ method: "POST"
1479
+ });
1480
+ }
1481
+ async refreshCloudProviderProfileModels(profileId) {
1482
+ return this.request(
1483
+ `/api/cloud-saas/provider-profiles/${encodeURIComponent(profileId)}/models/refresh`,
1484
+ { method: "POST" }
1485
+ );
1486
+ }
1487
+ async deleteCloudProviderProfile(profileId) {
1488
+ return this.request(`/api/cloud-saas/provider-profiles/${encodeURIComponent(profileId)}`, {
1489
+ method: "DELETE"
1490
+ });
1491
+ }
1492
+ // ── Recharge (Stripe) ───────────────────────────────────────────────
1493
+ async getRechargeConfig() {
1494
+ return this.request("/api/v1/recharge/config");
1495
+ }
1496
+ async createRechargeIntent(params) {
1497
+ return this.request("/api/v1/recharge/create-intent", {
1498
+ method: "POST",
1499
+ body: JSON.stringify(params)
1500
+ });
1501
+ }
1502
+ async getRechargeHistory(params) {
1503
+ const qs = new URLSearchParams();
1504
+ if (params?.limit) qs.set("limit", String(params.limit));
1505
+ if (params?.offset) qs.set("offset", String(params.offset));
1506
+ const query = qs.toString();
1507
+ return this.request(`/api/v1/recharge/history${query ? `?${query}` : ""}`);
1508
+ }
1509
+ async confirmRechargePayment(paymentIntentId) {
1510
+ return this.request("/api/v1/recharge/confirm", {
1511
+ method: "POST",
1512
+ body: JSON.stringify({ paymentIntentId })
1513
+ });
941
1514
  }
942
1515
  async getEntitlements(serverId) {
943
1516
  return this.request(`/api/servers/${serverId}/shop/entitlements`);
944
1517
  }
1518
+ async getAllEntitlements() {
1519
+ return this.request("/api/entitlements");
1520
+ }
1521
+ async verifyEntitlement(entitlementId) {
1522
+ return this.request(`/api/entitlements/${entitlementId}/verify`);
1523
+ }
1524
+ async cancelEntitlement(entitlementId, reason) {
1525
+ return this.request(`/api/entitlements/${entitlementId}/cancel`, {
1526
+ method: "POST",
1527
+ body: JSON.stringify({ reason })
1528
+ });
1529
+ }
945
1530
  // ── Task Center ───────────────────────────────────────────────────────
946
1531
  async getTaskCenter() {
947
1532
  return this.request("/api/tasks");
@@ -955,41 +1540,126 @@ var ShadowClient = class {
955
1540
  async getRewardHistory() {
956
1541
  return this.request("/api/tasks/rewards");
957
1542
  }
958
- // ── Server Apps ───────────────────────────────────────────────────────
959
- async listApps(serverId, params) {
1543
+ // ── API Tokens ────────────────────────────────────────────────────────
1544
+ async createApiToken(data) {
1545
+ return this.request("/api/tokens", {
1546
+ method: "POST",
1547
+ body: JSON.stringify(data)
1548
+ });
1549
+ }
1550
+ async listApiTokens() {
1551
+ return this.request("/api/tokens");
1552
+ }
1553
+ async deleteApiToken(tokenId) {
1554
+ return this.request(`/api/tokens/${tokenId}`, { method: "DELETE" });
1555
+ }
1556
+ // ── Discover ──────────────────────────────────────────────────────────
1557
+ async discoverFeed(params) {
960
1558
  const qs = new URLSearchParams();
961
- if (params?.status) qs.set("status", params.status);
1559
+ if (params?.type) qs.set("type", params.type);
962
1560
  if (params?.limit) qs.set("limit", String(params.limit));
963
1561
  if (params?.offset) qs.set("offset", String(params.offset));
964
- return this.request(`/api/servers/${serverId}/apps?${qs}`);
1562
+ return this.request(`/api/discover/feed?${qs}`);
965
1563
  }
966
- async getHomepageApp(serverId) {
967
- return this.request(`/api/servers/${serverId}/apps/homepage`);
1564
+ async discoverSearch(params) {
1565
+ const qs = new URLSearchParams({ q: params.q });
1566
+ if (params?.type) qs.set("type", params.type);
1567
+ if (params?.limit) qs.set("limit", String(params.limit));
1568
+ return this.request(`/api/discover/search?${qs}`);
968
1569
  }
969
- async getApp(serverId, appId) {
970
- return this.request(`/api/servers/${serverId}/apps/${appId}`);
1570
+ // ── Voice Enhance ─────────────────────────────────────────────────────
1571
+ async enhanceVoice(data) {
1572
+ return this.request("/api/voice/enhance", {
1573
+ method: "POST",
1574
+ body: JSON.stringify(data)
1575
+ });
971
1576
  }
972
- async createApp(serverId, data) {
973
- return this.request(`/api/servers/${serverId}/apps`, {
1577
+ async enhanceVoiceQuery(params) {
1578
+ const qs = new URLSearchParams({ transcript: params.transcript });
1579
+ if (params.language) qs.set("language", params.language);
1580
+ if (params.enableSelfCorrection !== void 0)
1581
+ qs.set("enableSelfCorrection", String(params.enableSelfCorrection));
1582
+ if (params.enableListFormatting !== void 0)
1583
+ qs.set("enableListFormatting", String(params.enableListFormatting));
1584
+ if (params.enableFillerRemoval !== void 0)
1585
+ qs.set("enableFillerRemoval", String(params.enableFillerRemoval));
1586
+ if (params.enableToneAdjustment !== void 0)
1587
+ qs.set("enableToneAdjustment", String(params.enableToneAdjustment));
1588
+ if (params.targetTone) qs.set("targetTone", params.targetTone);
1589
+ return this.request(`/api/voice/enhance?${qs}`);
1590
+ }
1591
+ async getVoiceConfig() {
1592
+ return this.request("/api/voice/config");
1593
+ }
1594
+ async updateVoiceConfig(data) {
1595
+ return this.request("/api/voice/config", {
974
1596
  method: "POST",
975
1597
  body: JSON.stringify(data)
976
1598
  });
977
1599
  }
978
- async updateApp(serverId, appId, data) {
979
- return this.request(`/api/servers/${serverId}/apps/${appId}`, {
980
- method: "PATCH",
1600
+ async voiceHealthCheck() {
1601
+ return this.request("/api/voice/health");
1602
+ }
1603
+ // ── Profile Comments ──────────────────────────────────────────────────
1604
+ async getProfileComments(profileUserId, params) {
1605
+ const qs = new URLSearchParams();
1606
+ if (params?.limit) qs.set("limit", String(params.limit));
1607
+ if (params?.offset) qs.set("offset", String(params.offset));
1608
+ return this.request(`/api/profile-comments/${profileUserId}?${qs}`);
1609
+ }
1610
+ async getProfileCommentStats(profileUserId) {
1611
+ return this.request(`/api/profile-comments/${profileUserId}/stats`);
1612
+ }
1613
+ async getCommentReplies(parentId, params) {
1614
+ const qs = new URLSearchParams();
1615
+ if (params?.limit) qs.set("limit", String(params.limit));
1616
+ if (params?.offset) qs.set("offset", String(params.offset));
1617
+ return this.request(`/api/profile-comments/replies/${parentId}?${qs}`);
1618
+ }
1619
+ async createProfileComment(data) {
1620
+ return this.request("/api/profile-comments", {
1621
+ method: "POST",
981
1622
  body: JSON.stringify(data)
982
1623
  });
983
1624
  }
984
- async deleteApp(serverId, appId) {
985
- return this.request(`/api/servers/${serverId}/apps/${appId}`, { method: "DELETE" });
1625
+ async deleteProfileComment(commentId) {
1626
+ return this.request(`/api/profile-comments/${commentId}`, { method: "DELETE" });
986
1627
  }
987
- async publishApp(serverId, data) {
988
- return this.request(`/api/servers/${serverId}/apps/publish`, {
1628
+ async addProfileCommentReaction(commentId, emoji) {
1629
+ return this.request(`/api/profile-comments/${commentId}/reactions`, {
1630
+ method: "POST",
1631
+ body: JSON.stringify({ emoji })
1632
+ });
1633
+ }
1634
+ async removeProfileCommentReaction(commentId, emoji) {
1635
+ return this.request(`/api/profile-comments/${commentId}/reactions`, {
1636
+ method: "DELETE",
1637
+ body: JSON.stringify({ emoji })
1638
+ });
1639
+ }
1640
+ // ── Agent Dashboard ───────────────────────────────────────────────────
1641
+ async getAgentDashboard(agentId) {
1642
+ return this.request(`/api/agents/${agentId}/dashboard`);
1643
+ }
1644
+ async addAgentDashboardEvent(agentId, data) {
1645
+ return this.request(`/api/agents/${agentId}/dashboard/events`, {
989
1646
  method: "POST",
990
1647
  body: JSON.stringify(data)
991
1648
  });
992
1649
  }
1650
+ // ── Channel Archive ───────────────────────────────────────────────────
1651
+ async archiveChannel(channelId, reason) {
1652
+ return this.request(`/api/channels/${channelId}/archive`, {
1653
+ method: "POST",
1654
+ body: JSON.stringify(reason ? { reason } : {})
1655
+ });
1656
+ }
1657
+ async unarchiveChannel(channelId) {
1658
+ return this.request(`/api/channels/${channelId}/unarchive`, { method: "POST" });
1659
+ }
1660
+ async getArchivedChannels(serverId) {
1661
+ return this.request(`/api/servers/${serverId}/channels/archived`);
1662
+ }
993
1663
  };
994
1664
 
995
1665
  // src/constants.ts
@@ -1111,9 +1781,9 @@ var ShadowSocket = class {
1111
1781
  sendMessage(data) {
1112
1782
  this.socket.emit("message:send", data);
1113
1783
  }
1114
- /** Send a typing indicator */
1115
- sendTyping(channelId) {
1116
- this.socket.emit("message:typing", { channelId });
1784
+ /** Send or clear a typing indicator */
1785
+ sendTyping(channelId, typing = true) {
1786
+ this.socket.emit("message:typing", { channelId, typing });
1117
1787
  }
1118
1788
  /** Update user presence status */
1119
1789
  updatePresence(status) {
@@ -1123,23 +1793,6 @@ var ShadowSocket = class {
1123
1793
  updateActivity(channelId, activity) {
1124
1794
  this.socket.emit("presence:activity", { channelId, activity });
1125
1795
  }
1126
- // ── DM actions ────────────────────────────────────────────────────────
1127
- /** Join a DM channel room */
1128
- joinDmChannel(dmChannelId) {
1129
- this.socket.emit("dm:join", { dmChannelId });
1130
- }
1131
- /** Leave a DM channel room */
1132
- leaveDmChannel(dmChannelId) {
1133
- this.socket.emit("dm:leave", { dmChannelId });
1134
- }
1135
- /** Send a DM message via WebSocket */
1136
- sendDmMessage(data) {
1137
- this.socket.emit("dm:send", data);
1138
- }
1139
- /** Send a DM typing indicator */
1140
- sendDmTyping(dmChannelId) {
1141
- this.socket.emit("dm:typing", { dmChannelId });
1142
- }
1143
1796
  };
1144
1797
  export {
1145
1798
  CLIENT_EVENTS,