@youdie006/prodex 0.8.3 → 0.9.0
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/README.md +13 -9
- package/dist/chatgpt-browser.d.ts +38 -0
- package/dist/chatgpt-browser.js +166 -23
- package/dist/chatgpt-browser.js.map +1 -1
- package/dist/cli-help.js +34 -19
- package/dist/cli-help.js.map +1 -1
- package/dist/cli-pro.d.ts +46 -1
- package/dist/cli-pro.js +146 -17
- package/dist/cli-pro.js.map +1 -1
- package/dist/cli.js +30 -3
- package/dist/cli.js.map +1 -1
- package/dist/mcp.d.ts +18 -1
- package/dist/mcp.js +17 -2
- package/dist/mcp.js.map +1 -1
- package/docs/claude.md +4 -1
- package/package.json +1 -1
package/dist/cli-help.js
CHANGED
|
@@ -6,22 +6,18 @@ export const CLI_VERSION = packageJson.version ?? "0.0.0";
|
|
|
6
6
|
export function printHelp(stdout) {
|
|
7
7
|
stdout(`prodex v${CLI_VERSION}
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
prodex
|
|
11
|
-
prodex
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
prodex
|
|
15
|
-
prodex
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
prodex
|
|
19
|
-
prodex onboard [--cwd /absolute/path/to/repo] [--source-cli /absolute/path/to/dist/cli.js]
|
|
20
|
-
prodex project prompt [--cwd /absolute/path/to/repo] [--source-cli /absolute/path/to/dist/cli.js]
|
|
21
|
-
prodex claude prompt [--cwd /absolute/path/to/repo] [--source-cli /absolute/path/to/dist/cli.js]
|
|
22
|
-
prodex claude config [--cwd /absolute/path/to/repo] [--source-cli /absolute/path/to/dist/cli.js]
|
|
9
|
+
Ask ChatGPT from the terminal (visible logged-in browser):
|
|
10
|
+
prodex ask "Explain this stack trace" # shortcut for: prodex pro browser ask
|
|
11
|
+
prodex ask --file src/auth.ts "Review this for security holes"
|
|
12
|
+
|
|
13
|
+
First-time setup:
|
|
14
|
+
prodex pro browser login # dedicated Chrome; interactive runs wait until your login is READY
|
|
15
|
+
prodex doctor # bridge + MCP + browser health
|
|
16
|
+
|
|
17
|
+
Ask / consult commands:
|
|
18
|
+
prodex ask [same flags as pro browser ask] "prompt" # top-level shortcut for pro browser ask
|
|
23
19
|
prodex pro ask [--dry-run] [--cwd /absolute/path/to/repo] [--file path] "prompt" # dry-run preview
|
|
24
|
-
prodex pro browser login [--cwd /absolute/path/to/repo] [--dry-run] [--source-cli /absolute/path/to/dist/cli.js] [--profile-dir path] [--port 9333] [--url https://chatgpt.com/...] [--launch-timeout-ms 5000] # preview/open visible browser login
|
|
20
|
+
prodex pro browser login [--cwd /absolute/path/to/repo] [--dry-run] [--source-cli /absolute/path/to/dist/cli.js] [--profile-dir path] [--port 9333] [--url https://chatgpt.com/...] [--launch-timeout-ms 5000] [--wait|--no-wait] [--wait-timeout-ms 300000] # preview/open visible browser login
|
|
25
21
|
prodex pro browser help [--source-cli /absolute/path/to/dist/cli.js]
|
|
26
22
|
prodex pro browser check [--source-cli /absolute/path/to/dist/cli.js] [--cwd /absolute/path/to/repo] [--port 9333] [--timeout-ms 1500]
|
|
27
23
|
prodex pro browser smoke [--source-cli /absolute/path/to/dist/cli.js] [--cwd /absolute/path/to/repo] [--port 9333] [--timeout-ms 90000]
|
|
@@ -30,6 +26,9 @@ Commands:
|
|
|
30
26
|
prodex pro latest [--source-cli /absolute/path/to/dist/cli.js] [--cwd /absolute/path/to/repo]
|
|
31
27
|
prodex pro list [--source-cli /absolute/path/to/dist/cli.js] [--cwd /absolute/path/to/repo]
|
|
32
28
|
prodex pro show <task-id|latest> [--source-cli /absolute/path/to/dist/cli.js] [--cwd /absolute/path/to/repo]
|
|
29
|
+
|
|
30
|
+
Bridge ledger (durable tasks/results/receipts/sessions under .bridge/):
|
|
31
|
+
prodex init [--cwd /absolute/path/to/repo]
|
|
33
32
|
prodex tasks create [--cwd /absolute/path/to/repo] --title "Title" --prompt "Prompt"
|
|
34
33
|
prodex tasks list [--status new|claimed|done|blocked] [--cwd /absolute/path/to/repo]
|
|
35
34
|
prodex tasks show <task-id|latest> [--cwd /absolute/path/to/repo]
|
|
@@ -44,7 +43,23 @@ Commands:
|
|
|
44
43
|
prodex receipts rotate-key [--cwd /absolute/path/to/repo]
|
|
45
44
|
prodex sessions list [--status preview|running|done|blocked] [--cwd /absolute/path/to/repo]
|
|
46
45
|
prodex sessions show <session-id|latest> [--cwd /absolute/path/to/repo]
|
|
47
|
-
|
|
46
|
+
|
|
47
|
+
Agent / MCP integration:
|
|
48
|
+
prodex mcp [--cwd /absolute/path/to/repo]
|
|
49
|
+
prodex setup [--cwd /absolute/path/to/repo] [--host 127.0.0.1] [--port 8787] [--token-ttl-hours <hours>] [--model Pro] [--pro-mode 기본|확장] [--effort 즉시|중간|높음|"매우 높음"] [--project "name"] [--clear-model|--clear-pro-mode|--clear-effort|--clear-project] [--interactive]
|
|
50
|
+
prodex start [--cwd /absolute/path/to/repo] [--source-cli /absolute/path/to/dist/cli.js]
|
|
51
|
+
prodex status [--cwd /absolute/path/to/repo] [--source-cli /absolute/path/to/dist/cli.js] [--show-token] [--url-only] [--unsafe-show-non-expiring-token]
|
|
52
|
+
prodex tunnel url [--cwd /absolute/path/to/repo] [--source-cli /absolute/path/to/dist/cli.js] --public-url https://... [--show-token] [--url-only]
|
|
53
|
+
prodex claude prompt [--cwd /absolute/path/to/repo] [--source-cli /absolute/path/to/dist/cli.js]
|
|
54
|
+
prodex claude config [--cwd /absolute/path/to/repo] [--source-cli /absolute/path/to/dist/cli.js]
|
|
55
|
+
prodex project prompt [--cwd /absolute/path/to/repo] [--source-cli /absolute/path/to/dist/cli.js]
|
|
56
|
+
prodex onboard [--cwd /absolute/path/to/repo] [--source-cli /absolute/path/to/dist/cli.js]
|
|
57
|
+
|
|
58
|
+
Maintenance:
|
|
59
|
+
prodex --version
|
|
60
|
+
prodex doctor [--cwd /absolute/path/to/repo] [--source-cli /absolute/path/to/dist/cli.js]
|
|
61
|
+
prodex release status [--cwd /absolute/path/to/repo] [--source-cli /absolute/path/to/dist/cli.js]
|
|
62
|
+
prodex release pack [--cwd /absolute/path/to/repo] [--source-cli /absolute/path/to/dist/cli.js] --pack-destination /absolute/path [--keep-workdir]`);
|
|
48
63
|
}
|
|
49
64
|
export function printInitHelp(stdout) {
|
|
50
65
|
stdout(`prodex init
|
|
@@ -141,7 +156,7 @@ export function printProHelp(stdout) {
|
|
|
141
156
|
Commands:
|
|
142
157
|
prodex pro ask [--dry-run] [--cwd /absolute/path/to/repo] [--file path] "prompt"
|
|
143
158
|
prodex pro browser help [--source-cli /absolute/path/to/dist/cli.js]
|
|
144
|
-
prodex pro browser login [--cwd /absolute/path/to/repo] [--dry-run] [--source-cli /absolute/path/to/dist/cli.js] [--launch-timeout-ms 5000]
|
|
159
|
+
prodex pro browser login [--cwd /absolute/path/to/repo] [--dry-run] [--source-cli /absolute/path/to/dist/cli.js] [--launch-timeout-ms 5000] [--wait|--no-wait] [--wait-timeout-ms 300000]
|
|
145
160
|
prodex pro browser check [--source-cli /absolute/path/to/dist/cli.js] [--cwd /absolute/path/to/repo]
|
|
146
161
|
prodex pro browser smoke [--source-cli /absolute/path/to/dist/cli.js] [--cwd /absolute/path/to/repo]
|
|
147
162
|
prodex pro browser models [--source-cli /absolute/path/to/dist/cli.js]
|
|
@@ -220,8 +235,8 @@ export function printProBrowserHelp(stdout, sourceCli) {
|
|
|
220
235
|
const cli = formatCliCommand(sourceCli);
|
|
221
236
|
const sourceCliOption = formatSourceCliOption(sourceCli);
|
|
222
237
|
const loginUsage = sourceCli
|
|
223
|
-
? `${cli} pro browser login${sourceCliOption} [--cwd /absolute/path/to/repo] [--dry-run] [--profile-dir path] [--port 9333] [--url https://chatgpt.com/...] [--launch-timeout-ms 5000]`
|
|
224
|
-
: "prodex pro browser login [--cwd /absolute/path/to/repo] [--dry-run] [--source-cli /absolute/path/to/dist/cli.js] [--profile-dir path] [--port 9333] [--url https://chatgpt.com/...] [--launch-timeout-ms 5000]";
|
|
238
|
+
? `${cli} pro browser login${sourceCliOption} [--cwd /absolute/path/to/repo] [--dry-run] [--profile-dir path] [--port 9333] [--url https://chatgpt.com/...] [--launch-timeout-ms 5000] [--wait|--no-wait] [--wait-timeout-ms 300000]`
|
|
239
|
+
: "prodex pro browser login [--cwd /absolute/path/to/repo] [--dry-run] [--source-cli /absolute/path/to/dist/cli.js] [--profile-dir path] [--port 9333] [--url https://chatgpt.com/...] [--launch-timeout-ms 5000] [--wait|--no-wait] [--wait-timeout-ms 300000]";
|
|
225
240
|
const checkUsage = sourceCli
|
|
226
241
|
? `${cli} pro browser check${sourceCliOption} [--cwd /absolute/path/to/repo] [--port 9333] [--timeout-ms 1500]`
|
|
227
242
|
: "prodex pro browser check [--source-cli /absolute/path/to/dist/cli.js] [--cwd /absolute/path/to/repo] [--port 9333] [--timeout-ms 1500]";
|
package/dist/cli-help.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli-help.js","sourceRoot":"","sources":["../src/cli-help.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAExE,MAAM,kBAAkB,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC1D,MAAM,WAAW,GAAG,kBAAkB,CAAC,iBAAiB,CAAyB,CAAC;AAClF,MAAM,CAAC,MAAM,WAAW,GAAG,WAAW,CAAC,OAAO,IAAI,OAAO,CAAC;AAE1D,MAAM,UAAU,SAAS,CAAC,MAA8B;IACtD,MAAM,CAAC,WAAW,WAAW
|
|
1
|
+
{"version":3,"file":"cli-help.js","sourceRoot":"","sources":["../src/cli-help.ts"],"names":[],"mappings":"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"}
|
package/dist/cli-pro.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ChatGptBrowserLaunch, sendChatGptPrompt } from "./chatgpt-browser.js";
|
|
1
|
+
import { type ChatGptBrowserLaunch, type SendChatGptProgressEvent, getChatGptBrowserStatus, sendChatGptPrompt } from "./chatgpt-browser.js";
|
|
2
2
|
import { type HelpRequestOptions } from "./cli-args.js";
|
|
3
3
|
import { type BrowserCommandOptions } from "./cli-shared.js";
|
|
4
4
|
import { BridgeStore } from "./store.js";
|
|
@@ -7,8 +7,53 @@ export type RunCliFn = (args: string[], io: CliIO) => Promise<number>;
|
|
|
7
7
|
export declare function runChatgptCommand(rest: string[], io: CliIO): Promise<number>;
|
|
8
8
|
export declare function runProCommand(rest: string[], io: CliIO, runCliFn: RunCliFn): Promise<number>;
|
|
9
9
|
export declare function runConsultsCommand(rest: string[], io: CliIO): Promise<number>;
|
|
10
|
+
/**
|
|
11
|
+
* Turns send progress events into stderr lines so multi-minute Pro consults do
|
|
12
|
+
* not look frozen. Phase transitions print immediately; the per-poll waiting
|
|
13
|
+
* heartbeat is throttled to one line per heartbeatMs.
|
|
14
|
+
*/
|
|
15
|
+
export declare function createBrowserSendProgressPrinter(write: (line: string) => void, heartbeatMs?: number): (event: SendChatGptProgressEvent) => void;
|
|
10
16
|
export declare function runAskProCommand(rest: string[], io: CliIO): Promise<number>;
|
|
11
17
|
export declare const PRO_BROWSER_SMOKE_TOKEN = "PRODEX_PRO_SMOKE_OK";
|
|
18
|
+
export interface BrowserConsultInput {
|
|
19
|
+
prompt: string;
|
|
20
|
+
model?: string;
|
|
21
|
+
pro_mode?: string;
|
|
22
|
+
effort?: string;
|
|
23
|
+
project?: string;
|
|
24
|
+
timeout_ms?: number;
|
|
25
|
+
files?: string[];
|
|
26
|
+
}
|
|
27
|
+
export interface BrowserConsultOutcome {
|
|
28
|
+
task_id: string;
|
|
29
|
+
status: string;
|
|
30
|
+
thread: string;
|
|
31
|
+
answer: string;
|
|
32
|
+
notes: string[];
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* MCP-facing wrapper around the visible-browser ask flow. Reuses the full CLI
|
|
36
|
+
* path (send pacing, task/session/receipt persistence, artifact save) by
|
|
37
|
+
* invoking runAskProCommand with captured io, then returns structured fields
|
|
38
|
+
* instead of printed lines. Registered only on the local stdio MCP server -
|
|
39
|
+
* never on the HTTP MCP surface, which is exposed to ChatGPT itself.
|
|
40
|
+
*/
|
|
41
|
+
export declare function performBrowserConsultForMcp(cwd: string, input: BrowserConsultInput): Promise<BrowserConsultOutcome>;
|
|
42
|
+
export interface LoginWaitDeps {
|
|
43
|
+
statusFn?: typeof getChatGptBrowserStatus;
|
|
44
|
+
sleepFn?: (ms: number) => Promise<void>;
|
|
45
|
+
now?: () => number;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Guided login: poll the visible browser until a logged-in ChatGPT tab with a
|
|
49
|
+
* usable composer appears, narrating each state change so the user knows what
|
|
50
|
+
* manual step (login, challenge, opening a chat) is still missing.
|
|
51
|
+
*/
|
|
52
|
+
export declare function waitForChatGptLoginReady(stderr: (line: string) => void, options: {
|
|
53
|
+
port: number;
|
|
54
|
+
timeoutMs?: number;
|
|
55
|
+
pollMs?: number;
|
|
56
|
+
}, deps?: LoginWaitDeps): Promise<boolean>;
|
|
12
57
|
export declare function assertBrowserLaunchStayedAlive(opened: ChatGptBrowserLaunch, timeoutMs?: number): Promise<void>;
|
|
13
58
|
export declare function browserSendBlockerFromError(error: unknown): {
|
|
14
59
|
code: string;
|
package/dist/cli-pro.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
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 { chatGptVisibilityBlocker, defaultChatGptProfileDir, getChatGptBrowserStatus, listChatGptModelOptions, normalizeChatGptTargetUrl, openChatGptBrowser, parseProMode, parseReasoningEffort, sendChatGptPrompt } from "./chatgpt-browser.js";
|
|
4
|
+
import { DEFAULT_CDP_PORT, resolveCdpPort, chatGptVisibilityBlocker, defaultChatGptProfileDir, getChatGptBrowserStatus, listChatGptModelOptions, normalizeChatGptTargetUrl, openChatGptBrowser, parseProMode, parseReasoningEffort, 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, hasAskProDryRunMode, hasAskProMode, hasAskProSendMode, isHelpSubcommand, parseAskProArgs, printHelpIfRequested, readFlag, readPortFlag, readPositionalsWithOptions, readPositiveNumberFlag, 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";
|
|
8
8
|
import { redactServerUrl } from "./cli-server.js";
|
|
9
|
-
import { errorMessage, firstLine, formatBlockedConsultRecordedMessage, formatBrowserCheckCommand, formatBrowserLoginCommand, formatBrowserSmokeCommand, formatBrowserTargetAskCommand, formatInitCommand, formatSetupCommand, isMissingFileError, computeSendPacingWaitMs, isUntrustedResultError, resolveMinSendIntervalMs, sourceAwareBrowserBlocker, sourceAwareBrowserNextStep, sourceAwareResultError, sourceAwareResultMessage, sourceAwareSetupMessage } from "./cli-shared.js";
|
|
9
|
+
import { errorMessage, firstLine, formatBlockedConsultRecordedMessage, formatProLatestCommand, formatBrowserCheckCommand, formatBrowserLoginCommand, formatBrowserSmokeCommand, formatBrowserTargetAskCommand, formatInitCommand, formatSetupCommand, isMissingFileError, computeSendPacingWaitMs, isUntrustedResultError, resolveMinSendIntervalMs, sourceAwareBrowserBlocker, sourceAwareBrowserNextStep, sourceAwareResultError, sourceAwareResultMessage, sourceAwareSetupMessage } from "./cli-shared.js";
|
|
10
10
|
import { getTokenExpiryStatus, loadBrowserDefaults, loadLocalConfig } from "./config.js";
|
|
11
11
|
import { BridgeStore, MAX_FETCHABLE_RESULT_ARTIFACT_BYTES } from "./store.js";
|
|
12
12
|
export async function runChatgptCommand(rest, io) {
|
|
@@ -17,7 +17,7 @@ export async function runChatgptCommand(rest, io) {
|
|
|
17
17
|
if (subcommand === "open") {
|
|
18
18
|
assertOnlyOptions(chatgptArgs, "chatgpt open", ["--port", "--profile-dir", "--url"]);
|
|
19
19
|
const opened = openChatGptBrowser({
|
|
20
|
-
port: readPortFlag(chatgptArgs, "--port")
|
|
20
|
+
port: resolveCdpPort(readPortFlag(chatgptArgs, "--port")),
|
|
21
21
|
profileDir: readFlag(chatgptArgs, "--profile-dir"),
|
|
22
22
|
url: readChatGptBrowserUrlFlag(chatgptArgs)
|
|
23
23
|
});
|
|
@@ -29,7 +29,7 @@ export async function runChatgptCommand(rest, io) {
|
|
|
29
29
|
}
|
|
30
30
|
if (subcommand === "status") {
|
|
31
31
|
assertOnlyOptions(chatgptArgs, "chatgpt status", ["--port"]);
|
|
32
|
-
const status = await getChatGptBrowserStatus({ port: readPortFlag(chatgptArgs, "--port")
|
|
32
|
+
const status = await getChatGptBrowserStatus({ port: resolveCdpPort(readPortFlag(chatgptArgs, "--port")) });
|
|
33
33
|
io.stdout(JSON.stringify(status, null, 2));
|
|
34
34
|
return 0;
|
|
35
35
|
}
|
|
@@ -38,7 +38,7 @@ export async function runChatgptCommand(rest, io) {
|
|
|
38
38
|
const targetCwd = resolveCwdFlag(io.cwd, chatgptArgs);
|
|
39
39
|
const targetStore = new BridgeStore(targetCwd);
|
|
40
40
|
const sourceCli = resolveOptionalFileFlag(io.cwd, chatgptArgs, "--source-cli");
|
|
41
|
-
const port = readPortFlag(chatgptArgs, "--port")
|
|
41
|
+
const port = resolveCdpPort(readPortFlag(chatgptArgs, "--port"));
|
|
42
42
|
const timeoutMs = readPositiveNumberFlag(chatgptArgs, "--timeout-ms") ?? 90000;
|
|
43
43
|
const commandOptions = {
|
|
44
44
|
...(readFlag(chatgptArgs, "--cwd") ? { cwd: targetCwd } : {}),
|
|
@@ -83,7 +83,8 @@ export async function runChatgptCommand(rest, io) {
|
|
|
83
83
|
result = await sendChatGptPrompt({
|
|
84
84
|
port,
|
|
85
85
|
prompt: smokePrompt,
|
|
86
|
-
timeoutMs
|
|
86
|
+
timeoutMs,
|
|
87
|
+
onProgress: createBrowserSendProgressPrinter(io.stderr)
|
|
87
88
|
});
|
|
88
89
|
}
|
|
89
90
|
catch (error) {
|
|
@@ -151,22 +152,25 @@ export async function runProCommand(rest, io, runCliFn) {
|
|
|
151
152
|
}
|
|
152
153
|
if (browserSubcommand === "login") {
|
|
153
154
|
if (printProBrowserHelpIfRequested(browserArgs, "pro browser login", io, {
|
|
154
|
-
valueFlags: ["--cwd", "--profile-dir", "--port", "--url", "--source-cli", "--launch-timeout-ms"],
|
|
155
|
-
booleanFlags: ["--dry-run"]
|
|
155
|
+
valueFlags: ["--cwd", "--profile-dir", "--port", "--url", "--source-cli", "--launch-timeout-ms", "--wait-timeout-ms"],
|
|
156
|
+
booleanFlags: ["--dry-run", "--wait", "--no-wait"]
|
|
156
157
|
})) {
|
|
157
158
|
return 0;
|
|
158
159
|
}
|
|
159
|
-
assertOnlyOptions(browserArgs, "pro browser login", ["--cwd", "--profile-dir", "--port", "--url", "--source-cli", "--launch-timeout-ms"], ["--dry-run"]);
|
|
160
|
+
assertOnlyOptions(browserArgs, "pro browser login", ["--cwd", "--profile-dir", "--port", "--url", "--source-cli", "--launch-timeout-ms", "--wait-timeout-ms"], ["--dry-run", "--wait", "--no-wait"]);
|
|
161
|
+
if (browserArgs.includes("--wait") && browserArgs.includes("--no-wait")) {
|
|
162
|
+
throw new Error("pro browser login cannot combine --wait and --no-wait");
|
|
163
|
+
}
|
|
160
164
|
const loginUrl = readChatGptBrowserUrlFlag(browserArgs);
|
|
161
165
|
const sourceCli = resolveOptionalFileFlag(io.cwd, browserArgs, "--source-cli");
|
|
162
166
|
const targetCwd = readFlag(browserArgs, "--cwd") ? resolveCwdFlag(io.cwd, browserArgs) : undefined;
|
|
163
167
|
const profileDir = readFlag(browserArgs, "--profile-dir");
|
|
164
|
-
const port = readPortFlag(browserArgs, "--port")
|
|
168
|
+
const port = resolveCdpPort(readPortFlag(browserArgs, "--port"));
|
|
165
169
|
const launchTimeoutMs = readPositiveNumberFlag(browserArgs, "--launch-timeout-ms");
|
|
166
170
|
const commandOptions = {
|
|
167
171
|
...(targetCwd ? { cwd: targetCwd } : {}),
|
|
168
172
|
...(profileDir ? { profileDir } : {}),
|
|
169
|
-
...(port !==
|
|
173
|
+
...(port !== DEFAULT_CDP_PORT ? { port } : {}),
|
|
170
174
|
...(readFlag(browserArgs, "--url") ? { url: loginUrl } : {}),
|
|
171
175
|
...(launchTimeoutMs !== undefined ? { launchTimeoutMs } : {})
|
|
172
176
|
};
|
|
@@ -195,7 +199,16 @@ export async function runProCommand(rest, io, runCliFn) {
|
|
|
195
199
|
sourceCli,
|
|
196
200
|
commandOptions
|
|
197
201
|
});
|
|
198
|
-
|
|
202
|
+
// Guided wait: interactive terminals walk the user to a verified READY
|
|
203
|
+
// state instead of returning while login is still unfinished. Scripts
|
|
204
|
+
// and agents (non-TTY) keep the immediate return unless --wait is
|
|
205
|
+
// passed; --no-wait always skips.
|
|
206
|
+
const shouldWaitForReady = !browserArgs.includes("--no-wait") && (browserArgs.includes("--wait") || process.stdout.isTTY === true);
|
|
207
|
+
if (!shouldWaitForReady)
|
|
208
|
+
return 0;
|
|
209
|
+
const waitTimeoutMs = readPositiveNumberFlag(browserArgs, "--wait-timeout-ms") ?? 300_000;
|
|
210
|
+
const ready = await waitForChatGptLoginReady(io.stderr, { port: opened.port, timeoutMs: waitTimeoutMs });
|
|
211
|
+
return ready ? 0 : 1;
|
|
199
212
|
}
|
|
200
213
|
if (browserSubcommand === "ask") {
|
|
201
214
|
if (printProBrowserHelpIfRequested(browserArgs, "pro browser ask", io, {
|
|
@@ -348,6 +361,34 @@ async function enforceVisibleBrowserSendPacing(cwd, stderr) {
|
|
|
348
361
|
// best effort: pacing must never block a legitimate send on a write failure
|
|
349
362
|
}
|
|
350
363
|
}
|
|
364
|
+
const PROGRESS_PHASE_LABELS = {
|
|
365
|
+
connecting: "connecting to browser",
|
|
366
|
+
tab_ready: "chatgpt tab ready",
|
|
367
|
+
selecting: "applying selection",
|
|
368
|
+
sent: "prompt sent, waiting for answer"
|
|
369
|
+
};
|
|
370
|
+
/**
|
|
371
|
+
* Turns send progress events into stderr lines so multi-minute Pro consults do
|
|
372
|
+
* not look frozen. Phase transitions print immediately; the per-poll waiting
|
|
373
|
+
* heartbeat is throttled to one line per heartbeatMs.
|
|
374
|
+
*/
|
|
375
|
+
export function createBrowserSendProgressPrinter(write, heartbeatMs = 10_000) {
|
|
376
|
+
let lastWaitingElapsedMs;
|
|
377
|
+
return (event) => {
|
|
378
|
+
if (event.phase === "waiting") {
|
|
379
|
+
if (lastWaitingElapsedMs !== undefined && event.elapsedMs - lastWaitingElapsedMs < heartbeatMs)
|
|
380
|
+
return;
|
|
381
|
+
lastWaitingElapsedMs = event.elapsedMs;
|
|
382
|
+
write(`progress: waiting ${Math.round(event.elapsedMs / 1000)}s${event.detail ? ` (${event.detail})` : ""}`);
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
if (event.phase === "answered") {
|
|
386
|
+
write(`progress: answer received after ${Math.round(event.elapsedMs / 1000)}s${event.detail ? ` (${event.detail})` : ""}`);
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
write(`progress: ${PROGRESS_PHASE_LABELS[event.phase]}${event.detail ? ` (${event.detail})` : ""}`);
|
|
390
|
+
};
|
|
391
|
+
}
|
|
351
392
|
export async function runAskProCommand(rest, io) {
|
|
352
393
|
const parsedAskPro = parseAskProArgs(rest);
|
|
353
394
|
const hasDryRunMode = parsedAskPro.optionArgs.includes("--dry-run");
|
|
@@ -409,7 +450,7 @@ export async function runAskProCommand(rest, io) {
|
|
|
409
450
|
...(selectionProMode ? { pro_mode: selectionProMode } : {}),
|
|
410
451
|
...(selectionEffort ? { effort: selectionEffort } : {})
|
|
411
452
|
};
|
|
412
|
-
const browserPort = hasSendMode ? (readPortFlag(parsedAskPro.optionArgs, "--port")
|
|
453
|
+
const browserPort = hasSendMode ? resolveCdpPort(readPortFlag(parsedAskPro.optionArgs, "--port")) : undefined;
|
|
413
454
|
// Pro extended can legitimately think for minutes, so its default timeout is
|
|
414
455
|
// higher; an explicit --timeout-ms always wins.
|
|
415
456
|
const defaultBrowserTimeoutMs = selectionProMode === "확장" ? 300_000 : 90_000;
|
|
@@ -480,7 +521,8 @@ export async function runAskProCommand(rest, io) {
|
|
|
480
521
|
projectNew: selectionProjectNew,
|
|
481
522
|
model: selectionModel,
|
|
482
523
|
proMode: selectionProMode,
|
|
483
|
-
effort: selectionEffort
|
|
524
|
+
effort: selectionEffort,
|
|
525
|
+
onProgress: createBrowserSendProgressPrinter(io.stderr)
|
|
484
526
|
});
|
|
485
527
|
}
|
|
486
528
|
catch (error) {
|
|
@@ -579,6 +621,10 @@ export async function runAskProCommand(rest, io) {
|
|
|
579
621
|
io.stdout(`${result.task_id}\t${result.status}\t${consult.url}`);
|
|
580
622
|
io.stdout("");
|
|
581
623
|
io.stdout(result.summary);
|
|
624
|
+
// Discoverability footer on stderr: the answer above is also persisted,
|
|
625
|
+
// and `pro latest` re-prints it without a new browser send.
|
|
626
|
+
const cwdForHints = readFlag(parsedAskPro.optionArgs, "--cwd") ? targetCwd : undefined;
|
|
627
|
+
io.stderr(`${answerArtifactPath ? `saved: ${answerArtifactPath}` : "saved: task result (answer too large for a fetchable artifact)"} | re-print: ${formatProLatestCommand(sourceCli, { cwd: cwdForHints })}`);
|
|
582
628
|
}
|
|
583
629
|
else {
|
|
584
630
|
await writeSessionBestEffort(targetStore, {
|
|
@@ -599,6 +645,84 @@ export async function runAskProCommand(rest, io) {
|
|
|
599
645
|
return 0;
|
|
600
646
|
}
|
|
601
647
|
export const PRO_BROWSER_SMOKE_TOKEN = "PRODEX_PRO_SMOKE_OK";
|
|
648
|
+
/**
|
|
649
|
+
* MCP-facing wrapper around the visible-browser ask flow. Reuses the full CLI
|
|
650
|
+
* path (send pacing, task/session/receipt persistence, artifact save) by
|
|
651
|
+
* invoking runAskProCommand with captured io, then returns structured fields
|
|
652
|
+
* instead of printed lines. Registered only on the local stdio MCP server -
|
|
653
|
+
* never on the HTTP MCP surface, which is exposed to ChatGPT itself.
|
|
654
|
+
*/
|
|
655
|
+
export async function performBrowserConsultForMcp(cwd, input) {
|
|
656
|
+
const stdoutLines = [];
|
|
657
|
+
const stderrLines = [];
|
|
658
|
+
const argv = [
|
|
659
|
+
"--send",
|
|
660
|
+
...(input.model !== undefined ? ["--model", input.model] : []),
|
|
661
|
+
...(input.pro_mode !== undefined ? ["--pro-mode", input.pro_mode] : []),
|
|
662
|
+
...(input.effort !== undefined ? ["--effort", input.effort] : []),
|
|
663
|
+
...(input.project !== undefined ? ["--project", input.project] : []),
|
|
664
|
+
...(input.timeout_ms !== undefined ? ["--timeout-ms", String(input.timeout_ms)] : []),
|
|
665
|
+
...(input.files ?? []).flatMap((file) => ["--file", file]),
|
|
666
|
+
"--",
|
|
667
|
+
input.prompt
|
|
668
|
+
];
|
|
669
|
+
await runAskProCommand(argv, {
|
|
670
|
+
cwd,
|
|
671
|
+
stdout: (line) => stdoutLines.push(line),
|
|
672
|
+
stderr: (line) => stderrLines.push(line),
|
|
673
|
+
allowAskProBrowserSend: true
|
|
674
|
+
});
|
|
675
|
+
const header = stdoutLines[0] ?? "";
|
|
676
|
+
const [taskId = "", status = "", thread = ""] = header.split("\t");
|
|
677
|
+
return {
|
|
678
|
+
task_id: taskId,
|
|
679
|
+
status,
|
|
680
|
+
thread,
|
|
681
|
+
answer: stdoutLines.slice(2).join("\n"),
|
|
682
|
+
notes: stderrLines.filter((line) => !line.startsWith("progress:"))
|
|
683
|
+
};
|
|
684
|
+
}
|
|
685
|
+
/**
|
|
686
|
+
* Guided login: poll the visible browser until a logged-in ChatGPT tab with a
|
|
687
|
+
* usable composer appears, narrating each state change so the user knows what
|
|
688
|
+
* manual step (login, challenge, opening a chat) is still missing.
|
|
689
|
+
*/
|
|
690
|
+
export async function waitForChatGptLoginReady(stderr, options, deps = {}) {
|
|
691
|
+
const statusFn = deps.statusFn ?? getChatGptBrowserStatus;
|
|
692
|
+
const sleepFn = deps.sleepFn ?? sleep;
|
|
693
|
+
const now = deps.now ?? Date.now;
|
|
694
|
+
const timeoutMs = options.timeoutMs ?? 300_000;
|
|
695
|
+
const pollMs = options.pollMs ?? 2_000;
|
|
696
|
+
const startedAt = now();
|
|
697
|
+
stderr("login: waiting for a logged-in ChatGPT tab (finish login in the opened window; Ctrl+C stops waiting)...");
|
|
698
|
+
let lastState = "";
|
|
699
|
+
while (now() - startedAt < timeoutMs) {
|
|
700
|
+
const status = await statusFn({ port: options.port, timeoutMs: 1_500 });
|
|
701
|
+
const state = !status.reachable
|
|
702
|
+
? "login: browser starting..."
|
|
703
|
+
: status.blocker
|
|
704
|
+
? `login: blocked - ${status.blocker.message}`
|
|
705
|
+
: !status.loggedInLikely
|
|
706
|
+
? "login: waiting for ChatGPT login in the opened window..."
|
|
707
|
+
: !status.hasComposer
|
|
708
|
+
? "login: logged in; open a chat so the prompt composer is visible..."
|
|
709
|
+
: "";
|
|
710
|
+
if (state === "") {
|
|
711
|
+
stderr(`login: READY - logged-in ChatGPT tab with composer detected (${Math.round((now() - startedAt) / 1000)}s).`);
|
|
712
|
+
return true;
|
|
713
|
+
}
|
|
714
|
+
if (state !== lastState) {
|
|
715
|
+
lastState = state;
|
|
716
|
+
stderr(state);
|
|
717
|
+
}
|
|
718
|
+
const remainingMs = timeoutMs - (now() - startedAt);
|
|
719
|
+
if (remainingMs <= 0)
|
|
720
|
+
break;
|
|
721
|
+
await sleepFn(Math.min(pollMs, Math.max(1, remainingMs)));
|
|
722
|
+
}
|
|
723
|
+
stderr(`login: not ready after ${Math.round(timeoutMs / 1000)}s. Finish login in the browser, then verify with \`prodex pro browser check\`.`);
|
|
724
|
+
return false;
|
|
725
|
+
}
|
|
602
726
|
export async function assertBrowserLaunchStayedAlive(opened, timeoutMs) {
|
|
603
727
|
const outcome = await waitForBrowserLaunchReady(opened, timeoutMs);
|
|
604
728
|
if (outcome.reachable)
|
|
@@ -635,12 +759,17 @@ export function browserSendBlockerFromError(error) {
|
|
|
635
759
|
next_step: "Update prodex (npm i -g @youdie006/prodex@latest); if it persists, report it at https://github.com/youdie006/prodex/issues or paste the prompt manually in the visible browser."
|
|
636
760
|
};
|
|
637
761
|
}
|
|
638
|
-
|
|
762
|
+
const timedOut = message.match(/^Timed out after (\d+)ms/);
|
|
763
|
+
if (timedOut) {
|
|
764
|
+
// Suggest a concrete doubled budget so the user can paste a rerun command
|
|
765
|
+
// instead of guessing what "raise --timeout-ms" means in milliseconds.
|
|
766
|
+
const usedMs = Number(timedOut[1]);
|
|
767
|
+
const suggestedMs = Number.isFinite(usedMs) && usedMs > 0 ? usedMs * 2 : 600_000;
|
|
639
768
|
return {
|
|
640
769
|
code: "send_timeout",
|
|
641
770
|
message,
|
|
642
771
|
retryable: true,
|
|
643
|
-
next_step:
|
|
772
|
+
next_step: `Rerun with a bigger budget: \`prodex pro browser ask --timeout-ms ${suggestedMs} "<same prompt>"\`.`
|
|
644
773
|
};
|
|
645
774
|
}
|
|
646
775
|
return {
|
|
@@ -847,7 +976,7 @@ export async function printProductCheck(store, io, args, configCwd = io.cwd) {
|
|
|
847
976
|
}
|
|
848
977
|
}
|
|
849
978
|
const browserStatus = await getChatGptBrowserStatus({
|
|
850
|
-
port: readPortFlag(args, "--port")
|
|
979
|
+
port: resolveCdpPort(readPortFlag(args, "--port")),
|
|
851
980
|
timeoutMs: readPositiveNumberFlag(args, "--timeout-ms") ?? 1500
|
|
852
981
|
});
|
|
853
982
|
const browserCommandOptions = {
|