ugly-app 0.1.682 → 0.1.683
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/cli/version.d.ts +1 -1
- package/dist/cli/version.js +1 -1
- package/dist/native/index.d.ts +1 -1
- package/dist/native/index.d.ts.map +1 -1
- package/dist/native/index.js +1 -1
- package/dist/native/index.js.map +1 -1
- package/dist/native/proxy.d.ts +11 -0
- package/dist/native/proxy.d.ts.map +1 -1
- package/dist/native/proxy.js +73 -0
- package/dist/native/proxy.js.map +1 -1
- package/package.json +1 -1
- package/src/cli/version.ts +1 -1
- package/src/native/index.ts +1 -1
- package/src/native/proxy.ts +70 -1
- package/src/native/proxyBootstrap.test.ts +84 -0
package/dist/cli/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const CLI_VERSION = "0.1.
|
|
1
|
+
export declare const CLI_VERSION = "0.1.683";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/dist/cli/version.js
CHANGED
package/dist/native/index.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ export type { TerminalFacade } from './facades/terminal.js';
|
|
|
17
17
|
export { MCP_CATALOG } from './mcp-catalog.js';
|
|
18
18
|
export { createProxyClient, nativeSigner } from './proxyClient.js';
|
|
19
19
|
export type { ProxyClient, ProxyClientOpts, ProxySocket, HostInfo, RTCFactory, } from './proxyClient.js';
|
|
20
|
-
export { isDesktopChannel, withProxy, dispatchNativeEvent, DESKTOP_NAMESPACES } from './proxy.js';
|
|
20
|
+
export { isDesktopChannel, withProxy, enableUglyProxy, dispatchNativeEvent, DESKTOP_NAMESPACES } from './proxy.js';
|
|
21
21
|
export type { WithProxyOpts } from './proxy.js';
|
|
22
22
|
export { fpTag, verifyFpTag, parseFingerprint, mskSigner } from './proxyCrypto.js';
|
|
23
23
|
export type { ProxyRole, ProxySigner } from './proxyCrypto.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/native/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,cAAc,EACd,OAAO,EACP,WAAW,EACX,SAAS,EACT,QAAQ,EACR,MAAM,EACN,SAAS,EACT,cAAc,EACd,aAAa,EACb,UAAU,EACV,QAAQ,EACR,YAAY,EACZ,UAAU,EACV,SAAS,EACT,WAAW,EACX,aAAa,EACb,aAAa,EACb,cAAc,EACd,aAAa,EACb,aAAa,EACb,QAAQ,EACR,aAAa,EACb,iBAAiB,EACjB,YAAY,EACZ,eAAe,EACf,YAAY,EACZ,eAAe,EACf,aAAa,EACb,WAAW,EACX,kBAAkB,EAClB,aAAa,EACb,eAAe,GAChB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,YAAY,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAC/E,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAC9E,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACtF,YAAY,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,YAAY,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACxD,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC/D,YAAY,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACtE,YAAY,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,YAAY,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAClD,YAAY,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAG/C,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACnE,YAAY,EACV,WAAW,EACX,eAAe,EACf,WAAW,EACX,QAAQ,EACR,UAAU,GACX,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/native/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,cAAc,EACd,OAAO,EACP,WAAW,EACX,SAAS,EACT,QAAQ,EACR,MAAM,EACN,SAAS,EACT,cAAc,EACd,aAAa,EACb,UAAU,EACV,QAAQ,EACR,YAAY,EACZ,UAAU,EACV,SAAS,EACT,WAAW,EACX,aAAa,EACb,aAAa,EACb,cAAc,EACd,aAAa,EACb,aAAa,EACb,QAAQ,EACR,aAAa,EACb,iBAAiB,EACjB,YAAY,EACZ,eAAe,EACf,YAAY,EACZ,eAAe,EACf,aAAa,EACb,WAAW,EACX,kBAAkB,EAClB,aAAa,EACb,eAAe,GAChB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,YAAY,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAC/E,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAC9E,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACtF,YAAY,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,YAAY,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACxD,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC/D,YAAY,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACtE,YAAY,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,YAAY,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAClD,YAAY,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAG/C,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACnE,YAAY,EACV,WAAW,EACX,eAAe,EACf,WAAW,EACX,QAAQ,EACR,UAAU,GACX,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,eAAe,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AACnH,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACnF,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC"}
|
package/dist/native/index.js
CHANGED
|
@@ -6,6 +6,6 @@ export { native, permissions, nativePlatform, isNativeAvailable } from './wrappe
|
|
|
6
6
|
export { MCP_CATALOG } from './mcp-catalog.js';
|
|
7
7
|
// ── Ugly Proxy (remote UglyNative over WebRTC) ──
|
|
8
8
|
export { createProxyClient, nativeSigner } from './proxyClient.js';
|
|
9
|
-
export { isDesktopChannel, withProxy, dispatchNativeEvent, DESKTOP_NAMESPACES } from './proxy.js';
|
|
9
|
+
export { isDesktopChannel, withProxy, enableUglyProxy, dispatchNativeEvent, DESKTOP_NAMESPACES } from './proxy.js';
|
|
10
10
|
export { fpTag, verifyFpTag, parseFingerprint, mskSigner } from './proxyCrypto.js';
|
|
11
11
|
//# sourceMappingURL=index.js.map
|
package/dist/native/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/native/index.ts"],"names":[],"mappings":"AAkCA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5C,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAE/E,OAAO,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAC9E,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAStF,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,mDAAmD;AACnD,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAQnE,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/native/index.ts"],"names":[],"mappings":"AAkCA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5C,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAE/E,OAAO,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAC9E,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAStF,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,mDAAmD;AACnD,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAQnE,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,eAAe,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAEnH,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC"}
|
package/dist/native/proxy.d.ts
CHANGED
|
@@ -20,6 +20,17 @@ export interface WithProxyOpts {
|
|
|
20
20
|
* to a `dispatchNativeEvent` (below) — then `base.subscribe` delivers them unchanged.
|
|
21
21
|
*/
|
|
22
22
|
export declare function withProxy(base: UglyNative, opts: WithProxyOpts): UglyNative;
|
|
23
|
+
/**
|
|
24
|
+
* Full mobile/web client bootstrap: returns an UglyNative whose desktop-capability calls
|
|
25
|
+
* lazily connect to a host and proxy. On the first such call (no host yet) it drives the
|
|
26
|
+
* native host-picker (proxy.pickHost) + status overlay (proxy.setStatus); afterwards it
|
|
27
|
+
* reconnects to the same host silently. Device-capability calls stay local. Wire the
|
|
28
|
+
* client's `onEvent` to dispatchNativeEvent so host events reach `subscribe`.
|
|
29
|
+
*/
|
|
30
|
+
export declare function enableUglyProxy(opts: {
|
|
31
|
+
native: UglyNative;
|
|
32
|
+
client: ProxyClient;
|
|
33
|
+
}): UglyNative;
|
|
23
34
|
/** Dispatch a proxy event onto the same 'uglyNative' CustomEvent bus base.subscribe reads. */
|
|
24
35
|
export declare function dispatchNativeEvent(win: {
|
|
25
36
|
dispatchEvent(e: unknown): boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../../src/native/proxy.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEhD,OAAO,KAAK,EAAE,WAAW,
|
|
1
|
+
{"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../../src/native/proxy.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEhD,OAAO,KAAK,EAAE,WAAW,EAAY,MAAM,kBAAkB,CAAC;AAE9D,4FAA4F;AAC5F,eAAO,MAAM,kBAAkB,EAAE,WAAW,CAAC,MAAM,CAYjD,CAAC;AAEH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAGzD;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,WAAW,CAAC;IACpB;;;;OAIG;IACH,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;IAC3C,+EAA+E;IAC/E,wBAAwB,CAAC,EAAE,OAAO,CAAC;CACpC;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,aAAa,GAAG,UAAU,CAc3E;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE;IACpC,MAAM,EAAE,UAAU,CAAC;IACnB,MAAM,EAAE,WAAW,CAAC;CACrB,GAAG,UAAU,CAyDb;AAED,8FAA8F;AAC9F,wBAAgB,mBAAmB,CACjC,GAAG,EAAE;IAAE,aAAa,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAAA;CAAE,EAC3C,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,OAAO,GACZ,IAAI,CAON"}
|
package/dist/native/proxy.js
CHANGED
|
@@ -38,6 +38,79 @@ export function withProxy(base, opts) {
|
|
|
38
38
|
subscribe: base.subscribe,
|
|
39
39
|
};
|
|
40
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* Full mobile/web client bootstrap: returns an UglyNative whose desktop-capability calls
|
|
43
|
+
* lazily connect to a host and proxy. On the first such call (no host yet) it drives the
|
|
44
|
+
* native host-picker (proxy.pickHost) + status overlay (proxy.setStatus); afterwards it
|
|
45
|
+
* reconnects to the same host silently. Device-capability calls stay local. Wire the
|
|
46
|
+
* client's `onEvent` to dispatchNativeEvent so host events reach `subscribe`.
|
|
47
|
+
*/
|
|
48
|
+
export function enableUglyProxy(opts) {
|
|
49
|
+
const { native, client } = opts;
|
|
50
|
+
let connecting = null;
|
|
51
|
+
let lastLabel;
|
|
52
|
+
const setStatus = (state, hostLabel) => {
|
|
53
|
+
void native.invoke('proxy.setStatus', { state, hostLabel }).catch(() => { });
|
|
54
|
+
};
|
|
55
|
+
async function ensureConnected() {
|
|
56
|
+
if (client.isOpen())
|
|
57
|
+
return true;
|
|
58
|
+
if (connecting)
|
|
59
|
+
return connecting;
|
|
60
|
+
connecting = (async () => {
|
|
61
|
+
try {
|
|
62
|
+
const existing = client.hostDeviceId();
|
|
63
|
+
if (existing) {
|
|
64
|
+
// Silent reconnect to the previously chosen host.
|
|
65
|
+
setStatus('reconnecting', lastLabel);
|
|
66
|
+
await client.connect(existing);
|
|
67
|
+
setStatus('connected', lastLabel);
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
setStatus('connecting');
|
|
71
|
+
const hosts = await client.listHosts();
|
|
72
|
+
const picked = (await native.invoke('proxy.pickHost', {
|
|
73
|
+
hosts: hosts.map((h) => ({ deviceId: h.deviceId, label: h.label, online: true, lastSeen: h.lastSeen })),
|
|
74
|
+
}));
|
|
75
|
+
if (!picked) {
|
|
76
|
+
setStatus('none');
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
lastLabel = hosts.find((h) => h.deviceId === picked.deviceId)?.label;
|
|
80
|
+
await client.connect(picked.deviceId);
|
|
81
|
+
setStatus('connected', lastLabel);
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
setStatus(client.hostDeviceId() ? 'offline' : 'none');
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
finally {
|
|
89
|
+
connecting = null;
|
|
90
|
+
}
|
|
91
|
+
})();
|
|
92
|
+
return connecting;
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
platform: native.platform,
|
|
96
|
+
subscribe: native.subscribe,
|
|
97
|
+
invoke: (async (channel, payload) => {
|
|
98
|
+
if (!isDesktopChannel(channel))
|
|
99
|
+
return native.invoke(channel, payload);
|
|
100
|
+
if (!client.isOpen() && !(await ensureConnected()))
|
|
101
|
+
throw new NativeUnavailableError(channel);
|
|
102
|
+
try {
|
|
103
|
+
return await client.invoke(channel, payload);
|
|
104
|
+
}
|
|
105
|
+
catch (e) {
|
|
106
|
+
// Mid-session drop: surface the overlay, reconnect to the same host, retry once.
|
|
107
|
+
if (!client.isOpen() && (await ensureConnected()))
|
|
108
|
+
return client.invoke(channel, payload);
|
|
109
|
+
throw e;
|
|
110
|
+
}
|
|
111
|
+
}),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
41
114
|
/** Dispatch a proxy event onto the same 'uglyNative' CustomEvent bus base.subscribe reads. */
|
|
42
115
|
export function dispatchNativeEvent(win, event, data) {
|
|
43
116
|
const detail = { event, data };
|
package/dist/native/proxy.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proxy.js","sourceRoot":"","sources":["../../src/native/proxy.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AAGvD,4FAA4F;AAC5F,MAAM,CAAC,MAAM,kBAAkB,GAAwB,IAAI,GAAG,CAAC;IAC7D,IAAI;IACJ,SAAS;IACT,KAAK;IACL,OAAO;IACP,SAAS;IACT,UAAU;IACV,SAAS;IACT,KAAK;IACL,SAAS;IACT,QAAQ;IACR,MAAM;CACP,CAAC,CAAC;AAEH,MAAM,UAAU,gBAAgB,CAAC,OAAe;IAC9C,MAAM,EAAE,GAAG,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;IACpF,OAAO,kBAAkB,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AACpC,CAAC;AAcD;;;;GAIG;AACH,MAAM,UAAU,SAAS,CAAC,IAAgB,EAAE,IAAmB;IAC7D,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,IAAI,CAAC,CAAC,EAAU,EAAE,EAAE,CAAC,gBAAgB,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;IACvG,OAAO;QACL,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,MAAM,EAAE,CAAC,CAAC,OAAe,EAAE,OAAgB,EAAE,EAAE;YAC7C,IAAI,WAAW,CAAC,OAAO,CAAC;gBAAE,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YACtE,IAAI,IAAI,CAAC,wBAAwB,IAAI,gBAAgB,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC/D,0EAA0E;gBAC1E,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,sBAAsB,CAAC,OAAO,CAAC,CAAC,CAAC;YAC7D,CAAC;YACD,OAAO,IAAI,CAAC,MAAM,CAAC,OAAgB,EAAE,OAAgB,CAAC,CAAC;QACzD,CAAC,CAAyB;QAC1B,SAAS,EAAE,IAAI,CAAC,SAAS;KAC1B,CAAC;AACJ,CAAC;AAED,8FAA8F;AAC9F,MAAM,UAAU,mBAAmB,CACjC,GAA2C,EAC3C,KAAa,EACb,IAAa;IAEb,MAAM,MAAM,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IAC/B,MAAM,EAAE,GACN,OAAO,WAAW,KAAK,UAAU;QAC/B,CAAC,CAAC,IAAI,WAAW,CAAC,YAAY,EAAE,EAAE,MAAM,EAAE,CAAC;QAC3C,CAAC,CAAE,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,EAAuB,CAAC;IAC3D,GAAG,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;AACxB,CAAC"}
|
|
1
|
+
{"version":3,"file":"proxy.js","sourceRoot":"","sources":["../../src/native/proxy.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ugly-app",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.683",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"comment:files": "Allowlist what ships to npm. dist = runtime; src = sourcemap targets (dist/*.js.map reference ../../src/); templates = CLI scaffold. Everything else at repo root (.pgdata local Postgres, coverage, assets/icons sources, test/, test-results/) is excluded by omission. The !negations strip the scaffold's installed deps + cruft (templates/node_modules is 200MB+ and must never ship). package.json/README/LICENSE ship automatically.",
|
|
6
6
|
"files": [
|
package/src/cli/version.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Auto-generated by prebuild — do not edit manually
|
|
2
|
-
export const CLI_VERSION = "0.1.
|
|
2
|
+
export const CLI_VERSION = "0.1.683";
|
package/src/native/index.ts
CHANGED
|
@@ -58,7 +58,7 @@ export type {
|
|
|
58
58
|
HostInfo,
|
|
59
59
|
RTCFactory,
|
|
60
60
|
} from './proxyClient.js';
|
|
61
|
-
export { isDesktopChannel, withProxy, dispatchNativeEvent, DESKTOP_NAMESPACES } from './proxy.js';
|
|
61
|
+
export { isDesktopChannel, withProxy, enableUglyProxy, dispatchNativeEvent, DESKTOP_NAMESPACES } from './proxy.js';
|
|
62
62
|
export type { WithProxyOpts } from './proxy.js';
|
|
63
63
|
export { fpTag, verifyFpTag, parseFingerprint, mskSigner } from './proxyCrypto.js';
|
|
64
64
|
export type { ProxyRole, ProxySigner } from './proxyCrypto.js';
|
package/src/native/proxy.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// base UglyNative with `withProxy(...)`; the core install.ts is untouched.
|
|
6
6
|
import type { UglyNative } from './protocol.js';
|
|
7
7
|
import { NativeUnavailableError } from './protocol.js';
|
|
8
|
-
import type { ProxyClient } from './proxyClient.js';
|
|
8
|
+
import type { ProxyClient, HostInfo } from './proxyClient.js';
|
|
9
9
|
|
|
10
10
|
/** Namespaces that require a desktop host (RCE-grade / filesystem / long-running tasks). */
|
|
11
11
|
export const DESKTOP_NAMESPACES: ReadonlySet<string> = new Set([
|
|
@@ -60,6 +60,75 @@ export function withProxy(base: UglyNative, opts: WithProxyOpts): UglyNative {
|
|
|
60
60
|
};
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
+
/**
|
|
64
|
+
* Full mobile/web client bootstrap: returns an UglyNative whose desktop-capability calls
|
|
65
|
+
* lazily connect to a host and proxy. On the first such call (no host yet) it drives the
|
|
66
|
+
* native host-picker (proxy.pickHost) + status overlay (proxy.setStatus); afterwards it
|
|
67
|
+
* reconnects to the same host silently. Device-capability calls stay local. Wire the
|
|
68
|
+
* client's `onEvent` to dispatchNativeEvent so host events reach `subscribe`.
|
|
69
|
+
*/
|
|
70
|
+
export function enableUglyProxy(opts: {
|
|
71
|
+
native: UglyNative;
|
|
72
|
+
client: ProxyClient;
|
|
73
|
+
}): UglyNative {
|
|
74
|
+
const { native, client } = opts;
|
|
75
|
+
let connecting: Promise<boolean> | null = null;
|
|
76
|
+
let lastLabel: string | undefined;
|
|
77
|
+
|
|
78
|
+
const setStatus = (state: string, hostLabel?: string): void => {
|
|
79
|
+
void native.invoke('proxy.setStatus' as never, { state, hostLabel } as never).catch(() => {});
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
async function ensureConnected(): Promise<boolean> {
|
|
83
|
+
if (client.isOpen()) return true;
|
|
84
|
+
if (connecting) return connecting;
|
|
85
|
+
connecting = (async () => {
|
|
86
|
+
try {
|
|
87
|
+
const existing = client.hostDeviceId();
|
|
88
|
+
if (existing) {
|
|
89
|
+
// Silent reconnect to the previously chosen host.
|
|
90
|
+
setStatus('reconnecting', lastLabel);
|
|
91
|
+
await client.connect(existing);
|
|
92
|
+
setStatus('connected', lastLabel);
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
setStatus('connecting');
|
|
96
|
+
const hosts = await client.listHosts();
|
|
97
|
+
const picked = (await native.invoke('proxy.pickHost' as never, {
|
|
98
|
+
hosts: hosts.map((h: HostInfo) => ({ deviceId: h.deviceId, label: h.label, online: true, lastSeen: h.lastSeen })),
|
|
99
|
+
} as never)) as { deviceId: string } | null;
|
|
100
|
+
if (!picked) { setStatus('none'); return false; }
|
|
101
|
+
lastLabel = hosts.find((h) => h.deviceId === picked.deviceId)?.label;
|
|
102
|
+
await client.connect(picked.deviceId);
|
|
103
|
+
setStatus('connected', lastLabel);
|
|
104
|
+
return true;
|
|
105
|
+
} catch {
|
|
106
|
+
setStatus(client.hostDeviceId() ? 'offline' : 'none');
|
|
107
|
+
return false;
|
|
108
|
+
} finally {
|
|
109
|
+
connecting = null;
|
|
110
|
+
}
|
|
111
|
+
})();
|
|
112
|
+
return connecting;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
platform: native.platform,
|
|
117
|
+
subscribe: native.subscribe,
|
|
118
|
+
invoke: (async (channel: string, payload: unknown) => {
|
|
119
|
+
if (!isDesktopChannel(channel)) return native.invoke(channel as never, payload as never);
|
|
120
|
+
if (!client.isOpen() && !(await ensureConnected())) throw new NativeUnavailableError(channel);
|
|
121
|
+
try {
|
|
122
|
+
return await client.invoke(channel, payload);
|
|
123
|
+
} catch (e) {
|
|
124
|
+
// Mid-session drop: surface the overlay, reconnect to the same host, retry once.
|
|
125
|
+
if (!client.isOpen() && (await ensureConnected())) return client.invoke(channel, payload);
|
|
126
|
+
throw e;
|
|
127
|
+
}
|
|
128
|
+
}) as UglyNative['invoke'],
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
63
132
|
/** Dispatch a proxy event onto the same 'uglyNative' CustomEvent bus base.subscribe reads. */
|
|
64
133
|
export function dispatchNativeEvent(
|
|
65
134
|
win: { dispatchEvent(e: unknown): boolean },
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
+
import { enableUglyProxy } from './proxy.js';
|
|
3
|
+
import { NativeUnavailableError } from './protocol.js';
|
|
4
|
+
import type { UglyNative } from './protocol.js';
|
|
5
|
+
import type { ProxyClient } from './proxyClient.js';
|
|
6
|
+
|
|
7
|
+
function fakeNative(pick: { deviceId: string } | null): {
|
|
8
|
+
native: UglyNative;
|
|
9
|
+
calls: Array<{ channel: string; payload: unknown }>;
|
|
10
|
+
} {
|
|
11
|
+
const calls: Array<{ channel: string; payload: unknown }> = [];
|
|
12
|
+
const native: UglyNative = {
|
|
13
|
+
platform: 'ios',
|
|
14
|
+
subscribe: vi.fn(() => () => {}) as unknown as UglyNative['subscribe'],
|
|
15
|
+
invoke: (async (channel: string, payload: unknown) => {
|
|
16
|
+
calls.push({ channel, payload });
|
|
17
|
+
if (channel === 'proxy.pickHost') return pick;
|
|
18
|
+
if (channel === 'proxy.setStatus') return undefined;
|
|
19
|
+
return { local: channel }; // device-cap local impl
|
|
20
|
+
}) as unknown as UglyNative['invoke'],
|
|
21
|
+
};
|
|
22
|
+
return { native, calls };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function fakeClient(): ProxyClient & { _open: boolean; _host: string | null } {
|
|
26
|
+
const c = {
|
|
27
|
+
_open: false,
|
|
28
|
+
_host: null as string | null,
|
|
29
|
+
listHosts: vi.fn(async () => [{ deviceId: 'h1', label: 'My Mac', isHost: true, lastSeen: 1 }]),
|
|
30
|
+
connect: vi.fn(async (id: string) => { c._open = true; c._host = id; }),
|
|
31
|
+
invoke: vi.fn(async (channel: string, payload: unknown) => ({ proxied: channel, payload })),
|
|
32
|
+
subscribe: vi.fn(async () => ({ snapshot: undefined, unsubscribe: () => {} })),
|
|
33
|
+
isOpen: () => c._open,
|
|
34
|
+
hostDeviceId: () => c._host,
|
|
35
|
+
close: () => { c._open = false; },
|
|
36
|
+
};
|
|
37
|
+
return c as ProxyClient & { _open: boolean; _host: string | null };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const statuses = (calls: Array<{ channel: string; payload: unknown }>) =>
|
|
41
|
+
calls.filter((c) => c.channel === 'proxy.setStatus').map((c) => (c.payload as { state: string }).state);
|
|
42
|
+
|
|
43
|
+
describe('enableUglyProxy', () => {
|
|
44
|
+
it('keeps device-cap calls local', async () => {
|
|
45
|
+
const { native } = fakeNative({ deviceId: 'h1' });
|
|
46
|
+
const client = fakeClient();
|
|
47
|
+
const n = enableUglyProxy({ native, client });
|
|
48
|
+
expect(await n.invoke('clipboard.write' as never, { text: 'x' } as never)).toEqual({ local: 'clipboard.write' });
|
|
49
|
+
expect(client.connect).not.toHaveBeenCalled();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('on first desktop-cap call: picks a host, connects, proxies, and reports status', async () => {
|
|
53
|
+
const { native, calls } = fakeNative({ deviceId: 'h1' });
|
|
54
|
+
const client = fakeClient();
|
|
55
|
+
const n = enableUglyProxy({ native, client });
|
|
56
|
+
const r = await n.invoke('fs.readFile' as never, { path: '/p' } as never);
|
|
57
|
+
expect(client.listHosts).toHaveBeenCalled();
|
|
58
|
+
expect(calls.some((c) => c.channel === 'proxy.pickHost')).toBe(true);
|
|
59
|
+
expect(client.connect).toHaveBeenCalledWith('h1');
|
|
60
|
+
expect(r).toEqual({ proxied: 'fs.readFile', payload: { path: '/p' } });
|
|
61
|
+
expect(statuses(calls)).toEqual(['connecting', 'connected']);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('throws + reports none when the user cancels the picker', async () => {
|
|
65
|
+
const { native, calls } = fakeNative(null);
|
|
66
|
+
const client = fakeClient();
|
|
67
|
+
const n = enableUglyProxy({ native, client });
|
|
68
|
+
await expect(n.invoke('process.spawn' as never, {} as never)).rejects.toBeInstanceOf(NativeUnavailableError);
|
|
69
|
+
expect(client.connect).not.toHaveBeenCalled();
|
|
70
|
+
expect(statuses(calls)).toEqual(['connecting', 'none']);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('reconnects silently to the same host on a later call (no re-pick)', async () => {
|
|
74
|
+
const { native, calls } = fakeNative({ deviceId: 'h1' });
|
|
75
|
+
const client = fakeClient();
|
|
76
|
+
const n = enableUglyProxy({ native, client });
|
|
77
|
+
await n.invoke('fs.readFile' as never, {} as never); // connect once
|
|
78
|
+
client._open = false; // simulate drop
|
|
79
|
+
const r = await n.invoke('fs.readFile' as never, { again: true } as never);
|
|
80
|
+
expect(r).toMatchObject({ proxied: 'fs.readFile' });
|
|
81
|
+
expect(calls.filter((c) => c.channel === 'proxy.pickHost')).toHaveLength(1); // only the first time
|
|
82
|
+
expect(statuses(calls)).toEqual(['connecting', 'connected', 'reconnecting', 'connected']);
|
|
83
|
+
});
|
|
84
|
+
});
|