@phone-use/sdk 0.3.1 → 0.4.0
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/package.json +1 -1
- package/src/backends/cloud-sandbox.ts +42 -0
- package/src/index.ts +1 -1
- package/src/observe.ts +5 -2
package/package.json
CHANGED
|
@@ -163,6 +163,48 @@ export class CloudSandboxBackend extends BaseDeviceBackend {
|
|
|
163
163
|
}
|
|
164
164
|
|
|
165
165
|
/** Upload a zipped .app bundle (base64) and install it on the sandbox device. */
|
|
166
|
+
/**
|
|
167
|
+
* Upload a zipped .app as a raw stream. Prefer this over {@link installApp}:
|
|
168
|
+
* base64 inside a JSON body inflates the payload by a third, which caps real
|
|
169
|
+
* apps near 144MB. Pass a `Bun.file(...)` (or any Blob) and the bytes go to the
|
|
170
|
+
* wire without being buffered in memory.
|
|
171
|
+
*/
|
|
172
|
+
async installAppStream(zip: Blob): Promise<{ installed?: string }> {
|
|
173
|
+
const response = await this.fetchImpl(`${this.endpoint}/app`, {
|
|
174
|
+
method: 'POST',
|
|
175
|
+
headers: {
|
|
176
|
+
authorization: `Bearer ${this.token}`,
|
|
177
|
+
'content-type': 'application/zip',
|
|
178
|
+
},
|
|
179
|
+
body: zip,
|
|
180
|
+
});
|
|
181
|
+
// A worker older than this SDK has no /app route. Fall back to the base64
|
|
182
|
+
// RPC rather than failing: a client should keep working against a fleet
|
|
183
|
+
// that has not been redeployed yet. The old ceiling (~144MB of zip) still
|
|
184
|
+
// applies on that path, and an app above it will say so.
|
|
185
|
+
if (response.status === 404) {
|
|
186
|
+
const bytes = await zip.arrayBuffer();
|
|
187
|
+
return this.installApp(Buffer.from(bytes).toString('base64'));
|
|
188
|
+
}
|
|
189
|
+
const body = (await response.json().catch(() => ({}))) as
|
|
190
|
+
| SandboxRpcResponse
|
|
191
|
+
| { error?: string };
|
|
192
|
+
if (!response.ok || !('ok' in body)) {
|
|
193
|
+
const message =
|
|
194
|
+
('error' in body && typeof body.error === 'string' ? body.error : undefined) ??
|
|
195
|
+
`HTTP ${response.status}`;
|
|
196
|
+
throw new PhoneUseError(message, { code: 'UNKNOWN', retryable: false });
|
|
197
|
+
}
|
|
198
|
+
if (!body.ok) {
|
|
199
|
+
throw new PhoneUseError(body.error.message, {
|
|
200
|
+
code: normalizeErrorCode(body.error.code),
|
|
201
|
+
retryable: body.error.retryable ?? false,
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
return body.result as { installed?: string };
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/** @deprecated Base64-in-JSON; use {@link installAppStream}. */
|
|
166
208
|
installApp(base64Zip: string): Promise<{ installed?: string }> {
|
|
167
209
|
return this.rpc('installApp', base64Zip);
|
|
168
210
|
}
|
package/src/index.ts
CHANGED
package/src/observe.ts
CHANGED
|
@@ -880,8 +880,11 @@ export class DeviceCore {
|
|
|
880
880
|
await this.press(el.ref);
|
|
881
881
|
return true;
|
|
882
882
|
} catch (error) {
|
|
883
|
-
|
|
884
|
-
//
|
|
883
|
+
// Straddled the edge, or sitting under a sheet/toast/overlay. Both are
|
|
884
|
+
// positional and both are what the nudge scroll below is for, so retry
|
|
885
|
+
// rather than throw — real apps stack overlays over their own chrome,
|
|
886
|
+
// and a covered element used to abort an entire crawl.
|
|
887
|
+
if (!/off-?screen|covered/i.test(describeError(error))) throw error;
|
|
885
888
|
}
|
|
886
889
|
}
|
|
887
890
|
// Known position → scroll toward it. Not in the realized tree at all →
|