ugly-app 0.1.640 → 0.1.642
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/agent/server/UglyBotTextStream.d.ts.map +1 -1
- package/dist/agent/server/UglyBotTextStream.js +16 -10
- package/dist/agent/server/UglyBotTextStream.js.map +1 -1
- package/dist/cli/version.d.ts +1 -1
- package/dist/cli/version.js +1 -1
- package/dist/client/bootstrapApp.d.ts.map +1 -1
- package/dist/client/bootstrapApp.js +38 -17
- package/dist/client/bootstrapApp.js.map +1 -1
- package/dist/native/conformance/harness.d.ts +44 -0
- package/dist/native/conformance/harness.d.ts.map +1 -0
- package/dist/native/conformance/harness.js +364 -0
- package/dist/native/conformance/harness.js.map +1 -0
- package/dist/server/AiImage.d.ts.map +1 -1
- package/dist/server/AiImage.js +10 -8
- package/dist/server/AiImage.js.map +1 -1
- package/dist/server/AiText.d.ts.map +1 -1
- package/dist/server/AiText.js +15 -8
- package/dist/server/AiText.js.map +1 -1
- package/package.json +3 -2
- package/src/agent/server/UglyBotTextStream.ts +17 -13
- package/src/cli/version.ts +1 -1
- package/src/client/bootstrapApp.tsx +36 -15
- package/src/native/conformance/harness.html +60 -0
- package/src/native/conformance/harness.ts +424 -0
- package/src/server/AiImage.ts +9 -8
- package/src/server/AiText.ts +14 -8
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UglyBotTextStream.d.ts","sourceRoot":"","sources":["../../../src/agent/server/UglyBotTextStream.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACjF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iDAAiD,CAAC;AACpF,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAO5E,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,WAAW,EAAE,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,+DAA+D;IAC/D,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,mBAAmB;IAClC,kFAAkF;IAClF,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,8CAA8C;IAC9C,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACtC;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,eAAe,CAAC;IAC5B,SAAS,EAAE,YAAY,CAAC;IACxB,iFAAiF;IACjF,OAAO,EAAE,WAAW,EAAE,CAAC;CACxB;
|
|
1
|
+
{"version":3,"file":"UglyBotTextStream.d.ts","sourceRoot":"","sources":["../../../src/agent/server/UglyBotTextStream.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACjF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iDAAiD,CAAC;AACpF,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAO5E,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,WAAW,EAAE,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,+DAA+D;IAC/D,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,mBAAmB;IAClC,kFAAkF;IAClF,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,8CAA8C;IAC9C,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACtC;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,eAAe,CAAC;IAC5B,SAAS,EAAE,YAAY,CAAC;IACxB,iFAAiF;IACjF,OAAO,EAAE,WAAW,EAAE,CAAC;CACxB;AAiKD;;;GAGG;AACH,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,kBAAkB,EACzB,MAAM,EAAE,mBAAmB,EAC3B,IAAI,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,WAAW,CAAA;CAAE,GAC9B,OAAO,CAAC,mBAAmB,CAAC,CAsC9B"}
|
|
@@ -15,21 +15,27 @@ import { getUserToken } from '../../server/auth/userContext.js';
|
|
|
15
15
|
const DEFAULT_AI_PROXY_URL = 'https://api.ugly.bot/v1/ai';
|
|
16
16
|
const DEFAULT_TIMEOUT_MS = 120_000;
|
|
17
17
|
const MAX_ATTEMPTS = 3;
|
|
18
|
-
/** Mirror of AiText.resolveConfig — pick endpoint + bearer for the billing mode.
|
|
18
|
+
/** Mirror of AiText.resolveConfig — pick endpoint + bearer for the billing mode.
|
|
19
|
+
* The agent loop is always user-facing, so bill the END USER whenever a session
|
|
20
|
+
* token is in context; fall back to the owner token only for self-hosted apps
|
|
21
|
+
* (or a missing user). We key off the actual token rather than
|
|
22
|
+
* UGLY_APP_AUTH_MODE, which is often unset in the Workers runtime — keying off
|
|
23
|
+
* the mode flag silently owner-bills the assistant (→ spurious 402 when the app
|
|
24
|
+
* owner's balance is empty even though the signed-in user has funds). */
|
|
19
25
|
function resolveConfig() {
|
|
20
|
-
const mode = process.env['UGLY_APP_AUTH_MODE'] === 'uglybot' ? 'user' : 'owner';
|
|
21
26
|
const baseUrl = process.env['AI_PROXY_URL'] ?? DEFAULT_AI_PROXY_URL;
|
|
22
27
|
const ownerToken = process.env['AI_PROXY_TOKEN'] ?? '';
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
28
|
+
const selfHost = process.env['UGLY_APP_AUTH_MODE'] === 'self';
|
|
29
|
+
const userToken = selfHost ? null : getUserToken();
|
|
30
|
+
if (userToken) {
|
|
31
|
+
return { endpoint: `${baseUrl}/user-billed/text`, bearer: userToken };
|
|
26
32
|
}
|
|
27
|
-
if (
|
|
28
|
-
throw new Error(
|
|
33
|
+
if (!ownerToken) {
|
|
34
|
+
throw new Error(selfHost
|
|
35
|
+
? '[agent] AI_PROXY_TOKEN is not set.'
|
|
36
|
+
: '[agent] No end-user session token in context and no AI_PROXY_TOKEN. Run the turn inside an auth-required handler.');
|
|
29
37
|
}
|
|
30
|
-
|
|
31
|
-
const bearer = mode === 'user' ? userToken : ownerToken;
|
|
32
|
-
return { endpoint, bearer };
|
|
38
|
+
return { endpoint: `${baseUrl}/text`, bearer: ownerToken };
|
|
33
39
|
}
|
|
34
40
|
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
35
41
|
function isTransient(status) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UglyBotTextStream.js","sourceRoot":"","sources":["../../../src/agent/server/UglyBotTextStream.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,EAAE;AACF,8EAA8E;AAC9E,2EAA2E;AAC3E,+EAA+E;AAC/E,4EAA4E;AAC5E,gFAAgF;AAChF,2EAA2E;AAC3E,gFAAgF;AAChF,yDAAyD;AACzD,EAAE;AACF,+EAA+E;AAC/E,uCAAuC;AAKvC,OAAO,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AAEhE,MAAM,oBAAoB,GAAG,4BAA4B,CAAC;AAC1D,MAAM,kBAAkB,GAAG,OAAO,CAAC;AACnC,MAAM,YAAY,GAAG,CAAC,CAAC;AAkCvB
|
|
1
|
+
{"version":3,"file":"UglyBotTextStream.js","sourceRoot":"","sources":["../../../src/agent/server/UglyBotTextStream.ts"],"names":[],"mappings":"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"}
|
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.642";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/dist/cli/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bootstrapApp.d.ts","sourceRoot":"","sources":["../../src/client/bootstrapApp.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEpE,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAUzE,OAAO,EAAmB,KAAK,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAInF,UAAU,mBAAmB;IAC3B,mFAAmF;IACnF,QAAQ,EAAE,eAAe,CAAC;IAE1B,mFAAmF;IACnF,QAAQ,CAAC,EAAE,eAAe,CAAC;IAE3B,uDAAuD;IACvD,cAAc,EAAE,aAAa,CAAC;QAC5B,QAAQ,EAAE,SAAS,CAAC;QACpB,QAAQ,CAAC,EAAE,SAAS,CAAC;QACrB,eAAe,CAAC,EAAE,MAAM,OAAO,CAAC;KACjC,CAAC,CAAC;IAEH,+EAA+E;IAC/E,MAAM,EAAE,MAAM,YAAY,CAAC;IAE3B,4DAA4D;IAC5D,IAAI,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;IAE5B,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,SAAS,CAAC;IAErB,6CAA6C;IAC7C,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,qFAAqF;IACrF,OAAO,CAAC,EAAE,qBAAqB,CAAC;IAEhC,iFAAiF;IACjF,QAAQ,CAAC,EAAE,KAAK,CAAC;IAEjB;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;
|
|
1
|
+
{"version":3,"file":"bootstrapApp.d.ts","sourceRoot":"","sources":["../../src/client/bootstrapApp.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEpE,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAUzE,OAAO,EAAmB,KAAK,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAInF,UAAU,mBAAmB;IAC3B,mFAAmF;IACnF,QAAQ,EAAE,eAAe,CAAC;IAE1B,mFAAmF;IACnF,QAAQ,CAAC,EAAE,eAAe,CAAC;IAE3B,uDAAuD;IACvD,cAAc,EAAE,aAAa,CAAC;QAC5B,QAAQ,EAAE,SAAS,CAAC;QACpB,QAAQ,CAAC,EAAE,SAAS,CAAC;QACrB,eAAe,CAAC,EAAE,MAAM,OAAO,CAAC;KACjC,CAAC,CAAC;IAEH,+EAA+E;IAC/E,MAAM,EAAE,MAAM,YAAY,CAAC;IAE3B,4DAA4D;IAC5D,IAAI,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;IAE5B,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,SAAS,CAAC;IAErB,6CAA6C;IAC7C,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,qFAAqF;IACrF,OAAO,CAAC,EAAE,qBAAqB,CAAC;IAEhC,iFAAiF;IACjF,QAAQ,CAAC,EAAE,KAAK,CAAC;IAEjB;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAiID,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,IAAI,CA6L/D"}
|
|
@@ -80,21 +80,29 @@ function silentAuthViaIframe(uglyBotUrl) {
|
|
|
80
80
|
* read can still come back empty — that's a safe no-op; the logged-out path's
|
|
81
81
|
* top-level redirect SSO remains the apex fallback.
|
|
82
82
|
*/
|
|
83
|
+
/**
|
|
84
|
+
* On EVERY page load, refresh the child session against the live ugly.bot
|
|
85
|
+
* account (hidden iframe → /auth/verify, which re-sets a fresh `auth_token`).
|
|
86
|
+
* If the refreshed account's userId does NOT match the session we rendered with,
|
|
87
|
+
* the child token is STALE (e.g. the user switched/merged accounts at ugly.bot,
|
|
88
|
+
* or a non-canonical duplicate id) — reload onto the live account.
|
|
89
|
+
*
|
|
90
|
+
* The `auth_token` cookie is HttpOnly, so JS can't clear it and a plain reload
|
|
91
|
+
* would just re-read the stale token (a stale, differently-scoped cookie can
|
|
92
|
+
* even shadow /auth/verify's fresh Set-Cookie). When we're logged in as the
|
|
93
|
+
* WRONG account we therefore clear it SERVER-SIDE via /auth/logout first, then
|
|
94
|
+
* reload so a clean SSO re-adopts the live account. Guarded per from→to
|
|
95
|
+
* transition so it can't loop.
|
|
96
|
+
*
|
|
97
|
+
* Works for same-site subdomains (iframe reads the `Lax` auth_token) and apex
|
|
98
|
+
* apps (reads the `SameSite=None` `ugly_sso` companion). No code → no reachable
|
|
99
|
+
* ugly.bot session; leave that to the auth-failed/logout paths.
|
|
100
|
+
*/
|
|
83
101
|
function silentAuthSync(uglyBotUrl, sessionUserId) {
|
|
84
102
|
if (typeof window === 'undefined')
|
|
85
103
|
return;
|
|
86
|
-
try {
|
|
87
|
-
if (sessionStorage.getItem(RECONCILE_TRIED_KEY))
|
|
88
|
-
return;
|
|
89
|
-
sessionStorage.setItem(RECONCILE_TRIED_KEY, '1');
|
|
90
|
-
}
|
|
91
|
-
catch {
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
104
|
void (async () => {
|
|
95
105
|
const code = await silentAuthViaIframe(uglyBotUrl).catch(() => null);
|
|
96
|
-
// No code → no reachable ugly.bot session (logged out there too). Leave the
|
|
97
|
-
// current state as-is.
|
|
98
106
|
if (!code)
|
|
99
107
|
return;
|
|
100
108
|
try {
|
|
@@ -109,14 +117,27 @@ function silentAuthSync(uglyBotUrl, sessionUserId) {
|
|
|
109
117
|
if (!token)
|
|
110
118
|
return;
|
|
111
119
|
const liveUserId = JSON.parse(atob(token.split('.')[1])).sub ?? null;
|
|
112
|
-
//
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
// the
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
120
|
+
// Already on the live account → /auth/verify just refreshed the cookie.
|
|
121
|
+
if (!liveUserId || liveUserId === sessionUserId)
|
|
122
|
+
return;
|
|
123
|
+
// Reload onto the live account — at most once per from→to transition so a
|
|
124
|
+
// child that can never match (or a failed re-adopt) can't reload-loop.
|
|
125
|
+
const guardKey = `${RECONCILE_TRIED_KEY}:${sessionUserId ?? 'out'}>${liveUserId}`;
|
|
126
|
+
try {
|
|
127
|
+
if (sessionStorage.getItem(guardKey))
|
|
128
|
+
return;
|
|
129
|
+
sessionStorage.setItem(guardKey, '1');
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
// Wrong account (not merely logged-out) → destroy the stale HttpOnly token
|
|
135
|
+
// server-side; a plain reload alone would just re-read it.
|
|
136
|
+
if (sessionUserId !== null) {
|
|
137
|
+
await fetch('/auth/logout', { method: 'POST' }).catch(() => { });
|
|
119
138
|
}
|
|
139
|
+
clearSilentSsoTried();
|
|
140
|
+
window.location.reload();
|
|
120
141
|
}
|
|
121
142
|
catch {
|
|
122
143
|
/* network hiccup — leave the session as-is */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bootstrapApp.js","sourceRoot":"","sources":["../../src/client/bootstrapApp.tsx"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"bootstrapApp.js","sourceRoot":"","sources":["../../src/client/bootstrapApp.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UglyNative cross-platform conformance harness.
|
|
3
|
+
*
|
|
4
|
+
* One page, run on every client (desktop Electron, iOS WKWebView, Android
|
|
5
|
+
* WebView). It drives the RAW `window.UglyNative` transport — `invoke` /
|
|
6
|
+
* `subscribe` — directly, because the bridge is the thing that actually
|
|
7
|
+
* differs per platform; the typed `ugly-app/native` facade is identical
|
|
8
|
+
* shared JS on all three and is unit-tested separately. Driving raw invoke
|
|
9
|
+
* therefore IS the conformance test of platform parity, and keeps this file
|
|
10
|
+
* dependency-free so it bundles + loads anywhere with zero module resolution.
|
|
11
|
+
*
|
|
12
|
+
* Each probe runs the same way everywhere and records a raw outcome
|
|
13
|
+
* (`ok` | `threw` | `timeout` | `skipped`). It does NOT decide pass/fail —
|
|
14
|
+
* the per-platform expectation table in the runner (ugly-studio) interprets
|
|
15
|
+
* each outcome against what that client is supposed to do. Adding a channel
|
|
16
|
+
* with no probe shows up as missing coverage; a handler regressing flips its
|
|
17
|
+
* outcome and the runner fails the diff.
|
|
18
|
+
*
|
|
19
|
+
* Device-gated capabilities (push, billing, auth, live activities) are marked
|
|
20
|
+
* `manual` and skipped here so an automated sim/emulator run never triggers a
|
|
21
|
+
* store sheet, OS permission prompt, or APNs/FCM round-trip — those live in
|
|
22
|
+
* the manual DEVICE-CHECKLIST.
|
|
23
|
+
*/
|
|
24
|
+
export type ProbeOutcome = 'ok' | 'threw' | 'timeout' | 'skipped';
|
|
25
|
+
export type ProbeCategory = 'round-trip' | 'stream' | 'event' | 'smoke' | 'manual';
|
|
26
|
+
export interface ProbeResult {
|
|
27
|
+
id: string;
|
|
28
|
+
namespace: string;
|
|
29
|
+
category: ProbeCategory;
|
|
30
|
+
outcome: ProbeOutcome;
|
|
31
|
+
detail?: string;
|
|
32
|
+
ms: number;
|
|
33
|
+
}
|
|
34
|
+
export interface ConformanceReport {
|
|
35
|
+
platform: string;
|
|
36
|
+
ts: number;
|
|
37
|
+
userAgent: string;
|
|
38
|
+
results: ProbeResult[];
|
|
39
|
+
}
|
|
40
|
+
/** The probe ids this harness knows about (for the runner's coverage check). */
|
|
41
|
+
export declare const PROBE_IDS: string[];
|
|
42
|
+
/** Run every probe sequentially and return the report (also set on window). */
|
|
43
|
+
export declare function runConformance(): Promise<ConformanceReport>;
|
|
44
|
+
//# sourceMappingURL=harness.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"harness.d.ts","sourceRoot":"","sources":["../../../src/native/conformance/harness.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAGH,MAAM,MAAM,YAAY,GAAG,IAAI,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,CAAC;AAClE,MAAM,MAAM,aAAa,GAAG,YAAY,GAAG,QAAQ,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,CAAC;AAEnF,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,aAAa,CAAC;IACxB,OAAO,EAAE,YAAY,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,WAAW,EAAE,CAAC;CACxB;AAuUD,gFAAgF;AAChF,eAAO,MAAM,SAAS,EAAE,MAAM,EAA4B,CAAC;AAgB3D,+EAA+E;AAC/E,wBAAsB,cAAc,IAAI,OAAO,CAAC,iBAAiB,CAAC,CAoCjE"}
|
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UglyNative cross-platform conformance harness.
|
|
3
|
+
*
|
|
4
|
+
* One page, run on every client (desktop Electron, iOS WKWebView, Android
|
|
5
|
+
* WebView). It drives the RAW `window.UglyNative` transport — `invoke` /
|
|
6
|
+
* `subscribe` — directly, because the bridge is the thing that actually
|
|
7
|
+
* differs per platform; the typed `ugly-app/native` facade is identical
|
|
8
|
+
* shared JS on all three and is unit-tested separately. Driving raw invoke
|
|
9
|
+
* therefore IS the conformance test of platform parity, and keeps this file
|
|
10
|
+
* dependency-free so it bundles + loads anywhere with zero module resolution.
|
|
11
|
+
*
|
|
12
|
+
* Each probe runs the same way everywhere and records a raw outcome
|
|
13
|
+
* (`ok` | `threw` | `timeout` | `skipped`). It does NOT decide pass/fail —
|
|
14
|
+
* the per-platform expectation table in the runner (ugly-studio) interprets
|
|
15
|
+
* each outcome against what that client is supposed to do. Adding a channel
|
|
16
|
+
* with no probe shows up as missing coverage; a handler regressing flips its
|
|
17
|
+
* outcome and the runner fails the diff.
|
|
18
|
+
*
|
|
19
|
+
* Device-gated capabilities (push, billing, auth, live activities) are marked
|
|
20
|
+
* `manual` and skipped here so an automated sim/emulator run never triggers a
|
|
21
|
+
* store sheet, OS permission prompt, or APNs/FCM round-trip — those live in
|
|
22
|
+
* the manual DEVICE-CHECKLIST.
|
|
23
|
+
*/
|
|
24
|
+
// ── Small async helpers ──────────────────────────────────────────────────────
|
|
25
|
+
const DEFAULT_TIMEOUT = 10_000;
|
|
26
|
+
class TimeoutError extends Error {
|
|
27
|
+
}
|
|
28
|
+
function withTimeout(p, ms, label) {
|
|
29
|
+
return new Promise((resolve, reject) => {
|
|
30
|
+
const t = setTimeout(() => reject(new TimeoutError(`timeout after ${ms}ms: ${label}`)), ms);
|
|
31
|
+
p.then((v) => {
|
|
32
|
+
clearTimeout(t);
|
|
33
|
+
resolve(v);
|
|
34
|
+
}, (e) => {
|
|
35
|
+
clearTimeout(t);
|
|
36
|
+
reject(e);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
/** Resolve with the first event of `name`, or reject on timeout. */
|
|
41
|
+
function waitEvent(n, name, ms) {
|
|
42
|
+
return new Promise((resolve, reject) => {
|
|
43
|
+
let unsub = () => { };
|
|
44
|
+
const t = setTimeout(() => {
|
|
45
|
+
unsub();
|
|
46
|
+
reject(new TimeoutError(`no '${name}' event within ${ms}ms`));
|
|
47
|
+
}, ms);
|
|
48
|
+
unsub = n.subscribe(name, (data) => {
|
|
49
|
+
clearTimeout(t);
|
|
50
|
+
unsub();
|
|
51
|
+
resolve(data);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
let seq = 0;
|
|
56
|
+
const uid = () => `c${Date.now().toString(36)}${(seq++).toString(36)}`;
|
|
57
|
+
function assert(cond, msg) {
|
|
58
|
+
if (!cond)
|
|
59
|
+
throw new Error(msg);
|
|
60
|
+
}
|
|
61
|
+
// ── Permission bootstrap ─────────────────────────────────────────────────────
|
|
62
|
+
// Gated namespaces (fs/secrets/serve/workers/net/process/browse) are checked
|
|
63
|
+
// per-origin by the host. Request a broad grant once up front; tolerate hosts
|
|
64
|
+
// that don't implement the permissions channel (e.g. iOS) — the individual
|
|
65
|
+
// gated probes will then simply record `threw`, which the expectation table
|
|
66
|
+
// already encodes for those platforms.
|
|
67
|
+
async function bootstrapPermissions(n) {
|
|
68
|
+
try {
|
|
69
|
+
await withTimeout(n.invoke('permissions.request', {
|
|
70
|
+
grants: {
|
|
71
|
+
fs: 'scoped',
|
|
72
|
+
secrets: 'full',
|
|
73
|
+
serve: true,
|
|
74
|
+
workers: true,
|
|
75
|
+
net: true,
|
|
76
|
+
process: ['node'],
|
|
77
|
+
browse: true,
|
|
78
|
+
},
|
|
79
|
+
}), DEFAULT_TIMEOUT, 'permissions.request');
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
/* host lacks permissions channel — gated probes will report their own outcome */
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// ── Probe registry ───────────────────────────────────────────────────────────
|
|
86
|
+
const PROBES = [
|
|
87
|
+
// ----- round-trip -----
|
|
88
|
+
{
|
|
89
|
+
id: 'clipboard.roundtrip',
|
|
90
|
+
category: 'round-trip',
|
|
91
|
+
async run(n) {
|
|
92
|
+
const text = `ugly-conf-${uid()}`;
|
|
93
|
+
await n.invoke('clipboard.write', { text });
|
|
94
|
+
const r = (await n.invoke('clipboard.read', undefined));
|
|
95
|
+
assert(r && r.text === text, `clipboard read back '${r?.text}' !== '${text}'`);
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
id: 'ui.getSafeArea',
|
|
100
|
+
category: 'round-trip',
|
|
101
|
+
async run(n) {
|
|
102
|
+
const r = (await n.invoke('ui.getSafeArea', undefined));
|
|
103
|
+
assert(r && typeof r.top === 'number', 'safe-area insets missing numeric top');
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
id: 'badge.set',
|
|
108
|
+
category: 'round-trip',
|
|
109
|
+
async run(n) {
|
|
110
|
+
await n.invoke('badge.set', { count: 0 });
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
id: 'capture.screenshot',
|
|
115
|
+
category: 'round-trip',
|
|
116
|
+
async run(n) {
|
|
117
|
+
const r = (await n.invoke('capture.screenshot', undefined));
|
|
118
|
+
assert(typeof r === 'string' && r.length > 100, 'screenshot returned no base64');
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
id: 'fs.roundtrip',
|
|
123
|
+
category: 'round-trip',
|
|
124
|
+
async run(n) {
|
|
125
|
+
const dir = `conf-${uid()}`;
|
|
126
|
+
const file = `${dir}/a.txt`;
|
|
127
|
+
await n.invoke('fs.mkdir', { path: dir, recursive: true });
|
|
128
|
+
try {
|
|
129
|
+
await n.invoke('fs.writeFile', { path: file, content: 'native-conf' });
|
|
130
|
+
const read = (await n.invoke('fs.readFile', { path: file }));
|
|
131
|
+
assert(read.content === 'native-conf', `fs read back '${read.content}'`);
|
|
132
|
+
const ex = (await n.invoke('fs.exists', { path: file }));
|
|
133
|
+
assert(ex.exists === true, 'fs.exists false for written file');
|
|
134
|
+
const st = (await n.invoke('fs.stat', { path: file }));
|
|
135
|
+
assert(st.isFile === true, 'fs.stat not a file');
|
|
136
|
+
}
|
|
137
|
+
finally {
|
|
138
|
+
await n.invoke('fs.rm', { path: dir, recursive: true, force: true }).catch(() => { });
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
id: 'secrets.roundtrip',
|
|
144
|
+
category: 'round-trip',
|
|
145
|
+
async run(n) {
|
|
146
|
+
const key = `conf-${uid()}`;
|
|
147
|
+
await n.invoke('secrets.set', { key, value: 'sekret' });
|
|
148
|
+
try {
|
|
149
|
+
const got = (await n.invoke('secrets.get', { key }));
|
|
150
|
+
assert(got.value === 'sekret', `secrets read back '${got.value}'`);
|
|
151
|
+
}
|
|
152
|
+
finally {
|
|
153
|
+
await n.invoke('secrets.delete', { key }).catch(() => { });
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
id: 'studio.app',
|
|
159
|
+
category: 'round-trip',
|
|
160
|
+
async run(n) {
|
|
161
|
+
const v = (await n.invoke('studio.app.version', undefined));
|
|
162
|
+
assert(typeof v === 'string' && v.length > 0, 'studio.app.version empty');
|
|
163
|
+
// platform is a local property on the transport, not an invoke channel.
|
|
164
|
+
assert(['ios', 'android', 'desktop'].includes(n.platform), `unexpected platform '${n.platform}'`);
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
id: 'studio.zoom',
|
|
169
|
+
category: 'round-trip',
|
|
170
|
+
async run(n) {
|
|
171
|
+
const cur = (await n.invoke('studio.zoom.get', undefined));
|
|
172
|
+
assert(typeof cur === 'number', 'studio.zoom.get not a number');
|
|
173
|
+
await n.invoke('studio.zoom.set', { level: cur });
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
// ----- stream / lifecycle -----
|
|
177
|
+
{
|
|
178
|
+
id: 'process.spawn',
|
|
179
|
+
category: 'stream',
|
|
180
|
+
async run(n) {
|
|
181
|
+
const { id } = (await n.invoke('process.spawn', {
|
|
182
|
+
cmd: 'node',
|
|
183
|
+
args: ['-e', 'process.stdout.write("NATIVE_OK")'],
|
|
184
|
+
opts: { env: { ELECTRON_RUN_AS_NODE: '1' } },
|
|
185
|
+
}));
|
|
186
|
+
assert(id, 'process.spawn returned no id');
|
|
187
|
+
const out = await withTimeout(new Promise((resolve) => {
|
|
188
|
+
let buf = '';
|
|
189
|
+
n.subscribe(`process.stdout:${id}`, (d) => (buf += d.chunk));
|
|
190
|
+
n.subscribe(`process.exit:${id}`, () => resolve(buf));
|
|
191
|
+
}), DEFAULT_TIMEOUT, 'process stdout/exit');
|
|
192
|
+
assert(out.includes('NATIVE_OK'), `process stdout was '${out}'`);
|
|
193
|
+
},
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
id: 'serve.roundtrip',
|
|
197
|
+
category: 'stream',
|
|
198
|
+
async run(n) {
|
|
199
|
+
const { port, url } = (await n.invoke('serve.start', {}));
|
|
200
|
+
assert(typeof port === 'number' && port > 0, 'serve.start gave no port');
|
|
201
|
+
try {
|
|
202
|
+
await fetch(url).catch(() => { }); // reachability is best-effort
|
|
203
|
+
}
|
|
204
|
+
finally {
|
|
205
|
+
await n.invoke('serve.stop', { port }).catch(() => { });
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
id: 'workers.roundtrip',
|
|
211
|
+
category: 'stream',
|
|
212
|
+
async run(n) {
|
|
213
|
+
const list0 = (await n.invoke('workers.list', undefined));
|
|
214
|
+
assert(Array.isArray(list0.ids), 'workers.list gave no ids array');
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
id: 'net.loopback',
|
|
219
|
+
category: 'stream',
|
|
220
|
+
async run(n) {
|
|
221
|
+
const srv = (await n.invoke('net.listen', {}));
|
|
222
|
+
assert(srv.port > 0, 'net.listen gave no port');
|
|
223
|
+
try {
|
|
224
|
+
// Echo: when a peer connects server-side, write a token back to it.
|
|
225
|
+
n.subscribe(`net.connection:${srv.id}`, (d) => {
|
|
226
|
+
const sock = d.socketId;
|
|
227
|
+
void n.invoke('net.write', { id: sock, data: 'PONG' });
|
|
228
|
+
});
|
|
229
|
+
const cli = (await n.invoke('net.connect', { host: '127.0.0.1', port: srv.port }));
|
|
230
|
+
const data = await withTimeout(waitEvent(n, `net.data:${cli.id}`, DEFAULT_TIMEOUT), DEFAULT_TIMEOUT, 'net.data');
|
|
231
|
+
assert(data.data.includes('PONG'), 'net loopback did not echo');
|
|
232
|
+
await n.invoke('net.close', { id: cli.id }).catch(() => { });
|
|
233
|
+
}
|
|
234
|
+
finally {
|
|
235
|
+
await n.invoke('net.close', { id: srv.id }).catch(() => { });
|
|
236
|
+
}
|
|
237
|
+
},
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
id: 'browse.extract',
|
|
241
|
+
category: 'stream',
|
|
242
|
+
async run(n) {
|
|
243
|
+
// The browse SSRF guard rejects data:/file: and private/loopback hosts, so
|
|
244
|
+
// this probe needs a real public URL (network-dependent, like the existing
|
|
245
|
+
// window-ugly-browse e2e). example.com is the canonical stable target.
|
|
246
|
+
const page = (await n.invoke('browse.extract', { url: 'https://example.com/' }));
|
|
247
|
+
assert(typeof page.content === 'string', 'browse.extract gave no content');
|
|
248
|
+
},
|
|
249
|
+
},
|
|
250
|
+
// ----- event-emitting -----
|
|
251
|
+
{
|
|
252
|
+
id: 'alarm.fired',
|
|
253
|
+
category: 'event',
|
|
254
|
+
async run(n) {
|
|
255
|
+
const id = `conf-${uid()}`;
|
|
256
|
+
const fired = waitEvent(n, 'alarm.fired', 8_000);
|
|
257
|
+
await n.invoke('alarm.schedule', { id, delaySeconds: 1, title: 'conf', body: 'conf' });
|
|
258
|
+
try {
|
|
259
|
+
await fired;
|
|
260
|
+
}
|
|
261
|
+
finally {
|
|
262
|
+
await n.invoke('alarm.cancel', { id }).catch(() => { });
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
id: 'tts.state',
|
|
268
|
+
category: 'event',
|
|
269
|
+
async run(n) {
|
|
270
|
+
const state = waitEvent(n, 'tts.state', 8_000);
|
|
271
|
+
await n.invoke('tts.start', { text: 'conformance check' });
|
|
272
|
+
try {
|
|
273
|
+
await state;
|
|
274
|
+
}
|
|
275
|
+
finally {
|
|
276
|
+
await n.invoke('tts.stop', undefined).catch(() => { });
|
|
277
|
+
}
|
|
278
|
+
},
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
id: 'gps.position',
|
|
282
|
+
category: 'event',
|
|
283
|
+
async run(n) {
|
|
284
|
+
const pos = waitEvent(n, 'gps.position', 8_000);
|
|
285
|
+
await n.invoke('gps.start', { activityType: 'run' });
|
|
286
|
+
try {
|
|
287
|
+
const p = (await pos);
|
|
288
|
+
assert(typeof p.lat === 'number' && typeof p.lng === 'number', 'gps.position malformed');
|
|
289
|
+
}
|
|
290
|
+
finally {
|
|
291
|
+
await n.invoke('gps.stop', undefined).catch(() => { });
|
|
292
|
+
}
|
|
293
|
+
},
|
|
294
|
+
},
|
|
295
|
+
// ----- smoke (origin-exempt page-ready handshake) -----
|
|
296
|
+
{
|
|
297
|
+
id: 'nav.ready',
|
|
298
|
+
category: 'smoke',
|
|
299
|
+
async run(n) {
|
|
300
|
+
await n.invoke('nav.ready', {});
|
|
301
|
+
},
|
|
302
|
+
},
|
|
303
|
+
// ----- manual / device-only (skipped in the automated lane) -----
|
|
304
|
+
{ id: 'push.register', category: 'manual', manual: true, run: async () => { } },
|
|
305
|
+
{ id: 'billing.purchase', category: 'manual', manual: true, run: async () => { } },
|
|
306
|
+
{ id: 'billing.restore', category: 'manual', manual: true, run: async () => { } },
|
|
307
|
+
{ id: 'auth.googleSignIn', category: 'manual', manual: true, run: async () => { } },
|
|
308
|
+
{ id: 'liveActivity.startWorkout', category: 'manual', manual: true, run: async () => { } },
|
|
309
|
+
{ id: 'liveActivity.startCardio', category: 'manual', manual: true, run: async () => { } },
|
|
310
|
+
];
|
|
311
|
+
/** The probe ids this harness knows about (for the runner's coverage check). */
|
|
312
|
+
export const PROBE_IDS = PROBES.map((p) => p.id);
|
|
313
|
+
async function runProbe(n, probe) {
|
|
314
|
+
const base = { id: probe.id, namespace: probe.id.split('.')[0], category: probe.category };
|
|
315
|
+
if (probe.manual)
|
|
316
|
+
return { ...base, outcome: 'skipped', detail: 'device-only (manual)', ms: 0 };
|
|
317
|
+
const started = Date.now();
|
|
318
|
+
try {
|
|
319
|
+
await withTimeout(probe.run(n), DEFAULT_TIMEOUT + 2_000, probe.id);
|
|
320
|
+
return { ...base, outcome: 'ok', ms: Date.now() - started };
|
|
321
|
+
}
|
|
322
|
+
catch (e) {
|
|
323
|
+
const outcome = e instanceof TimeoutError ? 'timeout' : 'threw';
|
|
324
|
+
const detail = e?.message ?? String(e);
|
|
325
|
+
return { ...base, outcome, detail, ms: Date.now() - started };
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
/** Run every probe sequentially and return the report (also set on window). */
|
|
329
|
+
export async function runConformance() {
|
|
330
|
+
const n = globalThis.UglyNative;
|
|
331
|
+
const ua = typeof navigator !== 'undefined' ? navigator.userAgent : '';
|
|
332
|
+
if (!n || typeof n.invoke !== 'function') {
|
|
333
|
+
const report = {
|
|
334
|
+
platform: 'unknown',
|
|
335
|
+
ts: Date.now(),
|
|
336
|
+
userAgent: ua,
|
|
337
|
+
results: [{ id: 'window.UglyNative', namespace: 'bridge', category: 'smoke', outcome: 'threw', detail: 'window.UglyNative is not present', ms: 0 }],
|
|
338
|
+
};
|
|
339
|
+
globalThis.__uglyConformance = report;
|
|
340
|
+
return report;
|
|
341
|
+
}
|
|
342
|
+
await bootstrapPermissions(n);
|
|
343
|
+
const results = [];
|
|
344
|
+
for (const probe of PROBES)
|
|
345
|
+
results.push(await runProbe(n, probe));
|
|
346
|
+
const report = { platform: n.platform, ts: Date.now(), userAgent: ua, results };
|
|
347
|
+
globalThis.__uglyConformance = report;
|
|
348
|
+
// If a sink is configured (mobile lanes can't be read in-process), POST it.
|
|
349
|
+
try {
|
|
350
|
+
const sink = new URLSearchParams(location.search).get('sink');
|
|
351
|
+
if (sink) {
|
|
352
|
+
const body = JSON.stringify(report);
|
|
353
|
+
// Prefer fetch (works cross-origin to the no-cors sink); fall back to beacon.
|
|
354
|
+
void fetch(sink, { method: 'POST', mode: 'no-cors', body }).catch(() => {
|
|
355
|
+
navigator.sendBeacon?.(sink, body);
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
catch {
|
|
360
|
+
/* no location/sink — desktop reads window.__uglyConformance directly */
|
|
361
|
+
}
|
|
362
|
+
return report;
|
|
363
|
+
}
|
|
364
|
+
//# sourceMappingURL=harness.js.map
|