@youdie006/prodex 0.16.10 → 0.16.12
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 +50 -2
- package/dist/cli-help.js +1 -0
- package/dist/cli-pro.js +38 -2
- package/package.json +1 -1
package/dist/chatgpt-browser.js
CHANGED
|
@@ -1120,7 +1120,7 @@ async function selectProject(cdp, options) {
|
|
|
1120
1120
|
}
|
|
1121
1121
|
if (!hit.ok || hit.x === undefined || hit.y === undefined) {
|
|
1122
1122
|
const detail = hit.reason && hit.reason !== "project not found in sidebar" ? ` (${hit.reason})` : "";
|
|
1123
|
-
throw new Error(`ChatGPT project not found in sidebar: ${options.project}${detail}
|
|
1123
|
+
throw new Error(`ChatGPT project not found in sidebar: ${options.project}${detail} List the visible names with \`prodex pro browser projects\`.`);
|
|
1124
1124
|
}
|
|
1125
1125
|
await verifiedClickAt(cdp, hit.x, hit.y, `project ${options.project}`);
|
|
1126
1126
|
const navigated = await waitForExpressionTrue(cdp, `location.href !== ${JSON.stringify(hrefBefore)}`, PROJECT_NAVIGATION_TIMEOUT_MS);
|
|
@@ -1178,12 +1178,20 @@ export async function sendChatGptPrompt(options) {
|
|
|
1178
1178
|
assertChatGptPageAvailable();
|
|
1179
1179
|
}
|
|
1180
1180
|
const page = pageResult.page;
|
|
1181
|
-
if (options.newChat) {
|
|
1181
|
+
if (options.newChat && !options.project && !options.projectNew) {
|
|
1182
1182
|
// Long accumulated threads eventually break acceptance detection, so
|
|
1183
1183
|
// start from a clean chat. Wait for the tab to actually reach the fresh
|
|
1184
1184
|
// empty chat (root URL, zero messages) rather than a fixed sleep: a slow
|
|
1185
1185
|
// SPA navigation could otherwise leave the old thread rendered, poisoning
|
|
1186
1186
|
// the answer-count baseline captured below and causing a false timeout.
|
|
1187
|
+
//
|
|
1188
|
+
// Skipped when a project is requested: the project home the selection step
|
|
1189
|
+
// navigates to IS the fresh composer for "a new chat in this project".
|
|
1190
|
+
// Navigating to the root new chat first leaves the SPA composer bound to
|
|
1191
|
+
// the ROOT conversation target even after entering the project, so the
|
|
1192
|
+
// thread silently lands outside the project (measured live: --new-chat
|
|
1193
|
+
// --project threads appeared in the root chat list, --project-only
|
|
1194
|
+
// threads appeared inside the project).
|
|
1187
1195
|
await evaluateOnPage(page, `location.assign("https://chatgpt.com/")`);
|
|
1188
1196
|
await waitForFreshChatGptPage(page, 8_000);
|
|
1189
1197
|
}
|
|
@@ -1370,6 +1378,46 @@ export function modelMenuOptionsExpression() {
|
|
|
1370
1378
|
// Read-only discovery: open the composer model menu, read the option labels,
|
|
1371
1379
|
// and press Escape. Nothing is clicked inside the menu, so the user's model
|
|
1372
1380
|
// selection is never changed.
|
|
1381
|
+
// Sidebar project names, extracted from the per-row options-button aria-labels
|
|
1382
|
+
// (English "Open project options for <name>", Korean "<name> 프로젝트 옵션 열기").
|
|
1383
|
+
export function sidebarProjectNamesExpression() {
|
|
1384
|
+
return `(() => {
|
|
1385
|
+
const names = [...document.querySelectorAll('[aria-label*="프로젝트 옵션"],[aria-label*="project options" i]')]
|
|
1386
|
+
.map((b) => (b.getAttribute("aria-label") || "").replace(/^open project options for /i, "").replace(/\\s*프로젝트 옵션 열기$/, "").trim())
|
|
1387
|
+
.filter(Boolean);
|
|
1388
|
+
return [...new Set(names)];
|
|
1389
|
+
})()`;
|
|
1390
|
+
}
|
|
1391
|
+
// Read-only discovery for --project/setup --project: list the sidebar project
|
|
1392
|
+
// names exactly as ChatGPT renders them, so nobody has to guess spelling or
|
|
1393
|
+
// case. Polls briefly because the Projects section hydrates after navigation.
|
|
1394
|
+
export async function listChatGptSidebarProjects(input = {}) {
|
|
1395
|
+
const port = resolveCdpPort(input.port);
|
|
1396
|
+
const timeoutMs = input.timeoutMs ?? 15_000;
|
|
1397
|
+
const pageResult = await findChatGptPage(port, computePageDiscoveryTimeout(timeoutMs), undefined);
|
|
1398
|
+
if (!pageResult.ok) {
|
|
1399
|
+
throwBlockerOrError(pageResult.blocker, "ChatGPT browser page is not available");
|
|
1400
|
+
}
|
|
1401
|
+
if (!pageResult.page) {
|
|
1402
|
+
if (pageResult.blocker)
|
|
1403
|
+
throw new ChatGptBrowserBlockerError(pageResult.blocker);
|
|
1404
|
+
assertChatGptPageAvailable();
|
|
1405
|
+
}
|
|
1406
|
+
const page = pageResult.page;
|
|
1407
|
+
const status = await readSettledChatGptPageStatus(page);
|
|
1408
|
+
const blocker = detectChatGptPageBlocker(status);
|
|
1409
|
+
if (blocker)
|
|
1410
|
+
throw new ChatGptBrowserBlockerError(blocker);
|
|
1411
|
+
let projects = [];
|
|
1412
|
+
const deadline = Date.now() + 6_000;
|
|
1413
|
+
for (;;) {
|
|
1414
|
+
projects = await evaluateOnPage(page, sidebarProjectNamesExpression());
|
|
1415
|
+
if (projects.length > 0 || Date.now() >= deadline)
|
|
1416
|
+
break;
|
|
1417
|
+
await sleep(300);
|
|
1418
|
+
}
|
|
1419
|
+
return { url: status.url, projects };
|
|
1420
|
+
}
|
|
1373
1421
|
export async function listChatGptModelOptions(input = {}) {
|
|
1374
1422
|
const port = resolveCdpPort(input.port);
|
|
1375
1423
|
const timeoutMs = input.timeoutMs ?? 15_000;
|
package/dist/cli-help.js
CHANGED
|
@@ -24,6 +24,7 @@ Ask / consult commands:
|
|
|
24
24
|
prodex pro browser check [--source-cli /absolute/path/to/dist/cli.js] [--cwd /absolute/path/to/repo] [--port 9333] [--timeout-ms 1500]
|
|
25
25
|
prodex pro browser smoke [--source-cli /absolute/path/to/dist/cli.js] [--cwd /absolute/path/to/repo] [--port 9333] [--timeout-ms 90000]
|
|
26
26
|
prodex pro browser models [--source-cli /absolute/path/to/dist/cli.js] [--port 9333] [--timeout-ms 15000] # read-only list of model menu options
|
|
27
|
+
prodex pro browser projects [--source-cli /absolute/path/to/dist/cli.js] [--port 9333] [--timeout-ms 15000] # read-only list of sidebar project names (for --project)
|
|
27
28
|
prodex pro browser ask [--source-cli /absolute/path/to/dist/cli.js] [--cwd /absolute/path/to/repo] [--port 9333] [--timeout-ms 90000] [--target-url url --confirm-target] [--new-chat] [--stdin] [--json] [--auto-login|--no-auto-login] [--file path] [--model Pro] [--pro-mode 기본|확장] [--effort 즉시|중간|높음|"매우 높음"] [--project "name" | --project-new "name"] "prompt" # explicit visible-browser send
|
|
28
29
|
prodex pro latest [--source-cli /absolute/path/to/dist/cli.js] [--cwd /absolute/path/to/repo]
|
|
29
30
|
prodex pro list [--source-cli /absolute/path/to/dist/cli.js] [--cwd /absolute/path/to/repo]
|
package/dist/cli-pro.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { buildDryRunBundle } from "./bundle.js";
|
|
4
|
-
import { DEFAULT_CDP_PORT, resolveCdpPort, chatGptVisibilityBlocker, defaultChatGptProfileDir, getChatGptBrowserStatus, listChatGptModelOptions, normalizeChatGptTargetUrl, openChatGptBrowser, parseProMode, parseReasoningEffort, readLastBrowserLoginLaunch, recordBrowserLoginLaunch, sendChatGptPrompt } from "./chatgpt-browser.js";
|
|
4
|
+
import { DEFAULT_CDP_PORT, resolveCdpPort, chatGptVisibilityBlocker, defaultChatGptProfileDir, getChatGptBrowserStatus, listChatGptModelOptions, listChatGptSidebarProjects, normalizeChatGptTargetUrl, openChatGptBrowser, parseProMode, parseReasoningEffort, readLastBrowserLoginLaunch, recordBrowserLoginLaunch, sendChatGptPrompt } from "./chatgpt-browser.js";
|
|
5
5
|
import { ASK_PRO_BOOLEAN_FLAGS, ASK_PRO_PREVIEW_VALUE_FLAGS, ASK_PRO_VALUE_FLAGS, assertHelpRequestArgs, assertNoExtraArgs, assertOnlyOptions, findHelpFlagIndexBeforePromptDelimiter, formatCliCommand, hasAskProDryRunMode, hasAskProMode, hasAskProSendMode, isHelpSubcommand, parseAskProArgs, printHelpIfRequested, readFlag, readPortFlag, readPositionalsWithOptions, readPositiveIntegerFlag, readRepeatedFlag, resolveCwdFlag, resolveOptionalFileFlag, unknownSubcommandError } from "./cli-args.js";
|
|
6
6
|
import { printProBrowserHelp, printProHelp } from "./cli-help.js";
|
|
7
7
|
import { listRawResultsForInspection, listTasksForInspection } from "./cli-ledger.js";
|
|
@@ -330,7 +330,36 @@ export async function runProCommand(rest, io, runCliFn) {
|
|
|
330
330
|
io.stdout("Use radio entries with `pro browser ask --model/--effort` (e.g. --model Pro).");
|
|
331
331
|
return 0;
|
|
332
332
|
}
|
|
333
|
-
|
|
333
|
+
if (browserSubcommand === "projects") {
|
|
334
|
+
if (printProBrowserHelpIfRequested(browserArgs, "pro browser projects", io, { valueFlags: ["--port", "--timeout-ms", "--source-cli"] }))
|
|
335
|
+
return 0;
|
|
336
|
+
assertOnlyOptions(browserArgs, "pro browser projects", ["--port", "--timeout-ms", "--source-cli"]);
|
|
337
|
+
const projectsSourceCli = resolveOptionalFileFlag(io.cwd, browserArgs, "--source-cli");
|
|
338
|
+
const projectsPort = readPortFlag(browserArgs, "--port");
|
|
339
|
+
const projectsTimeoutMs = readPositiveIntegerFlag(browserArgs, "--timeout-ms");
|
|
340
|
+
const projectsResolvedPort = resolveCdpPort(projectsPort);
|
|
341
|
+
let listed;
|
|
342
|
+
try {
|
|
343
|
+
listed = await listChatGptSidebarProjects({ port: projectsPort, timeoutMs: projectsTimeoutMs });
|
|
344
|
+
}
|
|
345
|
+
catch (error) {
|
|
346
|
+
const blocker = sourceAwareBrowserBlocker(browserSendBlockerFromError(error), projectsSourceCli, {
|
|
347
|
+
...(projectsResolvedPort !== DEFAULT_CDP_PORT ? { port: projectsResolvedPort } : {})
|
|
348
|
+
});
|
|
349
|
+
throw new Error(blocker.next_step ? `${blocker.message} Next: ${blocker.next_step}` : errorMessage(error));
|
|
350
|
+
}
|
|
351
|
+
if (listed.projects.length === 0) {
|
|
352
|
+
io.stdout("No projects visible in the ChatGPT sidebar (read-only check).");
|
|
353
|
+
io.stdout("If you expect projects, open the sidebar's Projects section once in the visible browser, then retry.");
|
|
354
|
+
return 0;
|
|
355
|
+
}
|
|
356
|
+
io.stdout("ChatGPT sidebar projects (read-only; exact names as rendered):");
|
|
357
|
+
for (const name of listed.projects)
|
|
358
|
+
io.stdout(` ${name}`);
|
|
359
|
+
io.stdout("Use with `pro browser ask --project \"<name>\"` or pin one with `prodex setup --project \"<name>\"`.");
|
|
360
|
+
return 0;
|
|
361
|
+
}
|
|
362
|
+
throw unknownSubcommandError("pro browser", browserSubcommand, ["login", "ask", "smoke", "check", "models", "projects"]);
|
|
334
363
|
}
|
|
335
364
|
if (subcommand === "open" || subcommand === "status" || subcommand === "smoke" || subcommand === "check" || subcommand === "doctor") {
|
|
336
365
|
throw new Error(`Use \`prodex pro browser ${subcommand === "doctor" ? "check" : subcommand}\` for explicit browser automation.`);
|
|
@@ -699,6 +728,13 @@ export async function runAskProCommand(rest, io) {
|
|
|
699
728
|
if (!selectionModel && !selectionProMode && !selectionEffort) {
|
|
700
729
|
persistenceWarnings.push("model_selection_warning: no model/effort was selected for this send (no per-ask flag, no saved default), so it used whatever the ChatGPT UI last had selected. Pin one with `prodex setup --model Pro` or pass --model/--effort.");
|
|
701
730
|
}
|
|
731
|
+
// In-project threads carry the project slug in their URL
|
|
732
|
+
// (/g/g-p-<project>/c/<id>); a bare /c/<id> after requesting a project
|
|
733
|
+
// means the thread landed at root - say so instead of leaving it to a
|
|
734
|
+
// sidebar audit (field-verified failure mode).
|
|
735
|
+
if ((selectionMetadata.project || selectionMetadata.project_new) && !/\/g\/g-p-/.test(consult.url ?? "")) {
|
|
736
|
+
persistenceWarnings.push("project_landing_warning: a project was requested but the answered thread URL is a root /c/ thread, so it likely landed OUTSIDE the project. Move it via the thread menu (Move to project) or re-run; list projects with `prodex pro browser projects`.");
|
|
737
|
+
}
|
|
702
738
|
// Truncation and other send warnings must be visible at runtime, not
|
|
703
739
|
// only inside the persisted receipt: a caller who never opens .bridge
|
|
704
740
|
// would otherwise treat a cut-off answer as complete.
|