@stablyai/playwright-base 0.1.7-next.3 → 0.1.7-next.4
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/ai/extract.d.ts +1 -2
- package/dist/ai/extract.js +6 -14
- package/dist/ai/verify-prompt.js +28 -12
- package/dist/internal/autoheal-patch.d.ts +1 -0
- package/dist/internal/autoheal-patch.js +178 -0
- package/dist/type-predicate/is-object.d.ts +1 -0
- package/dist/type-predicate/is-object.js +7 -0
- package/package.json +1 -1
package/dist/ai/extract.d.ts
CHANGED
|
@@ -4,10 +4,9 @@ export interface ExtractSchema extends z4.$ZodType {
|
|
|
4
4
|
safeParseAsync(data: unknown, params?: z4.ParseContext<z4.$ZodIssue>): Promise<z4.util.SafeParseResult<z4.output<this>>>;
|
|
5
5
|
}
|
|
6
6
|
export type SchemaOutput<T extends ExtractSchema> = z4.output<T>;
|
|
7
|
-
type ExtractSubject = Page | Locator;
|
|
8
7
|
type BaseExtractArgs = {
|
|
9
8
|
prompt: string;
|
|
10
|
-
pageOrLocator:
|
|
9
|
+
pageOrLocator: Page | Locator;
|
|
11
10
|
};
|
|
12
11
|
type ExtractArgsWithSchema<T extends ExtractSchema> = BaseExtractArgs & {
|
|
13
12
|
schema: T;
|
package/dist/ai/extract.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.extract = extract;
|
|
4
4
|
const runtime_1 = require("../runtime");
|
|
5
|
+
const is_object_1 = require("~/type-predicate/is-object");
|
|
5
6
|
const EXTRACT_ENDPOINT = "https://api.stably.ai/internal/v2/extract";
|
|
6
7
|
const zodV4 = (() => {
|
|
7
8
|
try {
|
|
@@ -11,23 +12,17 @@ const zodV4 = (() => {
|
|
|
11
12
|
return undefined;
|
|
12
13
|
}
|
|
13
14
|
})();
|
|
14
|
-
const isObject = (value) => {
|
|
15
|
-
return typeof value === "object" && value !== null;
|
|
16
|
-
};
|
|
17
15
|
const isExtractionResponse = (value) => {
|
|
18
|
-
if (!isObject(value)) {
|
|
16
|
+
if (!(0, is_object_1.isObject)(value)) {
|
|
19
17
|
return false;
|
|
20
18
|
}
|
|
21
19
|
if (value.success === true) {
|
|
22
20
|
return "value" in value;
|
|
23
21
|
}
|
|
24
|
-
|
|
25
|
-
return true;
|
|
26
|
-
}
|
|
27
|
-
return false;
|
|
22
|
+
return value.success === false && typeof value.error === "string";
|
|
28
23
|
};
|
|
29
24
|
const isErrorResponse = (value) => {
|
|
30
|
-
return isObject(value) && typeof value.error === "string";
|
|
25
|
+
return (0, is_object_1.isObject)(value) && typeof value.error === "string";
|
|
31
26
|
};
|
|
32
27
|
class ExtractValidationError extends Error {
|
|
33
28
|
issues;
|
|
@@ -49,7 +44,7 @@ async function extract({ prompt, pageOrLocator, schema, }) {
|
|
|
49
44
|
throw new Error("Schema support requires installing zod@4. Please add it to enable schemas.");
|
|
50
45
|
}
|
|
51
46
|
const jsonSchema = schema && zodV4
|
|
52
|
-
? zodV4
|
|
47
|
+
? zodV4?.toJSONSchema(schema)
|
|
53
48
|
: undefined;
|
|
54
49
|
const apiKey = (0, runtime_1.requireApiKey)();
|
|
55
50
|
const form = new FormData();
|
|
@@ -83,8 +78,5 @@ async function extract({ prompt, pageOrLocator, schema, }) {
|
|
|
83
78
|
? value
|
|
84
79
|
: JSON.stringify(value);
|
|
85
80
|
}
|
|
86
|
-
|
|
87
|
-
throw new Error(raw.error);
|
|
88
|
-
}
|
|
89
|
-
throw new Error("Extract failed");
|
|
81
|
+
throw new Error(isErrorResponse(raw) ? raw.error : "Extract failed");
|
|
90
82
|
}
|
package/dist/ai/verify-prompt.js
CHANGED
|
@@ -1,16 +1,32 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.verifyPrompt = verifyPrompt;
|
|
4
|
-
const
|
|
5
|
-
const zod_1 = require("zod");
|
|
4
|
+
const is_object_1 = require("~/type-predicate/is-object");
|
|
6
5
|
const runtime_1 = require("../runtime");
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
6
|
+
const PROMPT_ASSERTION_ENDPOINT = "https://api.stably.ai/internal/v1/assert";
|
|
7
|
+
const parseSuccessResponse = (value) => {
|
|
8
|
+
if (!(0, is_object_1.isObject)(value)) {
|
|
9
|
+
throw new Error("Verify prompt returned unexpected response shape");
|
|
10
|
+
}
|
|
11
|
+
const { success, reason } = value;
|
|
12
|
+
if (typeof success !== "boolean") {
|
|
13
|
+
throw new Error("Verify prompt returned unexpected response shape");
|
|
14
|
+
}
|
|
15
|
+
if (reason !== undefined && typeof reason !== "string") {
|
|
16
|
+
throw new Error("Verify prompt returned unexpected response shape");
|
|
17
|
+
}
|
|
18
|
+
return {
|
|
19
|
+
success,
|
|
20
|
+
reason,
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
const parseErrorResponse = (value) => {
|
|
24
|
+
if (!(0, is_object_1.isObject)(value)) {
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
const { error } = value;
|
|
28
|
+
return typeof error !== "string" ? undefined : { error };
|
|
29
|
+
};
|
|
14
30
|
async function verifyPrompt({ prompt, screenshot, }) {
|
|
15
31
|
const apiKey = (0, runtime_1.requireApiKey)();
|
|
16
32
|
const form = new FormData();
|
|
@@ -27,12 +43,12 @@ async function verifyPrompt({ prompt, screenshot, }) {
|
|
|
27
43
|
});
|
|
28
44
|
const parsed = await response.json().catch(() => undefined);
|
|
29
45
|
if (response.ok) {
|
|
30
|
-
const { success, reason } =
|
|
46
|
+
const { success, reason } = parseSuccessResponse(parsed);
|
|
31
47
|
return {
|
|
32
48
|
pass: success,
|
|
33
49
|
reason,
|
|
34
50
|
};
|
|
35
51
|
}
|
|
36
|
-
const err =
|
|
37
|
-
throw new Error(`Verify prompt failed (${response.status})${err
|
|
52
|
+
const err = parseErrorResponse(parsed);
|
|
53
|
+
throw new Error(`Verify prompt failed (${response.status})${err ? `: ${err.error}` : ""}`);
|
|
38
54
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function installAutohealZodPatch(): void;
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.installAutohealZodPatch = installAutohealZodPatch;
|
|
7
|
+
const module_1 = __importDefault(require("module"));
|
|
8
|
+
const module_2 = require("module");
|
|
9
|
+
const nodeRequire = (0, module_2.createRequire)(__filename);
|
|
10
|
+
let patchInstalled = false;
|
|
11
|
+
function installAutohealZodPatch() {
|
|
12
|
+
if (patchInstalled) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
patchInstalled = true;
|
|
16
|
+
let targetModule;
|
|
17
|
+
try {
|
|
18
|
+
targetModule = nodeRequire.resolve("@stablyai/internal-playwright-core/lib/server/stably/ai-tools/stablyApiCaller");
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const moduleAny = module_1.default;
|
|
24
|
+
const originalLoad = moduleAny._load;
|
|
25
|
+
const originalResolve = moduleAny._resolveFilename;
|
|
26
|
+
if (typeof originalLoad !== "function" || typeof originalResolve !== "function") {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
moduleAny._load = function loadPatchedModule(request, parent, isMain, options) {
|
|
30
|
+
const args = arguments;
|
|
31
|
+
let resolved;
|
|
32
|
+
try {
|
|
33
|
+
resolved = originalResolve.call(this, request, parent, isMain, options);
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return originalLoad.apply(this, args);
|
|
37
|
+
}
|
|
38
|
+
if (resolved === targetModule) {
|
|
39
|
+
const cached = nodeRequire.cache[targetModule];
|
|
40
|
+
if (cached?.loaded) {
|
|
41
|
+
return cached.exports;
|
|
42
|
+
}
|
|
43
|
+
const patchedExports = createPatchedAutohealModule();
|
|
44
|
+
const ModuleConstructor = moduleAny.Module ?? module_1.default.Module;
|
|
45
|
+
const syntheticModule = new ModuleConstructor(targetModule, parent ?? undefined);
|
|
46
|
+
syntheticModule.filename = targetModule;
|
|
47
|
+
syntheticModule.exports = patchedExports;
|
|
48
|
+
syntheticModule.loaded = true;
|
|
49
|
+
nodeRequire.cache[targetModule] = syntheticModule;
|
|
50
|
+
return patchedExports;
|
|
51
|
+
}
|
|
52
|
+
return originalLoad.apply(this, args);
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function createPatchedAutohealModule() {
|
|
56
|
+
const { createMultipartFormData, makeHTTPRequest, } = nodeRequire("@stablyai/internal-playwright-core/lib/server/stably/ai-tools/http-request");
|
|
57
|
+
const { STABLY_API_HOSTNAME, STABLY_API_PORT } = nodeRequire("@stablyai/internal-playwright-core/lib/server/stably/constants");
|
|
58
|
+
const LOCATOR_AUTOHEAL_ENDPOINT = "/v1/internal/sdk/autohealLocator";
|
|
59
|
+
const SCREENSHOT_AUTOHEAL_ENDPOINT = "/v1/internal/sdk/autohealToHaveScreenshot";
|
|
60
|
+
function parseLocatorResponse(raw) {
|
|
61
|
+
if (!isObject(raw)) {
|
|
62
|
+
throw new Error("Invalid auto-heal response");
|
|
63
|
+
}
|
|
64
|
+
const { reasoning, success } = raw;
|
|
65
|
+
if (typeof reasoning !== "string") {
|
|
66
|
+
throw new Error("Invalid auto-heal response: missing reasoning");
|
|
67
|
+
}
|
|
68
|
+
if (typeof success !== "boolean") {
|
|
69
|
+
throw new Error("Invalid auto-heal response: missing success flag");
|
|
70
|
+
}
|
|
71
|
+
if (success === true) {
|
|
72
|
+
const { coordinates } = raw;
|
|
73
|
+
if (!Array.isArray(coordinates) ||
|
|
74
|
+
coordinates.length !== 2 ||
|
|
75
|
+
!coordinates.every((value) => typeof value === "number" && Number.isFinite(value))) {
|
|
76
|
+
throw new Error("Invalid auto-heal response: missing coordinates");
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
reasoning,
|
|
80
|
+
success: true,
|
|
81
|
+
coordinates: [coordinates[0], coordinates[1]],
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
reasoning,
|
|
86
|
+
success: false,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
function parseScreenshotResponse(raw) {
|
|
90
|
+
if (!isObject(raw)) {
|
|
91
|
+
throw new Error("Invalid auto-heal response");
|
|
92
|
+
}
|
|
93
|
+
const { result, reason } = raw;
|
|
94
|
+
if (typeof result !== "boolean" || typeof reason !== "string") {
|
|
95
|
+
throw new Error("Invalid auto-heal response");
|
|
96
|
+
}
|
|
97
|
+
return { result, reason };
|
|
98
|
+
}
|
|
99
|
+
async function callAiToAutohealLocator({ description, screenshot, originalSelector, actionName, model = "claude-sonnet-4-5", }) {
|
|
100
|
+
const stablyApiKey = process.env.STABLY_API_KEY;
|
|
101
|
+
if (!stablyApiKey) {
|
|
102
|
+
throw new Error("STABLY_API_KEY environment variable is required for auto-healing");
|
|
103
|
+
}
|
|
104
|
+
const { formData, boundary } = createMultipartFormData({
|
|
105
|
+
fields: {
|
|
106
|
+
originalSelector,
|
|
107
|
+
description,
|
|
108
|
+
actionName,
|
|
109
|
+
},
|
|
110
|
+
files: [
|
|
111
|
+
{
|
|
112
|
+
fieldName: "file",
|
|
113
|
+
filename: "screenshot.png",
|
|
114
|
+
data: screenshot,
|
|
115
|
+
contentType: "image/png",
|
|
116
|
+
},
|
|
117
|
+
],
|
|
118
|
+
});
|
|
119
|
+
const options = {
|
|
120
|
+
hostname: STABLY_API_HOSTNAME,
|
|
121
|
+
port: STABLY_API_PORT,
|
|
122
|
+
path: `${LOCATOR_AUTOHEAL_ENDPOINT}?model=${encodeURIComponent(model)}`,
|
|
123
|
+
method: "POST",
|
|
124
|
+
headers: {
|
|
125
|
+
"Content-Type": `multipart/form-data; boundary=${boundary}`,
|
|
126
|
+
Authorization: stablyApiKey,
|
|
127
|
+
"Content-Length": formData.length.toString(),
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
const responseData = await makeHTTPRequest(options, formData);
|
|
131
|
+
const parsed = parseLocatorResponse(JSON.parse(responseData));
|
|
132
|
+
return {
|
|
133
|
+
coordinate: parsed.success ? parsed.coordinates : undefined,
|
|
134
|
+
reasoning: parsed.reasoning,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
async function callAiToAutohealScreenshot({ actualScreenshot, expectedScreenshot, model = "claude-sonnet-4-5", }) {
|
|
138
|
+
const stablyApiKey = process.env.STABLY_API_KEY;
|
|
139
|
+
if (!stablyApiKey) {
|
|
140
|
+
throw new Error("STABLY_API_KEY environment variable is required for auto-healing");
|
|
141
|
+
}
|
|
142
|
+
const files = [
|
|
143
|
+
{
|
|
144
|
+
fieldName: "actualScreenshot",
|
|
145
|
+
filename: "actual.png",
|
|
146
|
+
data: actualScreenshot,
|
|
147
|
+
contentType: "image/png",
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
fieldName: "expectedScreenshot",
|
|
151
|
+
filename: "expected.png",
|
|
152
|
+
data: expectedScreenshot,
|
|
153
|
+
contentType: "image/png",
|
|
154
|
+
},
|
|
155
|
+
];
|
|
156
|
+
const { formData, boundary } = createMultipartFormData({ files });
|
|
157
|
+
const options = {
|
|
158
|
+
hostname: STABLY_API_HOSTNAME,
|
|
159
|
+
port: STABLY_API_PORT,
|
|
160
|
+
path: `${SCREENSHOT_AUTOHEAL_ENDPOINT}?model=${encodeURIComponent(model)}`,
|
|
161
|
+
method: "POST",
|
|
162
|
+
headers: {
|
|
163
|
+
"Content-Type": `multipart/form-data; boundary=${boundary}`,
|
|
164
|
+
Authorization: stablyApiKey,
|
|
165
|
+
"Content-Length": formData.length.toString(),
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
const responseData = await makeHTTPRequest(options, formData);
|
|
169
|
+
return parseScreenshotResponse(JSON.parse(responseData));
|
|
170
|
+
}
|
|
171
|
+
return {
|
|
172
|
+
callAiToAutohealLocator,
|
|
173
|
+
callAiToAutohealScreenshot,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
function isObject(value) {
|
|
177
|
+
return typeof value === "object" && value !== null;
|
|
178
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const isObject: (value: unknown) => value is Record<string, unknown>;
|