opensteer 0.8.9 → 0.8.11
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/{chunk-RO6WAWWG.js → chunk-33FDEOQY.js} +1973 -55
- package/dist/chunk-33FDEOQY.js.map +1 -0
- package/dist/cli/bin.cjs +2474 -169
- package/dist/cli/bin.cjs.map +1 -1
- package/dist/cli/bin.js +398 -10
- package/dist/cli/bin.js.map +1 -1
- package/dist/index.cjs +206 -45
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +82 -20
- package/dist/index.d.ts +82 -20
- package/dist/index.js +9 -3
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
- package/skills/opensteer/SKILL.md +33 -14
- package/skills/opensteer/references/request-workflow.md +312 -193
- package/skills/opensteer/references/sdk-reference.md +102 -14
- package/skills/recorder/SKILL.md +54 -0
- package/skills/recorder/references/recorder-reference.md +71 -0
- package/dist/chunk-RO6WAWWG.js.map +0 -1
package/dist/cli/bin.cjs
CHANGED
|
@@ -59,7 +59,7 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
59
59
|
|
|
60
60
|
// package.json
|
|
61
61
|
var package_default = {
|
|
62
|
-
version: "0.8.
|
|
62
|
+
version: "0.8.10"};
|
|
63
63
|
util.promisify(child_process.execFile);
|
|
64
64
|
Math.floor(Date.now() - process.uptime() * 1e3);
|
|
65
65
|
({ ...process.env});
|
|
@@ -690,8 +690,8 @@ function buildBrowserWebSocketUrl(httpUrl, webSocketPath) {
|
|
|
690
690
|
const protocol = httpUrl.protocol === "https:" ? "wss:" : "ws:";
|
|
691
691
|
return `${protocol}//${httpUrl.host}${normalizeWebSocketPath(webSocketPath)}`;
|
|
692
692
|
}
|
|
693
|
-
function normalizeWebSocketPath(
|
|
694
|
-
return
|
|
693
|
+
function normalizeWebSocketPath(path18) {
|
|
694
|
+
return path18.startsWith("/") ? path18 : `/${path18}`;
|
|
695
695
|
}
|
|
696
696
|
function rewriteBrowserWebSocketHost(browserWsUrl, requestedUrl) {
|
|
697
697
|
try {
|
|
@@ -1305,30 +1305,30 @@ function isPlainObject(value) {
|
|
|
1305
1305
|
const prototype = Object.getPrototypeOf(value);
|
|
1306
1306
|
return prototype === Object.prototype || prototype === null;
|
|
1307
1307
|
}
|
|
1308
|
-
function canonicalizeJsonValue(value,
|
|
1308
|
+
function canonicalizeJsonValue(value, path18) {
|
|
1309
1309
|
if (value === null || typeof value === "string" || typeof value === "boolean") {
|
|
1310
1310
|
return value;
|
|
1311
1311
|
}
|
|
1312
1312
|
if (typeof value === "number") {
|
|
1313
1313
|
if (!Number.isFinite(value)) {
|
|
1314
|
-
throw new TypeError(`${
|
|
1314
|
+
throw new TypeError(`${path18} must be a finite JSON number`);
|
|
1315
1315
|
}
|
|
1316
1316
|
return value;
|
|
1317
1317
|
}
|
|
1318
1318
|
if (Array.isArray(value)) {
|
|
1319
|
-
return value.map((entry, index) => canonicalizeJsonValue(entry, `${
|
|
1319
|
+
return value.map((entry, index) => canonicalizeJsonValue(entry, `${path18}[${index}]`));
|
|
1320
1320
|
}
|
|
1321
1321
|
if (!isPlainObject(value)) {
|
|
1322
|
-
throw new TypeError(`${
|
|
1322
|
+
throw new TypeError(`${path18} must be a plain JSON object`);
|
|
1323
1323
|
}
|
|
1324
1324
|
const sorted = Object.keys(value).sort((left, right) => left.localeCompare(right));
|
|
1325
1325
|
const result = {};
|
|
1326
1326
|
for (const key of sorted) {
|
|
1327
1327
|
const entry = value[key];
|
|
1328
1328
|
if (entry === void 0) {
|
|
1329
|
-
throw new TypeError(`${
|
|
1329
|
+
throw new TypeError(`${path18}.${key} must not be undefined`);
|
|
1330
1330
|
}
|
|
1331
|
-
result[key] = canonicalizeJsonValue(entry, `${
|
|
1331
|
+
result[key] = canonicalizeJsonValue(entry, `${path18}.${key}`);
|
|
1332
1332
|
}
|
|
1333
1333
|
return result;
|
|
1334
1334
|
}
|
|
@@ -1825,31 +1825,31 @@ function oneOfSchema(members, options = {}) {
|
|
|
1825
1825
|
}
|
|
1826
1826
|
|
|
1827
1827
|
// ../protocol/src/validation.ts
|
|
1828
|
-
function validateJsonSchema(schema, value,
|
|
1829
|
-
return validateSchemaNode(schema, value,
|
|
1828
|
+
function validateJsonSchema(schema, value, path18 = "$") {
|
|
1829
|
+
return validateSchemaNode(schema, value, path18);
|
|
1830
1830
|
}
|
|
1831
|
-
function validateSchemaNode(schema, value,
|
|
1831
|
+
function validateSchemaNode(schema, value, path18) {
|
|
1832
1832
|
const issues = [];
|
|
1833
1833
|
if ("const" in schema && !isJsonValueEqual(schema.const, value)) {
|
|
1834
1834
|
issues.push({
|
|
1835
|
-
path:
|
|
1835
|
+
path: path18,
|
|
1836
1836
|
message: `must equal ${JSON.stringify(schema.const)}`
|
|
1837
1837
|
});
|
|
1838
1838
|
return issues;
|
|
1839
1839
|
}
|
|
1840
1840
|
if (schema.enum !== void 0 && !schema.enum.some((candidate) => isJsonValueEqual(candidate, value))) {
|
|
1841
1841
|
issues.push({
|
|
1842
|
-
path:
|
|
1842
|
+
path: path18,
|
|
1843
1843
|
message: `must be one of ${schema.enum.map((candidate) => JSON.stringify(candidate)).join(", ")}`
|
|
1844
1844
|
});
|
|
1845
1845
|
return issues;
|
|
1846
1846
|
}
|
|
1847
1847
|
if (schema.oneOf !== void 0) {
|
|
1848
|
-
const branchIssues = schema.oneOf.map((member) => validateSchemaNode(member, value,
|
|
1848
|
+
const branchIssues = schema.oneOf.map((member) => validateSchemaNode(member, value, path18));
|
|
1849
1849
|
const validBranches = branchIssues.filter((current) => current.length === 0).length;
|
|
1850
1850
|
if (validBranches !== 1) {
|
|
1851
1851
|
issues.push({
|
|
1852
|
-
path:
|
|
1852
|
+
path: path18,
|
|
1853
1853
|
message: validBranches === 0 ? "must match exactly one supported shape" : "matches multiple supported shapes"
|
|
1854
1854
|
});
|
|
1855
1855
|
return issues;
|
|
@@ -1857,11 +1857,11 @@ function validateSchemaNode(schema, value, path17) {
|
|
|
1857
1857
|
}
|
|
1858
1858
|
if (schema.anyOf !== void 0) {
|
|
1859
1859
|
const hasMatch = schema.anyOf.some(
|
|
1860
|
-
(member) => validateSchemaNode(member, value,
|
|
1860
|
+
(member) => validateSchemaNode(member, value, path18).length === 0
|
|
1861
1861
|
);
|
|
1862
1862
|
if (!hasMatch) {
|
|
1863
1863
|
issues.push({
|
|
1864
|
-
path:
|
|
1864
|
+
path: path18,
|
|
1865
1865
|
message: "must match at least one supported shape"
|
|
1866
1866
|
});
|
|
1867
1867
|
return issues;
|
|
@@ -1869,7 +1869,7 @@ function validateSchemaNode(schema, value, path17) {
|
|
|
1869
1869
|
}
|
|
1870
1870
|
if (schema.allOf !== void 0) {
|
|
1871
1871
|
for (const member of schema.allOf) {
|
|
1872
|
-
issues.push(...validateSchemaNode(member, value,
|
|
1872
|
+
issues.push(...validateSchemaNode(member, value, path18));
|
|
1873
1873
|
}
|
|
1874
1874
|
if (issues.length > 0) {
|
|
1875
1875
|
return issues;
|
|
@@ -1877,7 +1877,7 @@ function validateSchemaNode(schema, value, path17) {
|
|
|
1877
1877
|
}
|
|
1878
1878
|
if (schema.type !== void 0 && !matchesSchemaType(schema.type, value)) {
|
|
1879
1879
|
issues.push({
|
|
1880
|
-
path:
|
|
1880
|
+
path: path18,
|
|
1881
1881
|
message: `must be ${describeSchemaType(schema.type)}`
|
|
1882
1882
|
});
|
|
1883
1883
|
return issues;
|
|
@@ -1885,19 +1885,19 @@ function validateSchemaNode(schema, value, path17) {
|
|
|
1885
1885
|
if (typeof value === "string") {
|
|
1886
1886
|
if (schema.minLength !== void 0 && value.length < schema.minLength) {
|
|
1887
1887
|
issues.push({
|
|
1888
|
-
path:
|
|
1888
|
+
path: path18,
|
|
1889
1889
|
message: `must have length >= ${String(schema.minLength)}`
|
|
1890
1890
|
});
|
|
1891
1891
|
}
|
|
1892
1892
|
if (schema.maxLength !== void 0 && value.length > schema.maxLength) {
|
|
1893
1893
|
issues.push({
|
|
1894
|
-
path:
|
|
1894
|
+
path: path18,
|
|
1895
1895
|
message: `must have length <= ${String(schema.maxLength)}`
|
|
1896
1896
|
});
|
|
1897
1897
|
}
|
|
1898
1898
|
if (schema.pattern !== void 0 && !new RegExp(schema.pattern).test(value)) {
|
|
1899
1899
|
issues.push({
|
|
1900
|
-
path:
|
|
1900
|
+
path: path18,
|
|
1901
1901
|
message: `must match pattern ${schema.pattern}`
|
|
1902
1902
|
});
|
|
1903
1903
|
}
|
|
@@ -1906,25 +1906,25 @@ function validateSchemaNode(schema, value, path17) {
|
|
|
1906
1906
|
if (typeof value === "number") {
|
|
1907
1907
|
if (schema.minimum !== void 0 && value < schema.minimum) {
|
|
1908
1908
|
issues.push({
|
|
1909
|
-
path:
|
|
1909
|
+
path: path18,
|
|
1910
1910
|
message: `must be >= ${String(schema.minimum)}`
|
|
1911
1911
|
});
|
|
1912
1912
|
}
|
|
1913
1913
|
if (schema.maximum !== void 0 && value > schema.maximum) {
|
|
1914
1914
|
issues.push({
|
|
1915
|
-
path:
|
|
1915
|
+
path: path18,
|
|
1916
1916
|
message: `must be <= ${String(schema.maximum)}`
|
|
1917
1917
|
});
|
|
1918
1918
|
}
|
|
1919
1919
|
if (schema.exclusiveMinimum !== void 0 && value <= schema.exclusiveMinimum) {
|
|
1920
1920
|
issues.push({
|
|
1921
|
-
path:
|
|
1921
|
+
path: path18,
|
|
1922
1922
|
message: `must be > ${String(schema.exclusiveMinimum)}`
|
|
1923
1923
|
});
|
|
1924
1924
|
}
|
|
1925
1925
|
if (schema.exclusiveMaximum !== void 0 && value >= schema.exclusiveMaximum) {
|
|
1926
1926
|
issues.push({
|
|
1927
|
-
path:
|
|
1927
|
+
path: path18,
|
|
1928
1928
|
message: `must be < ${String(schema.exclusiveMaximum)}`
|
|
1929
1929
|
});
|
|
1930
1930
|
}
|
|
@@ -1933,13 +1933,13 @@ function validateSchemaNode(schema, value, path17) {
|
|
|
1933
1933
|
if (Array.isArray(value)) {
|
|
1934
1934
|
if (schema.minItems !== void 0 && value.length < schema.minItems) {
|
|
1935
1935
|
issues.push({
|
|
1936
|
-
path:
|
|
1936
|
+
path: path18,
|
|
1937
1937
|
message: `must have at least ${String(schema.minItems)} items`
|
|
1938
1938
|
});
|
|
1939
1939
|
}
|
|
1940
1940
|
if (schema.maxItems !== void 0 && value.length > schema.maxItems) {
|
|
1941
1941
|
issues.push({
|
|
1942
|
-
path:
|
|
1942
|
+
path: path18,
|
|
1943
1943
|
message: `must have at most ${String(schema.maxItems)} items`
|
|
1944
1944
|
});
|
|
1945
1945
|
}
|
|
@@ -1949,7 +1949,7 @@ function validateSchemaNode(schema, value, path17) {
|
|
|
1949
1949
|
const key = JSON.stringify(item);
|
|
1950
1950
|
if (seen.has(key)) {
|
|
1951
1951
|
issues.push({
|
|
1952
|
-
path:
|
|
1952
|
+
path: path18,
|
|
1953
1953
|
message: "must not contain duplicate items"
|
|
1954
1954
|
});
|
|
1955
1955
|
break;
|
|
@@ -1959,7 +1959,7 @@ function validateSchemaNode(schema, value, path17) {
|
|
|
1959
1959
|
}
|
|
1960
1960
|
if (schema.items !== void 0) {
|
|
1961
1961
|
for (let index = 0; index < value.length; index += 1) {
|
|
1962
|
-
issues.push(...validateSchemaNode(schema.items, value[index], `${
|
|
1962
|
+
issues.push(...validateSchemaNode(schema.items, value[index], `${path18}[${String(index)}]`));
|
|
1963
1963
|
}
|
|
1964
1964
|
}
|
|
1965
1965
|
return issues;
|
|
@@ -1969,7 +1969,7 @@ function validateSchemaNode(schema, value, path17) {
|
|
|
1969
1969
|
for (const requiredKey of schema.required ?? []) {
|
|
1970
1970
|
if (!(requiredKey in value)) {
|
|
1971
1971
|
issues.push({
|
|
1972
|
-
path: joinObjectPath(
|
|
1972
|
+
path: joinObjectPath(path18, requiredKey),
|
|
1973
1973
|
message: "is required"
|
|
1974
1974
|
});
|
|
1975
1975
|
}
|
|
@@ -1978,13 +1978,13 @@ function validateSchemaNode(schema, value, path17) {
|
|
|
1978
1978
|
const propertySchema = properties[key];
|
|
1979
1979
|
if (propertySchema !== void 0) {
|
|
1980
1980
|
issues.push(
|
|
1981
|
-
...validateSchemaNode(propertySchema, propertyValue, joinObjectPath(
|
|
1981
|
+
...validateSchemaNode(propertySchema, propertyValue, joinObjectPath(path18, key))
|
|
1982
1982
|
);
|
|
1983
1983
|
continue;
|
|
1984
1984
|
}
|
|
1985
1985
|
if (schema.additionalProperties === false) {
|
|
1986
1986
|
issues.push({
|
|
1987
|
-
path: joinObjectPath(
|
|
1987
|
+
path: joinObjectPath(path18, key),
|
|
1988
1988
|
message: "is not allowed"
|
|
1989
1989
|
});
|
|
1990
1990
|
continue;
|
|
@@ -1994,7 +1994,7 @@ function validateSchemaNode(schema, value, path17) {
|
|
|
1994
1994
|
...validateSchemaNode(
|
|
1995
1995
|
schema.additionalProperties,
|
|
1996
1996
|
propertyValue,
|
|
1997
|
-
joinObjectPath(
|
|
1997
|
+
joinObjectPath(path18, key)
|
|
1998
1998
|
)
|
|
1999
1999
|
);
|
|
2000
2000
|
}
|
|
@@ -2238,8 +2238,8 @@ function matchesNetworkRecordFilters(record, filters) {
|
|
|
2238
2238
|
}
|
|
2239
2239
|
}
|
|
2240
2240
|
if (filters.path !== void 0) {
|
|
2241
|
-
const
|
|
2242
|
-
if (!includesCaseInsensitive(
|
|
2241
|
+
const path18 = getParsedUrl().pathname;
|
|
2242
|
+
if (!includesCaseInsensitive(path18, filters.path)) {
|
|
2243
2243
|
return false;
|
|
2244
2244
|
}
|
|
2245
2245
|
}
|
|
@@ -2283,6 +2283,9 @@ function isBrowserCoreError(value) {
|
|
|
2283
2283
|
// ../browser-core/src/cdp-visual-stability.ts
|
|
2284
2284
|
var DEFAULT_VISUAL_STABILITY_SETTLE_MS = 750;
|
|
2285
2285
|
|
|
2286
|
+
// ../browser-core/src/post-load-tracker.ts
|
|
2287
|
+
var DEFAULT_POST_LOAD_TRACKER_QUIET_WINDOW_MS = 400;
|
|
2288
|
+
|
|
2286
2289
|
// ../protocol/src/identity.ts
|
|
2287
2290
|
var refPrefixes = [
|
|
2288
2291
|
"session",
|
|
@@ -7472,9 +7475,23 @@ var opensteerInspectStorageInputSchema = objectSchema(
|
|
|
7472
7475
|
title: "OpensteerInspectStorageInput"
|
|
7473
7476
|
}
|
|
7474
7477
|
);
|
|
7478
|
+
var opensteerComputerMouseButtonSchema = enumSchema(["left", "middle", "right"], {
|
|
7479
|
+
title: "OpensteerComputerMouseButton"
|
|
7480
|
+
});
|
|
7481
|
+
var opensteerComputerKeyModifierSchema = enumSchema(
|
|
7482
|
+
["Shift", "Control", "Alt", "Meta"],
|
|
7483
|
+
{
|
|
7484
|
+
title: "OpensteerComputerKeyModifier"
|
|
7485
|
+
}
|
|
7486
|
+
);
|
|
7475
7487
|
var opensteerDomClickInputSchema = objectSchema(
|
|
7476
7488
|
{
|
|
7477
7489
|
target: opensteerTargetInputSchema,
|
|
7490
|
+
button: opensteerComputerMouseButtonSchema,
|
|
7491
|
+
clickCount: integerSchema({ minimum: 1 }),
|
|
7492
|
+
modifiers: arraySchema(opensteerComputerKeyModifierSchema, {
|
|
7493
|
+
uniqueItems: true
|
|
7494
|
+
}),
|
|
7478
7495
|
persistAsDescription: stringSchema(),
|
|
7479
7496
|
captureNetwork: stringSchema({ minLength: 1 })
|
|
7480
7497
|
},
|
|
@@ -7574,18 +7591,6 @@ var opensteerSessionCloseOutputSchema = objectSchema(
|
|
|
7574
7591
|
required: ["closed"]
|
|
7575
7592
|
}
|
|
7576
7593
|
);
|
|
7577
|
-
var opensteerComputerMouseButtonSchema = enumSchema(
|
|
7578
|
-
["left", "middle", "right"],
|
|
7579
|
-
{
|
|
7580
|
-
title: "OpensteerComputerMouseButton"
|
|
7581
|
-
}
|
|
7582
|
-
);
|
|
7583
|
-
var opensteerComputerKeyModifierSchema = enumSchema(
|
|
7584
|
-
["Shift", "Control", "Alt", "Meta"],
|
|
7585
|
-
{
|
|
7586
|
-
title: "OpensteerComputerKeyModifier"
|
|
7587
|
-
}
|
|
7588
|
-
);
|
|
7589
7594
|
var opensteerComputerAnnotationSchema = enumSchema(opensteerComputerAnnotationNames, {
|
|
7590
7595
|
title: "OpensteerComputerAnnotation"
|
|
7591
7596
|
});
|
|
@@ -10299,7 +10304,7 @@ async function clearPersistedSessionRecord(rootPath, provider) {
|
|
|
10299
10304
|
await promises.rm(resolveLiveSessionRecordPath(rootPath, provider), { force: true });
|
|
10300
10305
|
}
|
|
10301
10306
|
function isPersistedCloudSessionRecord(value) {
|
|
10302
|
-
return value.layout === OPENSTEER_LIVE_SESSION_LAYOUT && value.version === OPENSTEER_LIVE_SESSION_VERSION && value.provider === "cloud" && typeof value.sessionId === "string" && value.sessionId.length > 0 && typeof value.
|
|
10307
|
+
return value.layout === OPENSTEER_LIVE_SESSION_LAYOUT && value.version === OPENSTEER_LIVE_SESSION_VERSION && value.provider === "cloud" && typeof value.sessionId === "string" && value.sessionId.length > 0 && typeof value.startedAt === "number" && Number.isFinite(value.startedAt) && typeof value.updatedAt === "number" && Number.isFinite(value.updatedAt);
|
|
10303
10308
|
}
|
|
10304
10309
|
function isPersistedLocalBrowserSessionRecord(value) {
|
|
10305
10310
|
return value.layout === OPENSTEER_LIVE_SESSION_LAYOUT && value.version === OPENSTEER_LIVE_SESSION_VERSION && value.provider === "local" && (value.engine === "playwright" || value.engine === "abp") && typeof value.pid === "number" && Number.isFinite(value.pid) && typeof value.startedAt === "number" && Number.isFinite(value.startedAt) && typeof value.updatedAt === "number" && Number.isFinite(value.updatedAt) && typeof value.userDataDir === "string" && value.userDataDir.length > 0;
|
|
@@ -11727,11 +11732,25 @@ function queryAllCookies(dbPath) {
|
|
|
11727
11732
|
FROM cookies`
|
|
11728
11733
|
);
|
|
11729
11734
|
stmt.setReadBigInts(true);
|
|
11730
|
-
return stmt.all();
|
|
11735
|
+
return stmt.all().map(toRawCookieRow);
|
|
11731
11736
|
} finally {
|
|
11732
11737
|
database.close();
|
|
11733
11738
|
}
|
|
11734
11739
|
}
|
|
11740
|
+
function toRawCookieRow(row) {
|
|
11741
|
+
return {
|
|
11742
|
+
host_key: row.host_key,
|
|
11743
|
+
name: row.name,
|
|
11744
|
+
value: row.value,
|
|
11745
|
+
encrypted_value: row.encrypted_value,
|
|
11746
|
+
path: row.path,
|
|
11747
|
+
expires_utc: row.expires_utc,
|
|
11748
|
+
is_secure: row.is_secure,
|
|
11749
|
+
is_httponly: row.is_httponly,
|
|
11750
|
+
samesite: row.samesite,
|
|
11751
|
+
is_persistent: row.is_persistent
|
|
11752
|
+
};
|
|
11753
|
+
}
|
|
11735
11754
|
async function resolveDecryptionKey(brandId, userDataDir) {
|
|
11736
11755
|
if (process.platform === "darwin") {
|
|
11737
11756
|
const password = await resolveKeychainPassword(brandId);
|
|
@@ -11975,14 +11994,14 @@ function toPortableBrowserProfileCookieRecord(cookie) {
|
|
|
11975
11994
|
if (!name || !domain) {
|
|
11976
11995
|
return null;
|
|
11977
11996
|
}
|
|
11978
|
-
const
|
|
11997
|
+
const path18 = typeof cookie.path === "string" && cookie.path.trim().length > 0 ? cookie.path : "/";
|
|
11979
11998
|
const expiresAt = typeof cookie.expires === "number" && Number.isFinite(cookie.expires) && cookie.expires > 0 ? Math.floor(cookie.expires * 1e3) : null;
|
|
11980
11999
|
const sameSite = normalizeSameSite(cookie.sameSite);
|
|
11981
12000
|
return {
|
|
11982
12001
|
name,
|
|
11983
12002
|
value: cookie.value,
|
|
11984
12003
|
domain,
|
|
11985
|
-
path:
|
|
12004
|
+
path: path18,
|
|
11986
12005
|
secure: cookie.secure,
|
|
11987
12006
|
httpOnly: cookie.httpOnly,
|
|
11988
12007
|
...sameSite === void 0 ? {} : { sameSite },
|
|
@@ -12082,7 +12101,11 @@ var OpensteerCloudClient = class {
|
|
|
12082
12101
|
...input.browser === void 0 ? {} : { browser: input.browser },
|
|
12083
12102
|
...input.context === void 0 ? {} : { context: input.context },
|
|
12084
12103
|
...input.browserProfile === void 0 ? {} : { browserProfile: input.browserProfile },
|
|
12085
|
-
...input.observability === void 0 ? {} : { observability: input.observability }
|
|
12104
|
+
...input.observability === void 0 ? {} : { observability: input.observability },
|
|
12105
|
+
...input.sourceType === void 0 ? {} : { sourceType: input.sourceType },
|
|
12106
|
+
...input.sourceRef === void 0 ? {} : { sourceRef: input.sourceRef },
|
|
12107
|
+
...input.localWorkspaceRootPath === void 0 ? {} : { localWorkspaceRootPath: input.localWorkspaceRootPath },
|
|
12108
|
+
...input.locality === void 0 ? {} : { locality: input.locality }
|
|
12086
12109
|
}
|
|
12087
12110
|
});
|
|
12088
12111
|
return await response.json();
|
|
@@ -12108,6 +12131,30 @@ var OpensteerCloudClient = class {
|
|
|
12108
12131
|
});
|
|
12109
12132
|
return await response.json();
|
|
12110
12133
|
}
|
|
12134
|
+
async getSessionRecording(sessionId) {
|
|
12135
|
+
const response = await this.request(`/v1/sessions/${encodeURIComponent(sessionId)}/recording`, {
|
|
12136
|
+
method: "GET"
|
|
12137
|
+
});
|
|
12138
|
+
return await response.json();
|
|
12139
|
+
}
|
|
12140
|
+
async startSessionRecording(sessionId) {
|
|
12141
|
+
const response = await this.request(
|
|
12142
|
+
`/v1/sessions/${encodeURIComponent(sessionId)}/recording/start`,
|
|
12143
|
+
{
|
|
12144
|
+
method: "POST"
|
|
12145
|
+
}
|
|
12146
|
+
);
|
|
12147
|
+
return await response.json();
|
|
12148
|
+
}
|
|
12149
|
+
async stopSessionRecording(sessionId) {
|
|
12150
|
+
const response = await this.request(
|
|
12151
|
+
`/v1/sessions/${encodeURIComponent(sessionId)}/recording/stop`,
|
|
12152
|
+
{
|
|
12153
|
+
method: "POST"
|
|
12154
|
+
}
|
|
12155
|
+
);
|
|
12156
|
+
return await response.json();
|
|
12157
|
+
}
|
|
12111
12158
|
async closeSession(sessionId) {
|
|
12112
12159
|
const response = await this.request(`/v1/sessions/${encodeURIComponent(sessionId)}`, {
|
|
12113
12160
|
method: "DELETE"
|
|
@@ -12298,9 +12345,11 @@ function resolveCloudConfig(input = {}) {
|
|
|
12298
12345
|
if (!baseUrl || baseUrl.trim().length === 0) {
|
|
12299
12346
|
throw new Error("provider=cloud requires OPENSTEER_BASE_URL or provider.baseUrl.");
|
|
12300
12347
|
}
|
|
12348
|
+
const appBaseUrl = cloudProvider?.appBaseUrl ?? input.environment?.OPENSTEER_CLOUD_APP_BASE_URL;
|
|
12301
12349
|
return {
|
|
12302
12350
|
apiKey: apiKey.trim(),
|
|
12303
12351
|
baseUrl: baseUrl.trim().replace(/\/+$/, ""),
|
|
12352
|
+
...appBaseUrl === void 0 || appBaseUrl.trim().length === 0 ? {} : { appBaseUrl: appBaseUrl.trim().replace(/\/+$/, "") },
|
|
12304
12353
|
...cloudProvider?.browserProfile === void 0 ? {} : { browserProfile: cloudProvider.browserProfile }
|
|
12305
12354
|
};
|
|
12306
12355
|
}
|
|
@@ -12315,15 +12364,7 @@ var OPENSTEER_RUNTIME_CORE_VERSION = package_default2.version;
|
|
|
12315
12364
|
// ../runtime-core/src/action-boundary.ts
|
|
12316
12365
|
var actionBoundaryDiagnosticsBySignal = /* @__PURE__ */ new WeakMap();
|
|
12317
12366
|
async function captureActionBoundarySnapshot(engine, pageRef) {
|
|
12318
|
-
|
|
12319
|
-
const mainFrame = frames.find((frame) => frame.isMainFrame);
|
|
12320
|
-
if (!mainFrame) {
|
|
12321
|
-
throw new Error(`page ${pageRef} does not expose a main frame`);
|
|
12322
|
-
}
|
|
12323
|
-
return {
|
|
12324
|
-
pageRef,
|
|
12325
|
-
documentRef: mainFrame.documentRef
|
|
12326
|
-
};
|
|
12367
|
+
return engine.getActionBoundarySnapshot({ pageRef });
|
|
12327
12368
|
}
|
|
12328
12369
|
function createActionBoundaryDiagnostics(input) {
|
|
12329
12370
|
return {
|
|
@@ -12440,6 +12481,7 @@ var NAVIGATION_VISUAL_STABILITY_PROFILE = {
|
|
|
12440
12481
|
scope: "visible-frames",
|
|
12441
12482
|
timeoutMs: 7e3
|
|
12442
12483
|
};
|
|
12484
|
+
var NAVIGATION_POST_LOAD_CAPTURE_WINDOW_MS = 1e3;
|
|
12443
12485
|
var defaultDomActionSettleObserver = {
|
|
12444
12486
|
async settle(input) {
|
|
12445
12487
|
if (input.trigger !== "dom-action") {
|
|
@@ -12475,6 +12517,13 @@ var defaultNavigationSettleObserver = {
|
|
|
12475
12517
|
return false;
|
|
12476
12518
|
}
|
|
12477
12519
|
try {
|
|
12520
|
+
await input.engine.waitForPostLoadQuiet({
|
|
12521
|
+
pageRef: input.pageRef,
|
|
12522
|
+
timeoutMs: effectiveTimeout,
|
|
12523
|
+
quietMs: DEFAULT_POST_LOAD_TRACKER_QUIET_WINDOW_MS,
|
|
12524
|
+
captureWindowMs: Math.min(NAVIGATION_POST_LOAD_CAPTURE_WINDOW_MS, effectiveTimeout),
|
|
12525
|
+
signal: input.signal
|
|
12526
|
+
});
|
|
12478
12527
|
await input.engine.waitForVisualStability({
|
|
12479
12528
|
pageRef: input.pageRef,
|
|
12480
12529
|
timeoutMs: effectiveTimeout,
|
|
@@ -13017,9 +13066,9 @@ var IFRAME_URL_ATTRIBUTES = /* @__PURE__ */ new Set([
|
|
|
13017
13066
|
"poster",
|
|
13018
13067
|
"ping"
|
|
13019
13068
|
]);
|
|
13020
|
-
function buildArrayFieldPathCandidates(
|
|
13021
|
-
const strict =
|
|
13022
|
-
const relaxedNodes = stripPositionClauses(
|
|
13069
|
+
function buildArrayFieldPathCandidates(path18) {
|
|
13070
|
+
const strict = path18.nodes.length ? buildPathCandidates(path18.nodes) : [];
|
|
13071
|
+
const relaxedNodes = stripPositionClauses(path18.nodes);
|
|
13023
13072
|
const relaxed = relaxedNodes.length ? buildPathCandidates(relaxedNodes) : [];
|
|
13024
13073
|
return dedupeSelectors([...strict, ...relaxed]);
|
|
13025
13074
|
}
|
|
@@ -13546,8 +13595,8 @@ function cloneStructuralElementAnchor(anchor) {
|
|
|
13546
13595
|
nodes: anchor.nodes.map(clonePathNode)
|
|
13547
13596
|
};
|
|
13548
13597
|
}
|
|
13549
|
-
function buildPathSelectorHint(
|
|
13550
|
-
const nodes =
|
|
13598
|
+
function buildPathSelectorHint(path18) {
|
|
13599
|
+
const nodes = path18?.nodes || [];
|
|
13551
13600
|
const last = nodes[nodes.length - 1];
|
|
13552
13601
|
if (!last) {
|
|
13553
13602
|
return "*";
|
|
@@ -13596,15 +13645,15 @@ function sanitizeStructuralElementAnchor(anchor) {
|
|
|
13596
13645
|
nodes: sanitizeNodes(anchor.nodes)
|
|
13597
13646
|
};
|
|
13598
13647
|
}
|
|
13599
|
-
function sanitizeReplayElementPath(
|
|
13648
|
+
function sanitizeReplayElementPath(path18) {
|
|
13600
13649
|
return {
|
|
13601
13650
|
resolution: "deterministic",
|
|
13602
|
-
context: sanitizeContext(
|
|
13603
|
-
nodes: sanitizeNodes(
|
|
13651
|
+
context: sanitizeContext(path18.context),
|
|
13652
|
+
nodes: sanitizeNodes(path18.nodes)
|
|
13604
13653
|
};
|
|
13605
13654
|
}
|
|
13606
|
-
function sanitizeElementPath(
|
|
13607
|
-
return sanitizeReplayElementPath(
|
|
13655
|
+
function sanitizeElementPath(path18) {
|
|
13656
|
+
return sanitizeReplayElementPath(path18);
|
|
13608
13657
|
}
|
|
13609
13658
|
function buildLocalStructuralElementAnchor(index, rawTargetNode) {
|
|
13610
13659
|
const targetNode = requireElementNode(index, rawTargetNode);
|
|
@@ -13727,8 +13776,8 @@ function buildTargetNotFoundMessage(domPath, diagnostics) {
|
|
|
13727
13776
|
}
|
|
13728
13777
|
return `${base} Target depth ${String(depth)}. Candidate counts: ${sample}.`;
|
|
13729
13778
|
}
|
|
13730
|
-
function buildArrayFieldCandidates(
|
|
13731
|
-
return buildArrayFieldPathCandidates(
|
|
13779
|
+
function buildArrayFieldCandidates(path18) {
|
|
13780
|
+
return buildArrayFieldPathCandidates(path18);
|
|
13732
13781
|
}
|
|
13733
13782
|
function firstDefinedAttribute(node, keys) {
|
|
13734
13783
|
for (const key of keys) {
|
|
@@ -15205,21 +15254,21 @@ var DefaultDomRuntime = class {
|
|
|
15205
15254
|
return match;
|
|
15206
15255
|
}
|
|
15207
15256
|
async resolvePathTarget(session, pageRef, rawPath, source, description, descriptor) {
|
|
15208
|
-
const
|
|
15209
|
-
const context = await this.resolvePathContext(session, pageRef,
|
|
15210
|
-
const target = resolveDomPathInScope(context.index,
|
|
15257
|
+
const path18 = sanitizeReplayElementPath(rawPath);
|
|
15258
|
+
const context = await this.resolvePathContext(session, pageRef, path18.context);
|
|
15259
|
+
const target = resolveDomPathInScope(context.index, path18.nodes, context.scope);
|
|
15211
15260
|
if (!target) {
|
|
15212
|
-
throwTargetNotFound(context.index,
|
|
15261
|
+
throwTargetNotFound(context.index, path18.nodes, context.scope);
|
|
15213
15262
|
}
|
|
15214
15263
|
if (target.node.nodeRef === void 0) {
|
|
15215
15264
|
throw new Error(
|
|
15216
|
-
`resolved path "${buildPathSelectorHint(
|
|
15265
|
+
`resolved path "${buildPathSelectorHint(path18)}" does not point to a live element`
|
|
15217
15266
|
);
|
|
15218
15267
|
}
|
|
15219
15268
|
const anchor = await this.buildAnchorFromSnapshotNode(session, context.snapshot, target.node);
|
|
15220
15269
|
return this.createResolvedTarget(source, context.snapshot, target.node, anchor, {
|
|
15221
15270
|
...description === void 0 ? {} : { description },
|
|
15222
|
-
replayPath:
|
|
15271
|
+
replayPath: path18,
|
|
15223
15272
|
...source === "path" || source === "descriptor" ? { selectorUsed: target.selector } : {},
|
|
15224
15273
|
...descriptor === void 0 ? {} : { descriptor }
|
|
15225
15274
|
});
|
|
@@ -15240,9 +15289,9 @@ var DefaultDomRuntime = class {
|
|
|
15240
15289
|
});
|
|
15241
15290
|
}
|
|
15242
15291
|
async queryAllByElementPath(session, pageRef, rawPath) {
|
|
15243
|
-
const
|
|
15244
|
-
const context = await this.resolvePathContext(session, pageRef,
|
|
15245
|
-
return queryAllDomPathInScope(context.index,
|
|
15292
|
+
const path18 = sanitizeReplayElementPath(rawPath);
|
|
15293
|
+
const context = await this.resolvePathContext(session, pageRef, path18.context);
|
|
15294
|
+
return queryAllDomPathInScope(context.index, path18.nodes, context.scope).filter(
|
|
15246
15295
|
(node) => node.nodeRef !== void 0
|
|
15247
15296
|
).map((node) => this.createSnapshotTarget(context.snapshot, node));
|
|
15248
15297
|
}
|
|
@@ -15428,16 +15477,16 @@ var DefaultDomRuntime = class {
|
|
|
15428
15477
|
const index = createSnapshotIndex(item.snapshot);
|
|
15429
15478
|
return this.resolveFirstArrayFieldTargetInNode(index, item.node, field.path);
|
|
15430
15479
|
}
|
|
15431
|
-
resolveFirstArrayFieldTargetInNode(index, rootNode,
|
|
15432
|
-
const normalizedPath = sanitizeElementPath(
|
|
15480
|
+
resolveFirstArrayFieldTargetInNode(index, rootNode, path18) {
|
|
15481
|
+
const normalizedPath = sanitizeElementPath(path18);
|
|
15433
15482
|
const selectors = buildArrayFieldCandidates(normalizedPath);
|
|
15434
15483
|
if (!selectors.length) {
|
|
15435
15484
|
return rootNode;
|
|
15436
15485
|
}
|
|
15437
15486
|
return resolveFirstWithinNodeBySelectors(index, rootNode, selectors);
|
|
15438
15487
|
}
|
|
15439
|
-
resolveUniqueArrayFieldTargetInNode(index, rootNode,
|
|
15440
|
-
const normalizedPath = sanitizeElementPath(
|
|
15488
|
+
resolveUniqueArrayFieldTargetInNode(index, rootNode, path18) {
|
|
15489
|
+
const normalizedPath = sanitizeElementPath(path18);
|
|
15441
15490
|
const selectors = buildArrayFieldCandidates(normalizedPath);
|
|
15442
15491
|
if (!selectors.length) {
|
|
15443
15492
|
return rootNode;
|
|
@@ -16813,7 +16862,7 @@ function diffStringMap(prefix, left, right, includeUnchanged, output) {
|
|
|
16813
16862
|
diffScalarField(`${prefix}.${key}`, left[key], right[key], includeUnchanged, output);
|
|
16814
16863
|
}
|
|
16815
16864
|
}
|
|
16816
|
-
function diffScalarField(
|
|
16865
|
+
function diffScalarField(path18, left, right, includeUnchanged, output) {
|
|
16817
16866
|
const leftValue = stringifyFieldValue(left);
|
|
16818
16867
|
const rightValue = stringifyFieldValue(right);
|
|
16819
16868
|
const kind = leftValue === void 0 ? rightValue === void 0 ? "unchanged" : "added" : rightValue === void 0 ? "removed" : leftValue === rightValue ? "unchanged" : "changed";
|
|
@@ -16821,7 +16870,7 @@ function diffScalarField(path17, left, right, includeUnchanged, output) {
|
|
|
16821
16870
|
return;
|
|
16822
16871
|
}
|
|
16823
16872
|
output.push({
|
|
16824
|
-
path:
|
|
16873
|
+
path: path18,
|
|
16825
16874
|
kind,
|
|
16826
16875
|
...leftValue === void 0 ? {} : { leftValue },
|
|
16827
16876
|
...rightValue === void 0 ? {} : { rightValue },
|
|
@@ -17827,9 +17876,9 @@ function matchReverseTargetHints(channel, codec, targetHints) {
|
|
|
17827
17876
|
matches.add(`host:${host}`);
|
|
17828
17877
|
}
|
|
17829
17878
|
}
|
|
17830
|
-
for (const
|
|
17831
|
-
if (url.pathname.includes(
|
|
17832
|
-
matches.add(`path:${
|
|
17879
|
+
for (const path18 of targetHints.paths ?? []) {
|
|
17880
|
+
if (url.pathname.includes(path18)) {
|
|
17881
|
+
matches.add(`path:${path18}`);
|
|
17833
17882
|
}
|
|
17834
17883
|
}
|
|
17835
17884
|
for (const operationName of targetHints.operationNames ?? []) {
|
|
@@ -19665,8 +19714,8 @@ function encodeDataPath(tokens) {
|
|
|
19665
19714
|
}
|
|
19666
19715
|
return out;
|
|
19667
19716
|
}
|
|
19668
|
-
function parseDataPath(
|
|
19669
|
-
const input =
|
|
19717
|
+
function parseDataPath(path18) {
|
|
19718
|
+
const input = path18.trim();
|
|
19670
19719
|
if (input.length === 0) {
|
|
19671
19720
|
return [];
|
|
19672
19721
|
}
|
|
@@ -19716,8 +19765,8 @@ function parseDataPath(path17) {
|
|
|
19716
19765
|
function inflateDataPathObject(flat) {
|
|
19717
19766
|
let root = {};
|
|
19718
19767
|
let initialized = false;
|
|
19719
|
-
for (const [
|
|
19720
|
-
const tokens = parseDataPath(
|
|
19768
|
+
for (const [path18, value] of Object.entries(flat)) {
|
|
19769
|
+
const tokens = parseDataPath(path18);
|
|
19721
19770
|
if (!tokens || tokens.length === 0) {
|
|
19722
19771
|
continue;
|
|
19723
19772
|
}
|
|
@@ -20049,8 +20098,8 @@ function buildVariantDescriptorFromCluster(descriptors) {
|
|
|
20049
20098
|
fields: mergedFields
|
|
20050
20099
|
};
|
|
20051
20100
|
}
|
|
20052
|
-
function minimizePathMatchClauses(
|
|
20053
|
-
const normalized = sanitizeElementPath(
|
|
20101
|
+
function minimizePathMatchClauses(path18, mode) {
|
|
20102
|
+
const normalized = sanitizeElementPath(path18);
|
|
20054
20103
|
const nodes = normalized.nodes.map((node, index) => {
|
|
20055
20104
|
const isLast = index === normalized.nodes.length - 1;
|
|
20056
20105
|
const attrs = node.attrs || {};
|
|
@@ -20154,8 +20203,8 @@ function seedMinimalAttrClause(attrs) {
|
|
|
20154
20203
|
}
|
|
20155
20204
|
return null;
|
|
20156
20205
|
}
|
|
20157
|
-
function relaxPathForSingleSample(
|
|
20158
|
-
const normalized = sanitizeElementPath(
|
|
20206
|
+
function relaxPathForSingleSample(path18, mode) {
|
|
20207
|
+
const normalized = sanitizeElementPath(path18);
|
|
20159
20208
|
const relaxedNodes = normalized.nodes.map((node, index) => {
|
|
20160
20209
|
const isLast = index === normalized.nodes.length - 1;
|
|
20161
20210
|
const attrs = normalizeAttrsForSingleSample(node.attrs || {});
|
|
@@ -20240,8 +20289,8 @@ function shouldKeepAttrForSingleSample(key) {
|
|
|
20240
20289
|
}
|
|
20241
20290
|
return true;
|
|
20242
20291
|
}
|
|
20243
|
-
function buildPathStructureKey(
|
|
20244
|
-
const normalized = sanitizeElementPath(
|
|
20292
|
+
function buildPathStructureKey(path18) {
|
|
20293
|
+
const normalized = sanitizeElementPath(path18);
|
|
20245
20294
|
return canonicalJsonString({
|
|
20246
20295
|
context: (normalized.context || []).map((hop) => ({
|
|
20247
20296
|
kind: hop.kind,
|
|
@@ -20368,30 +20417,30 @@ function buildArrayItemNode(fields) {
|
|
|
20368
20417
|
}
|
|
20369
20418
|
return node;
|
|
20370
20419
|
}
|
|
20371
|
-
function insertNodeAtPath(root,
|
|
20372
|
-
const tokens = parseDataPath(
|
|
20420
|
+
function insertNodeAtPath(root, path18, node) {
|
|
20421
|
+
const tokens = parseDataPath(path18);
|
|
20373
20422
|
if (!tokens || !tokens.length) {
|
|
20374
20423
|
throw new Error(
|
|
20375
|
-
`Invalid persisted extraction path "${
|
|
20424
|
+
`Invalid persisted extraction path "${path18}": expected a non-empty object path.`
|
|
20376
20425
|
);
|
|
20377
20426
|
}
|
|
20378
20427
|
if (tokens.some((token) => token.kind === "index")) {
|
|
20379
20428
|
throw new Error(
|
|
20380
|
-
`Invalid persisted extraction path "${
|
|
20429
|
+
`Invalid persisted extraction path "${path18}": nested array indices are not supported in cached descriptors.`
|
|
20381
20430
|
);
|
|
20382
20431
|
}
|
|
20383
20432
|
let current = root;
|
|
20384
20433
|
for (let index = 0; index < tokens.length; index += 1) {
|
|
20385
20434
|
const token = tokens[index];
|
|
20386
20435
|
if (!token || token.kind !== "prop") {
|
|
20387
|
-
throw new Error(`Invalid persisted extraction path "${
|
|
20436
|
+
throw new Error(`Invalid persisted extraction path "${path18}": expected object segment.`);
|
|
20388
20437
|
}
|
|
20389
20438
|
const isLast = index === tokens.length - 1;
|
|
20390
20439
|
if (isLast) {
|
|
20391
20440
|
const existing = current[token.key];
|
|
20392
20441
|
if (existing) {
|
|
20393
20442
|
throw new Error(
|
|
20394
|
-
`Conflicting persisted extraction path "${
|
|
20443
|
+
`Conflicting persisted extraction path "${path18}" detected while building descriptor tree.`
|
|
20395
20444
|
);
|
|
20396
20445
|
}
|
|
20397
20446
|
current[token.key] = node;
|
|
@@ -20406,7 +20455,7 @@ function insertNodeAtPath(root, path17, node) {
|
|
|
20406
20455
|
}
|
|
20407
20456
|
if (!isPersistedObjectNode(next)) {
|
|
20408
20457
|
throw new Error(
|
|
20409
|
-
`Conflicting persisted extraction path "${
|
|
20458
|
+
`Conflicting persisted extraction path "${path18}" detected at "${token.key}".`
|
|
20410
20459
|
);
|
|
20411
20460
|
}
|
|
20412
20461
|
current = next;
|
|
@@ -20441,7 +20490,7 @@ function buildItemRootForArrayIndex(entries) {
|
|
|
20441
20490
|
}
|
|
20442
20491
|
const paths = entries.map(
|
|
20443
20492
|
(entry) => isPersistablePathField(entry.source) ? sanitizeElementPath(entry.source.path) : null
|
|
20444
|
-
).filter((
|
|
20493
|
+
).filter((path18) => path18 !== null);
|
|
20445
20494
|
if (!paths.length) {
|
|
20446
20495
|
return null;
|
|
20447
20496
|
}
|
|
@@ -20462,7 +20511,7 @@ function getCommonPathPrefixLength(paths) {
|
|
|
20462
20511
|
if (!paths.length) {
|
|
20463
20512
|
return 0;
|
|
20464
20513
|
}
|
|
20465
|
-
const nodeChains = paths.map((
|
|
20514
|
+
const nodeChains = paths.map((path18) => path18.nodes);
|
|
20466
20515
|
const minLength = Math.min(...nodeChains.map((nodes) => nodes.length));
|
|
20467
20516
|
if (!Number.isFinite(minLength) || minLength <= 0) {
|
|
20468
20517
|
return 0;
|
|
@@ -20531,30 +20580,30 @@ function mergeElementPathsByMajority(paths) {
|
|
|
20531
20580
|
if (!paths.length) {
|
|
20532
20581
|
return null;
|
|
20533
20582
|
}
|
|
20534
|
-
const normalized = paths.map((
|
|
20583
|
+
const normalized = paths.map((path18) => sanitizeElementPath(path18));
|
|
20535
20584
|
const contextKey = pickModeString(
|
|
20536
|
-
normalized.map((
|
|
20585
|
+
normalized.map((path18) => canonicalJsonString(path18.context)),
|
|
20537
20586
|
1
|
|
20538
20587
|
);
|
|
20539
20588
|
if (!contextKey) {
|
|
20540
20589
|
return null;
|
|
20541
20590
|
}
|
|
20542
|
-
const sameContext = normalized.filter((
|
|
20591
|
+
const sameContext = normalized.filter((path18) => canonicalJsonString(path18.context) === contextKey);
|
|
20543
20592
|
if (!sameContext.length) {
|
|
20544
20593
|
return null;
|
|
20545
20594
|
}
|
|
20546
20595
|
const targetLength = pickModeNumber(
|
|
20547
|
-
sameContext.map((
|
|
20596
|
+
sameContext.map((path18) => path18.nodes.length),
|
|
20548
20597
|
1
|
|
20549
20598
|
) ?? sameContext[0]?.nodes.length ?? 0;
|
|
20550
|
-
const aligned = sameContext.filter((
|
|
20599
|
+
const aligned = sameContext.filter((path18) => path18.nodes.length === targetLength);
|
|
20551
20600
|
if (!aligned.length) {
|
|
20552
20601
|
return null;
|
|
20553
20602
|
}
|
|
20554
20603
|
const threshold = majorityThreshold(aligned.length);
|
|
20555
20604
|
const nodes = [];
|
|
20556
20605
|
for (let index = 0; index < targetLength; index += 1) {
|
|
20557
|
-
const nodesAtIndex = aligned.map((
|
|
20606
|
+
const nodesAtIndex = aligned.map((path18) => path18.nodes[index]).filter((node) => node !== void 0);
|
|
20558
20607
|
if (!nodesAtIndex.length) {
|
|
20559
20608
|
return null;
|
|
20560
20609
|
}
|
|
@@ -20800,8 +20849,8 @@ function clonePathContext(context) {
|
|
|
20800
20849
|
function clonePathNodes(nodes) {
|
|
20801
20850
|
return JSON.parse(JSON.stringify(nodes || []));
|
|
20802
20851
|
}
|
|
20803
|
-
function cloneElementPath2(
|
|
20804
|
-
return JSON.parse(JSON.stringify(
|
|
20852
|
+
function cloneElementPath2(path18) {
|
|
20853
|
+
return JSON.parse(JSON.stringify(path18));
|
|
20805
20854
|
}
|
|
20806
20855
|
function clonePersistedOpensteerExtractionNode(node) {
|
|
20807
20856
|
return JSON.parse(JSON.stringify(node));
|
|
@@ -21119,8 +21168,8 @@ function collectPersistedValueNodeRefs(node) {
|
|
|
21119
21168
|
return [
|
|
21120
21169
|
{
|
|
21121
21170
|
path: sanitizeElementPath(node.$path),
|
|
21122
|
-
replacePath: (
|
|
21123
|
-
node.$path = sanitizeElementPath(
|
|
21171
|
+
replacePath: (path18) => {
|
|
21172
|
+
node.$path = sanitizeElementPath(path18);
|
|
21124
21173
|
}
|
|
21125
21174
|
}
|
|
21126
21175
|
];
|
|
@@ -21134,13 +21183,13 @@ function collectPersistedValueNodeRefs(node) {
|
|
|
21134
21183
|
}
|
|
21135
21184
|
return refs;
|
|
21136
21185
|
}
|
|
21137
|
-
function hasPositionClause(
|
|
21138
|
-
return
|
|
21186
|
+
function hasPositionClause(path18) {
|
|
21187
|
+
return path18.nodes.some((node) => node.match.some((clause) => clause.kind === "position"));
|
|
21139
21188
|
}
|
|
21140
|
-
function stripPositionClauses2(
|
|
21189
|
+
function stripPositionClauses2(path18) {
|
|
21141
21190
|
return sanitizeElementPath({
|
|
21142
|
-
context:
|
|
21143
|
-
nodes:
|
|
21191
|
+
context: path18.context,
|
|
21192
|
+
nodes: path18.nodes.map((node) => ({
|
|
21144
21193
|
...node,
|
|
21145
21194
|
match: node.match.filter((clause) => clause.kind !== "position")
|
|
21146
21195
|
}))
|
|
@@ -21550,8 +21599,8 @@ function normalizeNonEmptyString2(name, value) {
|
|
|
21550
21599
|
function normalizeKey(value) {
|
|
21551
21600
|
return String(value ?? "").trim();
|
|
21552
21601
|
}
|
|
21553
|
-
function labelForPath(
|
|
21554
|
-
return
|
|
21602
|
+
function labelForPath(path18) {
|
|
21603
|
+
return path18.trim().length === 0 ? "$" : path18;
|
|
21555
21604
|
}
|
|
21556
21605
|
function sha256Hex3(value) {
|
|
21557
21606
|
return crypto.createHash("sha256").update(value).digest("hex");
|
|
@@ -23255,11 +23304,11 @@ var SandboxClock = class {
|
|
|
23255
23304
|
performanceNow() {
|
|
23256
23305
|
return this.mode === "manual" ? this.manualNow - this.startedAt : (globalThis.performance?.now() ?? 0) - this.performanceStartedAt;
|
|
23257
23306
|
}
|
|
23258
|
-
setTimeout(callback,
|
|
23259
|
-
return this.registerTimer(false, callback,
|
|
23307
|
+
setTimeout(callback, delay4 = 0, ...args) {
|
|
23308
|
+
return this.registerTimer(false, callback, delay4, args);
|
|
23260
23309
|
}
|
|
23261
|
-
setInterval(callback,
|
|
23262
|
-
return this.registerTimer(true, callback,
|
|
23310
|
+
setInterval(callback, delay4 = 0, ...args) {
|
|
23311
|
+
return this.registerTimer(true, callback, delay4, args);
|
|
23263
23312
|
}
|
|
23264
23313
|
clearTimeout(timerId) {
|
|
23265
23314
|
this.clearTimer(timerId);
|
|
@@ -23280,9 +23329,9 @@ var SandboxClock = class {
|
|
|
23280
23329
|
this.clearTimer(timerId);
|
|
23281
23330
|
}
|
|
23282
23331
|
}
|
|
23283
|
-
registerTimer(repeat, callback,
|
|
23332
|
+
registerTimer(repeat, callback, delay4, args) {
|
|
23284
23333
|
const timerId = this.nextTimerId++;
|
|
23285
|
-
const normalizedDelay = Math.max(0,
|
|
23334
|
+
const normalizedDelay = Math.max(0, delay4);
|
|
23286
23335
|
const record = {
|
|
23287
23336
|
callback,
|
|
23288
23337
|
args,
|
|
@@ -23789,6 +23838,7 @@ function diffInteractionTraces(left, right) {
|
|
|
23789
23838
|
// ../runtime-core/src/sdk/runtime.ts
|
|
23790
23839
|
var requireForAuthRecipeHook = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('bin.cjs', document.baseURI).href)));
|
|
23791
23840
|
var MUTATION_CAPTURE_FINALIZE_TIMEOUT_MS = 5e3;
|
|
23841
|
+
var PERSISTED_NETWORK_FLUSH_TIMEOUT_MS = 5e3;
|
|
23792
23842
|
var PENDING_OPERATION_EVENT_CAPTURE_LIMIT = 64;
|
|
23793
23843
|
var PENDING_OPERATION_EVENT_CAPTURE_SKEW_MS = 1e3;
|
|
23794
23844
|
var OpensteerSessionRuntime = class {
|
|
@@ -24452,6 +24502,9 @@ var OpensteerSessionRuntime = class {
|
|
|
24452
24502
|
const result = await this.requireDom().click({
|
|
24453
24503
|
pageRef,
|
|
24454
24504
|
target,
|
|
24505
|
+
...input.button === void 0 ? {} : { button: input.button },
|
|
24506
|
+
...input.clickCount === void 0 ? {} : { clickCount: input.clickCount },
|
|
24507
|
+
...input.modifiers === void 0 ? {} : { modifiers: input.modifiers },
|
|
24455
24508
|
timeout
|
|
24456
24509
|
});
|
|
24457
24510
|
return {
|
|
@@ -29928,6 +29981,29 @@ var OpensteerSessionRuntime = class {
|
|
|
29928
29981
|
return snapshot.sessionStorage?.filter((entry) => entry.origin === origin).find((entry) => pageUrl === void 0 || entry.origin === new URL(pageUrl).origin)?.entries.find((entry) => entry.key === key)?.value;
|
|
29929
29982
|
}
|
|
29930
29983
|
async flushPersistedNetworkHistory() {
|
|
29984
|
+
if (this.sessionRef === void 0) {
|
|
29985
|
+
return;
|
|
29986
|
+
}
|
|
29987
|
+
const root = await this.ensureRoot();
|
|
29988
|
+
try {
|
|
29989
|
+
await withDetachedTimeoutSignal(PERSISTED_NETWORK_FLUSH_TIMEOUT_MS, async (signal) => {
|
|
29990
|
+
const browserRecords = await this.readBrowserNetworkRecords(
|
|
29991
|
+
{
|
|
29992
|
+
includeBodies: true,
|
|
29993
|
+
includeCurrentPageOnly: false
|
|
29994
|
+
},
|
|
29995
|
+
signal
|
|
29996
|
+
);
|
|
29997
|
+
await this.networkHistory.persist(browserRecords, root.registry.savedNetwork, {
|
|
29998
|
+
bodyWriteMode: "authoritative",
|
|
29999
|
+
redactSecretHeaders: false
|
|
30000
|
+
});
|
|
30001
|
+
});
|
|
30002
|
+
} catch (error) {
|
|
30003
|
+
if (!isIgnorableRuntimeBindingError(error)) {
|
|
30004
|
+
throw error;
|
|
30005
|
+
}
|
|
30006
|
+
}
|
|
29931
30007
|
}
|
|
29932
30008
|
toDomTargetRef(target) {
|
|
29933
30009
|
if (target.kind === "description") {
|
|
@@ -30084,6 +30160,12 @@ var OpensteerSessionRuntime = class {
|
|
|
30084
30160
|
return "live";
|
|
30085
30161
|
} catch (error) {
|
|
30086
30162
|
if (isIgnorableRuntimeBindingError(error)) {
|
|
30163
|
+
const remainingPages = await engine.listPages({ sessionRef }).catch(() => void 0);
|
|
30164
|
+
const replacementPageRef = remainingPages?.[0]?.pageRef;
|
|
30165
|
+
if (replacementPageRef !== void 0) {
|
|
30166
|
+
this.pageRef = replacementPageRef;
|
|
30167
|
+
return "live";
|
|
30168
|
+
}
|
|
30087
30169
|
return "invalid";
|
|
30088
30170
|
}
|
|
30089
30171
|
throw error;
|
|
@@ -31439,12 +31521,12 @@ function extractReverseRuntimeValue(value, pointer) {
|
|
|
31439
31521
|
}
|
|
31440
31522
|
return readDotPath(value, pointer);
|
|
31441
31523
|
}
|
|
31442
|
-
function readDotPath(value,
|
|
31443
|
-
if (
|
|
31524
|
+
function readDotPath(value, path18) {
|
|
31525
|
+
if (path18.length === 0) {
|
|
31444
31526
|
return value;
|
|
31445
31527
|
}
|
|
31446
31528
|
let current = value;
|
|
31447
|
-
for (const segment of
|
|
31529
|
+
for (const segment of path18.split(".").filter((entry) => entry.length > 0)) {
|
|
31448
31530
|
if (current === null || current === void 0) {
|
|
31449
31531
|
return void 0;
|
|
31450
31532
|
}
|
|
@@ -31917,7 +31999,7 @@ function parseSetCookieHeader(value, requestUrl) {
|
|
|
31917
31999
|
}
|
|
31918
32000
|
const url = new URL(requestUrl);
|
|
31919
32001
|
let domain = url.hostname;
|
|
31920
|
-
let
|
|
32002
|
+
let path18 = defaultCookiePath(url.pathname);
|
|
31921
32003
|
let secure = url.protocol === "https:";
|
|
31922
32004
|
let expiresAt;
|
|
31923
32005
|
const cookieValue = rawValueParts.join("=").trim();
|
|
@@ -31930,7 +32012,7 @@ function parseSetCookieHeader(value, requestUrl) {
|
|
|
31930
32012
|
continue;
|
|
31931
32013
|
}
|
|
31932
32014
|
if (key === "path" && attributeValue.length > 0) {
|
|
31933
|
-
|
|
32015
|
+
path18 = attributeValue;
|
|
31934
32016
|
continue;
|
|
31935
32017
|
}
|
|
31936
32018
|
if (key === "secure") {
|
|
@@ -31956,7 +32038,7 @@ function parseSetCookieHeader(value, requestUrl) {
|
|
|
31956
32038
|
name,
|
|
31957
32039
|
value: cookieValue,
|
|
31958
32040
|
domain,
|
|
31959
|
-
path:
|
|
32041
|
+
path: path18,
|
|
31960
32042
|
secure,
|
|
31961
32043
|
...expiresAt === void 0 ? {} : { expiresAt }
|
|
31962
32044
|
}
|
|
@@ -33137,6 +33219,1770 @@ function screenshotMediaType(format2) {
|
|
|
33137
33219
|
return "image/webp";
|
|
33138
33220
|
}
|
|
33139
33221
|
}
|
|
33222
|
+
|
|
33223
|
+
// ../runtime-core/src/recorder/browser-scripts.ts
|
|
33224
|
+
var SINGLE_ATTRIBUTE_PRIORITY = Array.from(
|
|
33225
|
+
/* @__PURE__ */ new Set(["data-testid", "data-test", "data-qa", "data-cy", "id", ...STABLE_PRIMARY_ATTR_KEYS])
|
|
33226
|
+
);
|
|
33227
|
+
function createFlowRecorderInstallScript(options = {}) {
|
|
33228
|
+
const normalizedOptions = {
|
|
33229
|
+
showStopButton: options.showStopButton ?? true
|
|
33230
|
+
};
|
|
33231
|
+
return String.raw`(() => {
|
|
33232
|
+
const TOP_LEVEL_ONLY = (() => {
|
|
33233
|
+
try {
|
|
33234
|
+
return window.top === window.self;
|
|
33235
|
+
} catch {
|
|
33236
|
+
return false;
|
|
33237
|
+
}
|
|
33238
|
+
})();
|
|
33239
|
+
if (!TOP_LEVEL_ONLY) {
|
|
33240
|
+
return;
|
|
33241
|
+
}
|
|
33242
|
+
|
|
33243
|
+
const globalScope = globalThis;
|
|
33244
|
+
const recorderKey = "__opensteerFlowRecorder";
|
|
33245
|
+
const historyStateKey = "__opensteerFlowRecorderHistory";
|
|
33246
|
+
const recorderUiAttribute = "data-opensteer-recorder-ui";
|
|
33247
|
+
const queueLimit = 1000;
|
|
33248
|
+
const singleAttributePriority = ${JSON.stringify(SINGLE_ATTRIBUTE_PRIORITY)};
|
|
33249
|
+
const stablePrimaryAttrKeys = new Set(${JSON.stringify([...STABLE_PRIMARY_ATTR_KEYS])});
|
|
33250
|
+
const matchAttributePriority = ${JSON.stringify([...MATCH_ATTRIBUTE_PRIORITY])};
|
|
33251
|
+
const attributeDenyKeys = new Set(${JSON.stringify([...ATTRIBUTE_DENY_KEYS])});
|
|
33252
|
+
const lazyLoadingMediaTags = new Set(${JSON.stringify([...LAZY_LOADING_MEDIA_TAGS])});
|
|
33253
|
+
const volatileLazyLoadingAttrs = new Set(${JSON.stringify([...VOLATILE_LAZY_LOADING_ATTRS])});
|
|
33254
|
+
const volatileClassTokens = new Set(${JSON.stringify([...VOLATILE_CLASS_TOKENS])});
|
|
33255
|
+
const volatileLazyClassTokens = new Set(${JSON.stringify([...VOLATILE_LAZY_CLASS_TOKENS])});
|
|
33256
|
+
const options = ${JSON.stringify(normalizedOptions)};
|
|
33257
|
+
|
|
33258
|
+
const previous = globalScope[recorderKey];
|
|
33259
|
+
if (previous && typeof previous.dispose === "function") {
|
|
33260
|
+
previous.dispose();
|
|
33261
|
+
}
|
|
33262
|
+
|
|
33263
|
+
const queue = [];
|
|
33264
|
+
const cleanup = [];
|
|
33265
|
+
const inputFlushTimers = new Map();
|
|
33266
|
+
const pendingInputs = new Map();
|
|
33267
|
+
let historyStateCache = undefined;
|
|
33268
|
+
let stopRequested = false;
|
|
33269
|
+
let pendingWheel = undefined;
|
|
33270
|
+
|
|
33271
|
+
const actionTargetTags = new Set([
|
|
33272
|
+
"a",
|
|
33273
|
+
"button",
|
|
33274
|
+
"input",
|
|
33275
|
+
"label",
|
|
33276
|
+
"option",
|
|
33277
|
+
"select",
|
|
33278
|
+
"summary",
|
|
33279
|
+
"textarea",
|
|
33280
|
+
]);
|
|
33281
|
+
|
|
33282
|
+
function now() {
|
|
33283
|
+
return Date.now();
|
|
33284
|
+
}
|
|
33285
|
+
|
|
33286
|
+
function enqueue(entry) {
|
|
33287
|
+
queue.push({
|
|
33288
|
+
...entry,
|
|
33289
|
+
timestamp: typeof entry.timestamp === "number" ? entry.timestamp : now(),
|
|
33290
|
+
});
|
|
33291
|
+
if (queue.length > queueLimit) {
|
|
33292
|
+
queue.splice(0, queue.length - queueLimit);
|
|
33293
|
+
}
|
|
33294
|
+
}
|
|
33295
|
+
|
|
33296
|
+
function isRecorderUiNode(node) {
|
|
33297
|
+
let current = node instanceof Node ? node : null;
|
|
33298
|
+
while (current) {
|
|
33299
|
+
if (current instanceof Element && current.hasAttribute(recorderUiAttribute)) {
|
|
33300
|
+
return true;
|
|
33301
|
+
}
|
|
33302
|
+
const root = typeof current.getRootNode === "function" ? current.getRootNode() : null;
|
|
33303
|
+
if (root instanceof ShadowRoot) {
|
|
33304
|
+
current = root.host;
|
|
33305
|
+
continue;
|
|
33306
|
+
}
|
|
33307
|
+
current = current instanceof Element ? current.parentElement : null;
|
|
33308
|
+
}
|
|
33309
|
+
return false;
|
|
33310
|
+
}
|
|
33311
|
+
|
|
33312
|
+
function isValidAttributeName(name) {
|
|
33313
|
+
if (typeof name !== "string") {
|
|
33314
|
+
return false;
|
|
33315
|
+
}
|
|
33316
|
+
const normalized = name.trim();
|
|
33317
|
+
if (normalized.length === 0) {
|
|
33318
|
+
return false;
|
|
33319
|
+
}
|
|
33320
|
+
if (/[\s"'<>/]/.test(normalized)) {
|
|
33321
|
+
return false;
|
|
33322
|
+
}
|
|
33323
|
+
return /^[A-Za-z_][A-Za-z0-9_:\-.]*$/.test(normalized);
|
|
33324
|
+
}
|
|
33325
|
+
|
|
33326
|
+
function escapeAttributeValue(value) {
|
|
33327
|
+
return String(value).replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
33328
|
+
}
|
|
33329
|
+
|
|
33330
|
+
function escapeIdentifier(value) {
|
|
33331
|
+
if (typeof CSS !== "undefined" && typeof CSS.escape === "function") {
|
|
33332
|
+
return CSS.escape(value);
|
|
33333
|
+
}
|
|
33334
|
+
return String(value).replace(/[^A-Za-z0-9_-]/g, (character) => {
|
|
33335
|
+
const codePoint = character.codePointAt(0);
|
|
33336
|
+
return "\\" + (codePoint == null ? "" : codePoint.toString(16)) + " ";
|
|
33337
|
+
});
|
|
33338
|
+
}
|
|
33339
|
+
|
|
33340
|
+
function normalizeClassValue(element, rawValue) {
|
|
33341
|
+
const tag = element.tagName.toLowerCase();
|
|
33342
|
+
const tokens = String(rawValue)
|
|
33343
|
+
.split(/\s+/u)
|
|
33344
|
+
.map((token) => token.trim())
|
|
33345
|
+
.filter(Boolean)
|
|
33346
|
+
.filter((token) => !volatileClassTokens.has(token))
|
|
33347
|
+
.filter((token) => !(lazyLoadingMediaTags.has(tag) && volatileLazyClassTokens.has(token)));
|
|
33348
|
+
return tokens.join(" ");
|
|
33349
|
+
}
|
|
33350
|
+
|
|
33351
|
+
function shouldKeepAttribute(element, name, value) {
|
|
33352
|
+
const key = String(name || "")
|
|
33353
|
+
.trim()
|
|
33354
|
+
.toLowerCase();
|
|
33355
|
+
if (!key || !String(value || "").trim()) {
|
|
33356
|
+
return false;
|
|
33357
|
+
}
|
|
33358
|
+
if (!isValidAttributeName(key)) {
|
|
33359
|
+
return false;
|
|
33360
|
+
}
|
|
33361
|
+
if (key === "c") {
|
|
33362
|
+
return false;
|
|
33363
|
+
}
|
|
33364
|
+
if (/^on[a-z]/i.test(key)) {
|
|
33365
|
+
return false;
|
|
33366
|
+
}
|
|
33367
|
+
if (attributeDenyKeys.has(key)) {
|
|
33368
|
+
return false;
|
|
33369
|
+
}
|
|
33370
|
+
if (key.startsWith("data-os-") || key.startsWith("data-opensteer-")) {
|
|
33371
|
+
return false;
|
|
33372
|
+
}
|
|
33373
|
+
if (lazyLoadingMediaTags.has(element.tagName.toLowerCase()) && volatileLazyLoadingAttrs.has(key)) {
|
|
33374
|
+
return false;
|
|
33375
|
+
}
|
|
33376
|
+
return true;
|
|
33377
|
+
}
|
|
33378
|
+
|
|
33379
|
+
function readAttributeValue(element, key) {
|
|
33380
|
+
if (key === "class") {
|
|
33381
|
+
const normalized = normalizeClassValue(element, element.getAttribute("class") || "");
|
|
33382
|
+
return normalized.length === 0 ? undefined : normalized;
|
|
33383
|
+
}
|
|
33384
|
+
const value = element.getAttribute(key);
|
|
33385
|
+
if (!shouldKeepAttribute(element, key, value || "")) {
|
|
33386
|
+
return undefined;
|
|
33387
|
+
}
|
|
33388
|
+
return value || undefined;
|
|
33389
|
+
}
|
|
33390
|
+
|
|
33391
|
+
function buildSingleAttributeSelector(element, key, value) {
|
|
33392
|
+
if (!value) {
|
|
33393
|
+
return undefined;
|
|
33394
|
+
}
|
|
33395
|
+
const tag = element.tagName.toLowerCase();
|
|
33396
|
+
if (key === "id") {
|
|
33397
|
+
const idSelector = "#" + escapeIdentifier(value);
|
|
33398
|
+
return element.matches(idSelector)
|
|
33399
|
+
? idSelector
|
|
33400
|
+
: tag + '[id="' + escapeAttributeValue(value) + '"]';
|
|
33401
|
+
}
|
|
33402
|
+
return tag + "[" + key + '="' + escapeAttributeValue(value) + '"]';
|
|
33403
|
+
}
|
|
33404
|
+
|
|
33405
|
+
function isUniqueSelector(selector, element) {
|
|
33406
|
+
if (!selector) {
|
|
33407
|
+
return false;
|
|
33408
|
+
}
|
|
33409
|
+
let matches;
|
|
33410
|
+
try {
|
|
33411
|
+
matches = document.querySelectorAll(selector);
|
|
33412
|
+
} catch {
|
|
33413
|
+
return false;
|
|
33414
|
+
}
|
|
33415
|
+
return matches.length === 1 && matches[0] === element;
|
|
33416
|
+
}
|
|
33417
|
+
|
|
33418
|
+
function nearestRecordTarget(node) {
|
|
33419
|
+
if (isRecorderUiNode(node)) {
|
|
33420
|
+
return null;
|
|
33421
|
+
}
|
|
33422
|
+
let current = node instanceof Element ? node : null;
|
|
33423
|
+
while (current) {
|
|
33424
|
+
if (current.hasAttribute(recorderUiAttribute)) {
|
|
33425
|
+
return null;
|
|
33426
|
+
}
|
|
33427
|
+
const tag = current.tagName.toLowerCase();
|
|
33428
|
+
if (
|
|
33429
|
+
actionTargetTags.has(tag) ||
|
|
33430
|
+
current.hasAttribute("data-testid") ||
|
|
33431
|
+
current.hasAttribute("data-test") ||
|
|
33432
|
+
current.hasAttribute("data-qa") ||
|
|
33433
|
+
current.hasAttribute("data-cy") ||
|
|
33434
|
+
current.hasAttribute("role") ||
|
|
33435
|
+
current.hasAttribute("aria-label") ||
|
|
33436
|
+
current.hasAttribute("name")
|
|
33437
|
+
) {
|
|
33438
|
+
return current;
|
|
33439
|
+
}
|
|
33440
|
+
current = current.parentElement;
|
|
33441
|
+
}
|
|
33442
|
+
return node instanceof Element ? node : null;
|
|
33443
|
+
}
|
|
33444
|
+
|
|
33445
|
+
function buildSegmentSelector(element) {
|
|
33446
|
+
const tag = element.tagName.toLowerCase();
|
|
33447
|
+
for (const key of matchAttributePriority) {
|
|
33448
|
+
const value = readAttributeValue(element, key);
|
|
33449
|
+
if (!value) {
|
|
33450
|
+
continue;
|
|
33451
|
+
}
|
|
33452
|
+
if (key === "class") {
|
|
33453
|
+
const tokens = value
|
|
33454
|
+
.split(/\s+/u)
|
|
33455
|
+
.map((token) => token.trim())
|
|
33456
|
+
.filter(Boolean)
|
|
33457
|
+
.slice(0, 2);
|
|
33458
|
+
if (tokens.length === 0) {
|
|
33459
|
+
continue;
|
|
33460
|
+
}
|
|
33461
|
+
return tag + tokens.map((token) => "." + escapeIdentifier(token)).join("");
|
|
33462
|
+
}
|
|
33463
|
+
return key === "id"
|
|
33464
|
+
? tag + '[id="' + escapeAttributeValue(value) + '"]'
|
|
33465
|
+
: tag + "[" + key + '="' + escapeAttributeValue(value) + '"]';
|
|
33466
|
+
}
|
|
33467
|
+
return tag;
|
|
33468
|
+
}
|
|
33469
|
+
|
|
33470
|
+
function nthOfTypeSegment(element, baseSelector) {
|
|
33471
|
+
const parent = element.parentElement;
|
|
33472
|
+
if (!parent) {
|
|
33473
|
+
return baseSelector;
|
|
33474
|
+
}
|
|
33475
|
+
const sameType = Array.from(parent.children).filter(
|
|
33476
|
+
(child) => child.tagName === element.tagName,
|
|
33477
|
+
);
|
|
33478
|
+
if (sameType.length <= 1) {
|
|
33479
|
+
return baseSelector;
|
|
33480
|
+
}
|
|
33481
|
+
const index = sameType.indexOf(element) + 1;
|
|
33482
|
+
return baseSelector + ":nth-of-type(" + String(index) + ")";
|
|
33483
|
+
}
|
|
33484
|
+
|
|
33485
|
+
function buildSelector(node) {
|
|
33486
|
+
const element = nearestRecordTarget(node);
|
|
33487
|
+
if (!(element instanceof Element)) {
|
|
33488
|
+
return undefined;
|
|
33489
|
+
}
|
|
33490
|
+
|
|
33491
|
+
for (const key of singleAttributePriority) {
|
|
33492
|
+
const value = readAttributeValue(element, key);
|
|
33493
|
+
const selector = buildSingleAttributeSelector(element, key, value);
|
|
33494
|
+
if (selector && isUniqueSelector(selector, element)) {
|
|
33495
|
+
return selector;
|
|
33496
|
+
}
|
|
33497
|
+
if (value && !stablePrimaryAttrKeys.has(key) && key !== "id") {
|
|
33498
|
+
const tagQualified =
|
|
33499
|
+
element.tagName.toLowerCase() + "[" + key + '="' + escapeAttributeValue(value) + '"]';
|
|
33500
|
+
if (isUniqueSelector(tagQualified, element)) {
|
|
33501
|
+
return tagQualified;
|
|
33502
|
+
}
|
|
33503
|
+
}
|
|
33504
|
+
}
|
|
33505
|
+
|
|
33506
|
+
const segments = [];
|
|
33507
|
+
let current = element;
|
|
33508
|
+
let depth = 0;
|
|
33509
|
+
while (current && depth < 6) {
|
|
33510
|
+
const segment = nthOfTypeSegment(current, buildSegmentSelector(current));
|
|
33511
|
+
segments.unshift(segment);
|
|
33512
|
+
const selector = segments.join(" > ");
|
|
33513
|
+
if (isUniqueSelector(selector, element)) {
|
|
33514
|
+
return selector;
|
|
33515
|
+
}
|
|
33516
|
+
current = current.parentElement;
|
|
33517
|
+
depth += 1;
|
|
33518
|
+
}
|
|
33519
|
+
|
|
33520
|
+
const fallback = segments.join(" > ");
|
|
33521
|
+
return fallback.length > 0 ? fallback : element.tagName.toLowerCase();
|
|
33522
|
+
}
|
|
33523
|
+
|
|
33524
|
+
function readTargetValue(target) {
|
|
33525
|
+
if (target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement) {
|
|
33526
|
+
return target.value;
|
|
33527
|
+
}
|
|
33528
|
+
if (target instanceof HTMLSelectElement) {
|
|
33529
|
+
return target.value;
|
|
33530
|
+
}
|
|
33531
|
+
if (target instanceof HTMLElement && target.isContentEditable) {
|
|
33532
|
+
return target.textContent || "";
|
|
33533
|
+
}
|
|
33534
|
+
return undefined;
|
|
33535
|
+
}
|
|
33536
|
+
|
|
33537
|
+
function flushPendingInput(selector) {
|
|
33538
|
+
const pending = pendingInputs.get(selector);
|
|
33539
|
+
if (!pending) {
|
|
33540
|
+
return;
|
|
33541
|
+
}
|
|
33542
|
+
pendingInputs.delete(selector);
|
|
33543
|
+
const timer = inputFlushTimers.get(selector);
|
|
33544
|
+
if (timer !== undefined) {
|
|
33545
|
+
clearTimeout(timer);
|
|
33546
|
+
inputFlushTimers.delete(selector);
|
|
33547
|
+
}
|
|
33548
|
+
enqueue({
|
|
33549
|
+
kind: "type",
|
|
33550
|
+
selector,
|
|
33551
|
+
text: pending.text,
|
|
33552
|
+
timestamp: pending.timestamp,
|
|
33553
|
+
});
|
|
33554
|
+
}
|
|
33555
|
+
|
|
33556
|
+
function flushAllInputs() {
|
|
33557
|
+
for (const selector of Array.from(pendingInputs.keys())) {
|
|
33558
|
+
flushPendingInput(selector);
|
|
33559
|
+
}
|
|
33560
|
+
}
|
|
33561
|
+
|
|
33562
|
+
function flushPendingWheel() {
|
|
33563
|
+
if (!pendingWheel) {
|
|
33564
|
+
return;
|
|
33565
|
+
}
|
|
33566
|
+
clearTimeout(pendingWheel.timerId);
|
|
33567
|
+
enqueue({
|
|
33568
|
+
kind: "scroll",
|
|
33569
|
+
selector: pendingWheel.selector,
|
|
33570
|
+
deltaX: pendingWheel.deltaX,
|
|
33571
|
+
deltaY: pendingWheel.deltaY,
|
|
33572
|
+
timestamp: pendingWheel.timestamp,
|
|
33573
|
+
});
|
|
33574
|
+
pendingWheel = undefined;
|
|
33575
|
+
}
|
|
33576
|
+
|
|
33577
|
+
function scheduleInputFlush(selector) {
|
|
33578
|
+
const existing = inputFlushTimers.get(selector);
|
|
33579
|
+
if (existing !== undefined) {
|
|
33580
|
+
clearTimeout(existing);
|
|
33581
|
+
}
|
|
33582
|
+
const timerId = setTimeout(() => {
|
|
33583
|
+
flushPendingInput(selector);
|
|
33584
|
+
}, 400);
|
|
33585
|
+
inputFlushTimers.set(selector, timerId);
|
|
33586
|
+
}
|
|
33587
|
+
|
|
33588
|
+
function createDefaultHistoryState(currentUrl) {
|
|
33589
|
+
return {
|
|
33590
|
+
entries: [currentUrl],
|
|
33591
|
+
index: 0,
|
|
33592
|
+
};
|
|
33593
|
+
}
|
|
33594
|
+
|
|
33595
|
+
function normalizeHistoryState(state) {
|
|
33596
|
+
if (
|
|
33597
|
+
!state ||
|
|
33598
|
+
!Array.isArray(state.entries) ||
|
|
33599
|
+
!state.entries.every((entry) => typeof entry === "string") ||
|
|
33600
|
+
!Number.isInteger(state.index)
|
|
33601
|
+
) {
|
|
33602
|
+
return undefined;
|
|
33603
|
+
}
|
|
33604
|
+
const entries = state.entries.slice();
|
|
33605
|
+
if (entries.length === 0) {
|
|
33606
|
+
return createDefaultHistoryState(location.href);
|
|
33607
|
+
}
|
|
33608
|
+
return {
|
|
33609
|
+
entries,
|
|
33610
|
+
index: Math.min(entries.length - 1, Math.max(0, state.index)),
|
|
33611
|
+
};
|
|
33612
|
+
}
|
|
33613
|
+
|
|
33614
|
+
function writeHistoryState(state) {
|
|
33615
|
+
const normalized = normalizeHistoryState(state) ?? createDefaultHistoryState(location.href);
|
|
33616
|
+
historyStateCache = normalized;
|
|
33617
|
+
try {
|
|
33618
|
+
sessionStorage.setItem(historyStateKey, JSON.stringify(normalized));
|
|
33619
|
+
} catch {}
|
|
33620
|
+
return normalized;
|
|
33621
|
+
}
|
|
33622
|
+
|
|
33623
|
+
function applyHistoryState(state, mode, nextUrl) {
|
|
33624
|
+
const current = normalizeHistoryState(state) ?? createDefaultHistoryState(location.href);
|
|
33625
|
+
const nextState = {
|
|
33626
|
+
entries: current.entries.slice(),
|
|
33627
|
+
index: current.index,
|
|
33628
|
+
};
|
|
33629
|
+
if (mode === "replace") {
|
|
33630
|
+
nextState.entries[nextState.index] = nextUrl;
|
|
33631
|
+
} else if (mode === "push") {
|
|
33632
|
+
nextState.entries = nextState.entries.slice(0, nextState.index + 1);
|
|
33633
|
+
nextState.entries.push(nextUrl);
|
|
33634
|
+
nextState.index = nextState.entries.length - 1;
|
|
33635
|
+
} else if (mode === "back") {
|
|
33636
|
+
nextState.index = Math.max(0, nextState.index - 1);
|
|
33637
|
+
} else if (mode === "forward") {
|
|
33638
|
+
nextState.index = Math.min(nextState.entries.length - 1, nextState.index + 1);
|
|
33639
|
+
}
|
|
33640
|
+
return nextState;
|
|
33641
|
+
}
|
|
33642
|
+
|
|
33643
|
+
function updateHistoryState(mode, nextUrl) {
|
|
33644
|
+
return writeHistoryState(applyHistoryState(readHistoryState(), mode, nextUrl));
|
|
33645
|
+
}
|
|
33646
|
+
|
|
33647
|
+
function readHistoryState() {
|
|
33648
|
+
const cached = normalizeHistoryState(historyStateCache);
|
|
33649
|
+
if (cached) {
|
|
33650
|
+
historyStateCache = cached;
|
|
33651
|
+
return cached;
|
|
33652
|
+
}
|
|
33653
|
+
try {
|
|
33654
|
+
const raw = sessionStorage.getItem(historyStateKey);
|
|
33655
|
+
const parsed = normalizeHistoryState(raw ? JSON.parse(raw) : undefined);
|
|
33656
|
+
if (parsed) {
|
|
33657
|
+
historyStateCache = parsed;
|
|
33658
|
+
return parsed;
|
|
33659
|
+
}
|
|
33660
|
+
} catch {}
|
|
33661
|
+
return undefined;
|
|
33662
|
+
}
|
|
33663
|
+
|
|
33664
|
+
function classifyHistoryTraversal(currentUrl) {
|
|
33665
|
+
const state = readHistoryState();
|
|
33666
|
+
if (!state) {
|
|
33667
|
+
return undefined;
|
|
33668
|
+
}
|
|
33669
|
+
if (state.entries[state.index] === currentUrl) {
|
|
33670
|
+
return undefined;
|
|
33671
|
+
}
|
|
33672
|
+
if (state.entries[state.index - 1] === currentUrl) {
|
|
33673
|
+
writeHistoryState({
|
|
33674
|
+
entries: state.entries,
|
|
33675
|
+
index: state.index - 1,
|
|
33676
|
+
});
|
|
33677
|
+
return "go-back";
|
|
33678
|
+
}
|
|
33679
|
+
if (state.entries[state.index + 1] === currentUrl) {
|
|
33680
|
+
writeHistoryState({
|
|
33681
|
+
entries: state.entries,
|
|
33682
|
+
index: state.index + 1,
|
|
33683
|
+
});
|
|
33684
|
+
return "go-forward";
|
|
33685
|
+
}
|
|
33686
|
+
const existingIndex = state.entries.lastIndexOf(currentUrl);
|
|
33687
|
+
if (existingIndex !== -1 && existingIndex !== state.index) {
|
|
33688
|
+
writeHistoryState({
|
|
33689
|
+
entries: state.entries,
|
|
33690
|
+
index: existingIndex,
|
|
33691
|
+
});
|
|
33692
|
+
return existingIndex < state.index ? "go-back" : "go-forward";
|
|
33693
|
+
}
|
|
33694
|
+
return undefined;
|
|
33695
|
+
}
|
|
33696
|
+
|
|
33697
|
+
function onInstall() {
|
|
33698
|
+
const currentUrl = location.href;
|
|
33699
|
+
const navigationEntry =
|
|
33700
|
+
typeof performance.getEntriesByType === "function"
|
|
33701
|
+
? performance.getEntriesByType("navigation")[0]
|
|
33702
|
+
: undefined;
|
|
33703
|
+
const navigationType =
|
|
33704
|
+
navigationEntry && typeof navigationEntry.type === "string" ? navigationEntry.type : undefined;
|
|
33705
|
+
const existingState = readHistoryState();
|
|
33706
|
+
|
|
33707
|
+
if (!existingState) {
|
|
33708
|
+
updateHistoryState("replace", currentUrl);
|
|
33709
|
+
return;
|
|
33710
|
+
}
|
|
33711
|
+
|
|
33712
|
+
if (navigationType === "reload") {
|
|
33713
|
+
updateHistoryState("replace", currentUrl);
|
|
33714
|
+
enqueue({
|
|
33715
|
+
kind: "reload",
|
|
33716
|
+
url: currentUrl,
|
|
33717
|
+
});
|
|
33718
|
+
return;
|
|
33719
|
+
}
|
|
33720
|
+
|
|
33721
|
+
if (navigationType === "back_forward") {
|
|
33722
|
+
const traversal = classifyHistoryTraversal(currentUrl);
|
|
33723
|
+
if (traversal === "go-back" || traversal === "go-forward") {
|
|
33724
|
+
enqueue({
|
|
33725
|
+
kind: traversal,
|
|
33726
|
+
url: currentUrl,
|
|
33727
|
+
});
|
|
33728
|
+
return;
|
|
33729
|
+
}
|
|
33730
|
+
}
|
|
33731
|
+
|
|
33732
|
+
if (existingState.entries[existingState.index] !== currentUrl) {
|
|
33733
|
+
updateHistoryState("push", currentUrl);
|
|
33734
|
+
enqueue({
|
|
33735
|
+
kind: "navigate",
|
|
33736
|
+
url: currentUrl,
|
|
33737
|
+
source: "full-navigation",
|
|
33738
|
+
});
|
|
33739
|
+
}
|
|
33740
|
+
}
|
|
33741
|
+
|
|
33742
|
+
function modifierKeys(event) {
|
|
33743
|
+
const modifiers = [];
|
|
33744
|
+
if (event.altKey) {
|
|
33745
|
+
modifiers.push("Alt");
|
|
33746
|
+
}
|
|
33747
|
+
if (event.ctrlKey) {
|
|
33748
|
+
modifiers.push("Control");
|
|
33749
|
+
}
|
|
33750
|
+
if (event.metaKey) {
|
|
33751
|
+
modifiers.push("Meta");
|
|
33752
|
+
}
|
|
33753
|
+
if (event.shiftKey) {
|
|
33754
|
+
modifiers.push("Shift");
|
|
33755
|
+
}
|
|
33756
|
+
return modifiers;
|
|
33757
|
+
}
|
|
33758
|
+
|
|
33759
|
+
function addListener(target, type, listener, options) {
|
|
33760
|
+
target.addEventListener(type, listener, options);
|
|
33761
|
+
cleanup.push(() => {
|
|
33762
|
+
target.removeEventListener(type, listener, options);
|
|
33763
|
+
});
|
|
33764
|
+
}
|
|
33765
|
+
|
|
33766
|
+
function requestStop() {
|
|
33767
|
+
stopRequested = true;
|
|
33768
|
+
}
|
|
33769
|
+
|
|
33770
|
+
function mountStopButton() {
|
|
33771
|
+
if (document.querySelector("[" + recorderUiAttribute + "]")) {
|
|
33772
|
+
return true;
|
|
33773
|
+
}
|
|
33774
|
+
if (stopRequested) {
|
|
33775
|
+
return true;
|
|
33776
|
+
}
|
|
33777
|
+
const parent = document.body || document.documentElement;
|
|
33778
|
+
if (!(parent instanceof HTMLElement)) {
|
|
33779
|
+
return false;
|
|
33780
|
+
}
|
|
33781
|
+
|
|
33782
|
+
const host = document.createElement("div");
|
|
33783
|
+
host.setAttribute(recorderUiAttribute, "true");
|
|
33784
|
+
const shadowRoot = host.attachShadow({ mode: "open" });
|
|
33785
|
+
const style = document.createElement("style");
|
|
33786
|
+
style.textContent = [
|
|
33787
|
+
":host {",
|
|
33788
|
+
" all: initial;",
|
|
33789
|
+
" display: block;",
|
|
33790
|
+
" position: fixed;",
|
|
33791
|
+
" top: 16px;",
|
|
33792
|
+
" right: 16px;",
|
|
33793
|
+
" z-index: 2147483647;",
|
|
33794
|
+
" pointer-events: auto;",
|
|
33795
|
+
" color-scheme: only light;",
|
|
33796
|
+
" contain: content;",
|
|
33797
|
+
" isolation: isolate;",
|
|
33798
|
+
"}",
|
|
33799
|
+
"button {",
|
|
33800
|
+
" appearance: none;",
|
|
33801
|
+
" border: 1px solid rgba(15, 23, 42, 0.2);",
|
|
33802
|
+
" border-radius: 999px;",
|
|
33803
|
+
" background: rgba(15, 23, 42, 0.92);",
|
|
33804
|
+
" color: #ffffff;",
|
|
33805
|
+
" color-scheme: only light;",
|
|
33806
|
+
" cursor: pointer;",
|
|
33807
|
+
" font: 600 12px/1.2 ui-sans-serif, system-ui, sans-serif;",
|
|
33808
|
+
" letter-spacing: 0.01em;",
|
|
33809
|
+
" padding: 10px 14px;",
|
|
33810
|
+
" box-shadow: 0 10px 30px rgba(15, 23, 42, 0.22);",
|
|
33811
|
+
"}",
|
|
33812
|
+
"button:hover {",
|
|
33813
|
+
" background: rgba(15, 23, 42, 0.98);",
|
|
33814
|
+
"}",
|
|
33815
|
+
"button:focus-visible {",
|
|
33816
|
+
" outline: 2px solid #2563eb;",
|
|
33817
|
+
" outline-offset: 2px;",
|
|
33818
|
+
"}",
|
|
33819
|
+
].join("\n");
|
|
33820
|
+
const button = document.createElement("button");
|
|
33821
|
+
button.type = "button";
|
|
33822
|
+
button.textContent = "Stop recording";
|
|
33823
|
+
button.setAttribute("aria-label", "Stop recording");
|
|
33824
|
+
const consumePointerEvent = (event) => {
|
|
33825
|
+
event.preventDefault();
|
|
33826
|
+
event.stopPropagation();
|
|
33827
|
+
};
|
|
33828
|
+
for (const type of ["pointerdown", "mousedown", "mouseup", "click"]) {
|
|
33829
|
+
button.addEventListener(type, consumePointerEvent);
|
|
33830
|
+
}
|
|
33831
|
+
button.addEventListener("click", () => {
|
|
33832
|
+
requestStop();
|
|
33833
|
+
host.remove();
|
|
33834
|
+
});
|
|
33835
|
+
shadowRoot.append(style, button);
|
|
33836
|
+
parent.appendChild(host);
|
|
33837
|
+
cleanup.push(() => {
|
|
33838
|
+
host.remove();
|
|
33839
|
+
});
|
|
33840
|
+
return true;
|
|
33841
|
+
}
|
|
33842
|
+
|
|
33843
|
+
function ensureStopButtonMounted() {
|
|
33844
|
+
if (mountStopButton()) {
|
|
33845
|
+
return;
|
|
33846
|
+
}
|
|
33847
|
+
|
|
33848
|
+
const observer = new MutationObserver(() => {
|
|
33849
|
+
if (!mountStopButton()) {
|
|
33850
|
+
return;
|
|
33851
|
+
}
|
|
33852
|
+
observer.disconnect();
|
|
33853
|
+
});
|
|
33854
|
+
observer.observe(document, {
|
|
33855
|
+
childList: true,
|
|
33856
|
+
subtree: true,
|
|
33857
|
+
});
|
|
33858
|
+
cleanup.push(() => {
|
|
33859
|
+
observer.disconnect();
|
|
33860
|
+
});
|
|
33861
|
+
}
|
|
33862
|
+
|
|
33863
|
+
addListener(document, "click", (event) => {
|
|
33864
|
+
if (!event.isTrusted) {
|
|
33865
|
+
return;
|
|
33866
|
+
}
|
|
33867
|
+
const target = nearestRecordTarget(event.target);
|
|
33868
|
+
if (!(target instanceof Element)) {
|
|
33869
|
+
return;
|
|
33870
|
+
}
|
|
33871
|
+
const tag = target.tagName.toLowerCase();
|
|
33872
|
+
if (tag === "select" || tag === "option") {
|
|
33873
|
+
return;
|
|
33874
|
+
}
|
|
33875
|
+
const selector = buildSelector(target);
|
|
33876
|
+
if (!selector) {
|
|
33877
|
+
return;
|
|
33878
|
+
}
|
|
33879
|
+
enqueue({
|
|
33880
|
+
kind: "click",
|
|
33881
|
+
selector,
|
|
33882
|
+
button: event.button,
|
|
33883
|
+
modifiers: modifierKeys(event),
|
|
33884
|
+
});
|
|
33885
|
+
}, true);
|
|
33886
|
+
|
|
33887
|
+
addListener(document, "dblclick", (event) => {
|
|
33888
|
+
if (!event.isTrusted) {
|
|
33889
|
+
return;
|
|
33890
|
+
}
|
|
33891
|
+
const target = nearestRecordTarget(event.target);
|
|
33892
|
+
if (!(target instanceof Element)) {
|
|
33893
|
+
return;
|
|
33894
|
+
}
|
|
33895
|
+
const selector = buildSelector(target);
|
|
33896
|
+
if (!selector) {
|
|
33897
|
+
return;
|
|
33898
|
+
}
|
|
33899
|
+
enqueue({
|
|
33900
|
+
kind: "dblclick",
|
|
33901
|
+
selector,
|
|
33902
|
+
});
|
|
33903
|
+
}, true);
|
|
33904
|
+
|
|
33905
|
+
addListener(document, "input", (event) => {
|
|
33906
|
+
if (!event.isTrusted) {
|
|
33907
|
+
return;
|
|
33908
|
+
}
|
|
33909
|
+
const target = nearestRecordTarget(event.target);
|
|
33910
|
+
if (!(target instanceof Element)) {
|
|
33911
|
+
return;
|
|
33912
|
+
}
|
|
33913
|
+
const selector = buildSelector(target);
|
|
33914
|
+
const text = readTargetValue(target);
|
|
33915
|
+
if (!selector || typeof text !== "string") {
|
|
33916
|
+
return;
|
|
33917
|
+
}
|
|
33918
|
+
pendingInputs.set(selector, {
|
|
33919
|
+
selector,
|
|
33920
|
+
text,
|
|
33921
|
+
timestamp: now(),
|
|
33922
|
+
});
|
|
33923
|
+
scheduleInputFlush(selector);
|
|
33924
|
+
}, true);
|
|
33925
|
+
|
|
33926
|
+
addListener(document, "change", (event) => {
|
|
33927
|
+
if (!event.isTrusted) {
|
|
33928
|
+
return;
|
|
33929
|
+
}
|
|
33930
|
+
const target = nearestRecordTarget(event.target);
|
|
33931
|
+
if (!(target instanceof HTMLSelectElement)) {
|
|
33932
|
+
return;
|
|
33933
|
+
}
|
|
33934
|
+
const selector = buildSelector(target);
|
|
33935
|
+
if (!selector) {
|
|
33936
|
+
return;
|
|
33937
|
+
}
|
|
33938
|
+
const selectedOption = target.selectedOptions && target.selectedOptions.length > 0
|
|
33939
|
+
? target.selectedOptions[0]
|
|
33940
|
+
: undefined;
|
|
33941
|
+
enqueue({
|
|
33942
|
+
kind: "select-option",
|
|
33943
|
+
selector,
|
|
33944
|
+
value: target.value,
|
|
33945
|
+
...(selectedOption === undefined ? {} : { label: selectedOption.label || selectedOption.textContent || "" }),
|
|
33946
|
+
});
|
|
33947
|
+
}, true);
|
|
33948
|
+
|
|
33949
|
+
addListener(document, "keydown", (event) => {
|
|
33950
|
+
if (!event.isTrusted) {
|
|
33951
|
+
return;
|
|
33952
|
+
}
|
|
33953
|
+
const allowedKeys = new Set([
|
|
33954
|
+
"ArrowDown",
|
|
33955
|
+
"ArrowLeft",
|
|
33956
|
+
"ArrowRight",
|
|
33957
|
+
"ArrowUp",
|
|
33958
|
+
"Backspace",
|
|
33959
|
+
"Delete",
|
|
33960
|
+
"Enter",
|
|
33961
|
+
"Escape",
|
|
33962
|
+
"Tab",
|
|
33963
|
+
]);
|
|
33964
|
+
if (!allowedKeys.has(event.key)) {
|
|
33965
|
+
return;
|
|
33966
|
+
}
|
|
33967
|
+
const target = nearestRecordTarget(event.target);
|
|
33968
|
+
const selector = target instanceof Element ? buildSelector(target) : undefined;
|
|
33969
|
+
if (event.key === "Enter" && selector) {
|
|
33970
|
+
flushPendingInput(selector);
|
|
33971
|
+
}
|
|
33972
|
+
enqueue({
|
|
33973
|
+
kind: "keypress",
|
|
33974
|
+
key: event.key,
|
|
33975
|
+
modifiers: modifierKeys(event),
|
|
33976
|
+
...(selector === undefined ? {} : { selector }),
|
|
33977
|
+
});
|
|
33978
|
+
}, true);
|
|
33979
|
+
|
|
33980
|
+
addListener(document, "wheel", (event) => {
|
|
33981
|
+
if (!event.isTrusted) {
|
|
33982
|
+
return;
|
|
33983
|
+
}
|
|
33984
|
+
const target = nearestRecordTarget(event.target);
|
|
33985
|
+
const selector = target instanceof Element ? buildSelector(target) : undefined;
|
|
33986
|
+
if (pendingWheel && pendingWheel.selector === selector) {
|
|
33987
|
+
pendingWheel.deltaX += event.deltaX;
|
|
33988
|
+
pendingWheel.deltaY += event.deltaY;
|
|
33989
|
+
clearTimeout(pendingWheel.timerId);
|
|
33990
|
+
pendingWheel.timerId = setTimeout(flushPendingWheel, 250);
|
|
33991
|
+
return;
|
|
33992
|
+
}
|
|
33993
|
+
flushPendingWheel();
|
|
33994
|
+
pendingWheel = {
|
|
33995
|
+
selector,
|
|
33996
|
+
deltaX: event.deltaX,
|
|
33997
|
+
deltaY: event.deltaY,
|
|
33998
|
+
timestamp: now(),
|
|
33999
|
+
timerId: setTimeout(flushPendingWheel, 250),
|
|
34000
|
+
};
|
|
34001
|
+
}, true);
|
|
34002
|
+
|
|
34003
|
+
const originalPushState = history.pushState.bind(history);
|
|
34004
|
+
history.pushState = function pushState(state, unused, url) {
|
|
34005
|
+
const beforeUrl = location.href;
|
|
34006
|
+
const output = originalPushState.apply(this, arguments);
|
|
34007
|
+
const nextUrl = location.href;
|
|
34008
|
+
if (nextUrl !== beforeUrl) {
|
|
34009
|
+
updateHistoryState("push", nextUrl);
|
|
34010
|
+
enqueue({
|
|
34011
|
+
kind: "navigate",
|
|
34012
|
+
url: nextUrl,
|
|
34013
|
+
source: "push-state",
|
|
34014
|
+
});
|
|
34015
|
+
}
|
|
34016
|
+
return output;
|
|
34017
|
+
};
|
|
34018
|
+
cleanup.push(() => {
|
|
34019
|
+
history.pushState = originalPushState;
|
|
34020
|
+
});
|
|
34021
|
+
|
|
34022
|
+
const originalReplaceState = history.replaceState.bind(history);
|
|
34023
|
+
history.replaceState = function replaceState(state, unused, url) {
|
|
34024
|
+
const beforeUrl = location.href;
|
|
34025
|
+
const output = originalReplaceState.apply(this, arguments);
|
|
34026
|
+
const nextUrl = location.href;
|
|
34027
|
+
if (nextUrl !== beforeUrl) {
|
|
34028
|
+
updateHistoryState("replace", nextUrl);
|
|
34029
|
+
enqueue({
|
|
34030
|
+
kind: "navigate",
|
|
34031
|
+
url: nextUrl,
|
|
34032
|
+
source: "replace-state",
|
|
34033
|
+
});
|
|
34034
|
+
}
|
|
34035
|
+
return output;
|
|
34036
|
+
};
|
|
34037
|
+
cleanup.push(() => {
|
|
34038
|
+
history.replaceState = originalReplaceState;
|
|
34039
|
+
});
|
|
34040
|
+
|
|
34041
|
+
addListener(globalScope, "popstate", () => {
|
|
34042
|
+
const currentUrl = location.href;
|
|
34043
|
+
const traversal = classifyHistoryTraversal(currentUrl);
|
|
34044
|
+
if (traversal === "go-back" || traversal === "go-forward") {
|
|
34045
|
+
enqueue({
|
|
34046
|
+
kind: traversal,
|
|
34047
|
+
url: currentUrl,
|
|
34048
|
+
});
|
|
34049
|
+
return;
|
|
34050
|
+
}
|
|
34051
|
+
const state = readHistoryState();
|
|
34052
|
+
if (state?.entries[state.index] !== currentUrl) {
|
|
34053
|
+
updateHistoryState("push", currentUrl);
|
|
34054
|
+
enqueue({
|
|
34055
|
+
kind: "navigate",
|
|
34056
|
+
url: currentUrl,
|
|
34057
|
+
source: "history-traversal",
|
|
34058
|
+
});
|
|
34059
|
+
}
|
|
34060
|
+
});
|
|
34061
|
+
|
|
34062
|
+
addListener(globalScope, "hashchange", () => {
|
|
34063
|
+
const currentUrl = location.href;
|
|
34064
|
+
updateHistoryState("replace", currentUrl);
|
|
34065
|
+
enqueue({
|
|
34066
|
+
kind: "navigate",
|
|
34067
|
+
url: currentUrl,
|
|
34068
|
+
source: "hashchange",
|
|
34069
|
+
});
|
|
34070
|
+
});
|
|
34071
|
+
|
|
34072
|
+
function drain() {
|
|
34073
|
+
flushAllInputs();
|
|
34074
|
+
flushPendingWheel();
|
|
34075
|
+
return {
|
|
34076
|
+
url: location.href,
|
|
34077
|
+
focused: document.hasFocus(),
|
|
34078
|
+
visibilityState: document.visibilityState,
|
|
34079
|
+
stopRequested,
|
|
34080
|
+
events: queue.splice(0, queue.length),
|
|
34081
|
+
};
|
|
34082
|
+
}
|
|
34083
|
+
|
|
34084
|
+
globalScope[recorderKey] = {
|
|
34085
|
+
installed: true,
|
|
34086
|
+
debugSelector(target) {
|
|
34087
|
+
return buildSelector(target);
|
|
34088
|
+
},
|
|
34089
|
+
drain,
|
|
34090
|
+
requestStop,
|
|
34091
|
+
dispose() {
|
|
34092
|
+
flushAllInputs();
|
|
34093
|
+
flushPendingWheel();
|
|
34094
|
+
for (const dispose of cleanup.splice(0, cleanup.length)) {
|
|
34095
|
+
dispose();
|
|
34096
|
+
}
|
|
34097
|
+
for (const timerId of inputFlushTimers.values()) {
|
|
34098
|
+
clearTimeout(timerId);
|
|
34099
|
+
}
|
|
34100
|
+
inputFlushTimers.clear();
|
|
34101
|
+
pendingInputs.clear();
|
|
34102
|
+
delete globalScope[recorderKey];
|
|
34103
|
+
},
|
|
34104
|
+
};
|
|
34105
|
+
|
|
34106
|
+
if (options.showStopButton) {
|
|
34107
|
+
ensureStopButtonMounted();
|
|
34108
|
+
}
|
|
34109
|
+
onInstall();
|
|
34110
|
+
})();`;
|
|
34111
|
+
}
|
|
34112
|
+
var FLOW_RECORDER_INSTALL_SCRIPT = createFlowRecorderInstallScript();
|
|
34113
|
+
var FLOW_RECORDER_DRAIN_SCRIPT = String.raw`(() => {
|
|
34114
|
+
const recorder = globalThis.__opensteerFlowRecorder;
|
|
34115
|
+
if (!recorder || typeof recorder.drain !== "function") {
|
|
34116
|
+
return {
|
|
34117
|
+
url: location.href,
|
|
34118
|
+
focused: document.hasFocus(),
|
|
34119
|
+
visibilityState: document.visibilityState,
|
|
34120
|
+
stopRequested: false,
|
|
34121
|
+
events: [],
|
|
34122
|
+
};
|
|
34123
|
+
}
|
|
34124
|
+
return recorder.drain();
|
|
34125
|
+
})();`;
|
|
34126
|
+
|
|
34127
|
+
// ../runtime-core/src/recorder/event-collector.ts
|
|
34128
|
+
var FlowRecorderCollector = class {
|
|
34129
|
+
runtime;
|
|
34130
|
+
pollIntervalMs;
|
|
34131
|
+
onAction;
|
|
34132
|
+
installScript;
|
|
34133
|
+
pages = /* @__PURE__ */ new Map();
|
|
34134
|
+
actions = [];
|
|
34135
|
+
nextPageOrdinal = 0;
|
|
34136
|
+
runningLoop;
|
|
34137
|
+
loopStopRequested = false;
|
|
34138
|
+
stopDetected = false;
|
|
34139
|
+
focusedPageId;
|
|
34140
|
+
stopWaiters = [];
|
|
34141
|
+
constructor(runtime, options = {}) {
|
|
34142
|
+
this.runtime = runtime;
|
|
34143
|
+
this.pollIntervalMs = options.pollIntervalMs ?? 250;
|
|
34144
|
+
this.onAction = options.onAction;
|
|
34145
|
+
this.installScript = options.installScript ?? FLOW_RECORDER_INSTALL_SCRIPT;
|
|
34146
|
+
}
|
|
34147
|
+
async install() {
|
|
34148
|
+
await this.runtime.addInitScript({
|
|
34149
|
+
script: this.installScript
|
|
34150
|
+
});
|
|
34151
|
+
const { pages } = await this.runtime.listPages();
|
|
34152
|
+
for (const page of pages) {
|
|
34153
|
+
this.ensureKnownPage(page.pageRef, page.url, page.openerPageRef);
|
|
34154
|
+
}
|
|
34155
|
+
await Promise.all(
|
|
34156
|
+
pages.map(
|
|
34157
|
+
(page) => this.runtime.evaluate({
|
|
34158
|
+
script: this.installScript,
|
|
34159
|
+
pageRef: page.pageRef
|
|
34160
|
+
}).catch(() => void 0)
|
|
34161
|
+
)
|
|
34162
|
+
);
|
|
34163
|
+
const evaluatedPages = await this.readEvaluatedPages(pages);
|
|
34164
|
+
for (const page of evaluatedPages) {
|
|
34165
|
+
this.updateKnownPage(page.pageRef, page.currentUrl, page.openerPageRef);
|
|
34166
|
+
}
|
|
34167
|
+
this.focusedPageId = evaluatedPages.find((page) => page.focused)?.pageId ?? this.focusedPageId;
|
|
34168
|
+
}
|
|
34169
|
+
start() {
|
|
34170
|
+
if (this.runningLoop !== void 0) {
|
|
34171
|
+
return;
|
|
34172
|
+
}
|
|
34173
|
+
this.loopStopRequested = false;
|
|
34174
|
+
this.runningLoop = this.runLoop();
|
|
34175
|
+
}
|
|
34176
|
+
async stop() {
|
|
34177
|
+
this.loopStopRequested = true;
|
|
34178
|
+
if (this.runningLoop !== void 0) {
|
|
34179
|
+
await this.runningLoop;
|
|
34180
|
+
this.runningLoop = void 0;
|
|
34181
|
+
}
|
|
34182
|
+
if (!this.stopDetected) {
|
|
34183
|
+
await this.pollOnce().catch(() => void 0);
|
|
34184
|
+
}
|
|
34185
|
+
return this.actions.slice();
|
|
34186
|
+
}
|
|
34187
|
+
getActions() {
|
|
34188
|
+
return this.actions.slice();
|
|
34189
|
+
}
|
|
34190
|
+
getPages() {
|
|
34191
|
+
return [...this.pages.values()].map((page) => ({
|
|
34192
|
+
pageId: page.pageId,
|
|
34193
|
+
pageRef: page.pageRef,
|
|
34194
|
+
...page.openerPageRef === void 0 ? {} : { openerPageRef: page.openerPageRef },
|
|
34195
|
+
...page.openerPageId === void 0 ? {} : { openerPageId: page.openerPageId },
|
|
34196
|
+
currentUrl: page.currentUrl
|
|
34197
|
+
})).sort((left, right) => comparePageIds(left.pageId, right.pageId));
|
|
34198
|
+
}
|
|
34199
|
+
getFocusedPageId() {
|
|
34200
|
+
return this.focusedPageId;
|
|
34201
|
+
}
|
|
34202
|
+
async waitForStop() {
|
|
34203
|
+
if (this.stopDetected) {
|
|
34204
|
+
return;
|
|
34205
|
+
}
|
|
34206
|
+
await new Promise((resolve4) => {
|
|
34207
|
+
this.stopWaiters.push(resolve4);
|
|
34208
|
+
});
|
|
34209
|
+
}
|
|
34210
|
+
async pollOnce() {
|
|
34211
|
+
const pollTimestamp = Date.now();
|
|
34212
|
+
const { pages } = await this.runtime.listPages();
|
|
34213
|
+
const previousPageRefs = new Set(this.pages.keys());
|
|
34214
|
+
const evaluatedPages = await this.readEvaluatedPages(pages);
|
|
34215
|
+
if (!this.stopDetected && evaluatedPages.some((page) => page.stopRequested)) {
|
|
34216
|
+
this.stopDetected = true;
|
|
34217
|
+
this.loopStopRequested = true;
|
|
34218
|
+
for (const resolve4 of this.stopWaiters.splice(0, this.stopWaiters.length)) {
|
|
34219
|
+
resolve4();
|
|
34220
|
+
}
|
|
34221
|
+
}
|
|
34222
|
+
const actions = this.normalizePoll({
|
|
34223
|
+
pollTimestamp,
|
|
34224
|
+
previousPageRefs,
|
|
34225
|
+
listedPages: pages,
|
|
34226
|
+
evaluatedPages
|
|
34227
|
+
});
|
|
34228
|
+
if (actions.length === 0) {
|
|
34229
|
+
return [];
|
|
34230
|
+
}
|
|
34231
|
+
actions.sort((left, right) => {
|
|
34232
|
+
const timestampOrder = left.timestamp - right.timestamp;
|
|
34233
|
+
if (timestampOrder !== 0) {
|
|
34234
|
+
return timestampOrder;
|
|
34235
|
+
}
|
|
34236
|
+
return actionSortPriority(left.kind) - actionSortPriority(right.kind);
|
|
34237
|
+
});
|
|
34238
|
+
for (const action of actions) {
|
|
34239
|
+
this.actions.push(action);
|
|
34240
|
+
if (this.onAction !== void 0) {
|
|
34241
|
+
await this.onAction(action);
|
|
34242
|
+
}
|
|
34243
|
+
}
|
|
34244
|
+
return actions;
|
|
34245
|
+
}
|
|
34246
|
+
async runLoop() {
|
|
34247
|
+
while (!this.loopStopRequested) {
|
|
34248
|
+
try {
|
|
34249
|
+
await this.pollOnce();
|
|
34250
|
+
} catch {
|
|
34251
|
+
}
|
|
34252
|
+
if (this.loopStopRequested) {
|
|
34253
|
+
break;
|
|
34254
|
+
}
|
|
34255
|
+
await delay2(this.pollIntervalMs);
|
|
34256
|
+
}
|
|
34257
|
+
}
|
|
34258
|
+
async readEvaluatedPages(listedPages) {
|
|
34259
|
+
const pages = await Promise.all(
|
|
34260
|
+
listedPages.map(async (page) => {
|
|
34261
|
+
const knownPage = this.ensureKnownPage(page.pageRef, page.url, page.openerPageRef);
|
|
34262
|
+
const snapshot = await this.readSnapshot(page.pageRef, page.url);
|
|
34263
|
+
this.updateKnownPage(page.pageRef, snapshot.url, page.openerPageRef);
|
|
34264
|
+
return {
|
|
34265
|
+
pageRef: page.pageRef,
|
|
34266
|
+
pageId: knownPage.pageId,
|
|
34267
|
+
previousUrl: knownPage.currentUrl,
|
|
34268
|
+
currentUrl: snapshot.url,
|
|
34269
|
+
focused: snapshot.focused || snapshot.visibilityState === "visible",
|
|
34270
|
+
stopRequested: snapshot.stopRequested,
|
|
34271
|
+
events: snapshot.events,
|
|
34272
|
+
...page.openerPageRef === void 0 ? {} : { openerPageRef: page.openerPageRef },
|
|
34273
|
+
...knownPage.openerPageId === void 0 ? {} : { openerPageId: knownPage.openerPageId }
|
|
34274
|
+
};
|
|
34275
|
+
})
|
|
34276
|
+
);
|
|
34277
|
+
return pages;
|
|
34278
|
+
}
|
|
34279
|
+
async readSnapshot(pageRef, fallbackUrl) {
|
|
34280
|
+
const value = await this.runtime.evaluate({
|
|
34281
|
+
script: FLOW_RECORDER_DRAIN_SCRIPT,
|
|
34282
|
+
pageRef
|
|
34283
|
+
});
|
|
34284
|
+
return normalizeSnapshot(value, fallbackUrl);
|
|
34285
|
+
}
|
|
34286
|
+
normalizePoll(input) {
|
|
34287
|
+
const listedPageRefs = new Set(input.listedPages.map((page) => page.pageRef));
|
|
34288
|
+
const actions = [];
|
|
34289
|
+
const firstEventTimestampByPage = /* @__PURE__ */ new Map();
|
|
34290
|
+
for (const evaluatedPage of input.evaluatedPages) {
|
|
34291
|
+
const firstTimestamp = evaluatedPage.events[0]?.timestamp;
|
|
34292
|
+
if (firstTimestamp !== void 0) {
|
|
34293
|
+
firstEventTimestampByPage.set(evaluatedPage.pageRef, firstTimestamp);
|
|
34294
|
+
}
|
|
34295
|
+
}
|
|
34296
|
+
for (const listedPage of input.listedPages) {
|
|
34297
|
+
if (!input.previousPageRefs.has(listedPage.pageRef)) {
|
|
34298
|
+
const created = this.ensureKnownPage(
|
|
34299
|
+
listedPage.pageRef,
|
|
34300
|
+
listedPage.url,
|
|
34301
|
+
listedPage.openerPageRef
|
|
34302
|
+
);
|
|
34303
|
+
actions.push({
|
|
34304
|
+
kind: "new-tab",
|
|
34305
|
+
timestamp: Math.max(
|
|
34306
|
+
0,
|
|
34307
|
+
(firstEventTimestampByPage.get(listedPage.pageRef) ?? input.pollTimestamp) - 1
|
|
34308
|
+
),
|
|
34309
|
+
pageId: created.pageId,
|
|
34310
|
+
pageUrl: listedPage.url,
|
|
34311
|
+
detail: {
|
|
34312
|
+
kind: "new-tab",
|
|
34313
|
+
...created.openerPageId === void 0 ? {} : { openerPageId: created.openerPageId },
|
|
34314
|
+
initialUrl: listedPage.url
|
|
34315
|
+
}
|
|
34316
|
+
});
|
|
34317
|
+
}
|
|
34318
|
+
}
|
|
34319
|
+
for (const [pageRef, knownPage] of [...this.pages.entries()]) {
|
|
34320
|
+
if (listedPageRefs.has(pageRef)) {
|
|
34321
|
+
continue;
|
|
34322
|
+
}
|
|
34323
|
+
actions.push({
|
|
34324
|
+
kind: "close-tab",
|
|
34325
|
+
timestamp: input.pollTimestamp,
|
|
34326
|
+
pageId: knownPage.pageId,
|
|
34327
|
+
pageUrl: knownPage.currentUrl,
|
|
34328
|
+
detail: {
|
|
34329
|
+
kind: "close-tab"
|
|
34330
|
+
}
|
|
34331
|
+
});
|
|
34332
|
+
this.pages.delete(pageRef);
|
|
34333
|
+
}
|
|
34334
|
+
for (const evaluatedPage of input.evaluatedPages) {
|
|
34335
|
+
const knownPage = this.pages.get(evaluatedPage.pageRef);
|
|
34336
|
+
if (!knownPage) {
|
|
34337
|
+
continue;
|
|
34338
|
+
}
|
|
34339
|
+
if (evaluatedPage.previousUrl !== evaluatedPage.currentUrl && !evaluatedPage.events.some(
|
|
34340
|
+
(event) => event.kind === "navigate" || event.kind === "reload" || event.kind === "go-back" || event.kind === "go-forward"
|
|
34341
|
+
)) {
|
|
34342
|
+
actions.push({
|
|
34343
|
+
kind: "navigate",
|
|
34344
|
+
timestamp: Math.max(
|
|
34345
|
+
0,
|
|
34346
|
+
(firstEventTimestampByPage.get(evaluatedPage.pageRef) ?? input.pollTimestamp) - 1
|
|
34347
|
+
),
|
|
34348
|
+
pageId: knownPage.pageId,
|
|
34349
|
+
pageUrl: evaluatedPage.currentUrl,
|
|
34350
|
+
detail: {
|
|
34351
|
+
kind: "navigate",
|
|
34352
|
+
url: evaluatedPage.currentUrl,
|
|
34353
|
+
source: "poll"
|
|
34354
|
+
}
|
|
34355
|
+
});
|
|
34356
|
+
}
|
|
34357
|
+
actions.push(
|
|
34358
|
+
...evaluatedPage.events.flatMap(
|
|
34359
|
+
(event) => this.normalizeRawEvent(event, knownPage, evaluatedPage.currentUrl)
|
|
34360
|
+
)
|
|
34361
|
+
);
|
|
34362
|
+
this.updateKnownPage(
|
|
34363
|
+
evaluatedPage.pageRef,
|
|
34364
|
+
evaluatedPage.currentUrl,
|
|
34365
|
+
evaluatedPage.openerPageRef
|
|
34366
|
+
);
|
|
34367
|
+
}
|
|
34368
|
+
const focusedPage = input.evaluatedPages.find((page) => page.focused);
|
|
34369
|
+
if (focusedPage !== void 0 && focusedPage.pageId !== this.focusedPageId) {
|
|
34370
|
+
actions.push({
|
|
34371
|
+
kind: "switch-tab",
|
|
34372
|
+
timestamp: Math.max(
|
|
34373
|
+
0,
|
|
34374
|
+
(firstEventTimestampByPage.get(focusedPage.pageRef) ?? input.pollTimestamp) - 1
|
|
34375
|
+
),
|
|
34376
|
+
pageId: focusedPage.pageId,
|
|
34377
|
+
pageUrl: focusedPage.currentUrl,
|
|
34378
|
+
detail: {
|
|
34379
|
+
kind: "switch-tab",
|
|
34380
|
+
...this.focusedPageId === void 0 ? {} : { fromPageId: this.focusedPageId },
|
|
34381
|
+
toPageId: focusedPage.pageId
|
|
34382
|
+
}
|
|
34383
|
+
});
|
|
34384
|
+
this.focusedPageId = focusedPage.pageId;
|
|
34385
|
+
}
|
|
34386
|
+
return dedupeConsecutiveSwitchActions(actions);
|
|
34387
|
+
}
|
|
34388
|
+
normalizeRawEvent(event, page, currentUrl) {
|
|
34389
|
+
switch (event.kind) {
|
|
34390
|
+
case "navigate":
|
|
34391
|
+
return [
|
|
34392
|
+
{
|
|
34393
|
+
kind: "navigate",
|
|
34394
|
+
timestamp: event.timestamp,
|
|
34395
|
+
pageId: page.pageId,
|
|
34396
|
+
pageUrl: event.url,
|
|
34397
|
+
detail: {
|
|
34398
|
+
kind: "navigate",
|
|
34399
|
+
url: event.url,
|
|
34400
|
+
source: event.source
|
|
34401
|
+
}
|
|
34402
|
+
}
|
|
34403
|
+
];
|
|
34404
|
+
case "click":
|
|
34405
|
+
return [
|
|
34406
|
+
{
|
|
34407
|
+
kind: "click",
|
|
34408
|
+
timestamp: event.timestamp,
|
|
34409
|
+
pageId: page.pageId,
|
|
34410
|
+
pageUrl: currentUrl,
|
|
34411
|
+
selector: event.selector,
|
|
34412
|
+
detail: {
|
|
34413
|
+
kind: "click",
|
|
34414
|
+
button: event.button,
|
|
34415
|
+
modifiers: event.modifiers
|
|
34416
|
+
}
|
|
34417
|
+
}
|
|
34418
|
+
];
|
|
34419
|
+
case "dblclick":
|
|
34420
|
+
return [
|
|
34421
|
+
{
|
|
34422
|
+
kind: "dblclick",
|
|
34423
|
+
timestamp: event.timestamp,
|
|
34424
|
+
pageId: page.pageId,
|
|
34425
|
+
pageUrl: currentUrl,
|
|
34426
|
+
selector: event.selector,
|
|
34427
|
+
detail: {
|
|
34428
|
+
kind: "dblclick"
|
|
34429
|
+
}
|
|
34430
|
+
}
|
|
34431
|
+
];
|
|
34432
|
+
case "type":
|
|
34433
|
+
return [
|
|
34434
|
+
{
|
|
34435
|
+
kind: "type",
|
|
34436
|
+
timestamp: event.timestamp,
|
|
34437
|
+
pageId: page.pageId,
|
|
34438
|
+
pageUrl: currentUrl,
|
|
34439
|
+
selector: event.selector,
|
|
34440
|
+
detail: {
|
|
34441
|
+
kind: "type",
|
|
34442
|
+
text: event.text
|
|
34443
|
+
}
|
|
34444
|
+
}
|
|
34445
|
+
];
|
|
34446
|
+
case "keypress":
|
|
34447
|
+
return [
|
|
34448
|
+
{
|
|
34449
|
+
kind: "keypress",
|
|
34450
|
+
timestamp: event.timestamp,
|
|
34451
|
+
pageId: page.pageId,
|
|
34452
|
+
pageUrl: currentUrl,
|
|
34453
|
+
...event.selector === void 0 ? {} : { selector: event.selector },
|
|
34454
|
+
detail: {
|
|
34455
|
+
kind: "keypress",
|
|
34456
|
+
key: event.key,
|
|
34457
|
+
modifiers: event.modifiers
|
|
34458
|
+
}
|
|
34459
|
+
}
|
|
34460
|
+
];
|
|
34461
|
+
case "scroll":
|
|
34462
|
+
return [
|
|
34463
|
+
{
|
|
34464
|
+
kind: "scroll",
|
|
34465
|
+
timestamp: event.timestamp,
|
|
34466
|
+
pageId: page.pageId,
|
|
34467
|
+
pageUrl: currentUrl,
|
|
34468
|
+
...event.selector === void 0 ? {} : { selector: event.selector },
|
|
34469
|
+
detail: {
|
|
34470
|
+
kind: "scroll",
|
|
34471
|
+
deltaX: event.deltaX,
|
|
34472
|
+
deltaY: event.deltaY
|
|
34473
|
+
}
|
|
34474
|
+
}
|
|
34475
|
+
];
|
|
34476
|
+
case "select-option":
|
|
34477
|
+
return [
|
|
34478
|
+
{
|
|
34479
|
+
kind: "select-option",
|
|
34480
|
+
timestamp: event.timestamp,
|
|
34481
|
+
pageId: page.pageId,
|
|
34482
|
+
pageUrl: currentUrl,
|
|
34483
|
+
selector: event.selector,
|
|
34484
|
+
detail: {
|
|
34485
|
+
kind: "select-option",
|
|
34486
|
+
value: event.value,
|
|
34487
|
+
...event.label === void 0 ? {} : { label: event.label }
|
|
34488
|
+
}
|
|
34489
|
+
}
|
|
34490
|
+
];
|
|
34491
|
+
case "reload":
|
|
34492
|
+
return [
|
|
34493
|
+
{
|
|
34494
|
+
kind: "reload",
|
|
34495
|
+
timestamp: event.timestamp,
|
|
34496
|
+
pageId: page.pageId,
|
|
34497
|
+
pageUrl: event.url,
|
|
34498
|
+
detail: {
|
|
34499
|
+
kind: "reload",
|
|
34500
|
+
url: event.url
|
|
34501
|
+
}
|
|
34502
|
+
}
|
|
34503
|
+
];
|
|
34504
|
+
case "go-back":
|
|
34505
|
+
return [
|
|
34506
|
+
{
|
|
34507
|
+
kind: "go-back",
|
|
34508
|
+
timestamp: event.timestamp,
|
|
34509
|
+
pageId: page.pageId,
|
|
34510
|
+
pageUrl: event.url,
|
|
34511
|
+
detail: {
|
|
34512
|
+
kind: "go-back",
|
|
34513
|
+
url: event.url
|
|
34514
|
+
}
|
|
34515
|
+
}
|
|
34516
|
+
];
|
|
34517
|
+
case "go-forward":
|
|
34518
|
+
return [
|
|
34519
|
+
{
|
|
34520
|
+
kind: "go-forward",
|
|
34521
|
+
timestamp: event.timestamp,
|
|
34522
|
+
pageId: page.pageId,
|
|
34523
|
+
pageUrl: event.url,
|
|
34524
|
+
detail: {
|
|
34525
|
+
kind: "go-forward",
|
|
34526
|
+
url: event.url
|
|
34527
|
+
}
|
|
34528
|
+
}
|
|
34529
|
+
];
|
|
34530
|
+
}
|
|
34531
|
+
}
|
|
34532
|
+
ensureKnownPage(pageRef, url, openerPageRef) {
|
|
34533
|
+
const existing = this.pages.get(pageRef);
|
|
34534
|
+
if (existing !== void 0) {
|
|
34535
|
+
return existing;
|
|
34536
|
+
}
|
|
34537
|
+
const openerPageId = openerPageRef === void 0 ? void 0 : this.pages.get(openerPageRef)?.pageId;
|
|
34538
|
+
const page = {
|
|
34539
|
+
pageId: `page${String(this.nextPageOrdinal++)}`,
|
|
34540
|
+
pageRef,
|
|
34541
|
+
currentUrl: url,
|
|
34542
|
+
...openerPageRef === void 0 ? {} : { openerPageRef },
|
|
34543
|
+
...openerPageId === void 0 ? {} : { openerPageId }
|
|
34544
|
+
};
|
|
34545
|
+
this.pages.set(pageRef, page);
|
|
34546
|
+
return page;
|
|
34547
|
+
}
|
|
34548
|
+
updateKnownPage(pageRef, url, openerPageRef) {
|
|
34549
|
+
const current = this.pages.get(pageRef);
|
|
34550
|
+
if (current === void 0) {
|
|
34551
|
+
this.ensureKnownPage(pageRef, url, openerPageRef);
|
|
34552
|
+
return;
|
|
34553
|
+
}
|
|
34554
|
+
const openerPageId = openerPageRef === void 0 ? void 0 : this.pages.get(openerPageRef)?.pageId;
|
|
34555
|
+
this.pages.set(pageRef, {
|
|
34556
|
+
...current,
|
|
34557
|
+
currentUrl: url,
|
|
34558
|
+
...openerPageRef === void 0 ? {} : { openerPageRef },
|
|
34559
|
+
...openerPageId === void 0 ? {} : { openerPageId }
|
|
34560
|
+
});
|
|
34561
|
+
}
|
|
34562
|
+
};
|
|
34563
|
+
function normalizeSnapshot(value, fallbackUrl) {
|
|
34564
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
34565
|
+
return {
|
|
34566
|
+
url: fallbackUrl,
|
|
34567
|
+
focused: false,
|
|
34568
|
+
visibilityState: "hidden",
|
|
34569
|
+
stopRequested: false,
|
|
34570
|
+
events: []
|
|
34571
|
+
};
|
|
34572
|
+
}
|
|
34573
|
+
const snapshot = value;
|
|
34574
|
+
return {
|
|
34575
|
+
url: typeof snapshot.url === "string" ? snapshot.url : fallbackUrl,
|
|
34576
|
+
focused: snapshot.focused === true,
|
|
34577
|
+
visibilityState: snapshot.visibilityState === "visible" || snapshot.visibilityState === "prerender" || snapshot.visibilityState === "hidden" ? snapshot.visibilityState : "hidden",
|
|
34578
|
+
stopRequested: snapshot.stopRequested === true,
|
|
34579
|
+
events: Array.isArray(snapshot.events) ? snapshot.events : []
|
|
34580
|
+
};
|
|
34581
|
+
}
|
|
34582
|
+
function dedupeConsecutiveSwitchActions(actions) {
|
|
34583
|
+
const deduped = [];
|
|
34584
|
+
for (const action of actions) {
|
|
34585
|
+
const previous = deduped[deduped.length - 1];
|
|
34586
|
+
if (previous?.kind === "switch-tab" && action.kind === "switch-tab" && previous.pageId === action.pageId) {
|
|
34587
|
+
continue;
|
|
34588
|
+
}
|
|
34589
|
+
deduped.push(action);
|
|
34590
|
+
}
|
|
34591
|
+
return deduped;
|
|
34592
|
+
}
|
|
34593
|
+
function actionSortPriority(kind) {
|
|
34594
|
+
switch (kind) {
|
|
34595
|
+
case "new-tab":
|
|
34596
|
+
return 0;
|
|
34597
|
+
case "switch-tab":
|
|
34598
|
+
return 1;
|
|
34599
|
+
case "navigate":
|
|
34600
|
+
case "go-back":
|
|
34601
|
+
case "go-forward":
|
|
34602
|
+
case "reload":
|
|
34603
|
+
return 2;
|
|
34604
|
+
case "click":
|
|
34605
|
+
case "dblclick":
|
|
34606
|
+
return 3;
|
|
34607
|
+
case "type":
|
|
34608
|
+
case "keypress":
|
|
34609
|
+
case "select-option":
|
|
34610
|
+
return 4;
|
|
34611
|
+
case "scroll":
|
|
34612
|
+
return 5;
|
|
34613
|
+
case "close-tab":
|
|
34614
|
+
return 6;
|
|
34615
|
+
}
|
|
34616
|
+
}
|
|
34617
|
+
function comparePageIds(left, right) {
|
|
34618
|
+
const leftMatch = /^page(\d+)$/u.exec(left);
|
|
34619
|
+
const rightMatch = /^page(\d+)$/u.exec(right);
|
|
34620
|
+
if (leftMatch && rightMatch) {
|
|
34621
|
+
return Number(leftMatch[1]) - Number(rightMatch[1]);
|
|
34622
|
+
}
|
|
34623
|
+
return left.localeCompare(right);
|
|
34624
|
+
}
|
|
34625
|
+
function delay2(ms) {
|
|
34626
|
+
return new Promise((resolve4) => setTimeout(resolve4, ms));
|
|
34627
|
+
}
|
|
34628
|
+
|
|
34629
|
+
// ../runtime-core/src/recorder/codegen.ts
|
|
34630
|
+
var DISPATCH_KEY_SCRIPT = String.raw`(key) => {
|
|
34631
|
+
const target = document.activeElement;
|
|
34632
|
+
if (!(target instanceof Element)) {
|
|
34633
|
+
throw new Error("No active element is available for key replay.");
|
|
34634
|
+
}
|
|
34635
|
+
const normalizedKey = String(key);
|
|
34636
|
+
const eventInit = {
|
|
34637
|
+
key: normalizedKey,
|
|
34638
|
+
code: normalizedKey,
|
|
34639
|
+
bubbles: true,
|
|
34640
|
+
cancelable: true,
|
|
34641
|
+
};
|
|
34642
|
+
target.dispatchEvent(new KeyboardEvent("keydown", eventInit));
|
|
34643
|
+
target.dispatchEvent(new KeyboardEvent("keyup", eventInit));
|
|
34644
|
+
}`;
|
|
34645
|
+
var SELECT_OPTION_SCRIPT = String.raw`(selector, value) => {
|
|
34646
|
+
const target = document.querySelector(String(selector));
|
|
34647
|
+
if (!(target instanceof HTMLSelectElement)) {
|
|
34648
|
+
throw new Error("Unable to find a <select> element for option replay.");
|
|
34649
|
+
}
|
|
34650
|
+
target.value = String(value);
|
|
34651
|
+
target.dispatchEvent(new Event("input", { bubbles: true }));
|
|
34652
|
+
target.dispatchEvent(new Event("change", { bubbles: true }));
|
|
34653
|
+
}`;
|
|
34654
|
+
var HISTORY_ACTION_SCRIPT = String.raw`(action) => {
|
|
34655
|
+
const normalized = String(action);
|
|
34656
|
+
if (normalized === "back") {
|
|
34657
|
+
history.back();
|
|
34658
|
+
return;
|
|
34659
|
+
}
|
|
34660
|
+
if (normalized === "forward") {
|
|
34661
|
+
history.forward();
|
|
34662
|
+
return;
|
|
34663
|
+
}
|
|
34664
|
+
if (normalized === "reload") {
|
|
34665
|
+
location.reload();
|
|
34666
|
+
return;
|
|
34667
|
+
}
|
|
34668
|
+
throw new Error("Unsupported history action: " + normalized);
|
|
34669
|
+
}`;
|
|
34670
|
+
var WINDOW_SCROLL_SCRIPT = String.raw`(deltaX, deltaY) => {
|
|
34671
|
+
window.scrollBy(Number(deltaX), Number(deltaY));
|
|
34672
|
+
}`;
|
|
34673
|
+
function generateReplayScript(options) {
|
|
34674
|
+
const replayTarget = resolveReplayTarget(options);
|
|
34675
|
+
const initialPages = orderInitialPages(resolveInitialPages(options));
|
|
34676
|
+
const activeInitialPageId = resolveActiveInitialPageId(options, initialPages);
|
|
34677
|
+
const initialPageId = initialPages[0]?.pageId ?? "page0";
|
|
34678
|
+
const lines = [
|
|
34679
|
+
`import { Opensteer } from "opensteer";`,
|
|
34680
|
+
``,
|
|
34681
|
+
...renderOpensteerBootstrap(replayTarget),
|
|
34682
|
+
``,
|
|
34683
|
+
`const ${initialPageId} = (await opensteer.open(${JSON.stringify(initialPages[0]?.initialUrl ?? "")})).pageRef;`,
|
|
34684
|
+
`let activePageRef: string | undefined = ${initialPageId};`,
|
|
34685
|
+
``,
|
|
34686
|
+
`async function ensureActive(pageRef: string): Promise<void> {`,
|
|
34687
|
+
` if (activePageRef === pageRef) {`,
|
|
34688
|
+
` return;`,
|
|
34689
|
+
` }`,
|
|
34690
|
+
` await opensteer.activatePage({ pageRef });`,
|
|
34691
|
+
` activePageRef = pageRef;`,
|
|
34692
|
+
`}`,
|
|
34693
|
+
``,
|
|
34694
|
+
`async function dispatchKey(pageRef: string, key: string): Promise<void> {`,
|
|
34695
|
+
` await ensureActive(pageRef);`,
|
|
34696
|
+
` await opensteer.evaluate({`,
|
|
34697
|
+
` pageRef,`,
|
|
34698
|
+
` script: ${JSON.stringify(DISPATCH_KEY_SCRIPT)},`,
|
|
34699
|
+
` args: [key],`,
|
|
34700
|
+
` });`,
|
|
34701
|
+
`}`,
|
|
34702
|
+
``,
|
|
34703
|
+
`async function selectOption(pageRef: string, selector: string, value: string): Promise<void> {`,
|
|
34704
|
+
` await ensureActive(pageRef);`,
|
|
34705
|
+
` await opensteer.evaluate({`,
|
|
34706
|
+
` pageRef,`,
|
|
34707
|
+
` script: ${JSON.stringify(SELECT_OPTION_SCRIPT)},`,
|
|
34708
|
+
` args: [selector, value],`,
|
|
34709
|
+
` });`,
|
|
34710
|
+
`}`,
|
|
34711
|
+
``,
|
|
34712
|
+
`async function runHistoryAction(pageRef: string, action: "back" | "forward" | "reload"): Promise<void> {`,
|
|
34713
|
+
` await ensureActive(pageRef);`,
|
|
34714
|
+
` await opensteer.evaluate({`,
|
|
34715
|
+
` pageRef,`,
|
|
34716
|
+
` script: ${JSON.stringify(HISTORY_ACTION_SCRIPT)},`,
|
|
34717
|
+
` args: [action],`,
|
|
34718
|
+
` });`,
|
|
34719
|
+
`}`,
|
|
34720
|
+
``,
|
|
34721
|
+
`try {`
|
|
34722
|
+
];
|
|
34723
|
+
const declaredPages = /* @__PURE__ */ new Set([initialPageId]);
|
|
34724
|
+
for (const page of initialPages.slice(1)) {
|
|
34725
|
+
const openerPageId = page.openerPageId !== void 0 && declaredPages.has(page.openerPageId) ? page.openerPageId : void 0;
|
|
34726
|
+
lines.push(
|
|
34727
|
+
` const ${page.pageId} = (await opensteer.newPage(${renderNewPageInput(openerPageId, page.initialUrl)})).pageRef;`
|
|
34728
|
+
);
|
|
34729
|
+
lines.push(` activePageRef = ${page.pageId};`);
|
|
34730
|
+
declaredPages.add(page.pageId);
|
|
34731
|
+
}
|
|
34732
|
+
if (activeInitialPageId !== void 0 && activeInitialPageId !== initialPageId) {
|
|
34733
|
+
lines.push(` await ensureActive(${activeInitialPageId});`);
|
|
34734
|
+
}
|
|
34735
|
+
for (let index = 0; index < options.actions.length; index += 1) {
|
|
34736
|
+
const action = options.actions[index];
|
|
34737
|
+
const pageVar = action.pageId;
|
|
34738
|
+
if (action.kind === "type") {
|
|
34739
|
+
const nextAction = options.actions[index + 1];
|
|
34740
|
+
const mergedPressEnter = nextAction?.kind === "keypress" && nextAction.pageId === action.pageId && nextAction.selector === action.selector && nextAction.detail.kind === "keypress" && nextAction.detail.key === "Enter" && nextAction.detail.modifiers.length === 0;
|
|
34741
|
+
lines.push(` await ensureActive(${pageVar});`);
|
|
34742
|
+
lines.push(
|
|
34743
|
+
` await opensteer.input({ selector: ${JSON.stringify(requireSelector(action))}, text: ${JSON.stringify(action.detail.text)}${mergedPressEnter ? `, pressEnter: true` : ``} });`
|
|
34744
|
+
);
|
|
34745
|
+
if (mergedPressEnter) {
|
|
34746
|
+
index += 1;
|
|
34747
|
+
}
|
|
34748
|
+
continue;
|
|
34749
|
+
}
|
|
34750
|
+
if (action.kind === "switch-tab" && options.actions[index - 1]?.kind === "new-tab" && options.actions[index - 1]?.pageId === action.pageId) {
|
|
34751
|
+
continue;
|
|
34752
|
+
}
|
|
34753
|
+
switch (action.kind) {
|
|
34754
|
+
case "navigate":
|
|
34755
|
+
lines.push(` await ensureActive(${pageVar});`);
|
|
34756
|
+
lines.push(` await opensteer.goto(${JSON.stringify(action.detail.url)});`);
|
|
34757
|
+
break;
|
|
34758
|
+
case "click":
|
|
34759
|
+
lines.push(` await ensureActive(${pageVar});`);
|
|
34760
|
+
lines.push(
|
|
34761
|
+
` await opensteer.click({ selector: ${JSON.stringify(requireSelector(action))} });`
|
|
34762
|
+
);
|
|
34763
|
+
break;
|
|
34764
|
+
case "dblclick":
|
|
34765
|
+
lines.push(` await ensureActive(${pageVar});`);
|
|
34766
|
+
lines.push(
|
|
34767
|
+
` await opensteer.click({ selector: ${JSON.stringify(requireSelector(action))}, clickCount: 2 });`
|
|
34768
|
+
);
|
|
34769
|
+
break;
|
|
34770
|
+
case "keypress":
|
|
34771
|
+
lines.push(` await dispatchKey(${pageVar}, ${JSON.stringify(action.detail.key)});`);
|
|
34772
|
+
break;
|
|
34773
|
+
case "scroll": {
|
|
34774
|
+
const { direction, amount, isWindowScroll } = normalizeScrollAction(action);
|
|
34775
|
+
lines.push(` await ensureActive(${pageVar});`);
|
|
34776
|
+
if (isWindowScroll) {
|
|
34777
|
+
lines.push(` await opensteer.evaluate({`);
|
|
34778
|
+
lines.push(` pageRef: ${pageVar},`);
|
|
34779
|
+
lines.push(` script: ${JSON.stringify(WINDOW_SCROLL_SCRIPT)},`);
|
|
34780
|
+
lines.push(
|
|
34781
|
+
` args: [${String(action.detail.deltaX)}, ${String(action.detail.deltaY)}],`
|
|
34782
|
+
);
|
|
34783
|
+
lines.push(` });`);
|
|
34784
|
+
} else {
|
|
34785
|
+
lines.push(
|
|
34786
|
+
` await opensteer.scroll({ selector: ${JSON.stringify(requireSelector(action))}, direction: ${JSON.stringify(direction)}, amount: ${String(amount)} });`
|
|
34787
|
+
);
|
|
34788
|
+
}
|
|
34789
|
+
break;
|
|
34790
|
+
}
|
|
34791
|
+
case "select-option":
|
|
34792
|
+
lines.push(
|
|
34793
|
+
` await selectOption(${pageVar}, ${JSON.stringify(requireSelector(action))}, ${JSON.stringify(action.detail.value)});`
|
|
34794
|
+
);
|
|
34795
|
+
break;
|
|
34796
|
+
case "new-tab": {
|
|
34797
|
+
const openerPageVar = action.detail.openerPageId;
|
|
34798
|
+
const shouldUseWaitForPage = shouldUsePopupWait(options.actions, index, openerPageVar);
|
|
34799
|
+
const creationLine = shouldUseWaitForPage && openerPageVar !== void 0 ? ` const ${pageVar} = (await opensteer.waitForPage({ openerPageRef: ${openerPageVar}, timeoutMs: 30_000 })).pageRef;` : ` const ${pageVar} = (await opensteer.newPage(${renderNewPageInput(action.detail.openerPageId, action.detail.initialUrl)})).pageRef;`;
|
|
34800
|
+
lines.push(creationLine);
|
|
34801
|
+
lines.push(` activePageRef = ${pageVar};`);
|
|
34802
|
+
declaredPages.add(pageVar);
|
|
34803
|
+
break;
|
|
34804
|
+
}
|
|
34805
|
+
case "close-tab":
|
|
34806
|
+
lines.push(` await opensteer.closePage({ pageRef: ${pageVar} });`);
|
|
34807
|
+
lines.push(` if (activePageRef === ${pageVar}) {`);
|
|
34808
|
+
lines.push(` activePageRef = undefined;`);
|
|
34809
|
+
lines.push(` }`);
|
|
34810
|
+
break;
|
|
34811
|
+
case "switch-tab":
|
|
34812
|
+
lines.push(` await ensureActive(${pageVar});`);
|
|
34813
|
+
break;
|
|
34814
|
+
case "go-back":
|
|
34815
|
+
lines.push(` await runHistoryAction(${pageVar}, "back");`);
|
|
34816
|
+
break;
|
|
34817
|
+
case "go-forward":
|
|
34818
|
+
lines.push(` await runHistoryAction(${pageVar}, "forward");`);
|
|
34819
|
+
break;
|
|
34820
|
+
case "reload":
|
|
34821
|
+
lines.push(` await runHistoryAction(${pageVar}, "reload");`);
|
|
34822
|
+
break;
|
|
34823
|
+
}
|
|
34824
|
+
}
|
|
34825
|
+
lines.push(`} finally {`);
|
|
34826
|
+
lines.push(` await opensteer.close();`);
|
|
34827
|
+
lines.push(`}`);
|
|
34828
|
+
lines.push(``);
|
|
34829
|
+
return `${lines.join("\n")}
|
|
34830
|
+
`;
|
|
34831
|
+
}
|
|
34832
|
+
function resolveReplayTarget(options) {
|
|
34833
|
+
if (options.replayTarget !== void 0) {
|
|
34834
|
+
return options.replayTarget;
|
|
34835
|
+
}
|
|
34836
|
+
if (options.workspace !== void 0) {
|
|
34837
|
+
return {
|
|
34838
|
+
kind: "local",
|
|
34839
|
+
workspace: options.workspace
|
|
34840
|
+
};
|
|
34841
|
+
}
|
|
34842
|
+
throw new Error("Replay codegen requires either replayTarget or workspace.");
|
|
34843
|
+
}
|
|
34844
|
+
function resolveInitialPages(options) {
|
|
34845
|
+
if (options.initialPages !== void 0 && options.initialPages.length > 0) {
|
|
34846
|
+
const unique = /* @__PURE__ */ new Set();
|
|
34847
|
+
return options.initialPages.map((page) => {
|
|
34848
|
+
if (unique.has(page.pageId)) {
|
|
34849
|
+
throw new Error(`Duplicate initial page id "${page.pageId}" in recording bootstrap.`);
|
|
34850
|
+
}
|
|
34851
|
+
unique.add(page.pageId);
|
|
34852
|
+
return page;
|
|
34853
|
+
});
|
|
34854
|
+
}
|
|
34855
|
+
const startUrl = options.startUrl;
|
|
34856
|
+
if (startUrl === void 0) {
|
|
34857
|
+
throw new Error("Replay codegen requires startUrl when initialPages is not provided.");
|
|
34858
|
+
}
|
|
34859
|
+
return [
|
|
34860
|
+
{
|
|
34861
|
+
pageId: "page0",
|
|
34862
|
+
initialUrl: startUrl
|
|
34863
|
+
}
|
|
34864
|
+
];
|
|
34865
|
+
}
|
|
34866
|
+
function resolveActiveInitialPageId(options, initialPages) {
|
|
34867
|
+
if (options.activePageId !== void 0) {
|
|
34868
|
+
return options.activePageId;
|
|
34869
|
+
}
|
|
34870
|
+
return initialPages[0]?.pageId;
|
|
34871
|
+
}
|
|
34872
|
+
function renderOpensteerBootstrap(replayTarget) {
|
|
34873
|
+
if (replayTarget.kind === "local") {
|
|
34874
|
+
return [
|
|
34875
|
+
`const opensteer = new Opensteer({`,
|
|
34876
|
+
` workspace: ${JSON.stringify(replayTarget.workspace)},`,
|
|
34877
|
+
` browser: "persistent",`,
|
|
34878
|
+
`});`
|
|
34879
|
+
];
|
|
34880
|
+
}
|
|
34881
|
+
return [
|
|
34882
|
+
renderRequireEnvHelper(replayTarget),
|
|
34883
|
+
``,
|
|
34884
|
+
`const opensteer = new Opensteer({`,
|
|
34885
|
+
` provider: {`,
|
|
34886
|
+
` mode: "cloud",`,
|
|
34887
|
+
` baseUrl: requireEnv(${JSON.stringify(replayTarget.baseUrlEnvVar ?? "OPENSTEER_BASE_URL")}),`,
|
|
34888
|
+
` apiKey: requireEnv(${JSON.stringify(replayTarget.apiKeyEnvVar ?? "OPENSTEER_API_KEY")}),`,
|
|
34889
|
+
...renderCloudBrowserProfile(replayTarget),
|
|
34890
|
+
` },`,
|
|
34891
|
+
`});`
|
|
34892
|
+
];
|
|
34893
|
+
}
|
|
34894
|
+
function renderRequireEnvHelper(replayTarget) {
|
|
34895
|
+
const baseUrlEnvVar = replayTarget.baseUrlEnvVar ?? "OPENSTEER_BASE_URL";
|
|
34896
|
+
const apiKeyEnvVar = replayTarget.apiKeyEnvVar ?? "OPENSTEER_API_KEY";
|
|
34897
|
+
return [
|
|
34898
|
+
`function requireEnv(name: string): string {`,
|
|
34899
|
+
` const value = process.env[name];`,
|
|
34900
|
+
` if (typeof value === "string" && value.trim().length > 0) {`,
|
|
34901
|
+
` return value;`,
|
|
34902
|
+
` }`,
|
|
34903
|
+
` throw new Error(\`Missing environment variable \${name}. Set ${baseUrlEnvVar} and ${apiKeyEnvVar} before replaying this recording.\`);`,
|
|
34904
|
+
`}`
|
|
34905
|
+
].join("\n");
|
|
34906
|
+
}
|
|
34907
|
+
function renderCloudBrowserProfile(replayTarget) {
|
|
34908
|
+
if (replayTarget.browserProfileId === void 0) {
|
|
34909
|
+
return [];
|
|
34910
|
+
}
|
|
34911
|
+
return [
|
|
34912
|
+
` browserProfile: {`,
|
|
34913
|
+
` profileId: ${JSON.stringify(replayTarget.browserProfileId)},`,
|
|
34914
|
+
...replayTarget.reuseBrowserProfileIfActive ? [` reuseIfActive: true,`] : [],
|
|
34915
|
+
` },`
|
|
34916
|
+
];
|
|
34917
|
+
}
|
|
34918
|
+
function orderInitialPages(initialPages) {
|
|
34919
|
+
const ordered = [];
|
|
34920
|
+
const declared = /* @__PURE__ */ new Set();
|
|
34921
|
+
const remaining = [...initialPages];
|
|
34922
|
+
while (remaining.length > 0) {
|
|
34923
|
+
let advanced = false;
|
|
34924
|
+
for (let index = 0; index < remaining.length; index += 1) {
|
|
34925
|
+
const page = remaining[index];
|
|
34926
|
+
if (page.openerPageId !== void 0 && !declared.has(page.openerPageId)) {
|
|
34927
|
+
continue;
|
|
34928
|
+
}
|
|
34929
|
+
ordered.push(page);
|
|
34930
|
+
declared.add(page.pageId);
|
|
34931
|
+
remaining.splice(index, 1);
|
|
34932
|
+
advanced = true;
|
|
34933
|
+
break;
|
|
34934
|
+
}
|
|
34935
|
+
if (!advanced) {
|
|
34936
|
+
ordered.push(...remaining.splice(0, remaining.length));
|
|
34937
|
+
}
|
|
34938
|
+
}
|
|
34939
|
+
return ordered;
|
|
34940
|
+
}
|
|
34941
|
+
function requireSelector(action) {
|
|
34942
|
+
if (action.selector === void 0) {
|
|
34943
|
+
throw new Error(`Action "${action.kind}" on ${action.pageId} is missing a selector.`);
|
|
34944
|
+
}
|
|
34945
|
+
return action.selector;
|
|
34946
|
+
}
|
|
34947
|
+
function normalizeScrollAction(action) {
|
|
34948
|
+
const horizontal = Math.abs(action.detail.deltaX);
|
|
34949
|
+
const vertical = Math.abs(action.detail.deltaY);
|
|
34950
|
+
if (vertical >= horizontal) {
|
|
34951
|
+
return {
|
|
34952
|
+
direction: action.detail.deltaY < 0 ? "up" : "down",
|
|
34953
|
+
amount: Math.max(1, Math.round(vertical)),
|
|
34954
|
+
isWindowScroll: action.selector === void 0
|
|
34955
|
+
};
|
|
34956
|
+
}
|
|
34957
|
+
return {
|
|
34958
|
+
direction: action.detail.deltaX < 0 ? "left" : "right",
|
|
34959
|
+
amount: Math.max(1, Math.round(horizontal)),
|
|
34960
|
+
isWindowScroll: action.selector === void 0
|
|
34961
|
+
};
|
|
34962
|
+
}
|
|
34963
|
+
function shouldUsePopupWait(actions, newTabIndex, openerPageId) {
|
|
34964
|
+
if (openerPageId === void 0 || newTabIndex === 0) {
|
|
34965
|
+
return false;
|
|
34966
|
+
}
|
|
34967
|
+
const previousAction = actions[newTabIndex - 1];
|
|
34968
|
+
if (previousAction === void 0 || previousAction.pageId !== openerPageId) {
|
|
34969
|
+
return false;
|
|
34970
|
+
}
|
|
34971
|
+
return previousAction.kind === "click" || previousAction.kind === "dblclick" || previousAction.kind === "keypress";
|
|
34972
|
+
}
|
|
34973
|
+
function renderNewPageInput(openerPageId, initialUrl) {
|
|
34974
|
+
const argumentsList = [];
|
|
34975
|
+
if (openerPageId !== void 0) {
|
|
34976
|
+
argumentsList.push(`openerPageRef: ${openerPageId}`);
|
|
34977
|
+
}
|
|
34978
|
+
if (initialUrl.length > 0 && initialUrl !== "about:blank") {
|
|
34979
|
+
argumentsList.push(`url: ${JSON.stringify(initialUrl)}`);
|
|
34980
|
+
}
|
|
34981
|
+
if (argumentsList.length === 0) {
|
|
34982
|
+
return `{}`;
|
|
34983
|
+
}
|
|
34984
|
+
return `{ ${argumentsList.join(", ")} }`;
|
|
34985
|
+
}
|
|
33140
34986
|
var OpensteerSemanticRestError = class extends Error {
|
|
33141
34987
|
opensteerError;
|
|
33142
34988
|
statusCode;
|
|
@@ -33152,6 +34998,9 @@ var OpensteerSemanticRestClient = class {
|
|
|
33152
34998
|
this.connection = connection;
|
|
33153
34999
|
}
|
|
33154
35000
|
async invoke(operation, input) {
|
|
35001
|
+
return this.invokeInternal(operation, input, false);
|
|
35002
|
+
}
|
|
35003
|
+
async invokeInternal(operation, input, hasRetried) {
|
|
33155
35004
|
const endpoint = opensteerSemanticRestEndpoints.find((entry) => entry.name === operation);
|
|
33156
35005
|
if (!endpoint) {
|
|
33157
35006
|
throw new Error(`unsupported semantic operation ${operation}`);
|
|
@@ -33161,7 +35010,7 @@ var OpensteerSemanticRestClient = class {
|
|
|
33161
35010
|
});
|
|
33162
35011
|
let response;
|
|
33163
35012
|
try {
|
|
33164
|
-
response = await fetch(`${this.connection.
|
|
35013
|
+
response = await fetch(`${await this.connection.getBaseUrl()}${endpoint.path}`, {
|
|
33165
35014
|
method: "POST",
|
|
33166
35015
|
headers: {
|
|
33167
35016
|
authorization: await this.connection.getAuthorizationHeader(),
|
|
@@ -33183,6 +35032,9 @@ var OpensteerSemanticRestClient = class {
|
|
|
33183
35032
|
}
|
|
33184
35033
|
return envelope.data;
|
|
33185
35034
|
} catch (error) {
|
|
35035
|
+
if (!hasRetried && this.connection.handleError && await this.connection.handleError(error, { operation })) {
|
|
35036
|
+
return this.invokeInternal(operation, input, true);
|
|
35037
|
+
}
|
|
33186
35038
|
if (operation === "session.close" && isFetchFailure(error)) {
|
|
33187
35039
|
return { closed: true };
|
|
33188
35040
|
}
|
|
@@ -33316,7 +35168,10 @@ var OpensteerCloudAutomationClient = class {
|
|
|
33316
35168
|
}
|
|
33317
35169
|
async connect() {
|
|
33318
35170
|
const grant = await this.issueGrant("automation");
|
|
33319
|
-
|
|
35171
|
+
if (grant.transport !== "ws") {
|
|
35172
|
+
throw new Error(`cloud issued an invalid ${grant.kind} grant transport`);
|
|
35173
|
+
}
|
|
35174
|
+
const wsUrl = new URL(grant.url);
|
|
33320
35175
|
wsUrl.searchParams.set("token", grant.token);
|
|
33321
35176
|
const socket = new WebSocket2__default.default(wsUrl);
|
|
33322
35177
|
this.socket = socket;
|
|
@@ -33681,7 +35536,7 @@ var CloudSessionProxy = class {
|
|
|
33681
35536
|
cloud;
|
|
33682
35537
|
observability;
|
|
33683
35538
|
sessionId;
|
|
33684
|
-
|
|
35539
|
+
semanticGrant;
|
|
33685
35540
|
client;
|
|
33686
35541
|
automation;
|
|
33687
35542
|
workspaceStore;
|
|
@@ -33732,7 +35587,7 @@ var CloudSessionProxy = class {
|
|
|
33732
35587
|
reconnectable: this.workspace !== void 0 || this.sessionId !== void 0 || persisted !== void 0,
|
|
33733
35588
|
capabilities: {
|
|
33734
35589
|
semanticOperations: opensteerSemanticOperationNames,
|
|
33735
|
-
sessionGrants: ["automation", "view", "cdp"],
|
|
35590
|
+
sessionGrants: ["semantic", "automation", "view", "cdp"],
|
|
33736
35591
|
instrumentation: {
|
|
33737
35592
|
route: true,
|
|
33738
35593
|
interceptScript: true,
|
|
@@ -33974,13 +35829,12 @@ var CloudSessionProxy = class {
|
|
|
33974
35829
|
return this.requireClient().invoke("computer.execute", input);
|
|
33975
35830
|
}
|
|
33976
35831
|
async close() {
|
|
33977
|
-
const session = await this.loadPersistedSession() ?? (this.sessionId === void 0
|
|
35832
|
+
const session = await this.loadPersistedSession() ?? (this.sessionId === void 0 ? void 0 : {
|
|
33978
35833
|
layout: "opensteer-session",
|
|
33979
35834
|
version: 1,
|
|
33980
35835
|
provider: "cloud",
|
|
33981
35836
|
...this.workspace === void 0 ? {} : { workspace: this.workspace },
|
|
33982
35837
|
sessionId: this.sessionId,
|
|
33983
|
-
baseUrl: this.sessionBaseUrl,
|
|
33984
35838
|
startedAt: Date.now(),
|
|
33985
35839
|
updatedAt: Date.now()
|
|
33986
35840
|
});
|
|
@@ -33999,7 +35853,7 @@ var CloudSessionProxy = class {
|
|
|
33999
35853
|
this.automation = void 0;
|
|
34000
35854
|
this.client = void 0;
|
|
34001
35855
|
this.sessionId = void 0;
|
|
34002
|
-
this.
|
|
35856
|
+
this.semanticGrant = void 0;
|
|
34003
35857
|
if (this.cleanupRootOnClose) {
|
|
34004
35858
|
await promises.rm(this.rootPath, { recursive: true, force: true }).catch(() => void 0);
|
|
34005
35859
|
}
|
|
@@ -34015,39 +35869,56 @@ var CloudSessionProxy = class {
|
|
|
34015
35869
|
await this.automation?.close().catch(() => void 0);
|
|
34016
35870
|
this.automation = void 0;
|
|
34017
35871
|
this.sessionId = void 0;
|
|
34018
|
-
this.
|
|
35872
|
+
this.semanticGrant = void 0;
|
|
34019
35873
|
}
|
|
34020
35874
|
async ensureSession(input = {}) {
|
|
34021
35875
|
if (this.client) {
|
|
34022
35876
|
return;
|
|
34023
35877
|
}
|
|
34024
35878
|
assertSupportedCloudBrowserMode(input.browser);
|
|
35879
|
+
const localCloud = this.shouldUseLocalCloudTransport();
|
|
35880
|
+
const browserProfile = resolveCloudBrowserProfile(this.cloud, input);
|
|
34025
35881
|
const persisted = await this.loadPersistedSession();
|
|
34026
35882
|
if (persisted !== void 0 && await this.isReusableCloudSession(persisted.sessionId)) {
|
|
34027
|
-
|
|
35883
|
+
if (localCloud) {
|
|
35884
|
+
void this.syncRegistryToCloud();
|
|
35885
|
+
} else {
|
|
35886
|
+
await this.syncRegistryToCloud();
|
|
35887
|
+
}
|
|
34028
35888
|
this.bindClient(persisted);
|
|
34029
35889
|
return;
|
|
34030
35890
|
}
|
|
34031
|
-
|
|
34032
|
-
|
|
35891
|
+
if (localCloud) {
|
|
35892
|
+
void this.syncRegistryToCloud();
|
|
35893
|
+
} else {
|
|
35894
|
+
await this.syncRegistryToCloud();
|
|
35895
|
+
}
|
|
35896
|
+
const baseCreateInput = {
|
|
34033
35897
|
...this.workspace === void 0 ? {} : { name: this.workspace },
|
|
34034
35898
|
...input.launch === void 0 ? {} : { browser: input.launch },
|
|
34035
35899
|
...input.context === void 0 ? {} : { context: input.context },
|
|
34036
35900
|
...this.observability === void 0 ? {} : { observability: this.observability },
|
|
34037
|
-
...
|
|
34038
|
-
}
|
|
35901
|
+
...browserProfile === void 0 ? {} : { browserProfile }
|
|
35902
|
+
};
|
|
35903
|
+
const createInput = localCloud && this.workspace !== void 0 ? {
|
|
35904
|
+
...baseCreateInput,
|
|
35905
|
+
sourceType: "local-cloud",
|
|
35906
|
+
sourceRef: this.workspace,
|
|
35907
|
+
localWorkspaceRootPath: this.rootPath,
|
|
35908
|
+
locality: "auto"
|
|
35909
|
+
} : baseCreateInput;
|
|
35910
|
+
const session = await this.cloud.createSession(createInput);
|
|
34039
35911
|
const record = {
|
|
34040
35912
|
layout: "opensteer-session",
|
|
34041
35913
|
version: 1,
|
|
34042
35914
|
provider: "cloud",
|
|
34043
35915
|
...this.workspace === void 0 ? {} : { workspace: this.workspace },
|
|
34044
35916
|
sessionId: session.sessionId,
|
|
34045
|
-
baseUrl: session.baseUrl,
|
|
34046
35917
|
startedAt: Date.now(),
|
|
34047
35918
|
updatedAt: Date.now()
|
|
34048
35919
|
};
|
|
34049
35920
|
await this.writePersistedSession(record);
|
|
34050
|
-
this.bindClient(record);
|
|
35921
|
+
this.bindClient(record, session.initialGrants?.semantic);
|
|
34051
35922
|
}
|
|
34052
35923
|
async syncRegistryToCloud() {
|
|
34053
35924
|
if (this.workspace === void 0) {
|
|
@@ -34056,15 +35927,16 @@ var CloudSessionProxy = class {
|
|
|
34056
35927
|
try {
|
|
34057
35928
|
const workspaceStore = await this.ensureWorkspaceStore();
|
|
34058
35929
|
await syncLocalRegistryToCloud(this.cloud, this.workspace, workspaceStore);
|
|
34059
|
-
} catch
|
|
35930
|
+
} catch {
|
|
34060
35931
|
}
|
|
34061
35932
|
}
|
|
34062
|
-
bindClient(record) {
|
|
35933
|
+
bindClient(record, initialSemanticGrant) {
|
|
34063
35934
|
this.sessionId = record.sessionId;
|
|
34064
|
-
this.
|
|
35935
|
+
this.semanticGrant = initialSemanticGrant?.kind === "semantic" ? initialSemanticGrant : void 0;
|
|
34065
35936
|
this.client = new OpensteerSemanticRestClient({
|
|
34066
|
-
|
|
34067
|
-
getAuthorizationHeader: async () => this.
|
|
35937
|
+
getBaseUrl: async () => (await this.ensureSemanticGrant()).url,
|
|
35938
|
+
getAuthorizationHeader: async () => `Bearer ${(await this.ensureSemanticGrant()).token}`,
|
|
35939
|
+
handleError: (error) => this.handleSemanticClientError(error)
|
|
34068
35940
|
});
|
|
34069
35941
|
this.automation = new OpensteerCloudAutomationClient(this.cloud, record.sessionId);
|
|
34070
35942
|
}
|
|
@@ -34113,6 +35985,43 @@ var CloudSessionProxy = class {
|
|
|
34113
35985
|
}
|
|
34114
35986
|
return this.automation;
|
|
34115
35987
|
}
|
|
35988
|
+
async ensureSemanticGrant(forceRefresh = false) {
|
|
35989
|
+
if (!forceRefresh && this.semanticGrant?.kind === "semantic" && this.semanticGrant.expiresAt > Date.now() + 1e4) {
|
|
35990
|
+
return this.semanticGrant;
|
|
35991
|
+
}
|
|
35992
|
+
if (!this.sessionId) {
|
|
35993
|
+
throw new Error("Cloud session has not been initialized.");
|
|
35994
|
+
}
|
|
35995
|
+
const issued = await this.cloud.issueAccess(this.sessionId, ["semantic"]);
|
|
35996
|
+
const grant = issued.grants.semantic;
|
|
35997
|
+
if (!grant || grant.transport !== "http") {
|
|
35998
|
+
throw new Error("cloud did not issue a valid semantic grant");
|
|
35999
|
+
}
|
|
36000
|
+
this.semanticGrant = grant;
|
|
36001
|
+
return grant;
|
|
36002
|
+
}
|
|
36003
|
+
async handleSemanticClientError(error) {
|
|
36004
|
+
if (!(error instanceof OpensteerSemanticRestError)) {
|
|
36005
|
+
return false;
|
|
36006
|
+
}
|
|
36007
|
+
if (error.statusCode !== 401 && error.statusCode !== 404) {
|
|
36008
|
+
return false;
|
|
36009
|
+
}
|
|
36010
|
+
this.semanticGrant = void 0;
|
|
36011
|
+
try {
|
|
36012
|
+
await this.ensureSemanticGrant(true);
|
|
36013
|
+
return true;
|
|
36014
|
+
} catch {
|
|
36015
|
+
return false;
|
|
36016
|
+
}
|
|
36017
|
+
}
|
|
36018
|
+
shouldUseLocalCloudTransport() {
|
|
36019
|
+
if (this.workspace === void 0) {
|
|
36020
|
+
return false;
|
|
36021
|
+
}
|
|
36022
|
+
const config = this.cloud.getConfig();
|
|
36023
|
+
return isLoopbackBaseUrl(config.baseUrl);
|
|
36024
|
+
}
|
|
34116
36025
|
};
|
|
34117
36026
|
function resolveCloudBrowserProfile(cloud, input) {
|
|
34118
36027
|
return input.browserProfile ?? cloud.getConfig().browserProfile;
|
|
@@ -34128,6 +36037,15 @@ function assertSupportedCloudBrowserMode(browser) {
|
|
|
34128
36037
|
function isMissingCloudSessionError(error) {
|
|
34129
36038
|
return error instanceof Error && /\b404\b/.test(error.message);
|
|
34130
36039
|
}
|
|
36040
|
+
function isLoopbackBaseUrl(baseUrl) {
|
|
36041
|
+
let url;
|
|
36042
|
+
try {
|
|
36043
|
+
url = new URL(baseUrl);
|
|
36044
|
+
} catch {
|
|
36045
|
+
return false;
|
|
36046
|
+
}
|
|
36047
|
+
return url.hostname === "localhost" || url.hostname === "127.0.0.1" || url.hostname === "::1" || url.hostname === "[::1]";
|
|
36048
|
+
}
|
|
34131
36049
|
var OpensteerRuntime = class extends OpensteerSessionRuntime {
|
|
34132
36050
|
constructor(options = {}) {
|
|
34133
36051
|
const publicWorkspace = normalizeWorkspace2(options.workspace);
|
|
@@ -34360,9 +36278,11 @@ async function describeCloudLane(input) {
|
|
|
34360
36278
|
status: "connected",
|
|
34361
36279
|
current: input.current,
|
|
34362
36280
|
summary: input.record.sessionId,
|
|
34363
|
-
detail: input.record.baseUrl,
|
|
34364
36281
|
sessionId: input.record.sessionId,
|
|
34365
|
-
|
|
36282
|
+
...input.cloudConfig === void 0 ? {} : {
|
|
36283
|
+
detail: input.cloudConfig.baseUrl,
|
|
36284
|
+
baseUrl: input.cloudConfig.baseUrl
|
|
36285
|
+
}
|
|
34366
36286
|
};
|
|
34367
36287
|
if (input.cloudConfig === void 0) {
|
|
34368
36288
|
return base;
|
|
@@ -34406,6 +36326,214 @@ function formatLaneRow(input) {
|
|
|
34406
36326
|
const summary = input.summary.padEnd(16, " ");
|
|
34407
36327
|
return `${input.marker} ${provider} ${status} ${summary}${input.detail ?? ""}`.trimEnd();
|
|
34408
36328
|
}
|
|
36329
|
+
async function runOpensteerRecordCommand(input) {
|
|
36330
|
+
const stdout = input.stdout ?? process.stdout;
|
|
36331
|
+
const stderr = input.stderr ?? process.stderr;
|
|
36332
|
+
const outputPath = resolveRecordOutputPath({
|
|
36333
|
+
rootDir: input.rootDir,
|
|
36334
|
+
workspace: input.workspace,
|
|
36335
|
+
...input.outputPath === void 0 ? {} : { outputPath: input.outputPath }
|
|
36336
|
+
});
|
|
36337
|
+
const runtime = input.runtime;
|
|
36338
|
+
const collector = new FlowRecorderCollector(createRecorderRuntimeAdapter(runtime), {
|
|
36339
|
+
...input.pollIntervalMs === void 0 ? {} : { pollIntervalMs: input.pollIntervalMs },
|
|
36340
|
+
onAction: (action) => {
|
|
36341
|
+
stderr.write(`${formatRecordedAction(action)}
|
|
36342
|
+
`);
|
|
36343
|
+
}
|
|
36344
|
+
});
|
|
36345
|
+
stderr.write(
|
|
36346
|
+
`Recording browser actions for workspace "${input.workspace}". Click "Stop recording" in the browser when you're done.
|
|
36347
|
+
`
|
|
36348
|
+
);
|
|
36349
|
+
let closed = false;
|
|
36350
|
+
try {
|
|
36351
|
+
const opened = await runtime.open({
|
|
36352
|
+
url: input.url
|
|
36353
|
+
});
|
|
36354
|
+
await collector.install();
|
|
36355
|
+
collector.start();
|
|
36356
|
+
await collector.waitForStop();
|
|
36357
|
+
const actions = await collector.stop();
|
|
36358
|
+
const script = generateReplayScript({
|
|
36359
|
+
actions,
|
|
36360
|
+
workspace: input.workspace,
|
|
36361
|
+
startUrl: opened.url
|
|
36362
|
+
});
|
|
36363
|
+
await promises.mkdir(path7__default.default.dirname(outputPath), { recursive: true });
|
|
36364
|
+
await promises.writeFile(outputPath, script, "utf8");
|
|
36365
|
+
if (input.closeSession !== void 0) {
|
|
36366
|
+
await input.closeSession();
|
|
36367
|
+
closed = true;
|
|
36368
|
+
}
|
|
36369
|
+
stdout.write(`${outputPath}
|
|
36370
|
+
`);
|
|
36371
|
+
stderr.write(`Wrote replay script to ${outputPath}
|
|
36372
|
+
`);
|
|
36373
|
+
} finally {
|
|
36374
|
+
if (!closed) {
|
|
36375
|
+
await runtime.disconnect().catch(() => void 0);
|
|
36376
|
+
}
|
|
36377
|
+
}
|
|
36378
|
+
}
|
|
36379
|
+
async function runOpensteerCloudRecordCommand(input) {
|
|
36380
|
+
const stdout = input.stdout ?? process.stdout;
|
|
36381
|
+
const stderr = input.stderr ?? process.stderr;
|
|
36382
|
+
const outputPath = resolveRecordOutputPath({
|
|
36383
|
+
rootDir: input.rootDir,
|
|
36384
|
+
workspace: input.workspace,
|
|
36385
|
+
...input.outputPath === void 0 ? {} : { outputPath: input.outputPath }
|
|
36386
|
+
});
|
|
36387
|
+
let cloud;
|
|
36388
|
+
const resolveCloud = () => {
|
|
36389
|
+
cloud ??= new OpensteerCloudClient(input.cloudConfig);
|
|
36390
|
+
return cloud;
|
|
36391
|
+
};
|
|
36392
|
+
const runtime = input.runtime ?? new CloudSessionProxy(resolveCloud(), {
|
|
36393
|
+
rootDir: input.rootDir,
|
|
36394
|
+
workspace: input.workspace
|
|
36395
|
+
});
|
|
36396
|
+
const client = input.client ?? resolveCloud();
|
|
36397
|
+
const sleep5 = input.sleep ?? delay3;
|
|
36398
|
+
let closed = false;
|
|
36399
|
+
try {
|
|
36400
|
+
await runtime.open({
|
|
36401
|
+
url: input.url,
|
|
36402
|
+
...input.browser === void 0 ? {} : { browser: input.browser },
|
|
36403
|
+
...input.launch === void 0 ? {} : { launch: input.launch },
|
|
36404
|
+
...input.context === void 0 ? {} : { context: input.context }
|
|
36405
|
+
});
|
|
36406
|
+
const sessionId = await resolveCloudRecordingSessionId(runtime);
|
|
36407
|
+
const sessionUrl = buildCloudRecordingSessionUrl(input.cloudConfig, sessionId);
|
|
36408
|
+
await client.startSessionRecording(sessionId);
|
|
36409
|
+
stderr.write(
|
|
36410
|
+
`Recording browser actions for workspace "${input.workspace}". Open ${sessionUrl} and click "Stop recording" in the browser session toolbar when you're done.
|
|
36411
|
+
`
|
|
36412
|
+
);
|
|
36413
|
+
const completed = await waitForCloudRecordingCompletion({
|
|
36414
|
+
client,
|
|
36415
|
+
sessionId,
|
|
36416
|
+
...input.pollIntervalMs === void 0 ? {} : { pollIntervalMs: input.pollIntervalMs },
|
|
36417
|
+
sleep: sleep5
|
|
36418
|
+
});
|
|
36419
|
+
if (completed.result === void 0) {
|
|
36420
|
+
throw new Error("Cloud recording completed without a replay script.");
|
|
36421
|
+
}
|
|
36422
|
+
await promises.mkdir(path7__default.default.dirname(outputPath), { recursive: true });
|
|
36423
|
+
await promises.writeFile(outputPath, completed.result.script, "utf8");
|
|
36424
|
+
await runtime.close();
|
|
36425
|
+
closed = true;
|
|
36426
|
+
stdout.write(`${outputPath}
|
|
36427
|
+
`);
|
|
36428
|
+
stderr.write(`Cloud browser session: ${sessionUrl}
|
|
36429
|
+
`);
|
|
36430
|
+
stderr.write(`Wrote replay script to ${outputPath}
|
|
36431
|
+
`);
|
|
36432
|
+
} finally {
|
|
36433
|
+
if (!closed) {
|
|
36434
|
+
await runtime.close().catch(() => void 0);
|
|
36435
|
+
}
|
|
36436
|
+
}
|
|
36437
|
+
}
|
|
36438
|
+
function resolveRecordOutputPath(input) {
|
|
36439
|
+
if (input.outputPath !== void 0) {
|
|
36440
|
+
return path7__default.default.resolve(input.rootDir, input.outputPath);
|
|
36441
|
+
}
|
|
36442
|
+
return path7__default.default.join(
|
|
36443
|
+
resolveFilesystemWorkspacePath({
|
|
36444
|
+
rootDir: input.rootDir,
|
|
36445
|
+
workspace: input.workspace
|
|
36446
|
+
}),
|
|
36447
|
+
"recorded-flow.ts"
|
|
36448
|
+
);
|
|
36449
|
+
}
|
|
36450
|
+
function createRecorderRuntimeAdapter(runtime) {
|
|
36451
|
+
return {
|
|
36452
|
+
addInitScript: (input) => runtime.addInitScript(input),
|
|
36453
|
+
evaluate: async (input) => {
|
|
36454
|
+
const output = await runtime.evaluate({
|
|
36455
|
+
script: input.script,
|
|
36456
|
+
...input.pageRef === void 0 ? {} : { pageRef: input.pageRef }
|
|
36457
|
+
});
|
|
36458
|
+
return output.value;
|
|
36459
|
+
},
|
|
36460
|
+
listPages: async () => {
|
|
36461
|
+
const output = await runtime.listPages();
|
|
36462
|
+
return {
|
|
36463
|
+
pages: output.pages.map((page) => ({
|
|
36464
|
+
pageRef: page.pageRef,
|
|
36465
|
+
url: page.url,
|
|
36466
|
+
...page.openerPageRef === void 0 ? {} : { openerPageRef: page.openerPageRef }
|
|
36467
|
+
}))
|
|
36468
|
+
};
|
|
36469
|
+
}
|
|
36470
|
+
};
|
|
36471
|
+
}
|
|
36472
|
+
function buildCloudRecordingSessionUrl(cloudConfig, sessionId) {
|
|
36473
|
+
const baseUrl = cloudConfig.appBaseUrl;
|
|
36474
|
+
if (!baseUrl || baseUrl.trim().length === 0) {
|
|
36475
|
+
throw new Error(
|
|
36476
|
+
'record with provider=cloud requires OPENSTEER_CLOUD_APP_BASE_URL or "--cloud-app-base-url".'
|
|
36477
|
+
);
|
|
36478
|
+
}
|
|
36479
|
+
return `${baseUrl.replace(/\/+$/, "")}/browsers/${encodeURIComponent(sessionId)}`;
|
|
36480
|
+
}
|
|
36481
|
+
async function resolveCloudRecordingSessionId(runtime) {
|
|
36482
|
+
const info = await runtime.info();
|
|
36483
|
+
if (typeof info.sessionId !== "string" || info.sessionId.length === 0) {
|
|
36484
|
+
throw new Error("Cloud recording could not resolve the created session id.");
|
|
36485
|
+
}
|
|
36486
|
+
return info.sessionId;
|
|
36487
|
+
}
|
|
36488
|
+
async function waitForCloudRecordingCompletion(input) {
|
|
36489
|
+
const pollIntervalMs = input.pollIntervalMs ?? 1e3;
|
|
36490
|
+
for (; ; ) {
|
|
36491
|
+
const state = await input.client.getSessionRecording(input.sessionId);
|
|
36492
|
+
if (state.status === "completed") {
|
|
36493
|
+
return state;
|
|
36494
|
+
}
|
|
36495
|
+
if (state.status === "failed") {
|
|
36496
|
+
throw new Error(state.error ?? "Cloud recording failed.");
|
|
36497
|
+
}
|
|
36498
|
+
await input.sleep(pollIntervalMs);
|
|
36499
|
+
}
|
|
36500
|
+
}
|
|
36501
|
+
function formatRecordedAction(action) {
|
|
36502
|
+
const time = new Date(action.timestamp).toISOString().slice(11, 19);
|
|
36503
|
+
switch (action.kind) {
|
|
36504
|
+
case "click":
|
|
36505
|
+
return `[${time}] click ${action.pageId} -> ${action.selector ?? "<unknown>"}`;
|
|
36506
|
+
case "dblclick":
|
|
36507
|
+
return `[${time}] dblclick ${action.pageId} -> ${action.selector ?? "<unknown>"}`;
|
|
36508
|
+
case "type":
|
|
36509
|
+
return `[${time}] type ${action.pageId} -> ${action.selector ?? "<unknown>"} -> ${JSON.stringify(action.detail.text)}`;
|
|
36510
|
+
case "keypress":
|
|
36511
|
+
return `[${time}] keypress ${action.pageId} -> ${action.detail.key}`;
|
|
36512
|
+
case "scroll":
|
|
36513
|
+
return `[${time}] scroll ${action.pageId} -> (${String(action.detail.deltaX)}, ${String(action.detail.deltaY)})`;
|
|
36514
|
+
case "select-option":
|
|
36515
|
+
return `[${time}] select ${action.pageId} -> ${action.selector ?? "<unknown>"} -> ${JSON.stringify(action.detail.value)}`;
|
|
36516
|
+
case "navigate":
|
|
36517
|
+
return `[${time}] navigate ${action.pageId} -> ${action.detail.url}`;
|
|
36518
|
+
case "new-tab":
|
|
36519
|
+
return `[${time}] new-tab ${action.pageId} -> ${action.detail.initialUrl}`;
|
|
36520
|
+
case "close-tab":
|
|
36521
|
+
return `[${time}] close-tab ${action.pageId}`;
|
|
36522
|
+
case "switch-tab":
|
|
36523
|
+
return `[${time}] switch-tab -> ${action.detail.toPageId}`;
|
|
36524
|
+
case "go-back":
|
|
36525
|
+
return `[${time}] go-back ${action.pageId}`;
|
|
36526
|
+
case "go-forward":
|
|
36527
|
+
return `[${time}] go-forward ${action.pageId}`;
|
|
36528
|
+
case "reload":
|
|
36529
|
+
return `[${time}] reload ${action.pageId}`;
|
|
36530
|
+
}
|
|
36531
|
+
}
|
|
36532
|
+
function delay3(ms) {
|
|
36533
|
+
return new Promise((resolve4) => {
|
|
36534
|
+
setTimeout(resolve4, ms);
|
|
36535
|
+
});
|
|
36536
|
+
}
|
|
34409
36537
|
|
|
34410
36538
|
// src/cli/bin.ts
|
|
34411
36539
|
var OPERATION_ALIASES = /* @__PURE__ */ new Map([
|
|
@@ -34497,6 +36625,10 @@ async function main() {
|
|
|
34497
36625
|
await handleStatusCommand(parsed);
|
|
34498
36626
|
return;
|
|
34499
36627
|
}
|
|
36628
|
+
if (parsed.command[0] === "record") {
|
|
36629
|
+
await handleRecordCommandEntry(parsed);
|
|
36630
|
+
return;
|
|
36631
|
+
}
|
|
34500
36632
|
const operation = parsed.command[0] === "run" ? parsed.rest[0] : resolveOperation(parsed.command);
|
|
34501
36633
|
if (!operation) {
|
|
34502
36634
|
throw new Error(`Unknown command: ${parsed.command.join(" ")}`);
|
|
@@ -34641,6 +36773,100 @@ async function handleBrowserCommand(parsed) {
|
|
|
34641
36773
|
throw new Error(`Unknown browser command: ${parsed.command.join(" ")}`);
|
|
34642
36774
|
}
|
|
34643
36775
|
}
|
|
36776
|
+
async function handleRecordCommandEntry(parsed) {
|
|
36777
|
+
if (parsed.options.workspace === void 0) {
|
|
36778
|
+
throw new Error('record requires "--workspace <id>".');
|
|
36779
|
+
}
|
|
36780
|
+
const url = parsed.options.url ?? parsed.rest[0];
|
|
36781
|
+
if (url === void 0) {
|
|
36782
|
+
throw new Error('record requires "--url <value>" or a positional URL.');
|
|
36783
|
+
}
|
|
36784
|
+
const provider = resolveCliProvider(parsed);
|
|
36785
|
+
assertCloudCliOptionsMatchProvider(parsed, provider.mode);
|
|
36786
|
+
const engineName = resolveCliEngineName(parsed);
|
|
36787
|
+
if (engineName !== "playwright") {
|
|
36788
|
+
throw new Error("record requires engine=playwright.");
|
|
36789
|
+
}
|
|
36790
|
+
const rootDir = process2__default.default.cwd();
|
|
36791
|
+
const recordBrowser = parsed.options.browser;
|
|
36792
|
+
if (provider.mode === "cloud") {
|
|
36793
|
+
if (typeof recordBrowser === "object") {
|
|
36794
|
+
throw new Error('record does not support browser.mode="attach".');
|
|
36795
|
+
}
|
|
36796
|
+
const runtimeProvider = buildCliRuntimeProvider(parsed, provider.mode);
|
|
36797
|
+
const runtimeConfig = resolveOpensteerRuntimeConfig({
|
|
36798
|
+
...runtimeProvider === void 0 ? {} : { provider: runtimeProvider },
|
|
36799
|
+
environment: process2__default.default.env
|
|
36800
|
+
});
|
|
36801
|
+
await runOpensteerCloudRecordCommand({
|
|
36802
|
+
cloudConfig: runtimeConfig.cloud,
|
|
36803
|
+
workspace: parsed.options.workspace,
|
|
36804
|
+
url,
|
|
36805
|
+
rootDir,
|
|
36806
|
+
...recordBrowser === void 0 ? {} : { browser: recordBrowser },
|
|
36807
|
+
...parsed.options.launch === void 0 ? {} : { launch: parsed.options.launch },
|
|
36808
|
+
...parsed.options.context === void 0 ? {} : { context: parsed.options.context },
|
|
36809
|
+
...parsed.options.output === void 0 ? {} : { outputPath: parsed.options.output }
|
|
36810
|
+
});
|
|
36811
|
+
return;
|
|
36812
|
+
}
|
|
36813
|
+
if (parsed.options.launch?.headless === true) {
|
|
36814
|
+
throw new Error('record requires a headed browser. Remove "--headless true".');
|
|
36815
|
+
}
|
|
36816
|
+
if (recordBrowser !== void 0 && recordBrowser !== "persistent") {
|
|
36817
|
+
throw new Error('record only supports "--browser persistent".');
|
|
36818
|
+
}
|
|
36819
|
+
const launch = {
|
|
36820
|
+
...parsed.options.launch ?? {},
|
|
36821
|
+
headless: false
|
|
36822
|
+
};
|
|
36823
|
+
const browserManager = new OpensteerBrowserManager({
|
|
36824
|
+
rootDir,
|
|
36825
|
+
workspace: parsed.options.workspace,
|
|
36826
|
+
engineName,
|
|
36827
|
+
browser: "persistent",
|
|
36828
|
+
launch,
|
|
36829
|
+
...parsed.options.context === void 0 ? {} : { context: parsed.options.context }
|
|
36830
|
+
});
|
|
36831
|
+
const runtime = createOpensteerSemanticRuntime({
|
|
36832
|
+
provider: {
|
|
36833
|
+
mode: "local"
|
|
36834
|
+
},
|
|
36835
|
+
engine: engineName,
|
|
36836
|
+
runtimeOptions: {
|
|
36837
|
+
rootPath: browserManager.rootPath,
|
|
36838
|
+
cleanupRootOnClose: browserManager.cleanupRootOnDisconnect,
|
|
36839
|
+
workspace: parsed.options.workspace,
|
|
36840
|
+
browser: "persistent",
|
|
36841
|
+
launch,
|
|
36842
|
+
...parsed.options.context === void 0 ? {} : { context: parsed.options.context }
|
|
36843
|
+
}
|
|
36844
|
+
});
|
|
36845
|
+
await runOpensteerRecordCommand({
|
|
36846
|
+
runtime,
|
|
36847
|
+
closeSession: () => closeOwnedLocalBrowserSession(runtime, browserManager),
|
|
36848
|
+
workspace: parsed.options.workspace,
|
|
36849
|
+
url,
|
|
36850
|
+
rootDir,
|
|
36851
|
+
...parsed.options.output === void 0 ? {} : { outputPath: parsed.options.output }
|
|
36852
|
+
});
|
|
36853
|
+
}
|
|
36854
|
+
async function closeOwnedLocalBrowserSession(runtime, browserManager) {
|
|
36855
|
+
let closeError;
|
|
36856
|
+
try {
|
|
36857
|
+
await runtime.close();
|
|
36858
|
+
} catch (error) {
|
|
36859
|
+
closeError = error;
|
|
36860
|
+
}
|
|
36861
|
+
try {
|
|
36862
|
+
await browserManager.close();
|
|
36863
|
+
} catch (error) {
|
|
36864
|
+
closeError ??= error;
|
|
36865
|
+
}
|
|
36866
|
+
if (closeError !== void 0) {
|
|
36867
|
+
throw closeError;
|
|
36868
|
+
}
|
|
36869
|
+
}
|
|
34644
36870
|
function buildOperationInput(operation, parsed) {
|
|
34645
36871
|
if (parsed.options.inputJson !== void 0) {
|
|
34646
36872
|
return parsed.options.inputJson;
|
|
@@ -34721,6 +36947,46 @@ function resolveOperation(command) {
|
|
|
34721
36947
|
}
|
|
34722
36948
|
return void 0;
|
|
34723
36949
|
}
|
|
36950
|
+
var CLI_OPTION_SPECS = {
|
|
36951
|
+
workspace: { kind: "value" },
|
|
36952
|
+
url: { kind: "value" },
|
|
36953
|
+
output: { kind: "value" },
|
|
36954
|
+
engine: { kind: "value" },
|
|
36955
|
+
provider: { kind: "value" },
|
|
36956
|
+
"cloud-base-url": { kind: "value" },
|
|
36957
|
+
"cloud-api-key": { kind: "value" },
|
|
36958
|
+
"cloud-app-base-url": { kind: "value" },
|
|
36959
|
+
"cloud-profile-id": { kind: "value" },
|
|
36960
|
+
"cloud-profile-reuse-if-active": { kind: "boolean" },
|
|
36961
|
+
json: { kind: "boolean" },
|
|
36962
|
+
agent: { kind: "value", multiple: true },
|
|
36963
|
+
skill: { kind: "value", multiple: true },
|
|
36964
|
+
global: { kind: "boolean" },
|
|
36965
|
+
yes: { kind: "boolean" },
|
|
36966
|
+
copy: { kind: "boolean" },
|
|
36967
|
+
all: { kind: "boolean" },
|
|
36968
|
+
list: { kind: "boolean" },
|
|
36969
|
+
browser: { kind: "value" },
|
|
36970
|
+
"attach-endpoint": { kind: "value" },
|
|
36971
|
+
"attach-header": { kind: "value", multiple: true },
|
|
36972
|
+
"fresh-tab": { kind: "boolean" },
|
|
36973
|
+
headless: { kind: "boolean" },
|
|
36974
|
+
"executable-path": { kind: "value" },
|
|
36975
|
+
arg: { kind: "value", multiple: true },
|
|
36976
|
+
"timeout-ms": { kind: "value" },
|
|
36977
|
+
"context-json": { kind: "value" },
|
|
36978
|
+
"input-json": { kind: "value" },
|
|
36979
|
+
"schema-json": { kind: "value" },
|
|
36980
|
+
"source-user-data-dir": { kind: "value" },
|
|
36981
|
+
"source-profile-directory": { kind: "value" },
|
|
36982
|
+
selector: { kind: "value" },
|
|
36983
|
+
description: { kind: "value" },
|
|
36984
|
+
element: { kind: "value" },
|
|
36985
|
+
text: { kind: "value" },
|
|
36986
|
+
"press-enter": { kind: "boolean" },
|
|
36987
|
+
direction: { kind: "value" },
|
|
36988
|
+
amount: { kind: "value" }
|
|
36989
|
+
};
|
|
34724
36990
|
function parseCommandLine(argv) {
|
|
34725
36991
|
const leadingTokens = [];
|
|
34726
36992
|
let index = 0;
|
|
@@ -34734,18 +37000,43 @@ function parseCommandLine(argv) {
|
|
|
34734
37000
|
const rawOptions = /* @__PURE__ */ new Map();
|
|
34735
37001
|
while (index < argv.length) {
|
|
34736
37002
|
const token = argv[index];
|
|
37003
|
+
if (token === "--") {
|
|
37004
|
+
rest.push(...argv.slice(index + 1));
|
|
37005
|
+
break;
|
|
37006
|
+
}
|
|
34737
37007
|
if (!token.startsWith("--")) {
|
|
34738
37008
|
rest.push(token);
|
|
34739
37009
|
index += 1;
|
|
34740
37010
|
continue;
|
|
34741
37011
|
}
|
|
34742
|
-
const
|
|
34743
|
-
const
|
|
34744
|
-
|
|
34745
|
-
|
|
37012
|
+
const separator = token.indexOf("=");
|
|
37013
|
+
const key = token.slice(2, separator === -1 ? void 0 : separator);
|
|
37014
|
+
const spec = CLI_OPTION_SPECS[key];
|
|
37015
|
+
if (spec === void 0) {
|
|
37016
|
+
throw new Error(`Unknown option: --${key}.`);
|
|
37017
|
+
}
|
|
37018
|
+
if (separator !== -1) {
|
|
37019
|
+
const value = token.slice(separator + 1);
|
|
37020
|
+
rawOptions.set(key, [...rawOptions.get(key) ?? [], value]);
|
|
34746
37021
|
index += 1;
|
|
34747
37022
|
continue;
|
|
34748
37023
|
}
|
|
37024
|
+
const next = argv[index + 1];
|
|
37025
|
+
if (spec.kind === "boolean") {
|
|
37026
|
+
if (next === void 0 || next.startsWith("--")) {
|
|
37027
|
+
rawOptions.set(key, [...rawOptions.get(key) ?? [], "true"]);
|
|
37028
|
+
index += 1;
|
|
37029
|
+
continue;
|
|
37030
|
+
}
|
|
37031
|
+
rawOptions.set(key, [...rawOptions.get(key) ?? [], next]);
|
|
37032
|
+
index += 2;
|
|
37033
|
+
continue;
|
|
37034
|
+
}
|
|
37035
|
+
if (next === void 0 || next.startsWith("--")) {
|
|
37036
|
+
throw new Error(
|
|
37037
|
+
`Option "--${key}" requires a value.${next?.startsWith("--") === true ? ` Use "--${key}=<value>" when the value begins with "--".` : ``}`
|
|
37038
|
+
);
|
|
37039
|
+
}
|
|
34749
37040
|
rawOptions.set(key, [...rawOptions.get(key) ?? [], next]);
|
|
34750
37041
|
index += 2;
|
|
34751
37042
|
}
|
|
@@ -34776,6 +37067,8 @@ function parseCommandLine(argv) {
|
|
|
34776
37067
|
...timeoutMs === void 0 ? {} : { timeoutMs }
|
|
34777
37068
|
};
|
|
34778
37069
|
const workspace = readSingle(rawOptions, "workspace");
|
|
37070
|
+
const url = readSingle(rawOptions, "url");
|
|
37071
|
+
const output = readSingle(rawOptions, "output");
|
|
34779
37072
|
const sourceUserDataDir = readSingle(rawOptions, "source-user-data-dir");
|
|
34780
37073
|
const sourceProfileDirectory = readSingle(rawOptions, "source-profile-directory");
|
|
34781
37074
|
const selector = readSingle(rawOptions, "selector");
|
|
@@ -34792,6 +37085,7 @@ function parseCommandLine(argv) {
|
|
|
34792
37085
|
const provider = providerValue === void 0 ? void 0 : normalizeOpensteerProviderMode(providerValue, "--provider");
|
|
34793
37086
|
const cloudBaseUrl = readSingle(rawOptions, "cloud-base-url");
|
|
34794
37087
|
const cloudApiKey = readSingle(rawOptions, "cloud-api-key");
|
|
37088
|
+
const cloudAppBaseUrl = readSingle(rawOptions, "cloud-app-base-url");
|
|
34795
37089
|
const cloudProfileId = readSingle(rawOptions, "cloud-profile-id");
|
|
34796
37090
|
const cloudProfileReuseIfActive = readOptionalBoolean(
|
|
34797
37091
|
rawOptions,
|
|
@@ -34807,10 +37101,13 @@ function parseCommandLine(argv) {
|
|
|
34807
37101
|
const list = readOptionalBoolean(rawOptions, "list");
|
|
34808
37102
|
const options = {
|
|
34809
37103
|
...workspace === void 0 ? {} : { workspace },
|
|
37104
|
+
...url === void 0 ? {} : { url },
|
|
37105
|
+
...output === void 0 ? {} : { output },
|
|
34810
37106
|
...requestedEngineName === void 0 ? {} : { requestedEngineName },
|
|
34811
37107
|
...provider === void 0 ? {} : { provider },
|
|
34812
37108
|
...cloudBaseUrl === void 0 ? {} : { cloudBaseUrl },
|
|
34813
37109
|
...cloudApiKey === void 0 ? {} : { cloudApiKey },
|
|
37110
|
+
...cloudAppBaseUrl === void 0 ? {} : { cloudAppBaseUrl },
|
|
34814
37111
|
...cloudProfileId === void 0 ? {} : { cloudProfileId },
|
|
34815
37112
|
...cloudProfileReuseIfActive === void 0 ? {} : { cloudProfileReuseIfActive },
|
|
34816
37113
|
...json === void 0 ? {} : { json },
|
|
@@ -34895,7 +37192,7 @@ function buildCliRuntimeProvider(parsed, providerMode) {
|
|
|
34895
37192
|
return explicitProvider?.mode === "local" ? explicitProvider : void 0;
|
|
34896
37193
|
}
|
|
34897
37194
|
const browserProfile = buildCliBrowserProfile(parsed);
|
|
34898
|
-
const hasCloudOverrides = parsed.options.cloudBaseUrl !== void 0 || parsed.options.cloudApiKey !== void 0 || browserProfile !== void 0;
|
|
37195
|
+
const hasCloudOverrides = parsed.options.cloudBaseUrl !== void 0 || parsed.options.cloudApiKey !== void 0 || parsed.options.cloudAppBaseUrl !== void 0 || browserProfile !== void 0;
|
|
34899
37196
|
if (!hasCloudOverrides && explicitProvider?.mode !== "cloud") {
|
|
34900
37197
|
return void 0;
|
|
34901
37198
|
}
|
|
@@ -34903,11 +37200,12 @@ function buildCliRuntimeProvider(parsed, providerMode) {
|
|
|
34903
37200
|
mode: "cloud",
|
|
34904
37201
|
...parsed.options.cloudBaseUrl === void 0 ? {} : { baseUrl: parsed.options.cloudBaseUrl },
|
|
34905
37202
|
...parsed.options.cloudApiKey === void 0 ? {} : { apiKey: parsed.options.cloudApiKey },
|
|
37203
|
+
...parsed.options.cloudAppBaseUrl === void 0 ? {} : { appBaseUrl: parsed.options.cloudAppBaseUrl },
|
|
34906
37204
|
...browserProfile === void 0 ? {} : { browserProfile }
|
|
34907
37205
|
};
|
|
34908
37206
|
}
|
|
34909
37207
|
function assertCloudCliOptionsMatchProvider(parsed, providerMode) {
|
|
34910
|
-
if (providerMode !== "cloud" && (parsed.options.cloudBaseUrl !== void 0 || parsed.options.cloudApiKey !== void 0 || parsed.options.cloudProfileId !== void 0 || parsed.options.cloudProfileReuseIfActive === true)) {
|
|
37208
|
+
if (providerMode !== "cloud" && (parsed.options.cloudBaseUrl !== void 0 || parsed.options.cloudApiKey !== void 0 || parsed.options.cloudAppBaseUrl !== void 0 || parsed.options.cloudProfileId !== void 0 || parsed.options.cloudProfileReuseIfActive === true)) {
|
|
34911
37209
|
throw new Error(
|
|
34912
37210
|
'Cloud-specific options require provider=cloud. Set "--provider cloud" or OPENSTEER_PROVIDER=cloud.'
|
|
34913
37211
|
);
|
|
@@ -35006,6 +37304,9 @@ function resolveCommandLength(tokens) {
|
|
|
35006
37304
|
if (tokens[0] === "status") {
|
|
35007
37305
|
return 1;
|
|
35008
37306
|
}
|
|
37307
|
+
if (tokens[0] === "record") {
|
|
37308
|
+
return 1;
|
|
37309
|
+
}
|
|
35009
37310
|
for (let length = Math.min(3, tokens.length); length >= 1; length -= 1) {
|
|
35010
37311
|
if (OPERATION_ALIASES.has(tokens.slice(0, length).join(" "))) {
|
|
35011
37312
|
return length;
|
|
@@ -35023,6 +37324,7 @@ Usage:
|
|
|
35023
37324
|
opensteer click --workspace <id> (--element <n> | --selector <css> | --description <text>)
|
|
35024
37325
|
opensteer input --workspace <id> --text <value> (--element <n> | --selector <css> | --description <text>)
|
|
35025
37326
|
opensteer extract --workspace <id> --description <text> [--schema-json <json>]
|
|
37327
|
+
opensteer record --workspace <id> --url <url> [--output <path>]
|
|
35026
37328
|
opensteer close --workspace <id>
|
|
35027
37329
|
opensteer status [--workspace <id>] [--json]
|
|
35028
37330
|
|
|
@@ -35040,9 +37342,12 @@ Common options:
|
|
|
35040
37342
|
--help
|
|
35041
37343
|
--version
|
|
35042
37344
|
--workspace <id>
|
|
37345
|
+
--url <url>
|
|
37346
|
+
--output <path>
|
|
35043
37347
|
--provider local|cloud
|
|
35044
37348
|
--cloud-base-url <url>
|
|
35045
37349
|
--cloud-api-key <key>
|
|
37350
|
+
--cloud-app-base-url <url>
|
|
35046
37351
|
--cloud-profile-id <id>
|
|
35047
37352
|
--cloud-profile-reuse-if-active <true|false>
|
|
35048
37353
|
--json <true|false>
|