dsh-jiuling-remote-mobile 1.0.16 → 1.0.18
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/lib/client.js +94 -1
- package/lib/index.js +89 -14
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -230,6 +230,37 @@ window.__ModuleLoader__.load({
|
|
|
230
230
|
document.head.appendChild(styleTag);
|
|
231
231
|
}
|
|
232
232
|
|
|
233
|
+
/**
|
|
234
|
+
* 电脑目录的**访问缓存**:`电脑路径 → listing`(模块级,跨对话框开关存活)。
|
|
235
|
+
*
|
|
236
|
+
* ## 为什么必须有(用户报障原文)
|
|
237
|
+
*
|
|
238
|
+
* 「为什么我点击添加工作区的时候,每次点击电脑都要重新加载一遍目录,
|
|
239
|
+
* 这就算了,我加载好之后,随便点击一个文件夹,文件夹里面的内容还要重新加载
|
|
240
|
+
* 一遍」—— 同一个电脑目录,每次进「电脑」页签、每次点进同一个文件夹,
|
|
241
|
+
* 都要重走一趟「手机 → Cloudflare 隧道 → 电脑」,一个来回 1~3 秒。
|
|
242
|
+
* 用户要的是「加载一次,后续直接从本地读」。
|
|
243
|
+
*
|
|
244
|
+
* ## 为什么放模块级而不是组件内 ref
|
|
245
|
+
*
|
|
246
|
+
* 对话框每次打开都是**新挂载的组件**,组件内的 ref 一关就没了 —— 那样
|
|
247
|
+
* 「关掉再打开又转一遍圈」,正是用户抱怨的第一个现象。放模块级则一次加载
|
|
248
|
+
* 整条会话内都命中。
|
|
249
|
+
*
|
|
250
|
+
* ⚠️ 键是**电脑路径**原样(`C:\Users\...`),`''` 表示根目录 —— 与
|
|
251
|
+
* `bridge('listDesktop')` 的入参完全一致,不做任何改写。
|
|
252
|
+
*
|
|
253
|
+
* ⚠️ **失效只有两条路**:用户点「刷新」(见 `refreshAll`),
|
|
254
|
+
* 或者那一次加载失败(见下面的 catch)。不做 TTL 轮询 —— 用户明确要的是
|
|
255
|
+
* 「加载一次就直接从本地读」。
|
|
256
|
+
*/
|
|
257
|
+
var desktopDirCache = {};
|
|
258
|
+
|
|
259
|
+
/** 清掉访问缓存(配对 / 解绑 / 切页签时调用,避免跨电脑串味)。 */
|
|
260
|
+
function clearDesktopDirCache() {
|
|
261
|
+
desktopDirCache = {};
|
|
262
|
+
}
|
|
263
|
+
|
|
233
264
|
/**
|
|
234
265
|
* 调宿主桥的一条 RPC(令牌只在引擎进程里,浏览器侧不持有)。
|
|
235
266
|
*
|
|
@@ -384,6 +415,38 @@ window.__ModuleLoader__.load({
|
|
|
384
415
|
};
|
|
385
416
|
}, []);
|
|
386
417
|
|
|
418
|
+
/**
|
|
419
|
+
* 让官方前端的模型目录**失效重载**。
|
|
420
|
+
*
|
|
421
|
+
* ## ⚠️ 这一步为什么不能省
|
|
422
|
+
*
|
|
423
|
+
* 官方 `ModelCatalogDirectory` 在页面加载时就 `load()` 一次并进入
|
|
424
|
+
* `status === 'ready'`,此后**点菜单一个请求都不发**
|
|
425
|
+
* (`dsh-client-ui-model-selection/lib/client.js:100`:ready 时直接
|
|
426
|
+
* 返回旧值)。所以「配对之后模型列表变成电脑的」这件事,只把宿主那边的
|
|
427
|
+
* 缓存换掉是**看不见的** —— 前端那一层永远捧着配对前拉到的手机列表。
|
|
428
|
+
*
|
|
429
|
+
* 这正是用户报的「预设列表正确地加载了电脑预设,但模型列表还是没同步」:
|
|
430
|
+
* 预设走 `remoteExportList`,**没有缓存层**、每次重读,所以它是对的。
|
|
431
|
+
*
|
|
432
|
+
* `modelDirectories` 是官方那个服务实例(`super(ctx, "modelDirectories")`),
|
|
433
|
+
* `.catalog.refresh()` 就是官方在 `llm/adapters-updated` 时走的同一条路 ——
|
|
434
|
+
* 走公开方法,不碰前端 bundle 内部类。
|
|
435
|
+
*
|
|
436
|
+
* `ctx.get` 拿不到服务时静默跳过(见 `injected()` 里 `modelDirectories`
|
|
437
|
+
* 的注释):最坏是回到「要点一下刷新按钮」的旧行为,不会报错。
|
|
438
|
+
*/
|
|
439
|
+
var reloadModelCatalog = function () {
|
|
440
|
+
try {
|
|
441
|
+
var models = props.modelDirectories;
|
|
442
|
+
if (models && models.catalog && typeof models.catalog.refresh === 'function') {
|
|
443
|
+
models.catalog.refresh();
|
|
444
|
+
}
|
|
445
|
+
} catch (error) {
|
|
446
|
+
/* 服务结构变了就只更新宿主缓存,不打扰用户 */
|
|
447
|
+
}
|
|
448
|
+
};
|
|
449
|
+
|
|
387
450
|
// 每次打开都回到「手机」页签,避免上次停在电脑目录造成困惑。
|
|
388
451
|
react.useEffect(
|
|
389
452
|
function () {
|
|
@@ -424,6 +487,16 @@ window.__ModuleLoader__.load({
|
|
|
424
487
|
patch({ loading: false, listing: null, error: null });
|
|
425
488
|
return undefined;
|
|
426
489
|
}
|
|
490
|
+
// ⚠️ **电脑目录命中缓存就直接铺回去,不打桥**(用户报障:来回点文件夹
|
|
491
|
+
// 每次都重新加载)。缓存键用 `''` 表示根,与 `listDesktop` 的入参一致。
|
|
492
|
+
if (tab === 'desktop') {
|
|
493
|
+
var key = path === null ? '' : path;
|
|
494
|
+
var hit = desktopDirCache[key];
|
|
495
|
+
if (hit !== undefined) {
|
|
496
|
+
patch({ loading: false, listing: hit, error: null });
|
|
497
|
+
return undefined;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
427
500
|
patch({ loading: true, error: null });
|
|
428
501
|
var load =
|
|
429
502
|
tab === 'desktop'
|
|
@@ -432,10 +505,16 @@ window.__ModuleLoader__.load({
|
|
|
432
505
|
load.then(
|
|
433
506
|
function (listing) {
|
|
434
507
|
if (!aliveRef.current) return;
|
|
508
|
+
if (tab === 'desktop') {
|
|
509
|
+
desktopDirCache[path === null ? '' : path] = listing;
|
|
510
|
+
}
|
|
435
511
|
patch({ loading: false, listing: listing });
|
|
436
512
|
},
|
|
437
513
|
function (reason) {
|
|
438
514
|
if (!aliveRef.current) return;
|
|
515
|
+
// ⚠️ 失败**要清掉这一格缓存**:留着的话用户再点进来会看到
|
|
516
|
+
// 上一次的旧内容,而以为自己看的是电脑现状。
|
|
517
|
+
if (tab === 'desktop') delete desktopDirCache[path === null ? '' : path];
|
|
439
518
|
patch({
|
|
440
519
|
loading: false,
|
|
441
520
|
listing: null,
|
|
@@ -488,7 +567,14 @@ window.__ModuleLoader__.load({
|
|
|
488
567
|
code: '',
|
|
489
568
|
message: { kind: 'ok', text: '配对成功,正在连接电脑…' },
|
|
490
569
|
});
|
|
570
|
+
// ⚠️ 换了一台电脑,旧缓存里的路径完全无效(甚至同名不同机)——
|
|
571
|
+
// 必须作废,否则用户会看到上一台电脑的目录。
|
|
572
|
+
clearDesktopDirCache();
|
|
491
573
|
patch({ path: null, listing: null });
|
|
574
|
+
// ⚠️ 前端那份模型目录是**永久缓存**的(见 `reloadModelCatalog`),
|
|
575
|
+
// 不作废的话用户接下来看到的仍然是配对前那份手机模型列表 ——
|
|
576
|
+
// 就是用户报的「模型列表还是没同步」。
|
|
577
|
+
reloadModelCatalog();
|
|
492
578
|
},
|
|
493
579
|
function (reason) {
|
|
494
580
|
if (!aliveRef.current) return;
|
|
@@ -520,6 +606,8 @@ window.__ModuleLoader__.load({
|
|
|
520
606
|
code: '',
|
|
521
607
|
message: { kind: 'ok', text: '已解绑,请到电脑上生成新的配对码' },
|
|
522
608
|
});
|
|
609
|
+
// 解绑后旧凭据作废,缓存里的目录不该再露出来。
|
|
610
|
+
clearDesktopDirCache();
|
|
523
611
|
patch({ path: null, listing: null });
|
|
524
612
|
},
|
|
525
613
|
function (reason) {
|
|
@@ -611,11 +699,16 @@ window.__ModuleLoader__.load({
|
|
|
611
699
|
if (pairState.refreshing) return;
|
|
612
700
|
patchPair({ refreshing: true, refreshNote: null });
|
|
613
701
|
var target = state.tab === 'desktop' ? loc.path : '';
|
|
702
|
+
// ⚠️ **「刷新」必须清掉访问缓存**,否则新列表铺回 `listing` 之后,
|
|
703
|
+
// 用户一离开再回来又命中旧的那一格,看起来就是「刷新没生效」。
|
|
704
|
+
// 连同整棵已缓存的树一起作废:电脑上的目录可能已经变了。
|
|
705
|
+
clearDesktopDirCache();
|
|
614
706
|
bridge('refreshAll', target === '' ? {} : { path: target }).then(
|
|
615
707
|
function (result) {
|
|
616
708
|
if (!aliveRef.current) return;
|
|
617
|
-
//
|
|
709
|
+
// 目录树:直接把新列表铺回去(正在看的这一层),并写回缓存。
|
|
618
710
|
if (result && result.directory && result.directory.ok) {
|
|
711
|
+
desktopDirCache[target === '' ? '' : target] = result.directory.value;
|
|
619
712
|
patch({ listing: result.directory.value });
|
|
620
713
|
}
|
|
621
714
|
// 模型:让官方目录失效重载,下一次打开菜单就是电脑的。
|
package/lib/index.js
CHANGED
|
@@ -678,7 +678,7 @@ export function apply(ctx) {
|
|
|
678
678
|
const watchers = new Set();
|
|
679
679
|
|
|
680
680
|
/**
|
|
681
|
-
* 电脑侧的静默缓存:`{ catalog, presets, at }`。
|
|
681
|
+
* 电脑侧的静默缓存:`{ catalog, presets, directory, at }`。
|
|
682
682
|
*
|
|
683
683
|
* ## 为什么要缓存(用户需求原文)
|
|
684
684
|
*
|
|
@@ -686,10 +686,17 @@ export function apply(ctx) {
|
|
|
686
686
|
* 以及目录树这样体验会好很多……因为一般项目目录是不会变的,可以提前加载」。
|
|
687
687
|
*
|
|
688
688
|
* 配对后立刻拉一次,之后前端一打开模型菜单就有内容,不用等一轮往返。
|
|
689
|
-
*
|
|
690
|
-
*
|
|
689
|
+
*
|
|
690
|
+
* ⚠️ **`directory` 只缓存「根」那一层**(`listDirectory` 不给 path 时返回的
|
|
691
|
+
* home),因为「点开电脑页签」是每次都逃不掉的第一步,而它现在必然要过一轮
|
|
692
|
+
* 隧道往返 —— 用户看到的就是那个转圈(原话:「还是有一个加载过程,
|
|
693
|
+
* 不能预加载吗」)。
|
|
694
|
+
*
|
|
695
|
+
* **整棵目录树不进缓存**:电脑上几万个目录,全拉一遍既慢又没意义,而且
|
|
696
|
+
* 会大面积显示过期内容。点进子目录仍然实时拉(前端另有 `desktopDirCache`
|
|
697
|
+
* 兜住「同一层来回点」)。根目录的过期窗口由「刷新」按钮关闭。
|
|
691
698
|
*/
|
|
692
|
-
const desktopCache = { catalog: null, presets: null, at: 0 };
|
|
699
|
+
const desktopCache = { catalog: null, presets: null, directory: null, at: 0 };
|
|
693
700
|
|
|
694
701
|
/**
|
|
695
702
|
* 静默预拉电脑的模型目录与预设列表。
|
|
@@ -703,7 +710,7 @@ export function apply(ctx) {
|
|
|
703
710
|
*/
|
|
704
711
|
const prefetchDesktop = async (why) => {
|
|
705
712
|
if (config.token === EMPTY_TOKEN) return;
|
|
706
|
-
const [catalog, presets] = await Promise.all([
|
|
713
|
+
const [catalog, presets, directory] = await Promise.all([
|
|
707
714
|
callRemote(config, 'modelCatalog', {}).catch((error) => {
|
|
708
715
|
ctx.logger?.warn?.(`[dsh-remote-mobile] 预拉模型列表失败(${why}): ${String(error?.message ?? error)}`);
|
|
709
716
|
return null;
|
|
@@ -712,16 +719,49 @@ export function apply(ctx) {
|
|
|
712
719
|
ctx.logger?.warn?.(`[dsh-remote-mobile] 预拉预设列表失败(${why}): ${String(error?.message ?? error)}`);
|
|
713
720
|
return null;
|
|
714
721
|
}),
|
|
722
|
+
// 根目录那一层:不带 path ⇒ 电脑返回它的 home,正是用户点开
|
|
723
|
+
// 「电脑」页签第一眼看到的东西。见 `desktopCache.directory` 的注释。
|
|
724
|
+
callRemote(config, 'listDirectory', {}).catch((error) => {
|
|
725
|
+
ctx.logger?.warn?.(`[dsh-remote-mobile] 预拉目录树失败(${why}): ${String(error?.message ?? error)}`);
|
|
726
|
+
return null;
|
|
727
|
+
}),
|
|
715
728
|
]);
|
|
716
729
|
if (catalog !== null) desktopCache.catalog = catalog;
|
|
717
730
|
if (presets !== null) desktopCache.presets = presets;
|
|
731
|
+
if (directory !== null) desktopCache.directory = directory;
|
|
718
732
|
desktopCache.at = Date.now();
|
|
719
733
|
const models = catalog?.groups?.reduce((n, g) => n + (g.models?.length ?? 0), 0) ?? 0;
|
|
734
|
+
const dirs = directory?.entries?.length ?? directory?.items?.length ?? 0;
|
|
720
735
|
ctx.logger?.info?.(
|
|
721
|
-
`[dsh-remote-mobile] 已预拉电脑数据(${why}):${models} 个模型、${presets?.presets?.length ?? 0}
|
|
736
|
+
`[dsh-remote-mobile] 已预拉电脑数据(${why}):${models} 个模型、${presets?.presets?.length ?? 0} 个预设、根目录 ${dirs} 项`,
|
|
722
737
|
);
|
|
723
738
|
};
|
|
724
739
|
|
|
740
|
+
/**
|
|
741
|
+
* 取电脑的模型目录,缓存为空就**当场拉一次**。
|
|
742
|
+
*
|
|
743
|
+
* ⚠️ **不能只依赖配对时的预拉**。那是异步的后台动作,而前端页面一加载
|
|
744
|
+
* 就会调一次 `modelCatalog`(`ModelCatalogDirectory` 构造后立刻 `load()`,
|
|
745
|
+
* 见 `dsh-client-ui-model-selection/lib/client.js:354`)—— 谁先到谁决定
|
|
746
|
+
* 用户看到什么。拿不到电脑那份就返回手机本地列表,症状正是用户报的
|
|
747
|
+
* 「预设列表正确地加载了电脑预设,但模型列表还是没同步」:预设走
|
|
748
|
+
* `remoteExportList`,**没有缓存层**、每次重读,所以它是对的。
|
|
749
|
+
*
|
|
750
|
+
* @returns 电脑的模型目录;拉不到时 `null`。
|
|
751
|
+
*/
|
|
752
|
+
const desktopCatalog = async () => {
|
|
753
|
+
if (desktopCache.catalog !== null) return desktopCache.catalog;
|
|
754
|
+
try {
|
|
755
|
+
desktopCache.catalog = await callRemote(config, 'modelCatalog', {});
|
|
756
|
+
return desktopCache.catalog;
|
|
757
|
+
} catch (error) {
|
|
758
|
+
ctx.logger?.warn?.(
|
|
759
|
+
`[dsh-remote-mobile] 取电脑模型列表失败: ${String(error?.message ?? error)}`,
|
|
760
|
+
);
|
|
761
|
+
return null;
|
|
762
|
+
}
|
|
763
|
+
};
|
|
764
|
+
|
|
725
765
|
/**
|
|
726
766
|
* 把电脑的模型目录并进手机本地的目录。
|
|
727
767
|
*
|
|
@@ -1152,8 +1192,10 @@ export function apply(ctx) {
|
|
|
1152
1192
|
const originalCatalog = controller.modelCatalog.bind(controller);
|
|
1153
1193
|
controller.modelCatalog = async (...args) => {
|
|
1154
1194
|
const local = await originalCatalog(...args);
|
|
1155
|
-
if (!paired()
|
|
1156
|
-
|
|
1195
|
+
if (!paired()) return local;
|
|
1196
|
+
const desktop = await desktopCatalog();
|
|
1197
|
+
if (desktop === null) return local;
|
|
1198
|
+
return mergeCatalog(local, desktop);
|
|
1157
1199
|
};
|
|
1158
1200
|
undo.push(() => {
|
|
1159
1201
|
controller.modelCatalog = originalCatalog;
|
|
@@ -1379,6 +1421,10 @@ export function apply(ctx) {
|
|
|
1379
1421
|
// 解绑要能停掉全部旧连接、清掉全部搬运状态(见 `unpair`)。
|
|
1380
1422
|
follows,
|
|
1381
1423
|
mirrorStates,
|
|
1424
|
+
// ⚠️ **必须显式传进来**:`handleBridge` 是模块级函数,作用域里
|
|
1425
|
+
// 没有 `apply()` 的闭包变量;漏传就是 `workspaceMap is not defined`,
|
|
1426
|
+
// 用户点「在此新建对话」时必然失败(1.0.16 的真实报障)。
|
|
1427
|
+
workspaceMap,
|
|
1382
1428
|
persist: () => saveRemoteSessions(remoteSessions, ctx.logger),
|
|
1383
1429
|
// 模型/预设的静默缓存:配对后预拉、刷新按钮回写(见 desktopCache)。
|
|
1384
1430
|
desktopCache,
|
|
@@ -1556,7 +1602,7 @@ function desktopPathOfMirror(workspaceMap, dir) {
|
|
|
1556
1602
|
*
|
|
1557
1603
|
* @param ctx - Cordis 上下文。
|
|
1558
1604
|
* @param config - 远程配置。
|
|
1559
|
-
* @param io - `{ remoteSessions, follow, follows, mirrorStates, persist, body }`。
|
|
1605
|
+
* @param io - `{ remoteSessions, follow, follows, mirrorStates, persist, workspaceMap, body }`。
|
|
1560
1606
|
*/
|
|
1561
1607
|
async function handleBridge(ctx, config, io) {
|
|
1562
1608
|
const params = io.body?.params ?? {};
|
|
@@ -1565,9 +1611,24 @@ async function handleBridge(ctx, config, io) {
|
|
|
1565
1611
|
case 'ping':
|
|
1566
1612
|
return callRemote(config, 'ping', {});
|
|
1567
1613
|
|
|
1568
|
-
/**
|
|
1569
|
-
|
|
1570
|
-
|
|
1614
|
+
/**
|
|
1615
|
+
* 列电脑上的目录。
|
|
1616
|
+
*
|
|
1617
|
+
* ⚠️ **根那层命中预拉缓存就直接回,不打隧道**:用户点开「电脑」页签时
|
|
1618
|
+
* 看的正是这一层,而它每一轮往返都要穿过 Cloudflare 隧道 ——
|
|
1619
|
+
* 那就是用户抱怨的那个「还是有一个加载过程」(原话:「目录树都提前存档
|
|
1620
|
+
* 手机本地了,为什么还是还加载呢,不能预加载吗」)。
|
|
1621
|
+
*
|
|
1622
|
+
* 只在**没给 path**(= 要 home)且预拉成功过时才走缓存。子目录照旧实时拉,
|
|
1623
|
+
* 前端 `desktopDirCache` 负责「同一层来回点」,两边分工见各自的注释。
|
|
1624
|
+
*/
|
|
1625
|
+
case 'listDesktop': {
|
|
1626
|
+
const wantRoot = params.path === undefined || params.path === null || params.path === '';
|
|
1627
|
+
if (wantRoot && io.desktopCache?.directory != null) return io.desktopCache.directory;
|
|
1628
|
+
const listing = await callRemote(config, 'listDirectory', { path: params.path });
|
|
1629
|
+
if (wantRoot && io.desktopCache !== undefined) io.desktopCache.directory = listing;
|
|
1630
|
+
return listing;
|
|
1631
|
+
}
|
|
1571
1632
|
|
|
1572
1633
|
/**
|
|
1573
1634
|
* 电脑上的模型目录。
|
|
@@ -1611,9 +1672,23 @@ async function handleBridge(ctx, config, io) {
|
|
|
1611
1672
|
// ⚠️ 刷新到的模型/预设要**写回缓存**,否则「刷新按钮」只更新了它自己
|
|
1612
1673
|
// 那份响应,前端模型菜单读的是宿主拦截器里的缓存 —— 用户会看到
|
|
1613
1674
|
// 「点了刷新,模型列表没变」。
|
|
1675
|
+
//
|
|
1676
|
+
// 根目录那一层同理:刷完不回写的话,用户接下来点开「电脑」页签
|
|
1677
|
+
// 命中的还是**刷新前**那份预拉缓存,看起来就是「刷新没生效」。
|
|
1678
|
+
//
|
|
1679
|
+
// ⚠️ **判据是「拉回来的这一层就是缓存的那一层」**(同 path),
|
|
1680
|
+
// 不是「有没有带 path」:前端在根层点刷新时传的是当前显示的绝对路径
|
|
1681
|
+
// (`loc.path`,也就是 home),照样是带 path 的一次调用 —— 只看
|
|
1682
|
+
// `params.path` 空不空会把根层刷新漏掉。
|
|
1614
1683
|
if (io.desktopCache !== undefined) {
|
|
1615
1684
|
if (result.models?.ok === true) io.desktopCache.catalog = result.models.value;
|
|
1616
1685
|
if (result.presets?.ok === true) io.desktopCache.presets = result.presets.value;
|
|
1686
|
+
if (
|
|
1687
|
+
result.directory?.ok === true &&
|
|
1688
|
+
io.desktopCache.directory?.path === result.directory.value?.path
|
|
1689
|
+
) {
|
|
1690
|
+
io.desktopCache.directory = result.directory.value;
|
|
1691
|
+
}
|
|
1617
1692
|
io.desktopCache.at = Date.now();
|
|
1618
1693
|
}
|
|
1619
1694
|
return result;
|
|
@@ -1640,7 +1715,7 @@ async function handleBridge(ctx, config, io) {
|
|
|
1640
1715
|
// 这里给它一个**电脑目录专属的镜像工作区**(标题 = 电脑目录末段),
|
|
1641
1716
|
// 而不是手机的「默认工作区」—— 否则所有远程会话都堆在「默认工作区」
|
|
1642
1717
|
// 底下,看起来就像「建完又回到默认工作去了」(见 `ensureDesktopWorkspace`)。
|
|
1643
|
-
const workspace = await ensureDesktopWorkspace(ctx, params.path, workspaceMap);
|
|
1718
|
+
const workspace = await ensureDesktopWorkspace(ctx, params.path, io.workspaceMap);
|
|
1644
1719
|
const cwd = workspace.path;
|
|
1645
1720
|
// ⚠️ **必须走 `sessionController.create`,不能走 `ctx.sessions.create`。**
|
|
1646
1721
|
//
|
|
@@ -1770,4 +1845,4 @@ async function handleBridge(ctx, config, io) {
|
|
|
1770
1845
|
}
|
|
1771
1846
|
}
|
|
1772
1847
|
|
|
1773
|
-
export { callRemote, mirrorEvent, loadConfig, DEFAULT_ENDPOINT, CONFIG_PATH, PAIR_ENDPOINT, claimPairing };
|
|
1848
|
+
export { callRemote, mirrorEvent, loadConfig, DEFAULT_ENDPOINT, CONFIG_PATH, PAIR_ENDPOINT, claimPairing, handleBridge };
|