@youdie006/prodex 0.16.16 → 0.16.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chatgpt-browser.js +57 -8
- package/package.json +1 -1
package/dist/chatgpt-browser.js
CHANGED
|
@@ -32,8 +32,8 @@ const EFFORT_MENU_LABELS = {
|
|
|
32
32
|
"매우 높음": ["매우 높음", "Extra High"]
|
|
33
33
|
};
|
|
34
34
|
const PRO_MODE_SUBMENU_LABELS = {
|
|
35
|
-
"기본": ["Pro 기본", "Pro Standard"],
|
|
36
|
-
"확장": ["Pro 확장", "Pro Extended"]
|
|
35
|
+
"기본": ["Pro 기본", "Pro Standard", "Standard", "기본"],
|
|
36
|
+
"확장": ["Pro 확장", "Pro Extended", "Extended", "확장"]
|
|
37
37
|
};
|
|
38
38
|
const PRO_MODE_ALIASES = {
|
|
39
39
|
standard: "기본",
|
|
@@ -704,6 +704,34 @@ async function dispatchMouseClickAt(cdp, x, y) {
|
|
|
704
704
|
await cdp.send("Input.dispatchMouseEvent", { type: "mousePressed", x, y, button: "left", clickCount: 1 });
|
|
705
705
|
await cdp.send("Input.dispatchMouseEvent", { type: "mouseReleased", x, y, button: "left", clickCount: 1 });
|
|
706
706
|
}
|
|
707
|
+
// Retry a hover-verified click whose point is transiently covered (menu/modal
|
|
708
|
+
// open-close animations, project navigation settling): re-locate FRESH
|
|
709
|
+
// coordinates each attempt. Persistent covers still fail with the refusal.
|
|
710
|
+
async function verifiedClickWithRetry(cdp, locate, label, deadlineMs = 5_000) {
|
|
711
|
+
const deadline = Date.now() + deadlineMs;
|
|
712
|
+
let hit = await locate();
|
|
713
|
+
for (;;) {
|
|
714
|
+
if (!hit.ok || hit.x === undefined || hit.y === undefined) {
|
|
715
|
+
if (Date.now() >= deadline) {
|
|
716
|
+
throw new Error(hit.reason ?? `${label} not found`);
|
|
717
|
+
}
|
|
718
|
+
await sleep(300);
|
|
719
|
+
hit = await locate();
|
|
720
|
+
continue;
|
|
721
|
+
}
|
|
722
|
+
try {
|
|
723
|
+
await verifiedClickAt(cdp, hit.x, hit.y, label);
|
|
724
|
+
return;
|
|
725
|
+
}
|
|
726
|
+
catch (error) {
|
|
727
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
728
|
+
if (Date.now() >= deadline || !/Refusing to click/.test(message))
|
|
729
|
+
throw error;
|
|
730
|
+
await sleep(500);
|
|
731
|
+
hit = await locate();
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
}
|
|
707
735
|
async function dispatchEscapeKey(cdp) {
|
|
708
736
|
await cdp.send("Input.dispatchKeyEvent", { type: "keyDown", key: "Escape", code: "Escape", windowsVirtualKeyCode: 27 });
|
|
709
737
|
await cdp.send("Input.dispatchKeyEvent", { type: "keyUp", key: "Escape", code: "Escape", windowsVirtualKeyCode: 27 });
|
|
@@ -959,6 +987,27 @@ async function selectModelReasoning(cdp, options) {
|
|
|
959
987
|
const wantsProMode = Boolean(options.proMode) && (!options.model || /pro/i.test(options.model));
|
|
960
988
|
if (wantsProMode && options.proMode) {
|
|
961
989
|
// Open the Pro sub-mode submenu via the chevron, then pick 기본/확장.
|
|
990
|
+
// Forward-compat first: docs say Pro Standard/Extended persist, but this
|
|
991
|
+
// browser's picker may not render the affordance yet (staged rollout).
|
|
992
|
+
// Radix submenus open on ArrowRight from the focused row - keyboard is
|
|
993
|
+
// sturdier than a hover-revealed chevron when the flag lands.
|
|
994
|
+
const subLabelsForProbe = PRO_MODE_SUBMENU_LABELS[options.proMode];
|
|
995
|
+
const proRow = await cdp.evaluate(proRadioRectExpression());
|
|
996
|
+
if (proRow.ok && proRow.x !== undefined && proRow.y !== undefined) {
|
|
997
|
+
await cdp.send("Input.dispatchMouseEvent", { type: "mouseMoved", x: proRow.x, y: proRow.y });
|
|
998
|
+
await sleep(200);
|
|
999
|
+
await cdp.send("Input.dispatchKeyEvent", { type: "keyDown", key: "ArrowRight", code: "ArrowRight", windowsVirtualKeyCode: 39 });
|
|
1000
|
+
await cdp.send("Input.dispatchKeyEvent", { type: "keyUp", key: "ArrowRight", code: "ArrowRight", windowsVirtualKeyCode: 39 });
|
|
1001
|
+
const subViaKeyboard = await waitForExpressionTrue(cdp, menuItemPresentExpression(subLabelsForProbe), 1_500);
|
|
1002
|
+
if (subViaKeyboard) {
|
|
1003
|
+
const subHit = await cdp.evaluate(submenuItemRectExpression(subLabelsForProbe));
|
|
1004
|
+
if (subHit.ok && subHit.x !== undefined && subHit.y !== undefined) {
|
|
1005
|
+
await verifiedClickWithRetry(cdp, () => cdp.evaluate(submenuItemRectExpression(subLabelsForProbe)), subLabelsForProbe[0]);
|
|
1006
|
+
await assertSelectionCommitted(cdp, subLabelsForProbe[0]);
|
|
1007
|
+
return;
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
962
1011
|
const expander = await cdp.evaluate(proSubmenuExpanderRectExpression());
|
|
963
1012
|
if (!expander.ok || expander.x === undefined || expander.y === undefined) {
|
|
964
1013
|
// Only the expander-specific miss means "the 2026-07 update removed the
|
|
@@ -970,9 +1019,9 @@ async function selectModelReasoning(cdp, options) {
|
|
|
970
1019
|
}
|
|
971
1020
|
// The saved setup default injects pro_mode into plain asks, so the user
|
|
972
1021
|
// may never have typed --pro-mode - name the clear command too.
|
|
973
|
-
throw new Error("
|
|
1022
|
+
throw new Error("This ChatGPT UI does not expose Pro sub-modes yet (staged rollout: Pro Standard/Extended exist per OpenAI docs and may already show in another browser, but this picker renders Pro as a single row - keyboard and hover probes both came up empty). Use --model Pro here for now and retry --pro-mode after the UI updates. If this ask did not pass --pro-mode, a saved default is injecting it: run `prodex setup --clear-pro-mode`.");
|
|
974
1023
|
}
|
|
975
|
-
await
|
|
1024
|
+
await verifiedClickWithRetry(cdp, () => cdp.evaluate(proSubmenuExpanderRectExpression()), "Pro sub-mode expander");
|
|
976
1025
|
const subLabels = PRO_MODE_SUBMENU_LABELS[options.proMode];
|
|
977
1026
|
const subVisible = await waitForExpressionTrue(cdp, menuItemPresentExpression(subLabels), MENU_OPEN_TIMEOUT_MS);
|
|
978
1027
|
if (!subVisible)
|
|
@@ -981,7 +1030,7 @@ async function selectModelReasoning(cdp, options) {
|
|
|
981
1030
|
if (!sub.ok || sub.x === undefined || sub.y === undefined) {
|
|
982
1031
|
throw new Error(sub.reason ?? `ChatGPT Pro sub-mode not clickable: ${subLabels.join(" / ")}`);
|
|
983
1032
|
}
|
|
984
|
-
await
|
|
1033
|
+
await verifiedClickWithRetry(cdp, () => cdp.evaluate(submenuItemRectExpression(subLabels)), subLabels[0]);
|
|
985
1034
|
await assertSelectionCommitted(cdp, subLabels[0]);
|
|
986
1035
|
return;
|
|
987
1036
|
}
|
|
@@ -997,7 +1046,7 @@ async function selectModelReasoning(cdp, options) {
|
|
|
997
1046
|
if (!proRadio.ok || proRadio.x === undefined || proRadio.y === undefined) {
|
|
998
1047
|
throw new Error(proRadio.reason ?? "Pro option not found in the model menu");
|
|
999
1048
|
}
|
|
1000
|
-
await
|
|
1049
|
+
await verifiedClickWithRetry(cdp, () => cdp.evaluate(proRadioRectExpression()), "Pro");
|
|
1001
1050
|
await assertSelectionCommitted(cdp, "Pro");
|
|
1002
1051
|
return;
|
|
1003
1052
|
}
|
|
@@ -1011,7 +1060,7 @@ async function selectModelReasoning(cdp, options) {
|
|
|
1011
1060
|
if (primary.role === "menuitem" && primary.haspopup === "menu") {
|
|
1012
1061
|
throw new Error(`ChatGPT model "${primaryLabel}" opens a submenu of variants instead of committing; selecting it is not supported yet. Supported today: reasoning efforts (--effort) and --model Pro.`);
|
|
1013
1062
|
}
|
|
1014
|
-
await
|
|
1063
|
+
await verifiedClickWithRetry(cdp, () => cdp.evaluate(menuItemRectExpression(primaryCandidates)), primaryLabel);
|
|
1015
1064
|
await assertSelectionCommitted(cdp, primaryLabel);
|
|
1016
1065
|
}
|
|
1017
1066
|
catch (error) {
|
|
@@ -1141,7 +1190,7 @@ async function selectProject(cdp, options) {
|
|
|
1141
1190
|
const detail = hit.reason && hit.reason !== "project not found in sidebar" ? ` (${hit.reason})` : "";
|
|
1142
1191
|
throw new Error(`ChatGPT project not found in sidebar: ${options.project}${detail} List the visible names with \`prodex pro browser projects\`.`);
|
|
1143
1192
|
}
|
|
1144
|
-
await
|
|
1193
|
+
await verifiedClickWithRetry(cdp, () => cdp.evaluate(projectItemRectExpression(options.project)), `project ${options.project}`);
|
|
1145
1194
|
const navigated = await waitForExpressionTrue(cdp, `location.href !== ${JSON.stringify(hrefBefore)}`, PROJECT_NAVIGATION_TIMEOUT_MS);
|
|
1146
1195
|
if (!navigated) {
|
|
1147
1196
|
// The href staying put is fine ONLY when the tab was already on THIS
|