@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.cjs +468 -8
- package/dist/index.d.cts +703 -22
- package/dist/index.d.ts +703 -22
- package/dist/index.js +467 -8
- package/package.json +12 -2
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
|
-
|
|
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
|
|
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
|
|
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
|
+
"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.
|
|
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
|
},
|