opensteer 0.6.0 → 0.6.2
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/CHANGELOG.md +16 -1
- package/README.md +63 -0
- package/bin/opensteer.mjs +214 -3
- package/dist/browser-profile-client-CaL-mwqs.d.ts +168 -0
- package/dist/browser-profile-client-DK9qa_Dj.d.cts +168 -0
- package/dist/chunk-7RMY26CM.js +1130 -0
- package/dist/{chunk-SGZYTGY3.js → chunk-F2VDVOJO.js} +1890 -1520
- package/dist/chunk-WDRMHPWL.js +1320 -0
- package/dist/chunk-WJI7TGBQ.js +77 -0
- package/dist/cli/auth.cjs +1834 -0
- package/dist/cli/auth.d.cts +114 -0
- package/dist/cli/auth.d.ts +114 -0
- package/dist/cli/auth.js +15 -0
- package/dist/cli/profile.cjs +16222 -0
- package/dist/cli/profile.d.cts +79 -0
- package/dist/cli/profile.d.ts +79 -0
- package/dist/cli/profile.js +866 -0
- package/dist/cli/server.cjs +1816 -422
- package/dist/cli/server.js +310 -10
- package/dist/index.cjs +1772 -414
- package/dist/index.d.cts +141 -395
- package/dist/index.d.ts +141 -395
- package/dist/index.js +203 -8
- package/dist/types-BxiRblC7.d.cts +327 -0
- package/dist/types-BxiRblC7.d.ts +327 -0
- package/package.json +3 -2
- package/skills/opensteer/SKILL.md +34 -5
- package/skills/opensteer/references/cli-reference.md +10 -8
- package/skills/opensteer/references/sdk-reference.md +14 -3
package/dist/index.cjs
CHANGED
|
@@ -421,9 +421,12 @@ var init_extractor = __esm({
|
|
|
421
421
|
var index_exports = {};
|
|
422
422
|
__export(index_exports, {
|
|
423
423
|
ActionWsClient: () => ActionWsClient,
|
|
424
|
+
BrowserProfileClient: () => BrowserProfileClient,
|
|
425
|
+
CdpOverlayCursorRenderer: () => CdpOverlayCursorRenderer,
|
|
424
426
|
CloudCdpClient: () => CloudCdpClient,
|
|
425
427
|
CloudSessionClient: () => CloudSessionClient,
|
|
426
428
|
CounterResolutionError: () => CounterResolutionError,
|
|
429
|
+
CursorController: () => CursorController,
|
|
427
430
|
ElementPathError: () => ElementPathError,
|
|
428
431
|
LocalSelectorStorage: () => LocalSelectorStorage,
|
|
429
432
|
OPENSTEER_HIDDEN_ATTR: () => OPENSTEER_HIDDEN_ATTR,
|
|
@@ -445,6 +448,7 @@ __export(index_exports, {
|
|
|
445
448
|
OpensteerAgentProviderError: () => OpensteerAgentProviderError,
|
|
446
449
|
OpensteerCloudError: () => OpensteerCloudError,
|
|
447
450
|
OpensteerCuaAgentHandler: () => OpensteerCuaAgentHandler,
|
|
451
|
+
SvgCursorRenderer: () => SvgCursorRenderer,
|
|
448
452
|
buildArrayFieldPathCandidates: () => buildArrayFieldPathCandidates,
|
|
449
453
|
buildElementPathFromHandle: () => buildElementPathFromHandle,
|
|
450
454
|
buildElementPathFromSelector: () => buildElementPathFromSelector,
|
|
@@ -489,6 +493,7 @@ __export(index_exports, {
|
|
|
489
493
|
performInput: () => performInput,
|
|
490
494
|
performScroll: () => performScroll,
|
|
491
495
|
performSelect: () => performSelect,
|
|
496
|
+
planSnappyCursorMotion: () => planSnappyCursorMotion,
|
|
492
497
|
prepareSnapshot: () => prepareSnapshot,
|
|
493
498
|
pressKey: () => pressKey,
|
|
494
499
|
queryAllByElementPath: () => queryAllByElementPath,
|
|
@@ -510,7 +515,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
510
515
|
var import_crypto = require("crypto");
|
|
511
516
|
|
|
512
517
|
// src/browser/pool.ts
|
|
513
|
-
var
|
|
518
|
+
var import_playwright2 = require("playwright");
|
|
514
519
|
|
|
515
520
|
// src/browser/cdp-proxy.ts
|
|
516
521
|
var import_ws = __toESM(require("ws"), 1);
|
|
@@ -849,6 +854,19 @@ function errorMessage(error) {
|
|
|
849
854
|
return error instanceof Error ? error.message : String(error);
|
|
850
855
|
}
|
|
851
856
|
|
|
857
|
+
// src/browser/chromium-profile.ts
|
|
858
|
+
var import_node_util = require("util");
|
|
859
|
+
var import_node_child_process2 = require("child_process");
|
|
860
|
+
var import_node_crypto = require("crypto");
|
|
861
|
+
var import_promises = require("fs/promises");
|
|
862
|
+
var import_node_fs = require("fs");
|
|
863
|
+
var import_node_path = require("path");
|
|
864
|
+
var import_node_os = require("os");
|
|
865
|
+
var import_playwright = require("playwright");
|
|
866
|
+
|
|
867
|
+
// src/auth/keychain-store.ts
|
|
868
|
+
var import_node_child_process = require("child_process");
|
|
869
|
+
|
|
852
870
|
// src/browser/chrome.ts
|
|
853
871
|
var import_os = require("os");
|
|
854
872
|
var import_path = require("path");
|
|
@@ -859,9 +877,61 @@ function expandHome(p) {
|
|
|
859
877
|
return p;
|
|
860
878
|
}
|
|
861
879
|
|
|
880
|
+
// src/browser/chromium-profile.ts
|
|
881
|
+
var execFileAsync = (0, import_node_util.promisify)(import_node_child_process2.execFile);
|
|
882
|
+
function directoryExists(filePath) {
|
|
883
|
+
try {
|
|
884
|
+
return (0, import_node_fs.statSync)(filePath).isDirectory();
|
|
885
|
+
} catch {
|
|
886
|
+
return false;
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
function fileExists(filePath) {
|
|
890
|
+
try {
|
|
891
|
+
return (0, import_node_fs.statSync)(filePath).isFile();
|
|
892
|
+
} catch {
|
|
893
|
+
return false;
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
function resolveCookieDbPath(profileDir) {
|
|
897
|
+
const candidates = [(0, import_node_path.join)(profileDir, "Network", "Cookies"), (0, import_node_path.join)(profileDir, "Cookies")];
|
|
898
|
+
for (const candidate of candidates) {
|
|
899
|
+
if (fileExists(candidate)) {
|
|
900
|
+
return candidate;
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
return null;
|
|
904
|
+
}
|
|
905
|
+
function resolvePersistentChromiumLaunchProfile(inputPath) {
|
|
906
|
+
const expandedPath = expandHome(inputPath.trim());
|
|
907
|
+
if (!expandedPath) {
|
|
908
|
+
return {
|
|
909
|
+
userDataDir: inputPath
|
|
910
|
+
};
|
|
911
|
+
}
|
|
912
|
+
if (fileExists(expandedPath) && (0, import_node_path.basename)(expandedPath) === "Cookies") {
|
|
913
|
+
const directParent = (0, import_node_path.dirname)(expandedPath);
|
|
914
|
+
const profileDir = (0, import_node_path.basename)(directParent) === "Network" ? (0, import_node_path.dirname)(directParent) : directParent;
|
|
915
|
+
return {
|
|
916
|
+
userDataDir: (0, import_node_path.dirname)(profileDir),
|
|
917
|
+
profileDirectory: (0, import_node_path.basename)(profileDir)
|
|
918
|
+
};
|
|
919
|
+
}
|
|
920
|
+
if (directoryExists(expandedPath) && resolveCookieDbPath(expandedPath) && fileExists((0, import_node_path.join)((0, import_node_path.dirname)(expandedPath), "Local State"))) {
|
|
921
|
+
return {
|
|
922
|
+
userDataDir: (0, import_node_path.dirname)(expandedPath),
|
|
923
|
+
profileDirectory: (0, import_node_path.basename)(expandedPath)
|
|
924
|
+
};
|
|
925
|
+
}
|
|
926
|
+
return {
|
|
927
|
+
userDataDir: expandedPath
|
|
928
|
+
};
|
|
929
|
+
}
|
|
930
|
+
|
|
862
931
|
// src/browser/pool.ts
|
|
863
932
|
var BrowserPool = class {
|
|
864
933
|
browser = null;
|
|
934
|
+
persistentContext = null;
|
|
865
935
|
cdpProxy = null;
|
|
866
936
|
defaults;
|
|
867
937
|
constructor(defaults = {}) {
|
|
@@ -877,16 +947,23 @@ var BrowserPool = class {
|
|
|
877
947
|
if (connectUrl) {
|
|
878
948
|
return this.connectToRunning(connectUrl, options.timeout);
|
|
879
949
|
}
|
|
880
|
-
if (
|
|
881
|
-
return this.
|
|
950
|
+
if (profileDir) {
|
|
951
|
+
return this.launchPersistentProfile(options, channel, profileDir);
|
|
952
|
+
}
|
|
953
|
+
if (channel) {
|
|
954
|
+
return this.launchWithChannel(options, channel);
|
|
882
955
|
}
|
|
883
956
|
return this.launchSandbox(options);
|
|
884
957
|
}
|
|
885
958
|
async close() {
|
|
886
959
|
const browser = this.browser;
|
|
960
|
+
const persistentContext = this.persistentContext;
|
|
887
961
|
this.browser = null;
|
|
962
|
+
this.persistentContext = null;
|
|
888
963
|
try {
|
|
889
|
-
if (
|
|
964
|
+
if (persistentContext) {
|
|
965
|
+
await persistentContext.close();
|
|
966
|
+
} else if (browser) {
|
|
890
967
|
await browser.close();
|
|
891
968
|
}
|
|
892
969
|
} finally {
|
|
@@ -908,10 +985,11 @@ var BrowserPool = class {
|
|
|
908
985
|
const target = targets[0];
|
|
909
986
|
this.cdpProxy = new CDPProxy(browserWsUrl, target.id);
|
|
910
987
|
const proxyWsUrl = await this.cdpProxy.start();
|
|
911
|
-
browser = await
|
|
988
|
+
browser = await import_playwright2.chromium.connectOverCDP(proxyWsUrl, {
|
|
912
989
|
timeout: timeout ?? 3e4
|
|
913
990
|
});
|
|
914
991
|
this.browser = browser;
|
|
992
|
+
this.persistentContext = null;
|
|
915
993
|
const contexts = browser.contexts();
|
|
916
994
|
if (contexts.length === 0) {
|
|
917
995
|
throw new Error(
|
|
@@ -927,46 +1005,66 @@ var BrowserPool = class {
|
|
|
927
1005
|
await browser.close().catch(() => void 0);
|
|
928
1006
|
}
|
|
929
1007
|
this.browser = null;
|
|
1008
|
+
this.persistentContext = null;
|
|
930
1009
|
this.cdpProxy?.close();
|
|
931
1010
|
this.cdpProxy = null;
|
|
932
1011
|
throw error;
|
|
933
1012
|
}
|
|
934
1013
|
}
|
|
935
|
-
async
|
|
1014
|
+
async launchPersistentProfile(options, channel, profileDir) {
|
|
936
1015
|
const args = [];
|
|
937
|
-
|
|
938
|
-
|
|
1016
|
+
const launchProfile = resolvePersistentChromiumLaunchProfile(profileDir);
|
|
1017
|
+
if (launchProfile.profileDirectory) {
|
|
1018
|
+
args.push(`--profile-directory=${launchProfile.profileDirectory}`);
|
|
1019
|
+
}
|
|
1020
|
+
const context = await import_playwright2.chromium.launchPersistentContext(
|
|
1021
|
+
launchProfile.userDataDir,
|
|
1022
|
+
{
|
|
1023
|
+
channel,
|
|
1024
|
+
headless: options.headless ?? this.defaults.headless,
|
|
1025
|
+
executablePath: options.executablePath ?? this.defaults.executablePath ?? void 0,
|
|
1026
|
+
slowMo: options.slowMo ?? this.defaults.slowMo ?? 0,
|
|
1027
|
+
timeout: options.timeout,
|
|
1028
|
+
...options.context || {},
|
|
1029
|
+
args
|
|
1030
|
+
}
|
|
1031
|
+
);
|
|
1032
|
+
const browser = context.browser();
|
|
1033
|
+
if (!browser) {
|
|
1034
|
+
await context.close().catch(() => void 0);
|
|
1035
|
+
throw new Error("Persistent browser launch did not expose a browser instance.");
|
|
939
1036
|
}
|
|
940
|
-
|
|
1037
|
+
this.browser = browser;
|
|
1038
|
+
this.persistentContext = context;
|
|
1039
|
+
const pages = context.pages();
|
|
1040
|
+
const page = pages.length > 0 ? pages[0] : await context.newPage();
|
|
1041
|
+
return { browser, context, page, isExternal: false };
|
|
1042
|
+
}
|
|
1043
|
+
async launchWithChannel(options, channel) {
|
|
1044
|
+
const browser = await import_playwright2.chromium.launch({
|
|
941
1045
|
channel,
|
|
942
1046
|
headless: options.headless ?? this.defaults.headless,
|
|
943
1047
|
executablePath: options.executablePath ?? this.defaults.executablePath ?? void 0,
|
|
944
1048
|
slowMo: options.slowMo ?? this.defaults.slowMo ?? 0,
|
|
945
|
-
|
|
1049
|
+
timeout: options.timeout
|
|
946
1050
|
});
|
|
947
1051
|
this.browser = browser;
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
if (contexts.length > 0) {
|
|
952
|
-
context = contexts[0];
|
|
953
|
-
const pages = context.pages();
|
|
954
|
-
page = pages.length > 0 ? pages[0] : await context.newPage();
|
|
955
|
-
} else {
|
|
956
|
-
context = await browser.newContext(options.context || {});
|
|
957
|
-
page = await context.newPage();
|
|
958
|
-
}
|
|
1052
|
+
this.persistentContext = null;
|
|
1053
|
+
const context = await browser.newContext(options.context || {});
|
|
1054
|
+
const page = await context.newPage();
|
|
959
1055
|
return { browser, context, page, isExternal: false };
|
|
960
1056
|
}
|
|
961
1057
|
async launchSandbox(options) {
|
|
962
|
-
const browser = await
|
|
1058
|
+
const browser = await import_playwright2.chromium.launch({
|
|
963
1059
|
headless: options.headless ?? this.defaults.headless,
|
|
964
1060
|
executablePath: options.executablePath ?? this.defaults.executablePath ?? void 0,
|
|
965
|
-
slowMo: options.slowMo ?? this.defaults.slowMo ?? 0
|
|
1061
|
+
slowMo: options.slowMo ?? this.defaults.slowMo ?? 0,
|
|
1062
|
+
timeout: options.timeout
|
|
966
1063
|
});
|
|
967
1064
|
const context = await browser.newContext(options.context || {});
|
|
968
1065
|
const page = await context.newPage();
|
|
969
1066
|
this.browser = browser;
|
|
1067
|
+
this.persistentContext = null;
|
|
970
1068
|
return { browser, context, page, isExternal: false };
|
|
971
1069
|
}
|
|
972
1070
|
};
|
|
@@ -1248,6 +1346,10 @@ var DEFAULT_CONFIG = {
|
|
|
1248
1346
|
storage: {
|
|
1249
1347
|
rootDir: process.cwd()
|
|
1250
1348
|
},
|
|
1349
|
+
cursor: {
|
|
1350
|
+
enabled: false,
|
|
1351
|
+
profile: "snappy"
|
|
1352
|
+
},
|
|
1251
1353
|
model: "gpt-5.1",
|
|
1252
1354
|
debug: false
|
|
1253
1355
|
};
|
|
@@ -1301,7 +1403,7 @@ function loadDotenvValues(rootDir, baseEnv, options = {}) {
|
|
|
1301
1403
|
return values;
|
|
1302
1404
|
}
|
|
1303
1405
|
function resolveEnv(rootDir, options = {}) {
|
|
1304
|
-
const baseEnv = process.env;
|
|
1406
|
+
const baseEnv = options.baseEnv ?? process.env;
|
|
1305
1407
|
const dotenvValues = loadDotenvValues(rootDir, baseEnv, options);
|
|
1306
1408
|
return {
|
|
1307
1409
|
...dotenvValues,
|
|
@@ -1450,6 +1552,11 @@ function resolveOpensteerApiKey(env) {
|
|
|
1450
1552
|
if (!value) return void 0;
|
|
1451
1553
|
return value;
|
|
1452
1554
|
}
|
|
1555
|
+
function resolveOpensteerAccessToken(env) {
|
|
1556
|
+
const value = env.OPENSTEER_ACCESS_TOKEN?.trim();
|
|
1557
|
+
if (!value) return void 0;
|
|
1558
|
+
return value;
|
|
1559
|
+
}
|
|
1453
1560
|
function resolveOpensteerBaseUrl(env) {
|
|
1454
1561
|
const value = env.OPENSTEER_BASE_URL?.trim();
|
|
1455
1562
|
if (!value) return void 0;
|
|
@@ -1458,12 +1565,71 @@ function resolveOpensteerBaseUrl(env) {
|
|
|
1458
1565
|
function resolveOpensteerAuthScheme(env) {
|
|
1459
1566
|
return parseAuthScheme(env.OPENSTEER_AUTH_SCHEME, "OPENSTEER_AUTH_SCHEME");
|
|
1460
1567
|
}
|
|
1568
|
+
function resolveOpensteerCloudProfileId(env) {
|
|
1569
|
+
const value = env.OPENSTEER_CLOUD_PROFILE_ID?.trim();
|
|
1570
|
+
if (!value) return void 0;
|
|
1571
|
+
return value;
|
|
1572
|
+
}
|
|
1573
|
+
function resolveOpensteerCloudProfileReuseIfActive(env) {
|
|
1574
|
+
return parseBool(env.OPENSTEER_CLOUD_PROFILE_REUSE_IF_ACTIVE);
|
|
1575
|
+
}
|
|
1576
|
+
function parseCloudBrowserProfileReuseIfActive(value) {
|
|
1577
|
+
if (value == null) return void 0;
|
|
1578
|
+
if (typeof value !== "boolean") {
|
|
1579
|
+
throw new Error(
|
|
1580
|
+
`Invalid cloud.browserProfile.reuseIfActive value "${String(
|
|
1581
|
+
value
|
|
1582
|
+
)}". Use true or false.`
|
|
1583
|
+
);
|
|
1584
|
+
}
|
|
1585
|
+
return value;
|
|
1586
|
+
}
|
|
1587
|
+
function normalizeCloudBrowserProfileOptions(value, source) {
|
|
1588
|
+
if (value == null) {
|
|
1589
|
+
return void 0;
|
|
1590
|
+
}
|
|
1591
|
+
if (typeof value !== "object" || Array.isArray(value)) {
|
|
1592
|
+
throw new Error(
|
|
1593
|
+
`Invalid ${source} value "${String(value)}". Use an object with profileId and optional reuseIfActive.`
|
|
1594
|
+
);
|
|
1595
|
+
}
|
|
1596
|
+
const record = value;
|
|
1597
|
+
const rawProfileId = record.profileId;
|
|
1598
|
+
if (typeof rawProfileId !== "string" || !rawProfileId.trim()) {
|
|
1599
|
+
throw new Error(
|
|
1600
|
+
`${source}.profileId must be a non-empty string when browserProfile is provided.`
|
|
1601
|
+
);
|
|
1602
|
+
}
|
|
1603
|
+
return {
|
|
1604
|
+
profileId: rawProfileId.trim(),
|
|
1605
|
+
reuseIfActive: parseCloudBrowserProfileReuseIfActive(record.reuseIfActive)
|
|
1606
|
+
};
|
|
1607
|
+
}
|
|
1608
|
+
function resolveEnvCloudBrowserProfile(profileId, reuseIfActive) {
|
|
1609
|
+
if (reuseIfActive !== void 0 && !profileId) {
|
|
1610
|
+
throw new Error(
|
|
1611
|
+
"OPENSTEER_CLOUD_PROFILE_REUSE_IF_ACTIVE requires OPENSTEER_CLOUD_PROFILE_ID."
|
|
1612
|
+
);
|
|
1613
|
+
}
|
|
1614
|
+
if (!profileId) {
|
|
1615
|
+
return void 0;
|
|
1616
|
+
}
|
|
1617
|
+
return {
|
|
1618
|
+
profileId,
|
|
1619
|
+
reuseIfActive
|
|
1620
|
+
};
|
|
1621
|
+
}
|
|
1461
1622
|
function normalizeCloudOptions(value) {
|
|
1462
1623
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
1463
1624
|
return void 0;
|
|
1464
1625
|
}
|
|
1465
1626
|
return value;
|
|
1466
1627
|
}
|
|
1628
|
+
function normalizeNonEmptyString(value) {
|
|
1629
|
+
if (typeof value !== "string") return void 0;
|
|
1630
|
+
const normalized = value.trim();
|
|
1631
|
+
return normalized.length ? normalized : void 0;
|
|
1632
|
+
}
|
|
1467
1633
|
function parseCloudEnabled(value, source) {
|
|
1468
1634
|
if (value == null) return void 0;
|
|
1469
1635
|
if (typeof value === "boolean") return value;
|
|
@@ -1492,8 +1658,8 @@ function resolveCloudSelection(config, env = process.env) {
|
|
|
1492
1658
|
source: "default"
|
|
1493
1659
|
};
|
|
1494
1660
|
}
|
|
1495
|
-
function resolveConfigWithEnv(input = {}) {
|
|
1496
|
-
const processEnv = process.env;
|
|
1661
|
+
function resolveConfigWithEnv(input = {}, options = {}) {
|
|
1662
|
+
const processEnv = options.env ?? process.env;
|
|
1497
1663
|
const debugHint = typeof input.debug === "boolean" ? input.debug : parseBool(processEnv.OPENSTEER_DEBUG) === true;
|
|
1498
1664
|
const initialRootDir = input.storage?.rootDir ?? process.cwd();
|
|
1499
1665
|
const runtimeDefaults = mergeDeep(DEFAULT_CONFIG, {
|
|
@@ -1511,7 +1677,8 @@ function resolveConfigWithEnv(input = {}) {
|
|
|
1511
1677
|
const fileRootDir = typeof fileConfig.storage?.rootDir === "string" ? fileConfig.storage.rootDir : void 0;
|
|
1512
1678
|
const envRootDir = input.storage?.rootDir ?? fileRootDir ?? initialRootDir;
|
|
1513
1679
|
const env = resolveEnv(envRootDir, {
|
|
1514
|
-
debug: debugHint
|
|
1680
|
+
debug: debugHint,
|
|
1681
|
+
baseEnv: processEnv
|
|
1515
1682
|
});
|
|
1516
1683
|
if (env.OPENSTEER_AI_MODEL) {
|
|
1517
1684
|
throw new Error(
|
|
@@ -1532,6 +1699,9 @@ function resolveConfigWithEnv(input = {}) {
|
|
|
1532
1699
|
channel: env.OPENSTEER_CHANNEL || void 0,
|
|
1533
1700
|
profileDir: env.OPENSTEER_PROFILE_DIR || void 0
|
|
1534
1701
|
},
|
|
1702
|
+
cursor: {
|
|
1703
|
+
enabled: parseBool(env.OPENSTEER_CURSOR)
|
|
1704
|
+
},
|
|
1535
1705
|
model: env.OPENSTEER_MODEL || void 0,
|
|
1536
1706
|
debug: parseBool(env.OPENSTEER_DEBUG)
|
|
1537
1707
|
};
|
|
@@ -1539,13 +1709,27 @@ function resolveConfigWithEnv(input = {}) {
|
|
|
1539
1709
|
const mergedWithEnv = mergeDeep(mergedWithFile, envConfig);
|
|
1540
1710
|
const resolved = mergeDeep(mergedWithEnv, input);
|
|
1541
1711
|
const envApiKey = resolveOpensteerApiKey(env);
|
|
1712
|
+
const envAccessTokenRaw = resolveOpensteerAccessToken(env);
|
|
1542
1713
|
const envBaseUrl = resolveOpensteerBaseUrl(env);
|
|
1543
1714
|
const envAuthScheme = resolveOpensteerAuthScheme(env);
|
|
1715
|
+
if (envApiKey && envAccessTokenRaw) {
|
|
1716
|
+
throw new Error(
|
|
1717
|
+
"OPENSTEER_API_KEY and OPENSTEER_ACCESS_TOKEN are mutually exclusive. Set only one."
|
|
1718
|
+
);
|
|
1719
|
+
}
|
|
1720
|
+
const envAccessToken = envAccessTokenRaw || (envAuthScheme === "bearer" ? envApiKey : void 0);
|
|
1721
|
+
const envApiCredential = envAuthScheme === "bearer" && !envAccessTokenRaw ? void 0 : envApiKey;
|
|
1722
|
+
const envCloudProfileId = resolveOpensteerCloudProfileId(env);
|
|
1723
|
+
const envCloudProfileReuseIfActive = resolveOpensteerCloudProfileReuseIfActive(env);
|
|
1544
1724
|
const envCloudAnnounce = parseCloudAnnounce(
|
|
1545
1725
|
env.OPENSTEER_REMOTE_ANNOUNCE,
|
|
1546
1726
|
"OPENSTEER_REMOTE_ANNOUNCE"
|
|
1547
1727
|
);
|
|
1548
1728
|
const inputCloudOptions = normalizeCloudOptions(input.cloud);
|
|
1729
|
+
const inputCloudBrowserProfile = normalizeCloudBrowserProfileOptions(
|
|
1730
|
+
inputCloudOptions?.browserProfile,
|
|
1731
|
+
"cloud.browserProfile"
|
|
1732
|
+
);
|
|
1549
1733
|
const inputAuthScheme = parseAuthScheme(
|
|
1550
1734
|
inputCloudOptions?.authScheme,
|
|
1551
1735
|
"cloud.authScheme"
|
|
@@ -1557,26 +1741,65 @@ function resolveConfigWithEnv(input = {}) {
|
|
|
1557
1741
|
const inputHasCloudApiKey = Boolean(
|
|
1558
1742
|
inputCloudOptions && Object.prototype.hasOwnProperty.call(inputCloudOptions, "apiKey")
|
|
1559
1743
|
);
|
|
1744
|
+
const inputHasCloudAccessToken = Boolean(
|
|
1745
|
+
inputCloudOptions && Object.prototype.hasOwnProperty.call(inputCloudOptions, "accessToken")
|
|
1746
|
+
);
|
|
1560
1747
|
const inputHasCloudBaseUrl = Boolean(
|
|
1561
1748
|
inputCloudOptions && Object.prototype.hasOwnProperty.call(inputCloudOptions, "baseUrl")
|
|
1562
1749
|
);
|
|
1750
|
+
if (normalizeNonEmptyString(inputCloudOptions?.apiKey) && normalizeNonEmptyString(inputCloudOptions?.accessToken)) {
|
|
1751
|
+
throw new Error(
|
|
1752
|
+
"cloud.apiKey and cloud.accessToken are mutually exclusive. Set only one."
|
|
1753
|
+
);
|
|
1754
|
+
}
|
|
1563
1755
|
const cloudSelection = resolveCloudSelection({
|
|
1564
1756
|
cloud: resolved.cloud
|
|
1565
1757
|
}, env);
|
|
1566
1758
|
if (cloudSelection.cloud) {
|
|
1567
1759
|
const resolvedCloud = normalizeCloudOptions(resolved.cloud) ?? {};
|
|
1568
|
-
const
|
|
1760
|
+
const {
|
|
1761
|
+
apiKey: resolvedCloudApiKeyRaw,
|
|
1762
|
+
accessToken: resolvedCloudAccessTokenRaw,
|
|
1763
|
+
...resolvedCloudRest
|
|
1764
|
+
} = resolvedCloud;
|
|
1765
|
+
if (normalizeNonEmptyString(resolvedCloudApiKeyRaw) && normalizeNonEmptyString(resolvedCloudAccessTokenRaw)) {
|
|
1766
|
+
throw new Error(
|
|
1767
|
+
"Cloud config cannot include both apiKey and accessToken at the same time."
|
|
1768
|
+
);
|
|
1769
|
+
}
|
|
1770
|
+
const resolvedCloudBrowserProfile = normalizeCloudBrowserProfileOptions(
|
|
1771
|
+
resolvedCloud.browserProfile,
|
|
1772
|
+
"resolved.cloud.browserProfile"
|
|
1773
|
+
);
|
|
1774
|
+
const envCloudBrowserProfile = resolveEnvCloudBrowserProfile(
|
|
1775
|
+
envCloudProfileId,
|
|
1776
|
+
envCloudProfileReuseIfActive
|
|
1777
|
+
);
|
|
1778
|
+
const browserProfile = inputCloudBrowserProfile ?? envCloudBrowserProfile ?? resolvedCloudBrowserProfile;
|
|
1779
|
+
let authScheme = inputAuthScheme ?? envAuthScheme ?? parseAuthScheme(resolvedCloud.authScheme, "cloud.authScheme") ?? "api-key";
|
|
1569
1780
|
const announce = inputCloudAnnounce ?? envCloudAnnounce ?? parseCloudAnnounce(resolvedCloud.announce, "cloud.announce") ?? "always";
|
|
1781
|
+
const credentialOverriddenByInput = inputHasCloudApiKey || inputHasCloudAccessToken;
|
|
1782
|
+
let apiKey = normalizeNonEmptyString(resolvedCloudApiKeyRaw);
|
|
1783
|
+
let accessToken = normalizeNonEmptyString(resolvedCloudAccessTokenRaw);
|
|
1784
|
+
if (!credentialOverriddenByInput) {
|
|
1785
|
+
if (envAccessToken) {
|
|
1786
|
+
accessToken = envAccessToken;
|
|
1787
|
+
apiKey = void 0;
|
|
1788
|
+
} else if (envApiCredential) {
|
|
1789
|
+
apiKey = envApiCredential;
|
|
1790
|
+
accessToken = void 0;
|
|
1791
|
+
}
|
|
1792
|
+
}
|
|
1793
|
+
if (accessToken) {
|
|
1794
|
+
authScheme = "bearer";
|
|
1795
|
+
}
|
|
1570
1796
|
resolved.cloud = {
|
|
1571
|
-
...
|
|
1797
|
+
...resolvedCloudRest,
|
|
1798
|
+
...inputHasCloudApiKey ? { apiKey: resolvedCloudApiKeyRaw } : apiKey ? { apiKey } : {},
|
|
1799
|
+
...inputHasCloudAccessToken ? { accessToken: resolvedCloudAccessTokenRaw } : accessToken ? { accessToken } : {},
|
|
1572
1800
|
authScheme,
|
|
1573
|
-
announce
|
|
1574
|
-
|
|
1575
|
-
}
|
|
1576
|
-
if (envApiKey && cloudSelection.cloud && !inputHasCloudApiKey) {
|
|
1577
|
-
resolved.cloud = {
|
|
1578
|
-
...normalizeCloudOptions(resolved.cloud) ?? {},
|
|
1579
|
-
apiKey: envApiKey
|
|
1801
|
+
announce,
|
|
1802
|
+
...browserProfile ? { browserProfile } : {}
|
|
1580
1803
|
};
|
|
1581
1804
|
}
|
|
1582
1805
|
if (envBaseUrl && cloudSelection.cloud && !inputHasCloudBaseUrl) {
|
|
@@ -4969,6 +5192,16 @@ async function probeActionabilityState(element) {
|
|
|
4969
5192
|
|
|
4970
5193
|
// src/extract-value-normalization.ts
|
|
4971
5194
|
var URL_LIST_ATTRIBUTES = /* @__PURE__ */ new Set(["srcset", "imagesrcset", "ping"]);
|
|
5195
|
+
var IFRAME_URL_ATTRIBUTES = /* @__PURE__ */ new Set([
|
|
5196
|
+
"href",
|
|
5197
|
+
"src",
|
|
5198
|
+
"srcset",
|
|
5199
|
+
"imagesrcset",
|
|
5200
|
+
"action",
|
|
5201
|
+
"formaction",
|
|
5202
|
+
"poster",
|
|
5203
|
+
"ping"
|
|
5204
|
+
]);
|
|
4972
5205
|
function normalizeExtractedValue(raw, attribute) {
|
|
4973
5206
|
if (raw == null) return null;
|
|
4974
5207
|
const rawText = String(raw);
|
|
@@ -4984,6 +5217,19 @@ function normalizeExtractedValue(raw, attribute) {
|
|
|
4984
5217
|
const text = rawText.replace(/\s+/g, " ").trim();
|
|
4985
5218
|
return text || null;
|
|
4986
5219
|
}
|
|
5220
|
+
function resolveExtractedValueInContext(normalizedValue, options) {
|
|
5221
|
+
if (normalizedValue == null) return null;
|
|
5222
|
+
const normalizedAttribute = String(options.attribute || "").trim().toLowerCase();
|
|
5223
|
+
if (!options.insideIframe) return normalizedValue;
|
|
5224
|
+
if (!IFRAME_URL_ATTRIBUTES.has(normalizedAttribute)) return normalizedValue;
|
|
5225
|
+
const baseURI = String(options.baseURI || "").trim();
|
|
5226
|
+
if (!baseURI) return normalizedValue;
|
|
5227
|
+
try {
|
|
5228
|
+
return new URL(normalizedValue, baseURI).href;
|
|
5229
|
+
} catch {
|
|
5230
|
+
return normalizedValue;
|
|
5231
|
+
}
|
|
5232
|
+
}
|
|
4987
5233
|
function pickSingleListAttributeValue(attribute, raw) {
|
|
4988
5234
|
if (attribute === "ping") {
|
|
4989
5235
|
const firstUrl = raw.trim().split(/\s+/)[0] || "";
|
|
@@ -5139,6 +5385,36 @@ function readDescriptorToken(value, index) {
|
|
|
5139
5385
|
};
|
|
5140
5386
|
}
|
|
5141
5387
|
|
|
5388
|
+
// src/extract-value-reader.ts
|
|
5389
|
+
async function readExtractedValueFromHandle(element, options) {
|
|
5390
|
+
const insideIframe = await isElementInsideIframe(element);
|
|
5391
|
+
const payload = await element.evaluate(
|
|
5392
|
+
(target, browserOptions) => {
|
|
5393
|
+
const ownerDocument = target.ownerDocument;
|
|
5394
|
+
return {
|
|
5395
|
+
raw: browserOptions.attribute ? target.getAttribute(browserOptions.attribute) : target.textContent,
|
|
5396
|
+
baseURI: ownerDocument?.baseURI || null
|
|
5397
|
+
};
|
|
5398
|
+
},
|
|
5399
|
+
{
|
|
5400
|
+
attribute: options.attribute
|
|
5401
|
+
}
|
|
5402
|
+
);
|
|
5403
|
+
const normalizedValue = normalizeExtractedValue(
|
|
5404
|
+
payload.raw,
|
|
5405
|
+
options.attribute
|
|
5406
|
+
);
|
|
5407
|
+
return resolveExtractedValueInContext(normalizedValue, {
|
|
5408
|
+
attribute: options.attribute,
|
|
5409
|
+
baseURI: payload.baseURI,
|
|
5410
|
+
insideIframe
|
|
5411
|
+
});
|
|
5412
|
+
}
|
|
5413
|
+
async function isElementInsideIframe(element) {
|
|
5414
|
+
const ownerFrame = await element.ownerFrame();
|
|
5415
|
+
return !!ownerFrame?.parentFrame();
|
|
5416
|
+
}
|
|
5417
|
+
|
|
5142
5418
|
// src/html/counter-runtime.ts
|
|
5143
5419
|
var CounterResolutionError = class extends Error {
|
|
5144
5420
|
code;
|
|
@@ -5161,13 +5437,14 @@ async function resolveCounterElement(page, counter) {
|
|
|
5161
5437
|
if (entry.count > 1) {
|
|
5162
5438
|
throw buildCounterAmbiguousError(counter);
|
|
5163
5439
|
}
|
|
5164
|
-
const
|
|
5165
|
-
|
|
5166
|
-
|
|
5167
|
-
|
|
5440
|
+
const resolution = await resolveCounterElementInFrame(entry.frame, normalized);
|
|
5441
|
+
if (resolution.status === "ambiguous") {
|
|
5442
|
+
throw buildCounterAmbiguousError(counter);
|
|
5443
|
+
}
|
|
5444
|
+
if (resolution.status === "missing") {
|
|
5168
5445
|
throw buildCounterNotFoundError(counter);
|
|
5169
5446
|
}
|
|
5170
|
-
return element;
|
|
5447
|
+
return resolution.element;
|
|
5171
5448
|
}
|
|
5172
5449
|
async function resolveCountersBatch(page, requests) {
|
|
5173
5450
|
const out = {};
|
|
@@ -5181,41 +5458,57 @@ async function resolveCountersBatch(page, requests) {
|
|
|
5181
5458
|
}
|
|
5182
5459
|
}
|
|
5183
5460
|
const valueCache = /* @__PURE__ */ new Map();
|
|
5184
|
-
|
|
5185
|
-
|
|
5186
|
-
|
|
5187
|
-
|
|
5188
|
-
|
|
5189
|
-
|
|
5190
|
-
|
|
5191
|
-
|
|
5192
|
-
|
|
5193
|
-
|
|
5194
|
-
|
|
5195
|
-
|
|
5196
|
-
|
|
5197
|
-
|
|
5198
|
-
|
|
5199
|
-
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
normalized
|
|
5203
|
-
|
|
5204
|
-
|
|
5205
|
-
|
|
5206
|
-
|
|
5207
|
-
|
|
5208
|
-
|
|
5209
|
-
|
|
5210
|
-
|
|
5211
|
-
|
|
5461
|
+
const elementCache = /* @__PURE__ */ new Map();
|
|
5462
|
+
try {
|
|
5463
|
+
for (const request of requests) {
|
|
5464
|
+
const normalized = normalizeCounter(request.counter);
|
|
5465
|
+
if (normalized == null) {
|
|
5466
|
+
out[request.key] = null;
|
|
5467
|
+
continue;
|
|
5468
|
+
}
|
|
5469
|
+
const entry = scan.get(normalized);
|
|
5470
|
+
if (!entry || entry.count <= 0 || !entry.frame) {
|
|
5471
|
+
out[request.key] = null;
|
|
5472
|
+
continue;
|
|
5473
|
+
}
|
|
5474
|
+
const cacheKey = `${normalized}:${request.attribute || ""}`;
|
|
5475
|
+
if (valueCache.has(cacheKey)) {
|
|
5476
|
+
out[request.key] = valueCache.get(cacheKey);
|
|
5477
|
+
continue;
|
|
5478
|
+
}
|
|
5479
|
+
if (!elementCache.has(normalized)) {
|
|
5480
|
+
elementCache.set(
|
|
5481
|
+
normalized,
|
|
5482
|
+
await resolveCounterElementInFrame(entry.frame, normalized)
|
|
5483
|
+
);
|
|
5484
|
+
}
|
|
5485
|
+
const resolution = elementCache.get(normalized);
|
|
5486
|
+
if (resolution.status === "ambiguous") {
|
|
5487
|
+
throw buildCounterAmbiguousError(normalized);
|
|
5488
|
+
}
|
|
5489
|
+
if (resolution.status === "missing") {
|
|
5490
|
+
valueCache.set(cacheKey, null);
|
|
5491
|
+
out[request.key] = null;
|
|
5492
|
+
continue;
|
|
5493
|
+
}
|
|
5494
|
+
const value = await readCounterValueFromElement(
|
|
5495
|
+
resolution.element,
|
|
5496
|
+
request.attribute
|
|
5497
|
+
);
|
|
5498
|
+
if (value.status === "missing") {
|
|
5499
|
+
await resolution.element.dispose();
|
|
5500
|
+
elementCache.set(normalized, {
|
|
5501
|
+
status: "missing"
|
|
5502
|
+
});
|
|
5503
|
+
valueCache.set(cacheKey, null);
|
|
5504
|
+
out[request.key] = null;
|
|
5505
|
+
continue;
|
|
5506
|
+
}
|
|
5507
|
+
valueCache.set(cacheKey, value.value);
|
|
5508
|
+
out[request.key] = value.value;
|
|
5212
5509
|
}
|
|
5213
|
-
|
|
5214
|
-
|
|
5215
|
-
request.attribute
|
|
5216
|
-
);
|
|
5217
|
-
valueCache.set(cacheKey, normalizedValue);
|
|
5218
|
-
out[request.key] = normalizedValue;
|
|
5510
|
+
} finally {
|
|
5511
|
+
await disposeResolvedCounterElements(elementCache.values());
|
|
5219
5512
|
}
|
|
5220
5513
|
return out;
|
|
5221
5514
|
}
|
|
@@ -5287,73 +5580,79 @@ async function scanCounterOccurrences(page, counters) {
|
|
|
5287
5580
|
}
|
|
5288
5581
|
return out;
|
|
5289
5582
|
}
|
|
5290
|
-
async function
|
|
5291
|
-
|
|
5292
|
-
const
|
|
5293
|
-
|
|
5294
|
-
const
|
|
5295
|
-
|
|
5296
|
-
|
|
5297
|
-
|
|
5298
|
-
|
|
5299
|
-
|
|
5300
|
-
|
|
5301
|
-
|
|
5583
|
+
async function resolveCounterElementInFrame(frame, counter) {
|
|
5584
|
+
try {
|
|
5585
|
+
const handle = await frame.evaluateHandle((targetCounter) => {
|
|
5586
|
+
const matches = [];
|
|
5587
|
+
const walk = (root) => {
|
|
5588
|
+
const children = Array.from(root.children);
|
|
5589
|
+
for (const child of children) {
|
|
5590
|
+
if (child.getAttribute("c") === targetCounter) {
|
|
5591
|
+
matches.push(child);
|
|
5592
|
+
}
|
|
5593
|
+
walk(child);
|
|
5594
|
+
if (child.shadowRoot) {
|
|
5595
|
+
walk(child.shadowRoot);
|
|
5596
|
+
}
|
|
5302
5597
|
}
|
|
5598
|
+
};
|
|
5599
|
+
walk(document);
|
|
5600
|
+
if (!matches.length) {
|
|
5601
|
+
return "missing";
|
|
5303
5602
|
}
|
|
5304
|
-
|
|
5305
|
-
|
|
5306
|
-
|
|
5307
|
-
return
|
|
5603
|
+
if (matches.length > 1) {
|
|
5604
|
+
return "ambiguous";
|
|
5605
|
+
}
|
|
5606
|
+
return matches[0];
|
|
5607
|
+
}, String(counter));
|
|
5608
|
+
const element = handle.asElement();
|
|
5609
|
+
if (element) {
|
|
5610
|
+
return {
|
|
5611
|
+
status: "resolved",
|
|
5612
|
+
element
|
|
5613
|
+
};
|
|
5614
|
+
}
|
|
5615
|
+
const status = await handle.jsonValue();
|
|
5616
|
+
await handle.dispose();
|
|
5617
|
+
return status === "ambiguous" ? { status: "ambiguous" } : { status: "missing" };
|
|
5618
|
+
} catch (error) {
|
|
5619
|
+
if (isRecoverableCounterReadRace(error)) {
|
|
5620
|
+
return {
|
|
5621
|
+
status: "missing"
|
|
5622
|
+
};
|
|
5308
5623
|
}
|
|
5309
|
-
|
|
5310
|
-
}
|
|
5624
|
+
throw error;
|
|
5625
|
+
}
|
|
5311
5626
|
}
|
|
5312
|
-
async function
|
|
5627
|
+
async function readCounterValueFromElement(element, attribute) {
|
|
5313
5628
|
try {
|
|
5314
|
-
return await frame.evaluate(
|
|
5315
|
-
({ targetCounter, attribute: attribute2 }) => {
|
|
5316
|
-
const matches = [];
|
|
5317
|
-
const walk = (root) => {
|
|
5318
|
-
const children = Array.from(root.children);
|
|
5319
|
-
for (const child of children) {
|
|
5320
|
-
if (child.getAttribute("c") === targetCounter) {
|
|
5321
|
-
matches.push(child);
|
|
5322
|
-
}
|
|
5323
|
-
walk(child);
|
|
5324
|
-
if (child.shadowRoot) {
|
|
5325
|
-
walk(child.shadowRoot);
|
|
5326
|
-
}
|
|
5327
|
-
}
|
|
5328
|
-
};
|
|
5329
|
-
walk(document);
|
|
5330
|
-
if (!matches.length) {
|
|
5331
|
-
return {
|
|
5332
|
-
status: "missing"
|
|
5333
|
-
};
|
|
5334
|
-
}
|
|
5335
|
-
if (matches.length > 1) {
|
|
5336
|
-
return {
|
|
5337
|
-
status: "ambiguous"
|
|
5338
|
-
};
|
|
5339
|
-
}
|
|
5340
|
-
const target = matches[0];
|
|
5341
|
-
const value = attribute2 ? target.getAttribute(attribute2) : target.textContent;
|
|
5342
|
-
return {
|
|
5343
|
-
status: "ok",
|
|
5344
|
-
value
|
|
5345
|
-
};
|
|
5346
|
-
},
|
|
5347
|
-
{
|
|
5348
|
-
targetCounter: String(counter),
|
|
5349
|
-
attribute
|
|
5350
|
-
}
|
|
5351
|
-
);
|
|
5352
|
-
} catch {
|
|
5353
5629
|
return {
|
|
5354
|
-
status: "
|
|
5630
|
+
status: "ok",
|
|
5631
|
+
value: await readExtractedValueFromHandle(element, {
|
|
5632
|
+
attribute
|
|
5633
|
+
})
|
|
5355
5634
|
};
|
|
5635
|
+
} catch (error) {
|
|
5636
|
+
if (isRecoverableCounterReadRace(error)) {
|
|
5637
|
+
return {
|
|
5638
|
+
status: "missing"
|
|
5639
|
+
};
|
|
5640
|
+
}
|
|
5641
|
+
throw error;
|
|
5642
|
+
}
|
|
5643
|
+
}
|
|
5644
|
+
async function disposeResolvedCounterElements(resolutions) {
|
|
5645
|
+
const disposals = [];
|
|
5646
|
+
for (const resolution of resolutions) {
|
|
5647
|
+
if (resolution.status !== "resolved") continue;
|
|
5648
|
+
disposals.push(resolution.element.dispose());
|
|
5356
5649
|
}
|
|
5650
|
+
await Promise.all(disposals);
|
|
5651
|
+
}
|
|
5652
|
+
function isRecoverableCounterReadRace(error) {
|
|
5653
|
+
if (!(error instanceof Error)) return false;
|
|
5654
|
+
const message = error.message;
|
|
5655
|
+
return message.includes("Execution context was destroyed") || message.includes("Cannot find context with specified id") || message.includes("Cannot find execution context") || message.includes("Frame was detached") || message.includes("Element is not attached to the DOM") || message.includes("Element is detached");
|
|
5357
5656
|
}
|
|
5358
5657
|
function buildCounterNotFoundError(counter) {
|
|
5359
5658
|
return new CounterResolutionError(
|
|
@@ -5969,17 +6268,6 @@ async function performSelect(page, path5, options) {
|
|
|
5969
6268
|
}
|
|
5970
6269
|
|
|
5971
6270
|
// src/actions/extract.ts
|
|
5972
|
-
async function readFieldValueFromHandle(element, options) {
|
|
5973
|
-
const raw = await element.evaluate(
|
|
5974
|
-
(target, payload) => {
|
|
5975
|
-
return payload.attribute ? target.getAttribute(payload.attribute) : target.textContent;
|
|
5976
|
-
},
|
|
5977
|
-
{
|
|
5978
|
-
attribute: options.attribute
|
|
5979
|
-
}
|
|
5980
|
-
);
|
|
5981
|
-
return normalizeExtractedValue(raw, options.attribute);
|
|
5982
|
-
}
|
|
5983
6271
|
async function extractWithPaths(page, fields) {
|
|
5984
6272
|
const result = {};
|
|
5985
6273
|
for (const field of fields) {
|
|
@@ -5991,7 +6279,7 @@ async function extractWithPaths(page, fields) {
|
|
|
5991
6279
|
continue;
|
|
5992
6280
|
}
|
|
5993
6281
|
try {
|
|
5994
|
-
result[field.key] = await
|
|
6282
|
+
result[field.key] = await readExtractedValueFromHandle(
|
|
5995
6283
|
resolved.element,
|
|
5996
6284
|
{
|
|
5997
6285
|
attribute: field.attribute
|
|
@@ -6031,7 +6319,7 @@ async function extractArrayRowsWithPaths(page, array) {
|
|
|
6031
6319
|
field.candidates
|
|
6032
6320
|
) : item;
|
|
6033
6321
|
try {
|
|
6034
|
-
const value = target ? await
|
|
6322
|
+
const value = target ? await readExtractedValueFromHandle(target, {
|
|
6035
6323
|
attribute: field.attribute
|
|
6036
6324
|
}) : null;
|
|
6037
6325
|
if (key) {
|
|
@@ -6349,7 +6637,7 @@ async function closeTab(context, activePage, index) {
|
|
|
6349
6637
|
}
|
|
6350
6638
|
|
|
6351
6639
|
// src/actions/cookies.ts
|
|
6352
|
-
var
|
|
6640
|
+
var import_promises2 = require("fs/promises");
|
|
6353
6641
|
async function getCookies(context, url) {
|
|
6354
6642
|
return context.cookies(url ? [url] : void 0);
|
|
6355
6643
|
}
|
|
@@ -6361,10 +6649,10 @@ async function clearCookies(context) {
|
|
|
6361
6649
|
}
|
|
6362
6650
|
async function exportCookies(context, filePath, url) {
|
|
6363
6651
|
const cookies = await context.cookies(url ? [url] : void 0);
|
|
6364
|
-
await (0,
|
|
6652
|
+
await (0, import_promises2.writeFile)(filePath, JSON.stringify(cookies, null, 2), "utf-8");
|
|
6365
6653
|
}
|
|
6366
6654
|
async function importCookies(context, filePath) {
|
|
6367
|
-
const raw = await (0,
|
|
6655
|
+
const raw = await (0, import_promises2.readFile)(filePath, "utf-8");
|
|
6368
6656
|
const cookies = JSON.parse(raw);
|
|
6369
6657
|
await context.addCookies(cookies);
|
|
6370
6658
|
}
|
|
@@ -8331,13 +8619,13 @@ function dedupeNewest(entries) {
|
|
|
8331
8619
|
}
|
|
8332
8620
|
|
|
8333
8621
|
// src/cloud/cdp-client.ts
|
|
8334
|
-
var
|
|
8622
|
+
var import_playwright3 = require("playwright");
|
|
8335
8623
|
var CloudCdpClient = class {
|
|
8336
8624
|
async connect(args) {
|
|
8337
8625
|
const endpoint = withTokenQuery2(args.wsUrl, args.token);
|
|
8338
8626
|
let browser;
|
|
8339
8627
|
try {
|
|
8340
|
-
browser = await
|
|
8628
|
+
browser = await import_playwright3.chromium.connectOverCDP(endpoint);
|
|
8341
8629
|
} catch (error) {
|
|
8342
8630
|
const message = error instanceof Error ? error.message : "Failed to connect to cloud CDP endpoint.";
|
|
8343
8631
|
throw new OpensteerCloudError("CLOUD_TRANSPORT_ERROR", message);
|
|
@@ -8392,32 +8680,73 @@ function withTokenQuery2(wsUrl, token) {
|
|
|
8392
8680
|
return url.toString();
|
|
8393
8681
|
}
|
|
8394
8682
|
|
|
8395
|
-
// src/
|
|
8396
|
-
|
|
8397
|
-
|
|
8398
|
-
|
|
8399
|
-
|
|
8400
|
-
authScheme;
|
|
8401
|
-
constructor(baseUrl, key, authScheme = "api-key") {
|
|
8402
|
-
this.baseUrl = normalizeBaseUrl(baseUrl);
|
|
8403
|
-
this.key = key;
|
|
8404
|
-
this.authScheme = authScheme;
|
|
8683
|
+
// src/utils/strip-trailing-slashes.ts
|
|
8684
|
+
function stripTrailingSlashes(value) {
|
|
8685
|
+
let end = value.length;
|
|
8686
|
+
while (end > 0 && value.charCodeAt(end - 1) === 47) {
|
|
8687
|
+
end -= 1;
|
|
8405
8688
|
}
|
|
8406
|
-
|
|
8407
|
-
|
|
8408
|
-
|
|
8409
|
-
|
|
8410
|
-
|
|
8411
|
-
|
|
8412
|
-
|
|
8413
|
-
|
|
8414
|
-
|
|
8415
|
-
|
|
8416
|
-
|
|
8417
|
-
}
|
|
8418
|
-
|
|
8419
|
-
|
|
8420
|
-
|
|
8689
|
+
return end === value.length ? value : value.slice(0, end);
|
|
8690
|
+
}
|
|
8691
|
+
|
|
8692
|
+
// src/cloud/http-client.ts
|
|
8693
|
+
function normalizeCloudBaseUrl(baseUrl) {
|
|
8694
|
+
return stripTrailingSlashes(baseUrl);
|
|
8695
|
+
}
|
|
8696
|
+
function cloudAuthHeaders(key, authScheme) {
|
|
8697
|
+
if (authScheme === "bearer") {
|
|
8698
|
+
return {
|
|
8699
|
+
authorization: `Bearer ${key}`
|
|
8700
|
+
};
|
|
8701
|
+
}
|
|
8702
|
+
return {
|
|
8703
|
+
"x-api-key": key
|
|
8704
|
+
};
|
|
8705
|
+
}
|
|
8706
|
+
async function parseCloudHttpError(response) {
|
|
8707
|
+
let body = null;
|
|
8708
|
+
try {
|
|
8709
|
+
body = await response.json();
|
|
8710
|
+
} catch {
|
|
8711
|
+
body = null;
|
|
8712
|
+
}
|
|
8713
|
+
const code = typeof body?.code === "string" ? toCloudErrorCode(body.code) : "CLOUD_TRANSPORT_ERROR";
|
|
8714
|
+
const message = typeof body?.error === "string" ? body.error : `Cloud request failed with status ${response.status}.`;
|
|
8715
|
+
return new OpensteerCloudError(code, message, response.status, body?.details);
|
|
8716
|
+
}
|
|
8717
|
+
function toCloudErrorCode(code) {
|
|
8718
|
+
if (code === "CLOUD_AUTH_FAILED" || code === "CLOUD_SESSION_NOT_FOUND" || code === "CLOUD_SESSION_CLOSED" || code === "CLOUD_UNSUPPORTED_METHOD" || code === "CLOUD_INVALID_REQUEST" || code === "CLOUD_MODEL_NOT_ALLOWED" || code === "CLOUD_ACTION_FAILED" || code === "CLOUD_INTERNAL" || code === "CLOUD_CAPACITY_EXHAUSTED" || code === "CLOUD_RUNTIME_UNAVAILABLE" || code === "CLOUD_RUNTIME_MISMATCH" || code === "CLOUD_SESSION_STALE" || code === "CLOUD_CONTRACT_MISMATCH" || code === "CLOUD_CONTROL_PLANE_ERROR" || code === "CLOUD_PROXY_UNAVAILABLE" || code === "CLOUD_PROXY_REQUIRED" || code === "CLOUD_BILLING_LIMIT_REACHED" || code === "CLOUD_RATE_LIMITED" || code === "CLOUD_BROWSER_PROFILE_NOT_FOUND" || code === "CLOUD_BROWSER_PROFILE_BUSY" || code === "CLOUD_BROWSER_PROFILE_DISABLED" || code === "CLOUD_BROWSER_PROFILE_PROXY_UNAVAILABLE" || code === "CLOUD_BROWSER_PROFILE_SYNC_FAILED") {
|
|
8719
|
+
return code;
|
|
8720
|
+
}
|
|
8721
|
+
return "CLOUD_TRANSPORT_ERROR";
|
|
8722
|
+
}
|
|
8723
|
+
|
|
8724
|
+
// src/cloud/session-client.ts
|
|
8725
|
+
var CACHE_IMPORT_BATCH_SIZE = 200;
|
|
8726
|
+
var CloudSessionClient = class {
|
|
8727
|
+
baseUrl;
|
|
8728
|
+
key;
|
|
8729
|
+
authScheme;
|
|
8730
|
+
constructor(baseUrl, key, authScheme = "api-key") {
|
|
8731
|
+
this.baseUrl = normalizeCloudBaseUrl(baseUrl);
|
|
8732
|
+
this.key = key;
|
|
8733
|
+
this.authScheme = authScheme;
|
|
8734
|
+
}
|
|
8735
|
+
async create(request) {
|
|
8736
|
+
const response = await fetch(`${this.baseUrl}/sessions`, {
|
|
8737
|
+
method: "POST",
|
|
8738
|
+
headers: {
|
|
8739
|
+
"content-type": "application/json",
|
|
8740
|
+
...cloudAuthHeaders(this.key, this.authScheme)
|
|
8741
|
+
},
|
|
8742
|
+
body: JSON.stringify(request)
|
|
8743
|
+
});
|
|
8744
|
+
if (!response.ok) {
|
|
8745
|
+
throw await parseCloudHttpError(response);
|
|
8746
|
+
}
|
|
8747
|
+
let body;
|
|
8748
|
+
try {
|
|
8749
|
+
body = await response.json();
|
|
8421
8750
|
} catch {
|
|
8422
8751
|
throw new OpensteerCloudError(
|
|
8423
8752
|
"CLOUD_CONTRACT_MISMATCH",
|
|
@@ -8431,14 +8760,14 @@ var CloudSessionClient = class {
|
|
|
8431
8760
|
const response = await fetch(`${this.baseUrl}/sessions/${sessionId}`, {
|
|
8432
8761
|
method: "DELETE",
|
|
8433
8762
|
headers: {
|
|
8434
|
-
...this.
|
|
8763
|
+
...cloudAuthHeaders(this.key, this.authScheme)
|
|
8435
8764
|
}
|
|
8436
8765
|
});
|
|
8437
8766
|
if (response.status === 204) {
|
|
8438
8767
|
return;
|
|
8439
8768
|
}
|
|
8440
8769
|
if (!response.ok) {
|
|
8441
|
-
throw await
|
|
8770
|
+
throw await parseCloudHttpError(response);
|
|
8442
8771
|
}
|
|
8443
8772
|
}
|
|
8444
8773
|
async importSelectorCache(request) {
|
|
@@ -8461,29 +8790,16 @@ var CloudSessionClient = class {
|
|
|
8461
8790
|
method: "POST",
|
|
8462
8791
|
headers: {
|
|
8463
8792
|
"content-type": "application/json",
|
|
8464
|
-
...this.
|
|
8793
|
+
...cloudAuthHeaders(this.key, this.authScheme)
|
|
8465
8794
|
},
|
|
8466
8795
|
body: JSON.stringify({ entries })
|
|
8467
8796
|
});
|
|
8468
8797
|
if (!response.ok) {
|
|
8469
|
-
throw await
|
|
8798
|
+
throw await parseCloudHttpError(response);
|
|
8470
8799
|
}
|
|
8471
8800
|
return await response.json();
|
|
8472
8801
|
}
|
|
8473
|
-
authHeaders() {
|
|
8474
|
-
if (this.authScheme === "bearer") {
|
|
8475
|
-
return {
|
|
8476
|
-
authorization: `Bearer ${this.key}`
|
|
8477
|
-
};
|
|
8478
|
-
}
|
|
8479
|
-
return {
|
|
8480
|
-
"x-api-key": this.key
|
|
8481
|
-
};
|
|
8482
|
-
}
|
|
8483
8802
|
};
|
|
8484
|
-
function normalizeBaseUrl(baseUrl) {
|
|
8485
|
-
return baseUrl.replace(/\/+$/, "");
|
|
8486
|
-
}
|
|
8487
8803
|
function parseCreateResponse(body, status) {
|
|
8488
8804
|
const root = requireObject(
|
|
8489
8805
|
body,
|
|
@@ -8628,23 +8944,6 @@ function mergeImportResponse(first, second) {
|
|
|
8628
8944
|
skipped: first.skipped + second.skipped
|
|
8629
8945
|
};
|
|
8630
8946
|
}
|
|
8631
|
-
async function parseHttpError(response) {
|
|
8632
|
-
let body = null;
|
|
8633
|
-
try {
|
|
8634
|
-
body = await response.json();
|
|
8635
|
-
} catch {
|
|
8636
|
-
body = null;
|
|
8637
|
-
}
|
|
8638
|
-
const code = typeof body?.code === "string" ? toCloudErrorCode(body.code) : "CLOUD_TRANSPORT_ERROR";
|
|
8639
|
-
const message = typeof body?.error === "string" ? body.error : `Cloud request failed with status ${response.status}.`;
|
|
8640
|
-
return new OpensteerCloudError(code, message, response.status, body?.details);
|
|
8641
|
-
}
|
|
8642
|
-
function toCloudErrorCode(code) {
|
|
8643
|
-
if (code === "CLOUD_AUTH_FAILED" || code === "CLOUD_SESSION_NOT_FOUND" || code === "CLOUD_SESSION_CLOSED" || code === "CLOUD_UNSUPPORTED_METHOD" || code === "CLOUD_INVALID_REQUEST" || code === "CLOUD_MODEL_NOT_ALLOWED" || code === "CLOUD_ACTION_FAILED" || code === "CLOUD_INTERNAL" || code === "CLOUD_CAPACITY_EXHAUSTED" || code === "CLOUD_RUNTIME_UNAVAILABLE" || code === "CLOUD_RUNTIME_MISMATCH" || code === "CLOUD_SESSION_STALE" || code === "CLOUD_CONTRACT_MISMATCH" || code === "CLOUD_CONTROL_PLANE_ERROR") {
|
|
8644
|
-
return code;
|
|
8645
|
-
}
|
|
8646
|
-
return "CLOUD_TRANSPORT_ERROR";
|
|
8647
|
-
}
|
|
8648
8947
|
|
|
8649
8948
|
// src/cloud/runtime.ts
|
|
8650
8949
|
var DEFAULT_CLOUD_BASE_URL = "https://api.opensteer.com";
|
|
@@ -8669,9 +8968,6 @@ function resolveCloudBaseUrl() {
|
|
|
8669
8968
|
if (!value) return DEFAULT_CLOUD_BASE_URL;
|
|
8670
8969
|
return normalizeCloudBaseUrl(value);
|
|
8671
8970
|
}
|
|
8672
|
-
function normalizeCloudBaseUrl(value) {
|
|
8673
|
-
return value.replace(/\/+$/, "");
|
|
8674
|
-
}
|
|
8675
8971
|
function readCloudActionDescription(payload) {
|
|
8676
8972
|
const description = payload.description;
|
|
8677
8973
|
if (typeof description !== "string") return void 0;
|
|
@@ -10002,7 +10298,7 @@ function resolveAgentConfig(args) {
|
|
|
10002
10298
|
});
|
|
10003
10299
|
return {
|
|
10004
10300
|
mode: "cua",
|
|
10005
|
-
systemPrompt:
|
|
10301
|
+
systemPrompt: normalizeNonEmptyString2(agentConfig.systemPrompt) || DEFAULT_SYSTEM_PROMPT,
|
|
10006
10302
|
waitBetweenActionsMs: normalizeWaitBetween(agentConfig.waitBetweenActionsMs),
|
|
10007
10303
|
model
|
|
10008
10304
|
};
|
|
@@ -10021,7 +10317,7 @@ function createCuaClient(config) {
|
|
|
10021
10317
|
);
|
|
10022
10318
|
}
|
|
10023
10319
|
}
|
|
10024
|
-
function
|
|
10320
|
+
function normalizeNonEmptyString2(value) {
|
|
10025
10321
|
if (typeof value !== "string") return void 0;
|
|
10026
10322
|
const normalized = value.trim();
|
|
10027
10323
|
return normalized.length ? normalized : void 0;
|
|
@@ -10296,24 +10592,22 @@ var OpensteerCuaAgentHandler = class {
|
|
|
10296
10592
|
page;
|
|
10297
10593
|
config;
|
|
10298
10594
|
client;
|
|
10299
|
-
|
|
10595
|
+
cursorController;
|
|
10300
10596
|
onMutatingAction;
|
|
10301
|
-
cursorOverlayInjected = false;
|
|
10302
10597
|
constructor(options) {
|
|
10303
10598
|
this.page = options.page;
|
|
10304
10599
|
this.config = options.config;
|
|
10305
10600
|
this.client = options.client;
|
|
10306
|
-
this.
|
|
10601
|
+
this.cursorController = options.cursorController;
|
|
10307
10602
|
this.onMutatingAction = options.onMutatingAction;
|
|
10308
10603
|
}
|
|
10309
10604
|
async execute(options) {
|
|
10310
10605
|
const instruction = options.instruction;
|
|
10311
10606
|
const maxSteps = options.maxSteps ?? 20;
|
|
10312
10607
|
await this.initializeClient();
|
|
10313
|
-
const highlightCursor = options.highlightCursor === true;
|
|
10314
10608
|
this.client.setActionHandler(async (action) => {
|
|
10315
|
-
if (
|
|
10316
|
-
await this.
|
|
10609
|
+
if (this.cursorController.isEnabled()) {
|
|
10610
|
+
await this.maybePreviewCursor(action);
|
|
10317
10611
|
}
|
|
10318
10612
|
await executeAgentAction(this.page, action);
|
|
10319
10613
|
this.client.setCurrentUrl(this.page.url());
|
|
@@ -10344,6 +10638,7 @@ var OpensteerCuaAgentHandler = class {
|
|
|
10344
10638
|
const viewport = await this.resolveViewport();
|
|
10345
10639
|
this.client.setViewport(viewport.width, viewport.height);
|
|
10346
10640
|
this.client.setCurrentUrl(this.page.url());
|
|
10641
|
+
await this.cursorController.attachPage(this.page);
|
|
10347
10642
|
this.client.setScreenshotProvider(async () => {
|
|
10348
10643
|
const buffer = await this.page.screenshot({
|
|
10349
10644
|
fullPage: false,
|
|
@@ -10372,51 +10667,611 @@ var OpensteerCuaAgentHandler = class {
|
|
|
10372
10667
|
}
|
|
10373
10668
|
return DEFAULT_CUA_VIEWPORT;
|
|
10374
10669
|
}
|
|
10375
|
-
async
|
|
10670
|
+
async maybePreviewCursor(action) {
|
|
10376
10671
|
const x = typeof action.x === "number" ? action.x : null;
|
|
10377
10672
|
const y = typeof action.y === "number" ? action.y : null;
|
|
10378
10673
|
if (x == null || y == null) {
|
|
10379
10674
|
return;
|
|
10380
10675
|
}
|
|
10676
|
+
await this.cursorController.preview({ x, y }, "agent");
|
|
10677
|
+
}
|
|
10678
|
+
};
|
|
10679
|
+
function sleep4(ms) {
|
|
10680
|
+
return new Promise((resolve) => setTimeout(resolve, Math.max(0, ms)));
|
|
10681
|
+
}
|
|
10682
|
+
|
|
10683
|
+
// src/cursor/motion.ts
|
|
10684
|
+
var DEFAULT_SNAPPY_OPTIONS = {
|
|
10685
|
+
minDurationMs: 46,
|
|
10686
|
+
maxDurationMs: 170,
|
|
10687
|
+
maxPoints: 14
|
|
10688
|
+
};
|
|
10689
|
+
function planSnappyCursorMotion(from, to, options = {}) {
|
|
10690
|
+
const resolved = {
|
|
10691
|
+
...DEFAULT_SNAPPY_OPTIONS,
|
|
10692
|
+
...options
|
|
10693
|
+
};
|
|
10694
|
+
const dx = to.x - from.x;
|
|
10695
|
+
const dy = to.y - from.y;
|
|
10696
|
+
const distance = Math.hypot(dx, dy);
|
|
10697
|
+
if (distance < 5) {
|
|
10698
|
+
return {
|
|
10699
|
+
points: [roundPoint(to)],
|
|
10700
|
+
stepDelayMs: 0
|
|
10701
|
+
};
|
|
10702
|
+
}
|
|
10703
|
+
const durationMs = clamp(
|
|
10704
|
+
44 + distance * 0.26,
|
|
10705
|
+
resolved.minDurationMs,
|
|
10706
|
+
resolved.maxDurationMs
|
|
10707
|
+
);
|
|
10708
|
+
const rawPoints = clamp(
|
|
10709
|
+
Math.round(durationMs / 13),
|
|
10710
|
+
4,
|
|
10711
|
+
resolved.maxPoints
|
|
10712
|
+
);
|
|
10713
|
+
const sign = deterministicBendSign(from, to);
|
|
10714
|
+
const nx = -dy / distance;
|
|
10715
|
+
const ny = dx / distance;
|
|
10716
|
+
const bend = clamp(distance * 0.12, 8, 28) * sign;
|
|
10717
|
+
const c1 = {
|
|
10718
|
+
x: from.x + dx * 0.28 + nx * bend,
|
|
10719
|
+
y: from.y + dy * 0.28 + ny * bend
|
|
10720
|
+
};
|
|
10721
|
+
const c2 = {
|
|
10722
|
+
x: from.x + dx * 0.74 + nx * bend * 0.58,
|
|
10723
|
+
y: from.y + dy * 0.74 + ny * bend * 0.58
|
|
10724
|
+
};
|
|
10725
|
+
const points = [];
|
|
10726
|
+
for (let i = 1; i <= rawPoints; i += 1) {
|
|
10727
|
+
const t = i / rawPoints;
|
|
10728
|
+
const sampled = cubicBezier(from, c1, c2, to, t);
|
|
10729
|
+
if (i !== rawPoints) {
|
|
10730
|
+
const previous = points[points.length - 1];
|
|
10731
|
+
if (previous && distanceBetween(previous, sampled) < 1.4) {
|
|
10732
|
+
continue;
|
|
10733
|
+
}
|
|
10734
|
+
}
|
|
10735
|
+
points.push(roundPoint(sampled));
|
|
10736
|
+
}
|
|
10737
|
+
if (distance > 220) {
|
|
10738
|
+
const settle = {
|
|
10739
|
+
x: to.x - dx / distance,
|
|
10740
|
+
y: to.y - dy / distance
|
|
10741
|
+
};
|
|
10742
|
+
const last = points[points.length - 1];
|
|
10743
|
+
if (!last || distanceBetween(last, settle) > 1.2) {
|
|
10744
|
+
points.splice(Math.max(0, points.length - 1), 0, roundPoint(settle));
|
|
10745
|
+
}
|
|
10746
|
+
}
|
|
10747
|
+
const deduped = dedupeAdjacent(points);
|
|
10748
|
+
const stepDelayMs = deduped.length > 1 ? Math.round(durationMs / deduped.length) : 0;
|
|
10749
|
+
return {
|
|
10750
|
+
points: deduped.length ? deduped : [roundPoint(to)],
|
|
10751
|
+
stepDelayMs
|
|
10752
|
+
};
|
|
10753
|
+
}
|
|
10754
|
+
function cubicBezier(p0, p1, p2, p3, t) {
|
|
10755
|
+
const inv = 1 - t;
|
|
10756
|
+
const inv2 = inv * inv;
|
|
10757
|
+
const inv3 = inv2 * inv;
|
|
10758
|
+
const t2 = t * t;
|
|
10759
|
+
const t3 = t2 * t;
|
|
10760
|
+
return {
|
|
10761
|
+
x: inv3 * p0.x + 3 * inv2 * t * p1.x + 3 * inv * t2 * p2.x + t3 * p3.x,
|
|
10762
|
+
y: inv3 * p0.y + 3 * inv2 * t * p1.y + 3 * inv * t2 * p2.y + t3 * p3.y
|
|
10763
|
+
};
|
|
10764
|
+
}
|
|
10765
|
+
function deterministicBendSign(from, to) {
|
|
10766
|
+
const seed = Math.sin(
|
|
10767
|
+
from.x * 12.9898 + from.y * 78.233 + to.x * 37.719 + to.y * 19.113
|
|
10768
|
+
) * 43758.5453;
|
|
10769
|
+
const fractional = seed - Math.floor(seed);
|
|
10770
|
+
return fractional >= 0.5 ? 1 : -1;
|
|
10771
|
+
}
|
|
10772
|
+
function roundPoint(point) {
|
|
10773
|
+
return {
|
|
10774
|
+
x: Math.round(point.x * 100) / 100,
|
|
10775
|
+
y: Math.round(point.y * 100) / 100
|
|
10776
|
+
};
|
|
10777
|
+
}
|
|
10778
|
+
function distanceBetween(a, b) {
|
|
10779
|
+
return Math.hypot(a.x - b.x, a.y - b.y);
|
|
10780
|
+
}
|
|
10781
|
+
function dedupeAdjacent(points) {
|
|
10782
|
+
const out = [];
|
|
10783
|
+
for (const point of points) {
|
|
10784
|
+
const last = out[out.length - 1];
|
|
10785
|
+
if (last && last.x === point.x && last.y === point.y) {
|
|
10786
|
+
continue;
|
|
10787
|
+
}
|
|
10788
|
+
out.push(point);
|
|
10789
|
+
}
|
|
10790
|
+
return out;
|
|
10791
|
+
}
|
|
10792
|
+
function clamp(value, min, max) {
|
|
10793
|
+
return Math.min(max, Math.max(min, value));
|
|
10794
|
+
}
|
|
10795
|
+
|
|
10796
|
+
// src/cursor/renderers/svg-overlay.ts
|
|
10797
|
+
var PULSE_DURATION_MS = 220;
|
|
10798
|
+
var HOST_ELEMENT_ID = "__os_cr";
|
|
10799
|
+
var SvgCursorRenderer = class {
|
|
10800
|
+
page = null;
|
|
10801
|
+
active = false;
|
|
10802
|
+
reason = "disabled";
|
|
10803
|
+
lastMessage;
|
|
10804
|
+
async initialize(page) {
|
|
10805
|
+
this.page = page;
|
|
10806
|
+
if (page.isClosed()) {
|
|
10807
|
+
this.markInactive("page_closed");
|
|
10808
|
+
return;
|
|
10809
|
+
}
|
|
10810
|
+
try {
|
|
10811
|
+
await page.evaluate(injectCursor, HOST_ELEMENT_ID);
|
|
10812
|
+
this.active = true;
|
|
10813
|
+
this.reason = void 0;
|
|
10814
|
+
this.lastMessage = void 0;
|
|
10815
|
+
} catch (error) {
|
|
10816
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
10817
|
+
this.markInactive("renderer_error", message);
|
|
10818
|
+
}
|
|
10819
|
+
}
|
|
10820
|
+
isActive() {
|
|
10821
|
+
return this.active;
|
|
10822
|
+
}
|
|
10823
|
+
status() {
|
|
10824
|
+
return {
|
|
10825
|
+
enabled: true,
|
|
10826
|
+
active: this.active,
|
|
10827
|
+
reason: this.reason ? this.lastMessage ? `${this.reason}: ${this.lastMessage}` : this.reason : void 0
|
|
10828
|
+
};
|
|
10829
|
+
}
|
|
10830
|
+
async move(point, style) {
|
|
10831
|
+
if (!this.active || !this.page || this.page.isClosed()) return;
|
|
10381
10832
|
try {
|
|
10382
|
-
|
|
10383
|
-
|
|
10384
|
-
|
|
10385
|
-
|
|
10386
|
-
|
|
10387
|
-
|
|
10388
|
-
|
|
10389
|
-
|
|
10390
|
-
|
|
10391
|
-
|
|
10392
|
-
|
|
10393
|
-
|
|
10394
|
-
|
|
10395
|
-
|
|
10396
|
-
|
|
10397
|
-
|
|
10398
|
-
|
|
10833
|
+
const ok = await this.page.evaluate(moveCursor, {
|
|
10834
|
+
id: HOST_ELEMENT_ID,
|
|
10835
|
+
x: point.x,
|
|
10836
|
+
y: point.y,
|
|
10837
|
+
size: style.size,
|
|
10838
|
+
fill: colorToRgba(style.fillColor),
|
|
10839
|
+
outline: colorToRgba(style.outlineColor)
|
|
10840
|
+
});
|
|
10841
|
+
if (!ok) {
|
|
10842
|
+
await this.reinject();
|
|
10843
|
+
await this.page.evaluate(moveCursor, {
|
|
10844
|
+
id: HOST_ELEMENT_ID,
|
|
10845
|
+
x: point.x,
|
|
10846
|
+
y: point.y,
|
|
10847
|
+
size: style.size,
|
|
10848
|
+
fill: colorToRgba(style.fillColor),
|
|
10849
|
+
outline: colorToRgba(style.outlineColor)
|
|
10399
10850
|
});
|
|
10400
|
-
this.cursorOverlayInjected = true;
|
|
10401
10851
|
}
|
|
10402
|
-
|
|
10403
|
-
|
|
10404
|
-
|
|
10405
|
-
|
|
10406
|
-
|
|
10407
|
-
|
|
10408
|
-
|
|
10409
|
-
|
|
10852
|
+
} catch (error) {
|
|
10853
|
+
this.handleError(error);
|
|
10854
|
+
}
|
|
10855
|
+
}
|
|
10856
|
+
async pulse(point, style) {
|
|
10857
|
+
if (!this.active || !this.page || this.page.isClosed()) return;
|
|
10858
|
+
try {
|
|
10859
|
+
const ok = await this.page.evaluate(pulseCursor, {
|
|
10860
|
+
id: HOST_ELEMENT_ID,
|
|
10861
|
+
x: point.x,
|
|
10862
|
+
y: point.y,
|
|
10863
|
+
size: style.size,
|
|
10864
|
+
fill: colorToRgba(style.fillColor),
|
|
10865
|
+
outline: colorToRgba(style.outlineColor),
|
|
10866
|
+
halo: colorToRgba(style.haloColor),
|
|
10867
|
+
pulseMs: PULSE_DURATION_MS
|
|
10868
|
+
});
|
|
10869
|
+
if (!ok) {
|
|
10870
|
+
await this.reinject();
|
|
10871
|
+
await this.page.evaluate(pulseCursor, {
|
|
10872
|
+
id: HOST_ELEMENT_ID,
|
|
10873
|
+
x: point.x,
|
|
10874
|
+
y: point.y,
|
|
10875
|
+
size: style.size,
|
|
10876
|
+
fill: colorToRgba(style.fillColor),
|
|
10877
|
+
outline: colorToRgba(style.outlineColor),
|
|
10878
|
+
halo: colorToRgba(style.haloColor),
|
|
10879
|
+
pulseMs: PULSE_DURATION_MS
|
|
10880
|
+
});
|
|
10881
|
+
}
|
|
10882
|
+
} catch (error) {
|
|
10883
|
+
this.handleError(error);
|
|
10884
|
+
}
|
|
10885
|
+
}
|
|
10886
|
+
async clear() {
|
|
10887
|
+
if (!this.page || this.page.isClosed()) return;
|
|
10888
|
+
try {
|
|
10889
|
+
await this.page.evaluate(removeCursor, HOST_ELEMENT_ID);
|
|
10890
|
+
} catch {
|
|
10891
|
+
}
|
|
10892
|
+
}
|
|
10893
|
+
async dispose() {
|
|
10894
|
+
if (this.page && !this.page.isClosed()) {
|
|
10895
|
+
try {
|
|
10896
|
+
await this.page.evaluate(removeCursor, HOST_ELEMENT_ID);
|
|
10897
|
+
} catch {
|
|
10898
|
+
}
|
|
10899
|
+
}
|
|
10900
|
+
this.active = false;
|
|
10901
|
+
this.reason = "disabled";
|
|
10902
|
+
this.lastMessage = void 0;
|
|
10903
|
+
this.page = null;
|
|
10904
|
+
}
|
|
10905
|
+
async reinject() {
|
|
10906
|
+
if (!this.page || this.page.isClosed()) return;
|
|
10907
|
+
try {
|
|
10908
|
+
await this.page.evaluate(injectCursor, HOST_ELEMENT_ID);
|
|
10909
|
+
} catch {
|
|
10910
|
+
}
|
|
10911
|
+
}
|
|
10912
|
+
markInactive(reason, message) {
|
|
10913
|
+
this.active = false;
|
|
10914
|
+
this.reason = reason;
|
|
10915
|
+
this.lastMessage = message;
|
|
10916
|
+
}
|
|
10917
|
+
handleError(error) {
|
|
10918
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
10919
|
+
if (isPageGone(message)) {
|
|
10920
|
+
this.markInactive("page_closed", message);
|
|
10921
|
+
}
|
|
10922
|
+
}
|
|
10923
|
+
};
|
|
10924
|
+
function injectCursor(hostId) {
|
|
10925
|
+
const win = window;
|
|
10926
|
+
if (win[hostId]) return;
|
|
10927
|
+
const host = document.createElement("div");
|
|
10928
|
+
host.style.cssText = "position:fixed;top:0;left:0;width:0;height:0;z-index:2147483647;pointer-events:none;";
|
|
10929
|
+
const shadow = host.attachShadow({ mode: "closed" });
|
|
10930
|
+
const wrapper = document.createElement("div");
|
|
10931
|
+
wrapper.style.cssText = "position:fixed;top:0;left:0;pointer-events:none;will-change:transform;display:none;";
|
|
10932
|
+
wrapper.innerHTML = `
|
|
10933
|
+
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg" style="filter:drop-shadow(0 1px 2px rgba(0,0,0,0.3));display:block;">
|
|
10934
|
+
<path d="M3 2L3 23L8.5 17.5L13 26L17 24L12.5 15.5L20 15.5L3 2Z"
|
|
10935
|
+
fill="white" stroke="black" stroke-width="1.5" stroke-linejoin="round"/>
|
|
10936
|
+
</svg>
|
|
10937
|
+
<div data-role="pulse" style="position:absolute;top:0;left:0;width:24px;height:24px;border-radius:50%;pointer-events:none;opacity:0;transform:translate(-8px,-8px);"></div>
|
|
10938
|
+
`;
|
|
10939
|
+
shadow.appendChild(wrapper);
|
|
10940
|
+
document.documentElement.appendChild(host);
|
|
10941
|
+
Object.defineProperty(window, hostId, {
|
|
10942
|
+
value: {
|
|
10943
|
+
host,
|
|
10944
|
+
wrapper,
|
|
10945
|
+
path: wrapper.querySelector("path"),
|
|
10946
|
+
pulse: wrapper.querySelector('[data-role="pulse"]')
|
|
10947
|
+
},
|
|
10948
|
+
configurable: true,
|
|
10949
|
+
enumerable: false
|
|
10950
|
+
});
|
|
10951
|
+
}
|
|
10952
|
+
function moveCursor(args) {
|
|
10953
|
+
const refs = window[args.id];
|
|
10954
|
+
if (!refs) return false;
|
|
10955
|
+
const scale = args.size / 20;
|
|
10956
|
+
refs.wrapper.style.transform = `translate(${args.x}px, ${args.y}px) scale(${scale})`;
|
|
10957
|
+
refs.wrapper.style.display = "block";
|
|
10958
|
+
if (refs.path) {
|
|
10959
|
+
refs.path.setAttribute("fill", args.fill);
|
|
10960
|
+
refs.path.setAttribute("stroke", args.outline);
|
|
10961
|
+
}
|
|
10962
|
+
return true;
|
|
10963
|
+
}
|
|
10964
|
+
function pulseCursor(args) {
|
|
10965
|
+
const refs = window[args.id];
|
|
10966
|
+
if (!refs) return false;
|
|
10967
|
+
const scale = args.size / 20;
|
|
10968
|
+
refs.wrapper.style.transform = `translate(${args.x}px, ${args.y}px) scale(${scale})`;
|
|
10969
|
+
refs.wrapper.style.display = "block";
|
|
10970
|
+
if (refs.path) {
|
|
10971
|
+
refs.path.setAttribute("fill", args.fill);
|
|
10972
|
+
refs.path.setAttribute("stroke", args.outline);
|
|
10973
|
+
}
|
|
10974
|
+
const ring = refs.pulse;
|
|
10975
|
+
if (!ring) return true;
|
|
10976
|
+
ring.style.background = args.halo;
|
|
10977
|
+
ring.style.opacity = "0.7";
|
|
10978
|
+
ring.style.width = "24px";
|
|
10979
|
+
ring.style.height = "24px";
|
|
10980
|
+
ring.style.transition = `all ${args.pulseMs}ms ease-out`;
|
|
10981
|
+
ring.offsetHeight;
|
|
10982
|
+
ring.style.width = "48px";
|
|
10983
|
+
ring.style.height = "48px";
|
|
10984
|
+
ring.style.opacity = "0";
|
|
10985
|
+
ring.style.transform = "translate(-20px, -20px)";
|
|
10986
|
+
setTimeout(() => {
|
|
10987
|
+
ring.style.transition = "none";
|
|
10988
|
+
ring.style.width = "24px";
|
|
10989
|
+
ring.style.height = "24px";
|
|
10990
|
+
ring.style.transform = "translate(-8px, -8px)";
|
|
10991
|
+
ring.style.opacity = "0";
|
|
10992
|
+
}, args.pulseMs);
|
|
10993
|
+
return true;
|
|
10994
|
+
}
|
|
10995
|
+
function removeCursor(hostId) {
|
|
10996
|
+
const refs = window[hostId];
|
|
10997
|
+
if (refs) {
|
|
10998
|
+
refs.host.remove();
|
|
10999
|
+
delete window[hostId];
|
|
11000
|
+
}
|
|
11001
|
+
}
|
|
11002
|
+
function colorToRgba(c) {
|
|
11003
|
+
return `rgba(${Math.round(c.r)},${Math.round(c.g)},${Math.round(c.b)},${c.a})`;
|
|
11004
|
+
}
|
|
11005
|
+
function isPageGone(message) {
|
|
11006
|
+
const m = message.toLowerCase();
|
|
11007
|
+
return m.includes("closed") || m.includes("detached") || m.includes("destroyed") || m.includes("target");
|
|
11008
|
+
}
|
|
11009
|
+
|
|
11010
|
+
// src/cursor/controller.ts
|
|
11011
|
+
var DEFAULT_STYLE = {
|
|
11012
|
+
size: 20,
|
|
11013
|
+
fillColor: {
|
|
11014
|
+
r: 255,
|
|
11015
|
+
g: 255,
|
|
11016
|
+
b: 255,
|
|
11017
|
+
a: 0.96
|
|
11018
|
+
},
|
|
11019
|
+
outlineColor: {
|
|
11020
|
+
r: 0,
|
|
11021
|
+
g: 0,
|
|
11022
|
+
b: 0,
|
|
11023
|
+
a: 1
|
|
11024
|
+
},
|
|
11025
|
+
haloColor: {
|
|
11026
|
+
r: 35,
|
|
11027
|
+
g: 162,
|
|
11028
|
+
b: 255,
|
|
11029
|
+
a: 0.38
|
|
11030
|
+
},
|
|
11031
|
+
pulseScale: 2.15
|
|
11032
|
+
};
|
|
11033
|
+
var REINITIALIZE_BACKOFF_MS = 1e3;
|
|
11034
|
+
var FIRST_MOVE_CENTER_DISTANCE_THRESHOLD = 16;
|
|
11035
|
+
var FIRST_MOVE_MAX_TRAVEL = 220;
|
|
11036
|
+
var FIRST_MOVE_NEAR_TARGET_X_OFFSET = 28;
|
|
11037
|
+
var FIRST_MOVE_NEAR_TARGET_Y_OFFSET = 18;
|
|
11038
|
+
var MOTION_PLANNERS = {
|
|
11039
|
+
snappy: planSnappyCursorMotion
|
|
11040
|
+
};
|
|
11041
|
+
var CursorController = class {
|
|
11042
|
+
debug;
|
|
11043
|
+
renderer;
|
|
11044
|
+
page = null;
|
|
11045
|
+
listenerPage = null;
|
|
11046
|
+
lastPoint = null;
|
|
11047
|
+
initializedForPage = false;
|
|
11048
|
+
lastInitializeAttemptAt = 0;
|
|
11049
|
+
enabled;
|
|
11050
|
+
profile;
|
|
11051
|
+
style;
|
|
11052
|
+
onDomContentLoaded = () => {
|
|
11053
|
+
void this.restoreCursorAfterNavigation();
|
|
11054
|
+
};
|
|
11055
|
+
constructor(options = {}) {
|
|
11056
|
+
const config = options.config || {};
|
|
11057
|
+
this.debug = Boolean(options.debug);
|
|
11058
|
+
this.enabled = config.enabled === true;
|
|
11059
|
+
this.profile = config.profile ?? "snappy";
|
|
11060
|
+
this.style = mergeStyle(config.style);
|
|
11061
|
+
this.renderer = options.renderer ?? new SvgCursorRenderer();
|
|
11062
|
+
}
|
|
11063
|
+
setEnabled(enabled) {
|
|
11064
|
+
if (this.enabled && !enabled) {
|
|
11065
|
+
this.lastPoint = null;
|
|
11066
|
+
void this.clear();
|
|
11067
|
+
}
|
|
11068
|
+
this.enabled = enabled;
|
|
11069
|
+
}
|
|
11070
|
+
isEnabled() {
|
|
11071
|
+
return this.enabled;
|
|
11072
|
+
}
|
|
11073
|
+
getStatus() {
|
|
11074
|
+
if (!this.enabled) {
|
|
11075
|
+
return {
|
|
11076
|
+
enabled: false,
|
|
11077
|
+
active: false,
|
|
11078
|
+
reason: "disabled"
|
|
11079
|
+
};
|
|
11080
|
+
}
|
|
11081
|
+
const status = this.renderer.status();
|
|
11082
|
+
if (!this.initializedForPage && !status.active) {
|
|
11083
|
+
return {
|
|
11084
|
+
enabled: true,
|
|
11085
|
+
active: false,
|
|
11086
|
+
reason: "not_initialized"
|
|
11087
|
+
};
|
|
11088
|
+
}
|
|
11089
|
+
return status;
|
|
11090
|
+
}
|
|
11091
|
+
async attachPage(page) {
|
|
11092
|
+
if (this.page !== page) {
|
|
11093
|
+
this.detachPageListeners();
|
|
11094
|
+
this.page = page;
|
|
11095
|
+
this.lastPoint = null;
|
|
11096
|
+
this.initializedForPage = false;
|
|
11097
|
+
this.lastInitializeAttemptAt = 0;
|
|
11098
|
+
}
|
|
11099
|
+
this.attachPageListeners(page);
|
|
11100
|
+
}
|
|
11101
|
+
async preview(point, intent) {
|
|
11102
|
+
if (!this.enabled || !point) return;
|
|
11103
|
+
if (!this.page || this.page.isClosed()) return;
|
|
11104
|
+
try {
|
|
11105
|
+
await this.ensureInitialized();
|
|
11106
|
+
if (!this.renderer.isActive()) {
|
|
11107
|
+
await this.reinitializeIfEligible();
|
|
11108
|
+
}
|
|
11109
|
+
if (!this.renderer.isActive()) return;
|
|
11110
|
+
const start = this.resolveMotionStart(point);
|
|
11111
|
+
const motion = this.planMotion(start, point);
|
|
11112
|
+
for (const step of motion.points) {
|
|
11113
|
+
await this.renderer.move(step, this.style);
|
|
11114
|
+
if (motion.stepDelayMs > 0) {
|
|
11115
|
+
await sleep5(motion.stepDelayMs);
|
|
11116
|
+
}
|
|
11117
|
+
}
|
|
11118
|
+
if (shouldPulse(intent)) {
|
|
11119
|
+
await this.renderer.pulse(point, this.style);
|
|
11120
|
+
}
|
|
11121
|
+
this.lastPoint = point;
|
|
11122
|
+
} catch (error) {
|
|
11123
|
+
if (this.debug) {
|
|
11124
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
11125
|
+
console.warn(`[opensteer] cursor preview failed: ${message}`);
|
|
11126
|
+
}
|
|
11127
|
+
}
|
|
11128
|
+
}
|
|
11129
|
+
async clear() {
|
|
11130
|
+
try {
|
|
11131
|
+
await this.renderer.clear();
|
|
11132
|
+
} catch (error) {
|
|
11133
|
+
if (this.debug) {
|
|
11134
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
11135
|
+
console.warn(`[opensteer] cursor clear failed: ${message}`);
|
|
11136
|
+
}
|
|
11137
|
+
}
|
|
11138
|
+
}
|
|
11139
|
+
async dispose() {
|
|
11140
|
+
this.detachPageListeners();
|
|
11141
|
+
this.lastPoint = null;
|
|
11142
|
+
this.initializedForPage = false;
|
|
11143
|
+
this.lastInitializeAttemptAt = 0;
|
|
11144
|
+
this.page = null;
|
|
11145
|
+
await this.renderer.dispose();
|
|
11146
|
+
}
|
|
11147
|
+
async ensureInitialized() {
|
|
11148
|
+
if (!this.page || this.page.isClosed()) return;
|
|
11149
|
+
if (this.initializedForPage) return;
|
|
11150
|
+
await this.initializeRenderer();
|
|
11151
|
+
}
|
|
11152
|
+
attachPageListeners(page) {
|
|
11153
|
+
if (this.listenerPage === page) {
|
|
11154
|
+
return;
|
|
11155
|
+
}
|
|
11156
|
+
this.detachPageListeners();
|
|
11157
|
+
page.on("domcontentloaded", this.onDomContentLoaded);
|
|
11158
|
+
this.listenerPage = page;
|
|
11159
|
+
}
|
|
11160
|
+
detachPageListeners() {
|
|
11161
|
+
if (!this.listenerPage) {
|
|
11162
|
+
return;
|
|
11163
|
+
}
|
|
11164
|
+
this.listenerPage.off("domcontentloaded", this.onDomContentLoaded);
|
|
11165
|
+
this.listenerPage = null;
|
|
11166
|
+
}
|
|
11167
|
+
planMotion(from, to) {
|
|
11168
|
+
return MOTION_PLANNERS[this.profile](from, to);
|
|
11169
|
+
}
|
|
11170
|
+
async reinitializeIfEligible() {
|
|
11171
|
+
if (!this.page || this.page.isClosed()) return;
|
|
11172
|
+
const elapsed = Date.now() - this.lastInitializeAttemptAt;
|
|
11173
|
+
if (elapsed < REINITIALIZE_BACKOFF_MS) return;
|
|
11174
|
+
await this.initializeRenderer();
|
|
11175
|
+
}
|
|
11176
|
+
async initializeRenderer() {
|
|
11177
|
+
if (!this.page || this.page.isClosed()) return;
|
|
11178
|
+
this.lastInitializeAttemptAt = Date.now();
|
|
11179
|
+
await this.renderer.initialize(this.page);
|
|
11180
|
+
this.initializedForPage = true;
|
|
11181
|
+
}
|
|
11182
|
+
async restoreCursorAfterNavigation() {
|
|
11183
|
+
if (!this.enabled || !this.lastPoint) return;
|
|
11184
|
+
if (!this.page || this.page.isClosed()) return;
|
|
11185
|
+
try {
|
|
11186
|
+
if (!this.renderer.isActive()) {
|
|
11187
|
+
await this.reinitializeIfEligible();
|
|
11188
|
+
}
|
|
11189
|
+
if (!this.renderer.isActive()) {
|
|
11190
|
+
return;
|
|
11191
|
+
}
|
|
11192
|
+
await this.renderer.move(this.lastPoint, this.style);
|
|
10410
11193
|
} catch (error) {
|
|
10411
11194
|
if (this.debug) {
|
|
10412
11195
|
const message = error instanceof Error ? error.message : String(error);
|
|
10413
|
-
console.warn(
|
|
11196
|
+
console.warn(
|
|
11197
|
+
`[opensteer] cursor restore after navigation failed: ${message}`
|
|
11198
|
+
);
|
|
11199
|
+
}
|
|
11200
|
+
}
|
|
11201
|
+
}
|
|
11202
|
+
resolveMotionStart(target) {
|
|
11203
|
+
if (this.lastPoint) {
|
|
11204
|
+
return this.lastPoint;
|
|
11205
|
+
}
|
|
11206
|
+
const viewport = this.page?.viewportSize();
|
|
11207
|
+
if (!viewport?.width || !viewport?.height) {
|
|
11208
|
+
return target;
|
|
11209
|
+
}
|
|
11210
|
+
const centerPoint = {
|
|
11211
|
+
x: viewport.width / 2,
|
|
11212
|
+
y: viewport.height / 2
|
|
11213
|
+
};
|
|
11214
|
+
if (distanceBetween2(centerPoint, target) > FIRST_MOVE_CENTER_DISTANCE_THRESHOLD) {
|
|
11215
|
+
const dx = target.x - centerPoint.x;
|
|
11216
|
+
const dy = target.y - centerPoint.y;
|
|
11217
|
+
const distance = Math.hypot(dx, dy);
|
|
11218
|
+
if (distance > FIRST_MOVE_MAX_TRAVEL) {
|
|
11219
|
+
const ux = dx / distance;
|
|
11220
|
+
const uy = dy / distance;
|
|
11221
|
+
return {
|
|
11222
|
+
x: target.x - ux * FIRST_MOVE_MAX_TRAVEL,
|
|
11223
|
+
y: target.y - uy * FIRST_MOVE_MAX_TRAVEL
|
|
11224
|
+
};
|
|
10414
11225
|
}
|
|
11226
|
+
return centerPoint;
|
|
10415
11227
|
}
|
|
11228
|
+
return {
|
|
11229
|
+
x: clamp2(target.x - FIRST_MOVE_NEAR_TARGET_X_OFFSET, 0, viewport.width),
|
|
11230
|
+
y: clamp2(target.y - FIRST_MOVE_NEAR_TARGET_Y_OFFSET, 0, viewport.height)
|
|
11231
|
+
};
|
|
10416
11232
|
}
|
|
10417
11233
|
};
|
|
10418
|
-
function
|
|
10419
|
-
return
|
|
11234
|
+
function mergeStyle(style) {
|
|
11235
|
+
return {
|
|
11236
|
+
size: normalizeFinite(style?.size, DEFAULT_STYLE.size, 4, 48),
|
|
11237
|
+
pulseScale: normalizeFinite(
|
|
11238
|
+
style?.pulseScale,
|
|
11239
|
+
DEFAULT_STYLE.pulseScale,
|
|
11240
|
+
1,
|
|
11241
|
+
3
|
|
11242
|
+
),
|
|
11243
|
+
fillColor: normalizeColor(style?.fillColor, DEFAULT_STYLE.fillColor),
|
|
11244
|
+
outlineColor: normalizeColor(
|
|
11245
|
+
style?.outlineColor,
|
|
11246
|
+
DEFAULT_STYLE.outlineColor
|
|
11247
|
+
),
|
|
11248
|
+
haloColor: normalizeColor(style?.haloColor, DEFAULT_STYLE.haloColor)
|
|
11249
|
+
};
|
|
11250
|
+
}
|
|
11251
|
+
function normalizeColor(color, fallback) {
|
|
11252
|
+
if (!color) return { ...fallback };
|
|
11253
|
+
return {
|
|
11254
|
+
r: normalizeFinite(color.r, fallback.r, 0, 255),
|
|
11255
|
+
g: normalizeFinite(color.g, fallback.g, 0, 255),
|
|
11256
|
+
b: normalizeFinite(color.b, fallback.b, 0, 255),
|
|
11257
|
+
a: normalizeFinite(color.a, fallback.a, 0, 1)
|
|
11258
|
+
};
|
|
11259
|
+
}
|
|
11260
|
+
function normalizeFinite(value, fallback, min, max) {
|
|
11261
|
+
const numeric = typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
11262
|
+
return Math.min(max, Math.max(min, numeric));
|
|
11263
|
+
}
|
|
11264
|
+
function distanceBetween2(a, b) {
|
|
11265
|
+
return Math.hypot(a.x - b.x, a.y - b.y);
|
|
11266
|
+
}
|
|
11267
|
+
function clamp2(value, min, max) {
|
|
11268
|
+
return Math.min(max, Math.max(min, value));
|
|
11269
|
+
}
|
|
11270
|
+
function shouldPulse(intent) {
|
|
11271
|
+
return intent === "click" || intent === "dblclick" || intent === "rightclick" || intent === "agent";
|
|
11272
|
+
}
|
|
11273
|
+
function sleep5(ms) {
|
|
11274
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
10420
11275
|
}
|
|
10421
11276
|
|
|
10422
11277
|
// src/opensteer.ts
|
|
@@ -10445,6 +11300,7 @@ var Opensteer = class _Opensteer {
|
|
|
10445
11300
|
ownsBrowser = false;
|
|
10446
11301
|
snapshotCache = null;
|
|
10447
11302
|
agentExecutionInFlight = false;
|
|
11303
|
+
cursorController = null;
|
|
10448
11304
|
constructor(config = {}) {
|
|
10449
11305
|
const resolvedRuntime = resolveConfigWithEnv(config);
|
|
10450
11306
|
const resolved = resolvedRuntime.config;
|
|
@@ -10466,15 +11322,29 @@ var Opensteer = class _Opensteer {
|
|
|
10466
11322
|
if (cloudSelection.cloud) {
|
|
10467
11323
|
const cloudConfig = resolved.cloud && typeof resolved.cloud === "object" ? resolved.cloud : void 0;
|
|
10468
11324
|
const apiKey = cloudConfig?.apiKey?.trim();
|
|
10469
|
-
|
|
11325
|
+
const accessToken = cloudConfig?.accessToken?.trim();
|
|
11326
|
+
if (apiKey && accessToken) {
|
|
11327
|
+
throw new Error(
|
|
11328
|
+
"Cloud mode cannot use both cloud.apiKey and cloud.accessToken. Set only one credential."
|
|
11329
|
+
);
|
|
11330
|
+
}
|
|
11331
|
+
let credential = "";
|
|
11332
|
+
let authScheme = cloudConfig?.authScheme ?? "api-key";
|
|
11333
|
+
if (accessToken) {
|
|
11334
|
+
credential = accessToken;
|
|
11335
|
+
authScheme = "bearer";
|
|
11336
|
+
} else if (apiKey) {
|
|
11337
|
+
credential = apiKey;
|
|
11338
|
+
}
|
|
11339
|
+
if (!credential) {
|
|
10470
11340
|
throw new Error(
|
|
10471
|
-
"Cloud mode requires
|
|
11341
|
+
"Cloud mode requires credentials via cloud.apiKey/cloud.accessToken or OPENSTEER_API_KEY/OPENSTEER_ACCESS_TOKEN."
|
|
10472
11342
|
);
|
|
10473
11343
|
}
|
|
10474
11344
|
this.cloud = createCloudRuntimeState(
|
|
10475
|
-
|
|
11345
|
+
credential,
|
|
10476
11346
|
cloudConfig?.baseUrl,
|
|
10477
|
-
|
|
11347
|
+
authScheme
|
|
10478
11348
|
);
|
|
10479
11349
|
} else {
|
|
10480
11350
|
this.cloud = null;
|
|
@@ -10699,6 +11569,19 @@ var Opensteer = class _Opensteer {
|
|
|
10699
11569
|
}
|
|
10700
11570
|
return true;
|
|
10701
11571
|
}
|
|
11572
|
+
buildCloudSessionLaunchConfig(options) {
|
|
11573
|
+
const cloudConfig = this.config.cloud && typeof this.config.cloud === "object" ? this.config.cloud : void 0;
|
|
11574
|
+
const browserProfile = normalizeCloudBrowserProfilePreference(
|
|
11575
|
+
options.cloudBrowserProfile ?? cloudConfig?.browserProfile,
|
|
11576
|
+
options.cloudBrowserProfile ? "launch options" : "Opensteer config"
|
|
11577
|
+
);
|
|
11578
|
+
if (!browserProfile) {
|
|
11579
|
+
return void 0;
|
|
11580
|
+
}
|
|
11581
|
+
return {
|
|
11582
|
+
browserProfile
|
|
11583
|
+
};
|
|
11584
|
+
}
|
|
10702
11585
|
async launch(options = {}) {
|
|
10703
11586
|
if (this.pageRef && !this.ownsBrowser) {
|
|
10704
11587
|
throw new Error(
|
|
@@ -10721,6 +11604,7 @@ var Opensteer = class _Opensteer {
|
|
|
10721
11604
|
}
|
|
10722
11605
|
localRunId = this.cloud.localRunId || buildLocalRunId(this.namespace);
|
|
10723
11606
|
this.cloud.localRunId = localRunId;
|
|
11607
|
+
const launchConfig = this.buildCloudSessionLaunchConfig(options);
|
|
10724
11608
|
const session2 = await this.cloud.sessionClient.create({
|
|
10725
11609
|
cloudSessionContractVersion,
|
|
10726
11610
|
sourceType: "local-cloud",
|
|
@@ -10728,7 +11612,8 @@ var Opensteer = class _Opensteer {
|
|
|
10728
11612
|
localRunId,
|
|
10729
11613
|
name: this.namespace,
|
|
10730
11614
|
model: this.config.model,
|
|
10731
|
-
launchContext: options.context || void 0
|
|
11615
|
+
launchContext: options.context || void 0,
|
|
11616
|
+
launchConfig
|
|
10732
11617
|
});
|
|
10733
11618
|
sessionId = session2.sessionId;
|
|
10734
11619
|
actionClient = await ActionWsClient.connect({
|
|
@@ -10746,6 +11631,9 @@ var Opensteer = class _Opensteer {
|
|
|
10746
11631
|
this.pageRef = cdpConnection.page;
|
|
10747
11632
|
this.ownsBrowser = true;
|
|
10748
11633
|
this.snapshotCache = null;
|
|
11634
|
+
if (this.cursorController) {
|
|
11635
|
+
await this.cursorController.attachPage(this.pageRef);
|
|
11636
|
+
}
|
|
10749
11637
|
this.cloud.actionClient = actionClient;
|
|
10750
11638
|
this.cloud.sessionId = sessionId;
|
|
10751
11639
|
this.cloud.cloudSessionUrl = session2.cloudSessionUrl;
|
|
@@ -10786,6 +11674,9 @@ var Opensteer = class _Opensteer {
|
|
|
10786
11674
|
this.pageRef = session.page;
|
|
10787
11675
|
this.ownsBrowser = true;
|
|
10788
11676
|
this.snapshotCache = null;
|
|
11677
|
+
if (this.cursorController) {
|
|
11678
|
+
await this.cursorController.attachPage(this.pageRef);
|
|
11679
|
+
}
|
|
10789
11680
|
}
|
|
10790
11681
|
static from(page, config = {}) {
|
|
10791
11682
|
const resolvedRuntime = resolveConfigWithEnv(config);
|
|
@@ -10830,6 +11721,9 @@ var Opensteer = class _Opensteer {
|
|
|
10830
11721
|
if (sessionId) {
|
|
10831
11722
|
await this.cloud.sessionClient.close(sessionId).catch(() => void 0);
|
|
10832
11723
|
}
|
|
11724
|
+
if (this.cursorController) {
|
|
11725
|
+
await this.cursorController.dispose().catch(() => void 0);
|
|
11726
|
+
}
|
|
10833
11727
|
return;
|
|
10834
11728
|
}
|
|
10835
11729
|
if (this.ownsBrowser) {
|
|
@@ -10839,6 +11733,9 @@ var Opensteer = class _Opensteer {
|
|
|
10839
11733
|
this.pageRef = null;
|
|
10840
11734
|
this.contextRef = null;
|
|
10841
11735
|
this.ownsBrowser = false;
|
|
11736
|
+
if (this.cursorController) {
|
|
11737
|
+
await this.cursorController.dispose().catch(() => void 0);
|
|
11738
|
+
}
|
|
10842
11739
|
}
|
|
10843
11740
|
async syncLocalSelectorCacheToCloud() {
|
|
10844
11741
|
if (!this.cloud) return;
|
|
@@ -10965,12 +11862,22 @@ var Opensteer = class _Opensteer {
|
|
|
10965
11862
|
resolution.counter
|
|
10966
11863
|
);
|
|
10967
11864
|
}
|
|
10968
|
-
await this.
|
|
10969
|
-
|
|
10970
|
-
|
|
10971
|
-
|
|
10972
|
-
|
|
10973
|
-
|
|
11865
|
+
await this.runWithCursorPreview(
|
|
11866
|
+
() => this.resolveHandleTargetPoint(handle, options.position),
|
|
11867
|
+
"hover",
|
|
11868
|
+
async () => {
|
|
11869
|
+
await this.runWithPostActionWait(
|
|
11870
|
+
"hover",
|
|
11871
|
+
options.wait,
|
|
11872
|
+
async () => {
|
|
11873
|
+
await handle.hover({
|
|
11874
|
+
force: options.force,
|
|
11875
|
+
position: options.position
|
|
11876
|
+
});
|
|
11877
|
+
}
|
|
11878
|
+
);
|
|
11879
|
+
}
|
|
11880
|
+
);
|
|
10974
11881
|
} catch (err) {
|
|
10975
11882
|
const failure = classifyActionFailure({
|
|
10976
11883
|
action: "hover",
|
|
@@ -11005,25 +11912,31 @@ var Opensteer = class _Opensteer {
|
|
|
11005
11912
|
throw new Error("Unable to resolve element path for hover action.");
|
|
11006
11913
|
}
|
|
11007
11914
|
const path5 = resolution.path;
|
|
11008
|
-
const result = await this.
|
|
11915
|
+
const result = await this.runWithCursorPreview(
|
|
11916
|
+
() => this.resolvePathTargetPoint(path5, options.position),
|
|
11009
11917
|
"hover",
|
|
11010
|
-
options.wait,
|
|
11011
11918
|
async () => {
|
|
11012
|
-
|
|
11013
|
-
|
|
11014
|
-
|
|
11015
|
-
|
|
11016
|
-
|
|
11017
|
-
|
|
11018
|
-
|
|
11019
|
-
|
|
11020
|
-
|
|
11021
|
-
|
|
11022
|
-
|
|
11023
|
-
|
|
11024
|
-
|
|
11025
|
-
|
|
11026
|
-
|
|
11919
|
+
return await this.runWithPostActionWait(
|
|
11920
|
+
"hover",
|
|
11921
|
+
options.wait,
|
|
11922
|
+
async () => {
|
|
11923
|
+
const actionResult = await performHover(this.page, path5, options);
|
|
11924
|
+
if (!actionResult.ok) {
|
|
11925
|
+
const failure = actionResult.failure || classifyActionFailure({
|
|
11926
|
+
action: "hover",
|
|
11927
|
+
error: actionResult.error || defaultActionFailureMessage("hover"),
|
|
11928
|
+
fallbackMessage: defaultActionFailureMessage("hover")
|
|
11929
|
+
});
|
|
11930
|
+
throw this.buildActionError(
|
|
11931
|
+
"hover",
|
|
11932
|
+
options.description,
|
|
11933
|
+
failure,
|
|
11934
|
+
actionResult.usedSelector || null
|
|
11935
|
+
);
|
|
11936
|
+
}
|
|
11937
|
+
return actionResult;
|
|
11938
|
+
}
|
|
11939
|
+
);
|
|
11027
11940
|
}
|
|
11028
11941
|
);
|
|
11029
11942
|
this.snapshotCache = null;
|
|
@@ -11064,16 +11977,26 @@ var Opensteer = class _Opensteer {
|
|
|
11064
11977
|
resolution.counter
|
|
11065
11978
|
);
|
|
11066
11979
|
}
|
|
11067
|
-
await this.
|
|
11068
|
-
|
|
11069
|
-
|
|
11070
|
-
|
|
11071
|
-
await
|
|
11072
|
-
|
|
11073
|
-
|
|
11074
|
-
|
|
11980
|
+
await this.runWithCursorPreview(
|
|
11981
|
+
() => this.resolveHandleTargetPoint(handle),
|
|
11982
|
+
"input",
|
|
11983
|
+
async () => {
|
|
11984
|
+
await this.runWithPostActionWait(
|
|
11985
|
+
"input",
|
|
11986
|
+
options.wait,
|
|
11987
|
+
async () => {
|
|
11988
|
+
if (options.clear !== false) {
|
|
11989
|
+
await handle.fill(options.text);
|
|
11990
|
+
} else {
|
|
11991
|
+
await handle.type(options.text);
|
|
11992
|
+
}
|
|
11993
|
+
if (options.pressEnter) {
|
|
11994
|
+
await handle.press("Enter", { noWaitAfter: true });
|
|
11995
|
+
}
|
|
11996
|
+
}
|
|
11997
|
+
);
|
|
11075
11998
|
}
|
|
11076
|
-
|
|
11999
|
+
);
|
|
11077
12000
|
} catch (err) {
|
|
11078
12001
|
const failure = classifyActionFailure({
|
|
11079
12002
|
action: "input",
|
|
@@ -11108,25 +12031,31 @@ var Opensteer = class _Opensteer {
|
|
|
11108
12031
|
throw new Error("Unable to resolve element path for input action.");
|
|
11109
12032
|
}
|
|
11110
12033
|
const path5 = resolution.path;
|
|
11111
|
-
const result = await this.
|
|
12034
|
+
const result = await this.runWithCursorPreview(
|
|
12035
|
+
() => this.resolvePathTargetPoint(path5),
|
|
11112
12036
|
"input",
|
|
11113
|
-
options.wait,
|
|
11114
12037
|
async () => {
|
|
11115
|
-
|
|
11116
|
-
|
|
11117
|
-
|
|
11118
|
-
|
|
11119
|
-
|
|
11120
|
-
|
|
11121
|
-
|
|
11122
|
-
|
|
11123
|
-
|
|
11124
|
-
|
|
11125
|
-
|
|
11126
|
-
|
|
11127
|
-
|
|
11128
|
-
|
|
11129
|
-
|
|
12038
|
+
return await this.runWithPostActionWait(
|
|
12039
|
+
"input",
|
|
12040
|
+
options.wait,
|
|
12041
|
+
async () => {
|
|
12042
|
+
const actionResult = await performInput(this.page, path5, options);
|
|
12043
|
+
if (!actionResult.ok) {
|
|
12044
|
+
const failure = actionResult.failure || classifyActionFailure({
|
|
12045
|
+
action: "input",
|
|
12046
|
+
error: actionResult.error || defaultActionFailureMessage("input"),
|
|
12047
|
+
fallbackMessage: defaultActionFailureMessage("input")
|
|
12048
|
+
});
|
|
12049
|
+
throw this.buildActionError(
|
|
12050
|
+
"input",
|
|
12051
|
+
options.description,
|
|
12052
|
+
failure,
|
|
12053
|
+
actionResult.usedSelector || null
|
|
12054
|
+
);
|
|
12055
|
+
}
|
|
12056
|
+
return actionResult;
|
|
12057
|
+
}
|
|
12058
|
+
);
|
|
11130
12059
|
}
|
|
11131
12060
|
);
|
|
11132
12061
|
this.snapshotCache = null;
|
|
@@ -11167,21 +12096,27 @@ var Opensteer = class _Opensteer {
|
|
|
11167
12096
|
resolution.counter
|
|
11168
12097
|
);
|
|
11169
12098
|
}
|
|
11170
|
-
await this.
|
|
12099
|
+
await this.runWithCursorPreview(
|
|
12100
|
+
() => this.resolveHandleTargetPoint(handle),
|
|
11171
12101
|
"select",
|
|
11172
|
-
options.wait,
|
|
11173
12102
|
async () => {
|
|
11174
|
-
|
|
11175
|
-
|
|
11176
|
-
|
|
11177
|
-
|
|
11178
|
-
|
|
11179
|
-
|
|
11180
|
-
|
|
11181
|
-
|
|
11182
|
-
|
|
11183
|
-
|
|
11184
|
-
|
|
12103
|
+
await this.runWithPostActionWait(
|
|
12104
|
+
"select",
|
|
12105
|
+
options.wait,
|
|
12106
|
+
async () => {
|
|
12107
|
+
if (options.value != null) {
|
|
12108
|
+
await handle.selectOption(options.value);
|
|
12109
|
+
} else if (options.label != null) {
|
|
12110
|
+
await handle.selectOption({ label: options.label });
|
|
12111
|
+
} else if (options.index != null) {
|
|
12112
|
+
await handle.selectOption({ index: options.index });
|
|
12113
|
+
} else {
|
|
12114
|
+
throw new Error(
|
|
12115
|
+
"Select requires value, label, or index."
|
|
12116
|
+
);
|
|
12117
|
+
}
|
|
12118
|
+
}
|
|
12119
|
+
);
|
|
11185
12120
|
}
|
|
11186
12121
|
);
|
|
11187
12122
|
} catch (err) {
|
|
@@ -11218,25 +12153,31 @@ var Opensteer = class _Opensteer {
|
|
|
11218
12153
|
throw new Error("Unable to resolve element path for select action.");
|
|
11219
12154
|
}
|
|
11220
12155
|
const path5 = resolution.path;
|
|
11221
|
-
const result = await this.
|
|
12156
|
+
const result = await this.runWithCursorPreview(
|
|
12157
|
+
() => this.resolvePathTargetPoint(path5),
|
|
11222
12158
|
"select",
|
|
11223
|
-
options.wait,
|
|
11224
12159
|
async () => {
|
|
11225
|
-
|
|
11226
|
-
|
|
11227
|
-
|
|
11228
|
-
|
|
11229
|
-
|
|
11230
|
-
|
|
11231
|
-
|
|
11232
|
-
|
|
11233
|
-
|
|
11234
|
-
|
|
11235
|
-
|
|
11236
|
-
|
|
11237
|
-
|
|
11238
|
-
|
|
11239
|
-
|
|
12160
|
+
return await this.runWithPostActionWait(
|
|
12161
|
+
"select",
|
|
12162
|
+
options.wait,
|
|
12163
|
+
async () => {
|
|
12164
|
+
const actionResult = await performSelect(this.page, path5, options);
|
|
12165
|
+
if (!actionResult.ok) {
|
|
12166
|
+
const failure = actionResult.failure || classifyActionFailure({
|
|
12167
|
+
action: "select",
|
|
12168
|
+
error: actionResult.error || defaultActionFailureMessage("select"),
|
|
12169
|
+
fallbackMessage: defaultActionFailureMessage("select")
|
|
12170
|
+
});
|
|
12171
|
+
throw this.buildActionError(
|
|
12172
|
+
"select",
|
|
12173
|
+
options.description,
|
|
12174
|
+
failure,
|
|
12175
|
+
actionResult.usedSelector || null
|
|
12176
|
+
);
|
|
12177
|
+
}
|
|
12178
|
+
return actionResult;
|
|
12179
|
+
}
|
|
12180
|
+
);
|
|
11240
12181
|
}
|
|
11241
12182
|
);
|
|
11242
12183
|
this.snapshotCache = null;
|
|
@@ -11278,13 +12219,23 @@ var Opensteer = class _Opensteer {
|
|
|
11278
12219
|
);
|
|
11279
12220
|
}
|
|
11280
12221
|
const delta = getScrollDelta2(options);
|
|
11281
|
-
await this.
|
|
11282
|
-
|
|
11283
|
-
|
|
11284
|
-
|
|
11285
|
-
|
|
11286
|
-
|
|
11287
|
-
|
|
12222
|
+
await this.runWithCursorPreview(
|
|
12223
|
+
() => this.resolveHandleTargetPoint(handle),
|
|
12224
|
+
"scroll",
|
|
12225
|
+
async () => {
|
|
12226
|
+
await this.runWithPostActionWait(
|
|
12227
|
+
"scroll",
|
|
12228
|
+
options.wait,
|
|
12229
|
+
async () => {
|
|
12230
|
+
await handle.evaluate((el, value) => {
|
|
12231
|
+
if (el instanceof HTMLElement) {
|
|
12232
|
+
el.scrollBy(value.x, value.y);
|
|
12233
|
+
}
|
|
12234
|
+
}, delta);
|
|
12235
|
+
}
|
|
12236
|
+
);
|
|
12237
|
+
}
|
|
12238
|
+
);
|
|
11288
12239
|
} catch (err) {
|
|
11289
12240
|
const failure = classifyActionFailure({
|
|
11290
12241
|
action: "scroll",
|
|
@@ -11315,29 +12266,35 @@ var Opensteer = class _Opensteer {
|
|
|
11315
12266
|
`[c="${resolution.counter}"]`
|
|
11316
12267
|
);
|
|
11317
12268
|
}
|
|
11318
|
-
const result = await this.
|
|
12269
|
+
const result = await this.runWithCursorPreview(
|
|
12270
|
+
() => resolution.path ? this.resolvePathTargetPoint(resolution.path) : this.resolveViewportAnchorPoint(),
|
|
11319
12271
|
"scroll",
|
|
11320
|
-
options.wait,
|
|
11321
12272
|
async () => {
|
|
11322
|
-
|
|
11323
|
-
|
|
11324
|
-
|
|
11325
|
-
|
|
12273
|
+
return await this.runWithPostActionWait(
|
|
12274
|
+
"scroll",
|
|
12275
|
+
options.wait,
|
|
12276
|
+
async () => {
|
|
12277
|
+
const actionResult = await performScroll(
|
|
12278
|
+
this.page,
|
|
12279
|
+
resolution.path,
|
|
12280
|
+
options
|
|
12281
|
+
);
|
|
12282
|
+
if (!actionResult.ok) {
|
|
12283
|
+
const failure = actionResult.failure || classifyActionFailure({
|
|
12284
|
+
action: "scroll",
|
|
12285
|
+
error: actionResult.error || defaultActionFailureMessage("scroll"),
|
|
12286
|
+
fallbackMessage: defaultActionFailureMessage("scroll")
|
|
12287
|
+
});
|
|
12288
|
+
throw this.buildActionError(
|
|
12289
|
+
"scroll",
|
|
12290
|
+
options.description,
|
|
12291
|
+
failure,
|
|
12292
|
+
actionResult.usedSelector || null
|
|
12293
|
+
);
|
|
12294
|
+
}
|
|
12295
|
+
return actionResult;
|
|
12296
|
+
}
|
|
11326
12297
|
);
|
|
11327
|
-
if (!actionResult.ok) {
|
|
11328
|
-
const failure = actionResult.failure || classifyActionFailure({
|
|
11329
|
-
action: "scroll",
|
|
11330
|
-
error: actionResult.error || defaultActionFailureMessage("scroll"),
|
|
11331
|
-
fallbackMessage: defaultActionFailureMessage("scroll")
|
|
11332
|
-
});
|
|
11333
|
-
throw this.buildActionError(
|
|
11334
|
-
"scroll",
|
|
11335
|
-
options.description,
|
|
11336
|
-
failure,
|
|
11337
|
-
actionResult.usedSelector || null
|
|
11338
|
-
);
|
|
11339
|
-
}
|
|
11340
|
-
return actionResult;
|
|
11341
12298
|
}
|
|
11342
12299
|
);
|
|
11343
12300
|
this.snapshotCache = null;
|
|
@@ -11623,11 +12580,17 @@ var Opensteer = class _Opensteer {
|
|
|
11623
12580
|
resolution.counter
|
|
11624
12581
|
);
|
|
11625
12582
|
}
|
|
11626
|
-
await this.
|
|
12583
|
+
await this.runWithCursorPreview(
|
|
12584
|
+
() => this.resolveHandleTargetPoint(handle),
|
|
11627
12585
|
"uploadFile",
|
|
11628
|
-
options.wait,
|
|
11629
12586
|
async () => {
|
|
11630
|
-
await
|
|
12587
|
+
await this.runWithPostActionWait(
|
|
12588
|
+
"uploadFile",
|
|
12589
|
+
options.wait,
|
|
12590
|
+
async () => {
|
|
12591
|
+
await handle.setInputFiles(options.paths);
|
|
12592
|
+
}
|
|
12593
|
+
);
|
|
11631
12594
|
}
|
|
11632
12595
|
);
|
|
11633
12596
|
} catch (err) {
|
|
@@ -11664,29 +12627,35 @@ var Opensteer = class _Opensteer {
|
|
|
11664
12627
|
throw new Error("Unable to resolve element path for file upload.");
|
|
11665
12628
|
}
|
|
11666
12629
|
const path5 = resolution.path;
|
|
11667
|
-
const result = await this.
|
|
12630
|
+
const result = await this.runWithCursorPreview(
|
|
12631
|
+
() => this.resolvePathTargetPoint(path5),
|
|
11668
12632
|
"uploadFile",
|
|
11669
|
-
options.wait,
|
|
11670
12633
|
async () => {
|
|
11671
|
-
|
|
11672
|
-
|
|
11673
|
-
|
|
11674
|
-
|
|
12634
|
+
return await this.runWithPostActionWait(
|
|
12635
|
+
"uploadFile",
|
|
12636
|
+
options.wait,
|
|
12637
|
+
async () => {
|
|
12638
|
+
const actionResult = await performFileUpload(
|
|
12639
|
+
this.page,
|
|
12640
|
+
path5,
|
|
12641
|
+
options.paths
|
|
12642
|
+
);
|
|
12643
|
+
if (!actionResult.ok) {
|
|
12644
|
+
const failure = actionResult.failure || classifyActionFailure({
|
|
12645
|
+
action: "uploadFile",
|
|
12646
|
+
error: actionResult.error || defaultActionFailureMessage("uploadFile"),
|
|
12647
|
+
fallbackMessage: defaultActionFailureMessage("uploadFile")
|
|
12648
|
+
});
|
|
12649
|
+
throw this.buildActionError(
|
|
12650
|
+
"uploadFile",
|
|
12651
|
+
options.description,
|
|
12652
|
+
failure,
|
|
12653
|
+
actionResult.usedSelector || null
|
|
12654
|
+
);
|
|
12655
|
+
}
|
|
12656
|
+
return actionResult;
|
|
12657
|
+
}
|
|
11675
12658
|
);
|
|
11676
|
-
if (!actionResult.ok) {
|
|
11677
|
-
const failure = actionResult.failure || classifyActionFailure({
|
|
11678
|
-
action: "uploadFile",
|
|
11679
|
-
error: actionResult.error || defaultActionFailureMessage("uploadFile"),
|
|
11680
|
-
fallbackMessage: defaultActionFailureMessage("uploadFile")
|
|
11681
|
-
});
|
|
11682
|
-
throw this.buildActionError(
|
|
11683
|
-
"uploadFile",
|
|
11684
|
-
options.description,
|
|
11685
|
-
failure,
|
|
11686
|
-
actionResult.usedSelector || null
|
|
11687
|
-
);
|
|
11688
|
-
}
|
|
11689
|
-
return actionResult;
|
|
11690
12659
|
}
|
|
11691
12660
|
);
|
|
11692
12661
|
this.snapshotCache = null;
|
|
@@ -11822,6 +12791,25 @@ var Opensteer = class _Opensteer {
|
|
|
11822
12791
|
getConfig() {
|
|
11823
12792
|
return this.config;
|
|
11824
12793
|
}
|
|
12794
|
+
setCursorEnabled(enabled) {
|
|
12795
|
+
this.getCursorController().setEnabled(enabled);
|
|
12796
|
+
}
|
|
12797
|
+
getCursorState() {
|
|
12798
|
+
const controller = this.cursorController;
|
|
12799
|
+
if (!controller) {
|
|
12800
|
+
return {
|
|
12801
|
+
enabled: this.config.cursor?.enabled === true,
|
|
12802
|
+
active: false,
|
|
12803
|
+
reason: this.config.cursor?.enabled === true ? "not_initialized" : "disabled"
|
|
12804
|
+
};
|
|
12805
|
+
}
|
|
12806
|
+
const status = controller.getStatus();
|
|
12807
|
+
return {
|
|
12808
|
+
enabled: status.enabled,
|
|
12809
|
+
active: status.active,
|
|
12810
|
+
reason: status.reason
|
|
12811
|
+
};
|
|
12812
|
+
}
|
|
11825
12813
|
getStorage() {
|
|
11826
12814
|
return this.storage;
|
|
11827
12815
|
}
|
|
@@ -11849,24 +12837,107 @@ var Opensteer = class _Opensteer {
|
|
|
11849
12837
|
this.agentExecutionInFlight = true;
|
|
11850
12838
|
try {
|
|
11851
12839
|
const options = normalizeExecuteOptions(instructionOrOptions);
|
|
12840
|
+
const cursorController = this.getCursorController();
|
|
12841
|
+
const previousCursorEnabled = cursorController.isEnabled();
|
|
12842
|
+
if (options.highlightCursor !== void 0) {
|
|
12843
|
+
cursorController.setEnabled(options.highlightCursor);
|
|
12844
|
+
}
|
|
11852
12845
|
const handler = new OpensteerCuaAgentHandler({
|
|
11853
12846
|
page: this.page,
|
|
11854
12847
|
config: resolvedAgentConfig,
|
|
11855
12848
|
client: createCuaClient(resolvedAgentConfig),
|
|
11856
|
-
|
|
12849
|
+
cursorController,
|
|
11857
12850
|
onMutatingAction: () => {
|
|
11858
12851
|
this.snapshotCache = null;
|
|
11859
12852
|
}
|
|
11860
12853
|
});
|
|
11861
|
-
|
|
11862
|
-
|
|
11863
|
-
|
|
12854
|
+
try {
|
|
12855
|
+
const result = await handler.execute(options);
|
|
12856
|
+
this.snapshotCache = null;
|
|
12857
|
+
return result;
|
|
12858
|
+
} finally {
|
|
12859
|
+
if (options.highlightCursor !== void 0) {
|
|
12860
|
+
cursorController.setEnabled(previousCursorEnabled);
|
|
12861
|
+
}
|
|
12862
|
+
}
|
|
11864
12863
|
} finally {
|
|
11865
12864
|
this.agentExecutionInFlight = false;
|
|
11866
12865
|
}
|
|
11867
12866
|
}
|
|
11868
12867
|
};
|
|
11869
12868
|
}
|
|
12869
|
+
getCursorController() {
|
|
12870
|
+
if (!this.cursorController) {
|
|
12871
|
+
this.cursorController = new CursorController({
|
|
12872
|
+
config: this.config.cursor,
|
|
12873
|
+
debug: Boolean(this.config.debug)
|
|
12874
|
+
});
|
|
12875
|
+
if (this.pageRef) {
|
|
12876
|
+
void this.cursorController.attachPage(this.pageRef);
|
|
12877
|
+
}
|
|
12878
|
+
}
|
|
12879
|
+
return this.cursorController;
|
|
12880
|
+
}
|
|
12881
|
+
async runWithCursorPreview(pointResolver, intent, execute) {
|
|
12882
|
+
if (this.isCursorPreviewEnabled()) {
|
|
12883
|
+
const point = await pointResolver();
|
|
12884
|
+
await this.previewCursorPoint(point, intent);
|
|
12885
|
+
}
|
|
12886
|
+
return await execute();
|
|
12887
|
+
}
|
|
12888
|
+
isCursorPreviewEnabled() {
|
|
12889
|
+
return this.cursorController ? this.cursorController.isEnabled() : this.config.cursor?.enabled === true;
|
|
12890
|
+
}
|
|
12891
|
+
async previewCursorPoint(point, intent) {
|
|
12892
|
+
const cursor = this.getCursorController();
|
|
12893
|
+
await cursor.attachPage(this.page);
|
|
12894
|
+
await cursor.preview(point, intent);
|
|
12895
|
+
}
|
|
12896
|
+
resolveCursorPointFromBoundingBox(box, position) {
|
|
12897
|
+
if (position) {
|
|
12898
|
+
return {
|
|
12899
|
+
x: box.x + position.x,
|
|
12900
|
+
y: box.y + position.y
|
|
12901
|
+
};
|
|
12902
|
+
}
|
|
12903
|
+
return {
|
|
12904
|
+
x: box.x + box.width / 2,
|
|
12905
|
+
y: box.y + box.height / 2
|
|
12906
|
+
};
|
|
12907
|
+
}
|
|
12908
|
+
async resolveHandleTargetPoint(handle, position) {
|
|
12909
|
+
try {
|
|
12910
|
+
const box = await handle.boundingBox();
|
|
12911
|
+
if (!box) return null;
|
|
12912
|
+
return this.resolveCursorPointFromBoundingBox(box, position);
|
|
12913
|
+
} catch {
|
|
12914
|
+
return null;
|
|
12915
|
+
}
|
|
12916
|
+
}
|
|
12917
|
+
async resolvePathTargetPoint(path5, position) {
|
|
12918
|
+
if (!path5) {
|
|
12919
|
+
return null;
|
|
12920
|
+
}
|
|
12921
|
+
let resolved = null;
|
|
12922
|
+
try {
|
|
12923
|
+
resolved = await resolveElementPath(this.page, path5);
|
|
12924
|
+
return await this.resolveHandleTargetPoint(resolved.element, position);
|
|
12925
|
+
} catch {
|
|
12926
|
+
return null;
|
|
12927
|
+
} finally {
|
|
12928
|
+
await resolved?.element.dispose().catch(() => void 0);
|
|
12929
|
+
}
|
|
12930
|
+
}
|
|
12931
|
+
async resolveViewportAnchorPoint() {
|
|
12932
|
+
const viewport = this.page.viewportSize();
|
|
12933
|
+
if (viewport?.width && viewport?.height) {
|
|
12934
|
+
return {
|
|
12935
|
+
x: viewport.width / 2,
|
|
12936
|
+
y: viewport.height / 2
|
|
12937
|
+
};
|
|
12938
|
+
}
|
|
12939
|
+
return null;
|
|
12940
|
+
}
|
|
11870
12941
|
async runWithPostActionWait(action, waitOverride, execute) {
|
|
11871
12942
|
const waitSession = createPostActionWaitSession(
|
|
11872
12943
|
this.page,
|
|
@@ -11899,13 +12970,19 @@ var Opensteer = class _Opensteer {
|
|
|
11899
12970
|
resolution.counter
|
|
11900
12971
|
);
|
|
11901
12972
|
}
|
|
11902
|
-
await this.
|
|
11903
|
-
|
|
11904
|
-
|
|
11905
|
-
|
|
11906
|
-
|
|
11907
|
-
|
|
11908
|
-
|
|
12973
|
+
await this.runWithCursorPreview(
|
|
12974
|
+
() => this.resolveHandleTargetPoint(handle),
|
|
12975
|
+
method,
|
|
12976
|
+
async () => {
|
|
12977
|
+
await this.runWithPostActionWait(method, options.wait, async () => {
|
|
12978
|
+
await handle.click({
|
|
12979
|
+
button: options.button,
|
|
12980
|
+
clickCount: options.clickCount,
|
|
12981
|
+
modifiers: options.modifiers
|
|
12982
|
+
});
|
|
12983
|
+
});
|
|
12984
|
+
}
|
|
12985
|
+
);
|
|
11909
12986
|
} catch (err) {
|
|
11910
12987
|
const failure = classifyActionFailure({
|
|
11911
12988
|
action: method,
|
|
@@ -11940,25 +13017,31 @@ var Opensteer = class _Opensteer {
|
|
|
11940
13017
|
throw new Error("Unable to resolve element path for click action.");
|
|
11941
13018
|
}
|
|
11942
13019
|
const path5 = resolution.path;
|
|
11943
|
-
const result = await this.
|
|
13020
|
+
const result = await this.runWithCursorPreview(
|
|
13021
|
+
() => this.resolvePathTargetPoint(path5),
|
|
11944
13022
|
method,
|
|
11945
|
-
options.wait,
|
|
11946
13023
|
async () => {
|
|
11947
|
-
|
|
11948
|
-
|
|
11949
|
-
|
|
11950
|
-
|
|
11951
|
-
|
|
11952
|
-
|
|
11953
|
-
|
|
11954
|
-
|
|
11955
|
-
|
|
11956
|
-
|
|
11957
|
-
|
|
11958
|
-
|
|
11959
|
-
|
|
11960
|
-
|
|
11961
|
-
|
|
13024
|
+
return await this.runWithPostActionWait(
|
|
13025
|
+
method,
|
|
13026
|
+
options.wait,
|
|
13027
|
+
async () => {
|
|
13028
|
+
const actionResult = await performClick(this.page, path5, options);
|
|
13029
|
+
if (!actionResult.ok) {
|
|
13030
|
+
const failure = actionResult.failure || classifyActionFailure({
|
|
13031
|
+
action: method,
|
|
13032
|
+
error: actionResult.error || defaultActionFailureMessage(method),
|
|
13033
|
+
fallbackMessage: defaultActionFailureMessage(method)
|
|
13034
|
+
});
|
|
13035
|
+
throw this.buildActionError(
|
|
13036
|
+
method,
|
|
13037
|
+
options.description,
|
|
13038
|
+
failure,
|
|
13039
|
+
actionResult.usedSelector || null
|
|
13040
|
+
);
|
|
13041
|
+
}
|
|
13042
|
+
return actionResult;
|
|
13043
|
+
}
|
|
13044
|
+
);
|
|
11962
13045
|
}
|
|
11963
13046
|
);
|
|
11964
13047
|
this.snapshotCache = null;
|
|
@@ -12953,6 +14036,26 @@ function isInternalOrBlankPageUrl(url) {
|
|
|
12953
14036
|
if (url === "about:blank") return true;
|
|
12954
14037
|
return url.startsWith("chrome://") || url.startsWith("devtools://") || url.startsWith("edge://");
|
|
12955
14038
|
}
|
|
14039
|
+
function normalizeCloudBrowserProfilePreference(value, source) {
|
|
14040
|
+
if (!value) {
|
|
14041
|
+
return void 0;
|
|
14042
|
+
}
|
|
14043
|
+
const profileId = typeof value.profileId === "string" ? value.profileId.trim() : "";
|
|
14044
|
+
if (!profileId) {
|
|
14045
|
+
throw new Error(
|
|
14046
|
+
`Invalid cloud browser profile in ${source}: profileId must be a non-empty string.`
|
|
14047
|
+
);
|
|
14048
|
+
}
|
|
14049
|
+
if (value.reuseIfActive !== void 0 && typeof value.reuseIfActive !== "boolean") {
|
|
14050
|
+
throw new Error(
|
|
14051
|
+
`Invalid cloud browser profile in ${source}: reuseIfActive must be a boolean.`
|
|
14052
|
+
);
|
|
14053
|
+
}
|
|
14054
|
+
return {
|
|
14055
|
+
profileId,
|
|
14056
|
+
reuseIfActive: value.reuseIfActive
|
|
14057
|
+
};
|
|
14058
|
+
}
|
|
12956
14059
|
function buildLocalRunId(namespace) {
|
|
12957
14060
|
const normalized = namespace.trim() || "default";
|
|
12958
14061
|
return `${normalized}-${Date.now().toString(36)}-${(0, import_crypto.randomUUID)().slice(0, 8)}`;
|
|
@@ -12962,12 +14065,265 @@ function buildLocalRunId(namespace) {
|
|
|
12962
14065
|
init_resolver();
|
|
12963
14066
|
init_extractor();
|
|
12964
14067
|
init_model();
|
|
14068
|
+
|
|
14069
|
+
// src/cloud/browser-profile-client.ts
|
|
14070
|
+
var BrowserProfileClient = class {
|
|
14071
|
+
baseUrl;
|
|
14072
|
+
key;
|
|
14073
|
+
authScheme;
|
|
14074
|
+
constructor(baseUrl, key, authScheme = "api-key") {
|
|
14075
|
+
this.baseUrl = normalizeCloudBaseUrl(baseUrl);
|
|
14076
|
+
this.key = key;
|
|
14077
|
+
this.authScheme = authScheme;
|
|
14078
|
+
}
|
|
14079
|
+
async list(request = {}) {
|
|
14080
|
+
const query = new URLSearchParams();
|
|
14081
|
+
if (request.cursor) {
|
|
14082
|
+
query.set("cursor", request.cursor);
|
|
14083
|
+
}
|
|
14084
|
+
if (typeof request.limit === "number" && Number.isFinite(request.limit)) {
|
|
14085
|
+
query.set("limit", String(Math.max(1, Math.trunc(request.limit))));
|
|
14086
|
+
}
|
|
14087
|
+
if (request.status) {
|
|
14088
|
+
query.set("status", request.status);
|
|
14089
|
+
}
|
|
14090
|
+
const querySuffix = query.toString() ? `?${query.toString()}` : "";
|
|
14091
|
+
const response = await fetch(
|
|
14092
|
+
`${this.baseUrl}/browser-profiles${querySuffix}`,
|
|
14093
|
+
{
|
|
14094
|
+
method: "GET",
|
|
14095
|
+
headers: {
|
|
14096
|
+
...cloudAuthHeaders(this.key, this.authScheme)
|
|
14097
|
+
}
|
|
14098
|
+
}
|
|
14099
|
+
);
|
|
14100
|
+
if (!response.ok) {
|
|
14101
|
+
throw await parseCloudHttpError(response);
|
|
14102
|
+
}
|
|
14103
|
+
return await response.json();
|
|
14104
|
+
}
|
|
14105
|
+
async get(profileId) {
|
|
14106
|
+
const normalized = profileId.trim();
|
|
14107
|
+
const response = await fetch(
|
|
14108
|
+
`${this.baseUrl}/browser-profiles/${encodeURIComponent(normalized)}`,
|
|
14109
|
+
{
|
|
14110
|
+
method: "GET",
|
|
14111
|
+
headers: {
|
|
14112
|
+
...cloudAuthHeaders(this.key, this.authScheme)
|
|
14113
|
+
}
|
|
14114
|
+
}
|
|
14115
|
+
);
|
|
14116
|
+
if (!response.ok) {
|
|
14117
|
+
throw await parseCloudHttpError(response);
|
|
14118
|
+
}
|
|
14119
|
+
return await response.json();
|
|
14120
|
+
}
|
|
14121
|
+
async create(request) {
|
|
14122
|
+
const response = await fetch(`${this.baseUrl}/browser-profiles`, {
|
|
14123
|
+
method: "POST",
|
|
14124
|
+
headers: {
|
|
14125
|
+
"content-type": "application/json",
|
|
14126
|
+
...cloudAuthHeaders(this.key, this.authScheme)
|
|
14127
|
+
},
|
|
14128
|
+
body: JSON.stringify(request)
|
|
14129
|
+
});
|
|
14130
|
+
if (!response.ok) {
|
|
14131
|
+
throw await parseCloudHttpError(response);
|
|
14132
|
+
}
|
|
14133
|
+
return await response.json();
|
|
14134
|
+
}
|
|
14135
|
+
};
|
|
14136
|
+
|
|
14137
|
+
// src/cursor/renderers/cdp-overlay.ts
|
|
14138
|
+
var PULSE_DELAY_MS = 30;
|
|
14139
|
+
var CdpOverlayCursorRenderer = class {
|
|
14140
|
+
page = null;
|
|
14141
|
+
session = null;
|
|
14142
|
+
active = false;
|
|
14143
|
+
reason = "disabled";
|
|
14144
|
+
lastMessage;
|
|
14145
|
+
lastPoint = null;
|
|
14146
|
+
async initialize(page) {
|
|
14147
|
+
this.page = page;
|
|
14148
|
+
if (page.isClosed()) {
|
|
14149
|
+
this.markInactive("page_closed");
|
|
14150
|
+
return;
|
|
14151
|
+
}
|
|
14152
|
+
await this.createSession();
|
|
14153
|
+
}
|
|
14154
|
+
isActive() {
|
|
14155
|
+
return this.active;
|
|
14156
|
+
}
|
|
14157
|
+
status() {
|
|
14158
|
+
return {
|
|
14159
|
+
enabled: true,
|
|
14160
|
+
active: this.active,
|
|
14161
|
+
reason: this.reason ? this.lastMessage ? `${this.reason}: ${this.lastMessage}` : this.reason : void 0
|
|
14162
|
+
};
|
|
14163
|
+
}
|
|
14164
|
+
async move(point, style) {
|
|
14165
|
+
await this.sendWithRecovery(async (session) => {
|
|
14166
|
+
await session.send("Overlay.highlightQuad", {
|
|
14167
|
+
quad: buildCursorQuad(point, style.size),
|
|
14168
|
+
color: toProtocolRgba(style.fillColor),
|
|
14169
|
+
outlineColor: toProtocolRgba(style.outlineColor)
|
|
14170
|
+
});
|
|
14171
|
+
});
|
|
14172
|
+
this.lastPoint = point;
|
|
14173
|
+
}
|
|
14174
|
+
async pulse(point, style) {
|
|
14175
|
+
const pulseSize = style.size * style.pulseScale;
|
|
14176
|
+
const pulseFill = {
|
|
14177
|
+
...style.fillColor,
|
|
14178
|
+
a: Math.min(1, style.fillColor.a * 0.14)
|
|
14179
|
+
};
|
|
14180
|
+
const pulseOutline = {
|
|
14181
|
+
...style.haloColor,
|
|
14182
|
+
a: Math.min(1, style.haloColor.a * 0.9)
|
|
14183
|
+
};
|
|
14184
|
+
await this.sendWithRecovery(async (session) => {
|
|
14185
|
+
await session.send("Overlay.highlightQuad", {
|
|
14186
|
+
quad: buildCursorQuad(point, pulseSize),
|
|
14187
|
+
color: toProtocolRgba(pulseFill),
|
|
14188
|
+
outlineColor: toProtocolRgba(pulseOutline)
|
|
14189
|
+
});
|
|
14190
|
+
});
|
|
14191
|
+
await sleep6(PULSE_DELAY_MS);
|
|
14192
|
+
await this.move(point, style);
|
|
14193
|
+
}
|
|
14194
|
+
async clear() {
|
|
14195
|
+
if (!this.session) return;
|
|
14196
|
+
try {
|
|
14197
|
+
await this.session.send("Overlay.hideHighlight");
|
|
14198
|
+
} catch {
|
|
14199
|
+
this.markInactive("cdp_detached");
|
|
14200
|
+
}
|
|
14201
|
+
}
|
|
14202
|
+
async dispose() {
|
|
14203
|
+
await this.cleanupSession();
|
|
14204
|
+
this.active = false;
|
|
14205
|
+
this.reason = "disabled";
|
|
14206
|
+
this.lastMessage = void 0;
|
|
14207
|
+
this.lastPoint = null;
|
|
14208
|
+
this.page = null;
|
|
14209
|
+
}
|
|
14210
|
+
async sendWithRecovery(operation) {
|
|
14211
|
+
if (!this.active || !this.session) return;
|
|
14212
|
+
try {
|
|
14213
|
+
await operation(this.session);
|
|
14214
|
+
} catch (error) {
|
|
14215
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
14216
|
+
this.lastMessage = message;
|
|
14217
|
+
if (!isRecoverableProtocolError(message) || !this.page) {
|
|
14218
|
+
this.markInactive("renderer_error", message);
|
|
14219
|
+
return;
|
|
14220
|
+
}
|
|
14221
|
+
await this.createSession();
|
|
14222
|
+
if (!this.active || !this.session) {
|
|
14223
|
+
return;
|
|
14224
|
+
}
|
|
14225
|
+
try {
|
|
14226
|
+
await operation(this.session);
|
|
14227
|
+
} catch (retryError) {
|
|
14228
|
+
const retryMessage = retryError instanceof Error ? retryError.message : String(retryError);
|
|
14229
|
+
this.markInactive("renderer_error", retryMessage);
|
|
14230
|
+
}
|
|
14231
|
+
}
|
|
14232
|
+
}
|
|
14233
|
+
async createSession() {
|
|
14234
|
+
if (!this.page || this.page.isClosed()) {
|
|
14235
|
+
this.markInactive("page_closed");
|
|
14236
|
+
return;
|
|
14237
|
+
}
|
|
14238
|
+
await this.cleanupSession();
|
|
14239
|
+
try {
|
|
14240
|
+
const session = await this.page.context().newCDPSession(this.page);
|
|
14241
|
+
await session.send("DOM.enable");
|
|
14242
|
+
await session.send("Overlay.enable");
|
|
14243
|
+
this.session = session;
|
|
14244
|
+
this.active = true;
|
|
14245
|
+
this.reason = void 0;
|
|
14246
|
+
this.lastMessage = void 0;
|
|
14247
|
+
} catch (error) {
|
|
14248
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
14249
|
+
this.markInactive(inferSetupReason(message), message);
|
|
14250
|
+
await this.cleanupSession();
|
|
14251
|
+
}
|
|
14252
|
+
}
|
|
14253
|
+
async cleanupSession() {
|
|
14254
|
+
const session = this.session;
|
|
14255
|
+
this.session = null;
|
|
14256
|
+
if (!session) return;
|
|
14257
|
+
try {
|
|
14258
|
+
await session.detach();
|
|
14259
|
+
} catch {
|
|
14260
|
+
}
|
|
14261
|
+
}
|
|
14262
|
+
markInactive(reason, message) {
|
|
14263
|
+
this.active = false;
|
|
14264
|
+
this.reason = reason;
|
|
14265
|
+
this.lastMessage = message;
|
|
14266
|
+
}
|
|
14267
|
+
};
|
|
14268
|
+
function buildCursorQuad(point, size) {
|
|
14269
|
+
const x = point.x;
|
|
14270
|
+
const y = point.y;
|
|
14271
|
+
return [
|
|
14272
|
+
// Point 0: Tip (the hotspot)
|
|
14273
|
+
roundPointValue(x),
|
|
14274
|
+
roundPointValue(y),
|
|
14275
|
+
// Point 1: Right shoulder — extends right and down
|
|
14276
|
+
roundPointValue(x + size * 0.45),
|
|
14277
|
+
roundPointValue(y + size * 0.78),
|
|
14278
|
+
// Point 2: Tail — bottom of the cursor shaft
|
|
14279
|
+
roundPointValue(x + size * 0.12),
|
|
14280
|
+
roundPointValue(y + size * 1.3),
|
|
14281
|
+
// Point 3: Left edge — stays close to the shaft
|
|
14282
|
+
roundPointValue(x - size * 0.04),
|
|
14283
|
+
roundPointValue(y + size * 0.62)
|
|
14284
|
+
];
|
|
14285
|
+
}
|
|
14286
|
+
function inferSetupReason(message) {
|
|
14287
|
+
const lowered = message.toLowerCase();
|
|
14288
|
+
if (lowered.includes("not supported") || lowered.includes("only supported") || lowered.includes("unknown command")) {
|
|
14289
|
+
return "unsupported";
|
|
14290
|
+
}
|
|
14291
|
+
return "cdp_unavailable";
|
|
14292
|
+
}
|
|
14293
|
+
function isRecoverableProtocolError(message) {
|
|
14294
|
+
const lowered = message.toLowerCase();
|
|
14295
|
+
return lowered.includes("session closed") || lowered.includes("target closed") || lowered.includes("has been closed") || lowered.includes("detached");
|
|
14296
|
+
}
|
|
14297
|
+
function toProtocolRgba(color) {
|
|
14298
|
+
return {
|
|
14299
|
+
r: clampColor(color.r),
|
|
14300
|
+
g: clampColor(color.g),
|
|
14301
|
+
b: clampColor(color.b),
|
|
14302
|
+
a: clampAlpha(color.a)
|
|
14303
|
+
};
|
|
14304
|
+
}
|
|
14305
|
+
function clampColor(value) {
|
|
14306
|
+
return Math.min(255, Math.max(0, Math.round(value)));
|
|
14307
|
+
}
|
|
14308
|
+
function clampAlpha(value) {
|
|
14309
|
+
const normalized = Number.isFinite(value) ? value : 1;
|
|
14310
|
+
return Math.min(1, Math.max(0, normalized));
|
|
14311
|
+
}
|
|
14312
|
+
function roundPointValue(value) {
|
|
14313
|
+
return Math.round(value * 100) / 100;
|
|
14314
|
+
}
|
|
14315
|
+
function sleep6(ms) {
|
|
14316
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
14317
|
+
}
|
|
12965
14318
|
// Annotate the CommonJS export names for ESM import in node:
|
|
12966
14319
|
0 && (module.exports = {
|
|
12967
14320
|
ActionWsClient,
|
|
14321
|
+
BrowserProfileClient,
|
|
14322
|
+
CdpOverlayCursorRenderer,
|
|
12968
14323
|
CloudCdpClient,
|
|
12969
14324
|
CloudSessionClient,
|
|
12970
14325
|
CounterResolutionError,
|
|
14326
|
+
CursorController,
|
|
12971
14327
|
ElementPathError,
|
|
12972
14328
|
LocalSelectorStorage,
|
|
12973
14329
|
OPENSTEER_HIDDEN_ATTR,
|
|
@@ -12989,6 +14345,7 @@ init_model();
|
|
|
12989
14345
|
OpensteerAgentProviderError,
|
|
12990
14346
|
OpensteerCloudError,
|
|
12991
14347
|
OpensteerCuaAgentHandler,
|
|
14348
|
+
SvgCursorRenderer,
|
|
12992
14349
|
buildArrayFieldPathCandidates,
|
|
12993
14350
|
buildElementPathFromHandle,
|
|
12994
14351
|
buildElementPathFromSelector,
|
|
@@ -13033,6 +14390,7 @@ init_model();
|
|
|
13033
14390
|
performInput,
|
|
13034
14391
|
performScroll,
|
|
13035
14392
|
performSelect,
|
|
14393
|
+
planSnappyCursorMotion,
|
|
13036
14394
|
prepareSnapshot,
|
|
13037
14395
|
pressKey,
|
|
13038
14396
|
queryAllByElementPath,
|