@youdie006/prodex 0.16.4 → 0.16.6
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 +21 -3
- package/dist/cli-pro.js +34 -9
- package/dist/repo-write.js +6 -0
- package/package.json +1 -1
package/dist/chatgpt-browser.js
CHANGED
|
@@ -857,6 +857,13 @@ export function projectItemRectExpression(name) {
|
|
|
857
857
|
}
|
|
858
858
|
}
|
|
859
859
|
if (!target) return { ok: false, reason: "project not found in sidebar" };
|
|
860
|
+
// 2026-07 ChatGPT update: the project row (li) is no longer a link - the
|
|
861
|
+
// navigation affordance is a dedicated "Open project home" button inside
|
|
862
|
+
// the row (verified live: clicking the row does nothing, clicking the home
|
|
863
|
+
// button navigates to /g/g-p-...). Prefer it; fall back to the row click
|
|
864
|
+
// for the old UI.
|
|
865
|
+
const home = target.querySelector('[aria-label*="project home" i],[aria-label*="프로젝트 홈"]');
|
|
866
|
+
if (home) return clickPoint(home);
|
|
860
867
|
return clickPoint(target, 18);
|
|
861
868
|
})()`;
|
|
862
869
|
}
|
|
@@ -903,7 +910,11 @@ async function selectModelReasoning(cdp, options) {
|
|
|
903
910
|
// Open the Pro sub-mode submenu via the chevron, then pick 기본/확장.
|
|
904
911
|
const expander = await cdp.evaluate(proSubmenuExpanderRectExpression());
|
|
905
912
|
if (!expander.ok || expander.x === undefined || expander.y === undefined) {
|
|
906
|
-
|
|
913
|
+
// The 2026-07 ChatGPT update removed the Pro sub-mode chevron from the
|
|
914
|
+
// model picker (verified live, including on hover) - Pro is a plain
|
|
915
|
+
// radio now. Name the change and the working alternative instead of a
|
|
916
|
+
// bare "expander not found".
|
|
917
|
+
throw new Error("The ChatGPT model menu no longer offers Pro sub-modes (the 2026-07 update removed the Pro submenu; Pro is a single mode now). Use --model Pro instead of --pro-mode. If ChatGPT restores sub-modes, update prodex (npm i -g @youdie006/prodex@latest).");
|
|
907
918
|
}
|
|
908
919
|
await verifiedClickAt(cdp, expander.x, expander.y, "Pro sub-mode expander");
|
|
909
920
|
const subLabels = PRO_MODE_SUBMENU_LABELS[options.proMode];
|
|
@@ -1063,7 +1074,14 @@ async function selectProject(cdp, options) {
|
|
|
1063
1074
|
await verifiedClickAt(cdp, hit.x, hit.y, `project ${options.project}`);
|
|
1064
1075
|
const navigated = await waitForExpressionTrue(cdp, `location.href !== ${JSON.stringify(hrefBefore)}`, PROJECT_NAVIGATION_TIMEOUT_MS);
|
|
1065
1076
|
if (!navigated) {
|
|
1066
|
-
|
|
1077
|
+
// The href staying put is fine when the tab was ALREADY on this project's
|
|
1078
|
+
// home: the click landed on the requested row's own navigation control
|
|
1079
|
+
// (hover-verified above), so an unchanged project URL means "already
|
|
1080
|
+
// there", not a failed click.
|
|
1081
|
+
const alreadyInProject = await cdp.evaluate(`/^https:\\/\\/chatgpt\\.com\\/g\\/g-p-/.test(location.href)`);
|
|
1082
|
+
if (!alreadyInProject) {
|
|
1083
|
+
throw new Error(`Clicking project "${options.project}" did not navigate the visible tab. If the tab is already inside this project, omit --project and retry.`);
|
|
1084
|
+
}
|
|
1067
1085
|
}
|
|
1068
1086
|
const composerReady = await waitForExpressionTrue(cdp, `Boolean(document.querySelector('#prompt-textarea,[contenteditable="true"],textarea'))`, PROJECT_NAVIGATION_TIMEOUT_MS);
|
|
1069
1087
|
if (!composerReady) {
|
|
@@ -1724,7 +1742,7 @@ function composerExpressionHelpers() {
|
|
|
1724
1742
|
};
|
|
1725
1743
|
`;
|
|
1726
1744
|
}
|
|
1727
|
-
function answerExpression() {
|
|
1745
|
+
export function answerExpression() {
|
|
1728
1746
|
const excludedTextSelector = JSON.stringify(CHATGPT_RUNTIME_BLOCKER_TEXT_EXCLUDED_ANCESTORS);
|
|
1729
1747
|
const blockerScanExcludedSelector = JSON.stringify(CHATGPT_BLOCKER_SCAN_EXCLUDED_ANCESTORS);
|
|
1730
1748
|
const streamingSelector = JSON.stringify(CHATGPT_STREAMING_SELECTOR);
|
package/dist/cli-pro.js
CHANGED
|
@@ -297,17 +297,31 @@ export async function runProCommand(rest, io, runCliFn) {
|
|
|
297
297
|
if (printProBrowserHelpIfRequested(browserArgs, "pro browser models", io, { valueFlags: ["--port", "--timeout-ms", "--source-cli"] }))
|
|
298
298
|
return 0;
|
|
299
299
|
assertOnlyOptions(browserArgs, "pro browser models", ["--port", "--timeout-ms", "--source-cli"]);
|
|
300
|
-
const
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
300
|
+
const modelsSourceCli = resolveOptionalFileFlag(io.cwd, browserArgs, "--source-cli");
|
|
301
|
+
const modelsPort = readPortFlag(browserArgs, "--port");
|
|
302
|
+
let listed;
|
|
303
|
+
try {
|
|
304
|
+
listed = await listChatGptModelOptions({
|
|
305
|
+
port: modelsPort,
|
|
306
|
+
timeoutMs: readPositiveIntegerFlag(browserArgs, "--timeout-ms")
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
catch (error) {
|
|
310
|
+
// Keep the next step actionable for a custom --port: the raw blocker
|
|
311
|
+
// suggests the default-port login command, which would not fix a
|
|
312
|
+
// 9444-style setup.
|
|
313
|
+
const blocker = sourceAwareBrowserBlocker(browserSendBlockerFromError(error), modelsSourceCli, {
|
|
314
|
+
...(modelsPort !== undefined && modelsPort !== DEFAULT_CDP_PORT ? { port: modelsPort } : {})
|
|
315
|
+
});
|
|
316
|
+
throw new Error(blocker.next_step ? `${blocker.message} Next: ${blocker.next_step}` : errorMessage(error));
|
|
317
|
+
}
|
|
304
318
|
io.stdout("Model menu options in the visible ChatGPT tab (read-only; nothing was selected):");
|
|
305
319
|
for (const option of listed.options) {
|
|
306
320
|
const marker = option.checked ? "*" : " ";
|
|
307
321
|
const suffix = option.kind === "submenu" ? " (has sub-variants; not selectable via --model yet)" : "";
|
|
308
322
|
io.stdout(`${marker} ${option.label}${suffix}`);
|
|
309
323
|
}
|
|
310
|
-
io.stdout("Use radio entries with `pro browser ask --model/--effort
|
|
324
|
+
io.stdout("Use radio entries with `pro browser ask --model/--effort` (e.g. --model Pro).");
|
|
311
325
|
return 0;
|
|
312
326
|
}
|
|
313
327
|
throw unknownSubcommandError("pro browser", browserSubcommand, ["login", "ask", "smoke", "check", "models"]);
|
|
@@ -1197,10 +1211,21 @@ export async function printProductCheck(store, io, args, configCwd = io.cwd) {
|
|
|
1197
1211
|
catch {
|
|
1198
1212
|
// config unreadable: already reported by the config line above
|
|
1199
1213
|
}
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1214
|
+
let browserStatus;
|
|
1215
|
+
try {
|
|
1216
|
+
browserStatus = await getChatGptBrowserStatus({
|
|
1217
|
+
port: resolveCdpPort(readPortFlag(args, "--port")),
|
|
1218
|
+
timeoutMs: readPositiveIntegerFlag(args, "--timeout-ms") ?? 1500
|
|
1219
|
+
});
|
|
1220
|
+
}
|
|
1221
|
+
catch (error) {
|
|
1222
|
+
// A reachable-but-broken page (e.g. a failing in-page evaluate) must show
|
|
1223
|
+
// as a check failure like doctor does, not crash the whole check with an
|
|
1224
|
+
// internal "Runtime.evaluate failed".
|
|
1225
|
+
io.stdout(`chatgpt: check failed - ${errorMessage(error)}`);
|
|
1226
|
+
io.stdout("next: Reload the ChatGPT tab in the dedicated browser (or rerun `prodex pro browser login`), then retry.");
|
|
1227
|
+
return false;
|
|
1228
|
+
}
|
|
1204
1229
|
const browserCommandOptions = {
|
|
1205
1230
|
cwd: setupHintCwd,
|
|
1206
1231
|
port: readPortFlag(args, "--port") ?? undefined
|
package/dist/repo-write.js
CHANGED
|
@@ -77,6 +77,12 @@ export async function applyRepoWriteDryRun(root, store, input) {
|
|
|
77
77
|
const current = await readWritableExistingFile(root, metadata.path);
|
|
78
78
|
const currentPreimage = sha256(current.content);
|
|
79
79
|
if (currentPreimage !== input.preimage_sha256) {
|
|
80
|
+
// A retry after a successful apply sees the file already holding the new
|
|
81
|
+
// content - name that instead of the generic conflict message so the
|
|
82
|
+
// caller can tell "already done" from "someone else changed the file".
|
|
83
|
+
if (currentPreimage === metadata.new_sha256) {
|
|
84
|
+
throw new Error(`Write for ${metadata.path} was already applied (the file already matches the reviewed content). Re-run repo_write_file_dry_run if you need a new change.`);
|
|
85
|
+
}
|
|
80
86
|
throw new Error(`File preimage changed for ${metadata.path}`);
|
|
81
87
|
}
|
|
82
88
|
const newContent = await readDryRunReplacementContent(store, metadata);
|