@shadowob/sdk 1.1.3 → 1.1.5

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
@@ -45,12 +45,13 @@ var ShadowClient = class {
45
45
  const url = `${this.baseUrl}${path}`;
46
46
  const controller = new AbortController();
47
47
  const timeout = setTimeout(() => controller.abort(), 6e4);
48
+ const isFormData = init?.body instanceof FormData;
48
49
  try {
49
50
  const res = await fetch(url, {
50
51
  ...init,
51
52
  signal: init?.signal ?? controller.signal,
52
53
  headers: {
53
- "Content-Type": "application/json",
54
+ ...!isFormData ? { "Content-Type": "application/json" } : {},
54
55
  Authorization: `Bearer ${this.token}`,
55
56
  ...init?.headers
56
57
  }
@@ -175,8 +176,11 @@ var ShadowClient = class {
175
176
  });
176
177
  }
177
178
  // ── Agents ────────────────────────────────────────────────────────────
178
- async listAgents() {
179
- return this.request("/api/agents");
179
+ async listAgents(options) {
180
+ const params = new URLSearchParams();
181
+ if (options?.includeRentals) params.set("includeRentals", "true");
182
+ const query = params.toString();
183
+ return this.request(`/api/agents${query ? `?${query}` : ""}`);
180
184
  }
181
185
  async createAgent(data) {
182
186
  return this.request("/api/agents", {
@@ -292,15 +296,113 @@ var ShadowClient = class {
292
296
  async getServerAccess(serverIdOrSlug) {
293
297
  return this.request(`/api/servers/${serverIdOrSlug}/access`);
294
298
  }
299
+ // ── Server App Integrations ───────────────────────────────────────────
300
+ async listServerApps(serverIdOrSlug) {
301
+ return this.request(`/api/servers/${serverIdOrSlug}/apps`);
302
+ }
303
+ async listServerAppCatalog(serverIdOrSlug) {
304
+ return this.request(`/api/servers/${serverIdOrSlug}/apps/catalog`);
305
+ }
306
+ async discoverServerApp(serverIdOrSlug, data) {
307
+ return this.request(`/api/servers/${serverIdOrSlug}/apps/discover`, {
308
+ method: "POST",
309
+ body: JSON.stringify(data)
310
+ });
311
+ }
312
+ async installServerApp(serverIdOrSlug, data) {
313
+ return this.request(`/api/servers/${serverIdOrSlug}/apps`, {
314
+ method: "POST",
315
+ body: JSON.stringify(data)
316
+ });
317
+ }
318
+ async installServerAppFromCatalog(serverIdOrSlug, catalogEntryId, data = {}) {
319
+ return this.request(
320
+ `/api/servers/${serverIdOrSlug}/apps/catalog/${encodeURIComponent(catalogEntryId)}/install`,
321
+ {
322
+ method: "POST",
323
+ body: JSON.stringify(data)
324
+ }
325
+ );
326
+ }
327
+ async getServerApp(serverIdOrSlug, appKey) {
328
+ return this.request(`/api/servers/${serverIdOrSlug}/apps/${encodeURIComponent(appKey)}`);
329
+ }
330
+ async deleteServerApp(serverIdOrSlug, appKey) {
331
+ return this.request(`/api/servers/${serverIdOrSlug}/apps/${encodeURIComponent(appKey)}`, {
332
+ method: "DELETE"
333
+ });
334
+ }
335
+ async grantServerAppToBuddy(serverIdOrSlug, appKey, data) {
336
+ return this.request(
337
+ `/api/servers/${serverIdOrSlug}/apps/${encodeURIComponent(appKey)}/grants`,
338
+ {
339
+ method: "POST",
340
+ body: JSON.stringify(data)
341
+ }
342
+ );
343
+ }
344
+ async getServerAppSkills(serverIdOrSlug, appKey) {
345
+ return this.request(`/api/servers/${serverIdOrSlug}/apps/${encodeURIComponent(appKey)}/skills`);
346
+ }
347
+ async createServerAppLaunch(serverIdOrSlug, appKey) {
348
+ return this.request(
349
+ `/api/servers/${serverIdOrSlug}/apps/${encodeURIComponent(appKey)}/launch`,
350
+ {
351
+ method: "POST"
352
+ }
353
+ );
354
+ }
355
+ async introspectServerAppToken(serverIdOrSlug, appKey, token) {
356
+ const url = `${this.baseUrl}/api/servers/${serverIdOrSlug}/apps/${encodeURIComponent(
357
+ appKey
358
+ )}/oauth/introspect`;
359
+ const res = await fetch(url, {
360
+ method: "POST",
361
+ headers: {
362
+ Authorization: `Bearer ${token}`,
363
+ "Content-Type": "application/json"
364
+ },
365
+ body: JSON.stringify({ token })
366
+ });
367
+ if (!res.ok) {
368
+ const body = await res.text().catch(() => "");
369
+ const message = sanitizeErrorBody(body);
370
+ throw new Error(`Shadow API POST /oauth/introspect failed (${res.status}): ${message}`);
371
+ }
372
+ return await res.json();
373
+ }
374
+ async callServerAppCommand(serverIdOrSlug, appKey, commandName, data) {
375
+ return this.request(
376
+ `/api/servers/${serverIdOrSlug}/apps/${encodeURIComponent(appKey)}/commands/${encodeURIComponent(
377
+ commandName
378
+ )}`,
379
+ {
380
+ method: "POST",
381
+ body: JSON.stringify(data ?? {})
382
+ }
383
+ );
384
+ }
385
+ async callServerAppCommandMultipart(serverIdOrSlug, appKey, commandName, data) {
386
+ const form = new FormData();
387
+ form.set("input", JSON.stringify(data.input ?? {}));
388
+ if (data.channelId) form.set("channelId", data.channelId);
389
+ form.set(data.field ?? "file", data.file, data.filename);
390
+ return this.request(
391
+ `/api/servers/${serverIdOrSlug}/apps/${encodeURIComponent(appKey)}/commands/${encodeURIComponent(
392
+ commandName
393
+ )}`,
394
+ {
395
+ method: "POST",
396
+ body: form
397
+ }
398
+ );
399
+ }
295
400
  async updateServer(serverIdOrSlug, data) {
296
401
  return this.request(`/api/servers/${serverIdOrSlug}`, {
297
402
  method: "PATCH",
298
403
  body: JSON.stringify(data)
299
404
  });
300
405
  }
301
- async updateServerHomepage(serverIdOrSlug, homepageHtml) {
302
- return this.updateServer(serverIdOrSlug, { homepageHtml });
303
- }
304
406
  async deleteServer(serverId) {
305
407
  return this.request(`/api/servers/${serverId}`, { method: "DELETE" });
306
408
  }
@@ -365,6 +467,14 @@ var ShadowClient = class {
365
467
  const ch = await this.request(`/api/channels/${channelId}`);
366
468
  return { ...ch, description: ch.topic };
367
469
  }
470
+ async getChannelBootstrap(channelId, options) {
471
+ const params = new URLSearchParams();
472
+ if (options?.messagesLimit) params.set("messagesLimit", String(options.messagesLimit));
473
+ const query = params.toString();
474
+ return this.request(
475
+ `/api/channels/${channelId}/bootstrap${query ? `?${query}` : ""}`
476
+ );
477
+ }
368
478
  async getChannelAccess(channelId) {
369
479
  return this.request(`/api/channels/${channelId}/access`);
370
480
  }
@@ -412,6 +522,43 @@ var ShadowClient = class {
412
522
  async removeChannelMember(channelId, userId) {
413
523
  return this.request(`/api/channels/${channelId}/members/${userId}`, { method: "DELETE" });
414
524
  }
525
+ async getVoiceState(channelId) {
526
+ return this.request(`/api/channels/${channelId}/voice/state`);
527
+ }
528
+ async joinVoiceChannel(channelId, options) {
529
+ return this.request(`/api/channels/${channelId}/voice/join`, {
530
+ method: "POST",
531
+ body: JSON.stringify(options ?? {})
532
+ });
533
+ }
534
+ async renewVoiceCredentials(channelId, options) {
535
+ return this.request(`/api/channels/${channelId}/voice/renew`, {
536
+ method: "POST",
537
+ body: JSON.stringify(options ?? {})
538
+ });
539
+ }
540
+ async leaveVoiceChannel(channelId, options) {
541
+ return this.request(`/api/channels/${channelId}/voice/leave`, {
542
+ method: "POST",
543
+ body: JSON.stringify(options ?? {})
544
+ });
545
+ }
546
+ async updateVoiceState(channelId, data) {
547
+ return this.request(`/api/channels/${channelId}/voice/state`, {
548
+ method: "PATCH",
549
+ body: JSON.stringify(data)
550
+ });
551
+ }
552
+ async getVoicePolicy(channelId, agentId) {
553
+ const params = new URLSearchParams({ agentId });
554
+ return this.request(`/api/channels/${channelId}/voice-policy?${params}`);
555
+ }
556
+ async updateVoicePolicy(channelId, data) {
557
+ return this.request(`/api/channels/${channelId}/voice-policy`, {
558
+ method: "PUT",
559
+ body: JSON.stringify(data)
560
+ });
561
+ }
415
562
  // ── Channel Buddy Policy ─────────────────────────────────────────────
416
563
  async setBuddyPolicy(channelId, agentId, data) {
417
564
  return this.request(`/api/channels/${channelId}/agents/${agentId}/policy`, {
@@ -628,9 +775,19 @@ var ShadowClient = class {
628
775
  return res.json();
629
776
  }
630
777
  async resolveAttachmentMediaUrl(attachmentId, options) {
631
- const disposition = options?.disposition ?? "inline";
778
+ const params = new URLSearchParams();
779
+ params.set("disposition", options?.disposition ?? "inline");
780
+ if (options?.variant) params.set("variant", options.variant);
632
781
  return this.request(
633
- `/api/attachments/${attachmentId}/media-url?disposition=${disposition}`
782
+ `/api/attachments/${attachmentId}/media-url?${params}`
783
+ );
784
+ }
785
+ async resolveWorkspaceMediaUrl(serverId, fileId, options) {
786
+ const params = new URLSearchParams();
787
+ params.set("disposition", options?.disposition ?? "inline");
788
+ if (options?.contentRef) params.set("contentRef", options.contentRef);
789
+ return this.request(
790
+ `/api/servers/${serverId}/workspace/files/${fileId}/media-url?${params}`
634
791
  );
635
792
  }
636
793
  /**
@@ -856,9 +1013,21 @@ var ShadowClient = class {
856
1013
  async listOAuthAccounts() {
857
1014
  return this.request("/api/auth/oauth/accounts");
858
1015
  }
1016
+ async createOAuthConnectUrl(provider, redirect) {
1017
+ return this.request(`/api/auth/oauth/${provider}/link`, {
1018
+ method: "POST",
1019
+ body: JSON.stringify({ redirect })
1020
+ });
1021
+ }
859
1022
  async unlinkOAuthAccount(accountId) {
860
1023
  return this.request(`/api/auth/oauth/accounts/${accountId}`, { method: "DELETE" });
861
1024
  }
1025
+ async listAuthSessions() {
1026
+ return this.request("/api/auth/sessions");
1027
+ }
1028
+ async revokeAuthSession(sessionId) {
1029
+ return this.request(`/api/auth/sessions/${sessionId}`, { method: "DELETE" });
1030
+ }
862
1031
  async changePassword(data) {
863
1032
  return this.request("/api/auth/password", {
864
1033
  method: "PUT",
@@ -986,6 +1155,21 @@ var ShadowClient = class {
986
1155
  body: JSON.stringify({ appId })
987
1156
  });
988
1157
  }
1158
+ async sendOAuthChannelMessage(channelId, content, opts) {
1159
+ return this.request(`/api/oauth/channels/${channelId}/messages`, {
1160
+ method: "POST",
1161
+ body: JSON.stringify({
1162
+ content,
1163
+ ...opts?.metadata ? { metadata: opts.metadata } : {}
1164
+ })
1165
+ });
1166
+ }
1167
+ async sendOAuthBuddyMessage(buddyId, data) {
1168
+ return this.request(`/api/oauth/buddies/${buddyId}/messages`, {
1169
+ method: "POST",
1170
+ body: JSON.stringify(data)
1171
+ });
1172
+ }
989
1173
  // ── Marketplace / Rentals ─────────────────────────────────────────────
990
1174
  async browseListings(params) {
991
1175
  const qs = new URLSearchParams();
@@ -1093,6 +1277,9 @@ var ShadowClient = class {
1093
1277
  async getScopeNeutralProduct(productId) {
1094
1278
  return this.request(`/api/products/${productId}`);
1095
1279
  }
1280
+ async getCommerceProductContext(productId) {
1281
+ return this.request(`/api/commerce/products/${productId}/context`);
1282
+ }
1096
1283
  async getShopProduct(shopId, productId) {
1097
1284
  return this.request(`/api/shops/${shopId}/products/${productId}`);
1098
1285
  }
@@ -1283,6 +1470,11 @@ var ShadowClient = class {
1283
1470
  method: "POST"
1284
1471
  });
1285
1472
  }
1473
+ async completeOrder(serverId, orderId) {
1474
+ return this.request(`/api/servers/${serverId}/shop/orders/${orderId}/complete`, {
1475
+ method: "POST"
1476
+ });
1477
+ }
1286
1478
  async getProductReviews(serverId, productId) {
1287
1479
  return this.request(`/api/servers/${serverId}/shop/products/${productId}/reviews`);
1288
1480
  }
@@ -1518,6 +1710,23 @@ var ShadowClient = class {
1518
1710
  async getAllEntitlements() {
1519
1711
  return this.request("/api/entitlements");
1520
1712
  }
1713
+ async getEntitlement(entitlementId) {
1714
+ return this.request(`/api/entitlements/${entitlementId}`);
1715
+ }
1716
+ async getOAuthCommerceEntitlementAccess(params) {
1717
+ const qs = new URLSearchParams();
1718
+ if (params?.resourceType) qs.set("resourceType", params.resourceType);
1719
+ if (params?.resourceId) qs.set("resourceId", params.resourceId);
1720
+ if (params?.capability) qs.set("capability", params.capability);
1721
+ const query = qs.toString();
1722
+ return this.request(`/api/oauth/commerce/entitlements${query ? `?${query}` : ""}`);
1723
+ }
1724
+ async redeemOAuthCommerceEntitlement(data) {
1725
+ return this.request("/api/oauth/commerce/entitlements/redeem", {
1726
+ method: "POST",
1727
+ body: JSON.stringify(data)
1728
+ });
1729
+ }
1521
1730
  async verifyEntitlement(entitlementId) {
1522
1731
  return this.request(`/api/entitlements/${entitlementId}/verify`);
1523
1732
  }
@@ -1527,6 +1736,12 @@ var ShadowClient = class {
1527
1736
  body: JSON.stringify({ reason })
1528
1737
  });
1529
1738
  }
1739
+ async cancelEntitlementRenewal(entitlementId, reason) {
1740
+ return this.request(`/api/entitlements/${entitlementId}/cancel-renewal`, {
1741
+ method: "POST",
1742
+ body: JSON.stringify({ reason })
1743
+ });
1744
+ }
1530
1745
  // ── Task Center ───────────────────────────────────────────────────────
1531
1746
  async getTaskCenter() {
1532
1747
  return this.request("/api/tasks");
@@ -1567,6 +1782,16 @@ var ShadowClient = class {
1567
1782
  if (params?.limit) qs.set("limit", String(params.limit));
1568
1783
  return this.request(`/api/discover/search?${qs}`);
1569
1784
  }
1785
+ async discoverCommerce(params) {
1786
+ const qs = new URLSearchParams();
1787
+ if (params?.q) qs.set("q", params.q);
1788
+ if (params?.limit) qs.set("limit", String(params.limit));
1789
+ const suffix = qs.toString();
1790
+ return this.request(`/api/discover/business${suffix ? `?${suffix}` : ""}`);
1791
+ }
1792
+ async discoverBusinessHub(params) {
1793
+ return this.discoverCommerce(params);
1794
+ }
1570
1795
  // ── Voice Enhance ─────────────────────────────────────────────────────
1571
1796
  async enhanceVoice(data) {
1572
1797
  return this.request("/api/voice/enhance", {
@@ -1776,6 +2001,53 @@ var ShadowSocket = class {
1776
2001
  leaveChannel(channelId) {
1777
2002
  this.socket.emit("channel:leave", { channelId });
1778
2003
  }
2004
+ joinVoiceChannel(channelId, options) {
2005
+ return new Promise((resolve) => {
2006
+ this.socket.emit(
2007
+ "voice:join",
2008
+ { channelId, ...options },
2009
+ (res) => {
2010
+ resolve(res ?? { ok: false, error: "Voice join failed" });
2011
+ }
2012
+ );
2013
+ });
2014
+ }
2015
+ leaveVoiceChannel(channelId, options) {
2016
+ return new Promise((resolve) => {
2017
+ this.socket.emit(
2018
+ "voice:leave",
2019
+ { channelId, ...options },
2020
+ (res) => {
2021
+ resolve(res ?? { ok: true });
2022
+ }
2023
+ );
2024
+ });
2025
+ }
2026
+ renewVoiceCredentials(channelId, options) {
2027
+ return new Promise((resolve) => {
2028
+ this.socket.emit(
2029
+ "voice:token:renew",
2030
+ { channelId, ...options },
2031
+ (res) => {
2032
+ resolve(res ?? { ok: false, error: "Voice token renewal failed" });
2033
+ }
2034
+ );
2035
+ });
2036
+ }
2037
+ updateVoiceState(channelId, data) {
2038
+ return new Promise((resolve) => {
2039
+ this.socket.emit(
2040
+ "voice:state:update",
2041
+ { channelId, ...data },
2042
+ (res) => {
2043
+ resolve(res ?? { ok: true });
2044
+ }
2045
+ );
2046
+ });
2047
+ }
2048
+ sendVoiceHeartbeat(channelId, options) {
2049
+ this.socket.emit("voice:heartbeat", { channelId, ...options });
2050
+ }
1779
2051
  // ── Client actions ────────────────────────────────────────────────────
1780
2052
  /** Send a message via WebSocket (text-only; for file attachments use REST) */
1781
2053
  sendMessage(data) {
@@ -1794,11 +2066,198 @@ var ShadowSocket = class {
1794
2066
  this.socket.emit("presence:activity", { channelId, activity });
1795
2067
  }
1796
2068
  };
2069
+
2070
+ // src/voice.ts
2071
+ var cachedAgoraRTC = null;
2072
+ async function loadAgoraRTC() {
2073
+ if (cachedAgoraRTC) return cachedAgoraRTC;
2074
+ try {
2075
+ const module = await import("agora-rtc-sdk-ng");
2076
+ cachedAgoraRTC = module.default;
2077
+ return cachedAgoraRTC;
2078
+ } catch (error) {
2079
+ throw new Error(
2080
+ `Agora RTC SDK is required for ShadowVoiceConsumer. Install agora-rtc-sdk-ng in this app to use browser voice media. ${error instanceof Error ? error.message : String(error)}`
2081
+ );
2082
+ }
2083
+ }
2084
+ function createVoiceClientId() {
2085
+ return `shadow-sdk-${globalThis.crypto?.randomUUID?.() ?? `${Date.now()}-${Math.random()}`}`;
2086
+ }
2087
+ var ShadowVoiceConsumer = class {
2088
+ constructor(options) {
2089
+ this.options = options;
2090
+ this.clientId = options.clientId ?? createVoiceClientId();
2091
+ }
2092
+ rtc = null;
2093
+ screenRtc = null;
2094
+ audioTrack = null;
2095
+ screenTrack = null;
2096
+ session = null;
2097
+ clientId;
2098
+ tokenRenewTimer = null;
2099
+ get joinResult() {
2100
+ return this.session;
2101
+ }
2102
+ async join() {
2103
+ const AgoraRTC = await loadAgoraRTC();
2104
+ this.session = await this.options.client.joinVoiceChannel(this.options.channelId, {
2105
+ muted: this.options.muted,
2106
+ clientId: this.clientId
2107
+ });
2108
+ const { credentials } = this.session;
2109
+ const rtc = AgoraRTC.createClient({ mode: "rtc", codec: "vp8" });
2110
+ this.rtc = rtc;
2111
+ try {
2112
+ this.bindTokenRenewal(rtc);
2113
+ rtc.on("user-published", async (user, mediaType) => {
2114
+ await rtc.subscribe(user, mediaType);
2115
+ if (mediaType === "audio" && user.audioTrack) {
2116
+ this.options.onRemoteAudio?.({ uid: user.uid, track: user.audioTrack });
2117
+ }
2118
+ if (mediaType === "video" && user.videoTrack) {
2119
+ this.options.onRemoteScreen?.({ uid: user.uid, track: user.videoTrack });
2120
+ }
2121
+ });
2122
+ await rtc.join(
2123
+ credentials.appId,
2124
+ credentials.agoraChannelName,
2125
+ credentials.token,
2126
+ credentials.uid
2127
+ );
2128
+ this.audioTrack = await AgoraRTC.createMicrophoneAudioTrack();
2129
+ await this.audioTrack.setEnabled(!this.options.muted);
2130
+ await rtc.publish([this.audioTrack]);
2131
+ this.scheduleTokenRenewal();
2132
+ return this.session;
2133
+ } catch (error) {
2134
+ this.clearTokenRenewal();
2135
+ this.audioTrack?.stop();
2136
+ this.audioTrack?.close();
2137
+ this.audioTrack = null;
2138
+ await rtc.leave().catch(() => void 0);
2139
+ this.rtc = null;
2140
+ await this.options.client.leaveVoiceChannel(this.options.channelId, { clientId: this.clientId }).catch(() => void 0);
2141
+ this.session = null;
2142
+ throw error;
2143
+ }
2144
+ }
2145
+ async setMuted(muted) {
2146
+ await this.audioTrack?.setEnabled(!muted);
2147
+ await this.options.client.updateVoiceState(this.options.channelId, {
2148
+ clientId: this.clientId,
2149
+ muted
2150
+ });
2151
+ }
2152
+ async startScreenShare() {
2153
+ if (!this.session || this.screenRtc || this.screenTrack) return;
2154
+ const { credentials } = this.session;
2155
+ const AgoraRTC = await loadAgoraRTC();
2156
+ const screenRtc = AgoraRTC.createClient({ mode: "rtc", codec: "vp8" });
2157
+ this.screenRtc = screenRtc;
2158
+ try {
2159
+ this.bindTokenRenewal(screenRtc);
2160
+ await screenRtc.join(
2161
+ credentials.appId,
2162
+ credentials.agoraChannelName,
2163
+ credentials.screenToken,
2164
+ credentials.screenUid
2165
+ );
2166
+ const trackResult = await AgoraRTC.createScreenVideoTrack(
2167
+ { encoderConfig: "1080p_1" },
2168
+ "disable"
2169
+ );
2170
+ const screenTrack = Array.isArray(trackResult) ? trackResult[0] : trackResult;
2171
+ this.screenTrack = screenTrack;
2172
+ await screenRtc.publish([this.screenTrack]);
2173
+ await this.options.client.updateVoiceState(this.options.channelId, {
2174
+ clientId: this.clientId,
2175
+ screenSharing: true
2176
+ });
2177
+ } catch (error) {
2178
+ this.screenTrack?.stop();
2179
+ this.screenTrack?.close();
2180
+ this.screenTrack = null;
2181
+ await screenRtc.leave().catch(() => void 0);
2182
+ this.screenRtc = null;
2183
+ await this.options.client.updateVoiceState(this.options.channelId, {
2184
+ clientId: this.clientId,
2185
+ screenSharing: false
2186
+ }).catch(() => void 0);
2187
+ throw error;
2188
+ }
2189
+ }
2190
+ async stopScreenShare() {
2191
+ this.screenTrack?.stop();
2192
+ this.screenTrack?.close();
2193
+ this.screenTrack = null;
2194
+ await this.screenRtc?.leave();
2195
+ this.screenRtc = null;
2196
+ await this.options.client.updateVoiceState(this.options.channelId, {
2197
+ clientId: this.clientId,
2198
+ screenSharing: false
2199
+ });
2200
+ }
2201
+ async leave() {
2202
+ this.clearTokenRenewal();
2203
+ await this.stopScreenShare();
2204
+ this.audioTrack?.stop();
2205
+ this.audioTrack?.close();
2206
+ this.audioTrack = null;
2207
+ await this.rtc?.leave();
2208
+ this.rtc = null;
2209
+ await this.options.client.leaveVoiceChannel(this.options.channelId, { clientId: this.clientId });
2210
+ this.session = null;
2211
+ }
2212
+ bindTokenRenewal(rtc) {
2213
+ rtc.on("token-privilege-will-expire", () => {
2214
+ void this.renewTokens();
2215
+ });
2216
+ rtc.on("token-privilege-did-expire", () => {
2217
+ void this.renewTokens();
2218
+ });
2219
+ }
2220
+ scheduleTokenRenewal() {
2221
+ this.clearTokenRenewal();
2222
+ const expiresAt = this.session?.credentials.expiresAt;
2223
+ if (!expiresAt) return;
2224
+ const renewAt = new Date(expiresAt).getTime() - 5 * 6e4;
2225
+ const delay = Math.max(3e4, renewAt - Date.now());
2226
+ this.tokenRenewTimer = setTimeout(() => {
2227
+ void this.renewTokens();
2228
+ }, delay);
2229
+ }
2230
+ clearTokenRenewal() {
2231
+ if (this.tokenRenewTimer) {
2232
+ clearTimeout(this.tokenRenewTimer);
2233
+ this.tokenRenewTimer = null;
2234
+ }
2235
+ }
2236
+ async renewTokens() {
2237
+ if (!this.session) return;
2238
+ const result = await this.options.client.renewVoiceCredentials(this.options.channelId, {
2239
+ clientId: this.clientId
2240
+ });
2241
+ this.session = {
2242
+ ...this.session,
2243
+ credentials: result.credentials,
2244
+ state: result.state
2245
+ };
2246
+ if (result.credentials.token) {
2247
+ await this.rtc?.renewToken?.(result.credentials.token);
2248
+ }
2249
+ if (result.credentials.screenToken) {
2250
+ await this.screenRtc?.renewToken?.(result.credentials.screenToken);
2251
+ }
2252
+ this.scheduleTokenRenewal();
2253
+ }
2254
+ };
1797
2255
  export {
1798
2256
  CLIENT_EVENTS,
1799
2257
  SERVER_EVENTS,
1800
2258
  ShadowClient,
1801
2259
  ShadowSocket,
2260
+ ShadowVoiceConsumer,
1802
2261
  channelRoom,
1803
2262
  threadRoom,
1804
2263
  userRoom
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "@shadowob/sdk",
3
- "version": "1.1.3",
3
+ "version": "1.1.5",
4
4
  "description": "Shadow SDK — typed REST client and real-time Socket.IO event listener for Shadow servers",
5
+ "license": "MIT",
5
6
  "type": "module",
6
7
  "main": "./dist/index.js",
7
8
  "module": "./dist/index.js",
@@ -21,9 +22,18 @@
21
22
  ],
22
23
  "dependencies": {
23
24
  "socket.io-client": "^4.8.1",
24
- "@shadowob/shared": "1.1.3"
25
+ "@shadowob/shared": "1.1.5"
26
+ },
27
+ "peerDependencies": {
28
+ "agora-rtc-sdk-ng": "^4.24.3"
29
+ },
30
+ "peerDependenciesMeta": {
31
+ "agora-rtc-sdk-ng": {
32
+ "optional": true
33
+ }
25
34
  },
26
35
  "devDependencies": {
36
+ "agora-rtc-sdk-ng": "^4.24.3",
27
37
  "tsup": "^8.5.0",
28
38
  "typescript": "^5.9.3"
29
39
  },