@youdie006/prodex 0.32.0 → 0.33.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/dist/chatgpt-browser.js +73 -1
- package/dist/cli-help.js +6 -0
- package/dist/cli-pro.js +60 -4
- package/package.json +1 -1
package/dist/chatgpt-browser.js
CHANGED
|
@@ -2516,7 +2516,61 @@ export async function navigateChatGptTabTo(url, options = {}) {
|
|
|
2516
2516
|
cdp.close();
|
|
2517
2517
|
}
|
|
2518
2518
|
}
|
|
2519
|
-
/**
|
|
2519
|
+
/**
|
|
2520
|
+
* Which project a delete means - or why it refuses to guess.
|
|
2521
|
+
*
|
|
2522
|
+
* Deleting a project is not undoable from here, so the request has to identify
|
|
2523
|
+
* exactly one. Names are matched exactly (a typo must not delete a neighbour),
|
|
2524
|
+
* and an id is accepted for the case this account actually has: two projects
|
|
2525
|
+
* sharing a name.
|
|
2526
|
+
*/
|
|
2527
|
+
export function resolveProjectToDelete(projects, request) {
|
|
2528
|
+
if (request.id) {
|
|
2529
|
+
const byId = projects.find((project) => project.id === request.id);
|
|
2530
|
+
return byId ? { ok: true, id: byId.id, name: byId.name } : { ok: false, reason: `No project has the id ${request.id}.` };
|
|
2531
|
+
}
|
|
2532
|
+
const name = request.name?.trim();
|
|
2533
|
+
if (!name)
|
|
2534
|
+
return { ok: false, reason: "Name the project to delete with --name, or identify it with --id." };
|
|
2535
|
+
const matches = projects.filter((project) => project.name === name);
|
|
2536
|
+
if (matches.length === 0) {
|
|
2537
|
+
return { ok: false, reason: `No project is named exactly "${name}". Run \`prodex pro browser projects\` to see the names as they are stored.` };
|
|
2538
|
+
}
|
|
2539
|
+
if (matches.length > 1) {
|
|
2540
|
+
const ids = matches.map((project) => project.id).join(", ");
|
|
2541
|
+
return {
|
|
2542
|
+
ok: false,
|
|
2543
|
+
reason: `More than one project is named "${name}" (${ids}). Pass --id to say which one, since deleting the wrong one cannot be undone here.`
|
|
2544
|
+
};
|
|
2545
|
+
}
|
|
2546
|
+
return { ok: true, id: matches[0].id, name: matches[0].name };
|
|
2547
|
+
}
|
|
2548
|
+
/** Delete one project by id. The caller is responsible for confirming intent. */
|
|
2549
|
+
export function deleteProjectExpression(projectId) {
|
|
2550
|
+
return `(async () => {
|
|
2551
|
+
let token = "";
|
|
2552
|
+
try {
|
|
2553
|
+
const session = await fetch("/api/auth/session", { credentials: "include" });
|
|
2554
|
+
if (!session.ok) return { ok: false, reason: "session_http_" + session.status };
|
|
2555
|
+
const parsed = await session.json();
|
|
2556
|
+
token = (parsed && parsed.accessToken) || "";
|
|
2557
|
+
} catch (error) {
|
|
2558
|
+
return { ok: false, reason: "session_error" };
|
|
2559
|
+
}
|
|
2560
|
+
try {
|
|
2561
|
+
const response = await fetch("/backend-api/gizmos/" + ${JSON.stringify(projectId)}, {
|
|
2562
|
+
method: "DELETE",
|
|
2563
|
+
credentials: "include",
|
|
2564
|
+
headers: token ? { Authorization: "Bearer " + token, "Content-Type": "application/json" } : { "Content-Type": "application/json" }
|
|
2565
|
+
});
|
|
2566
|
+
const body = await response.text();
|
|
2567
|
+
if (!response.ok) return { ok: false, reason: "delete_http_" + response.status + " " + body.slice(0, 120) };
|
|
2568
|
+
return { ok: true, reason: "" };
|
|
2569
|
+
} catch (error) {
|
|
2570
|
+
return { ok: false, reason: "delete_error" };
|
|
2571
|
+
}
|
|
2572
|
+
})()`;
|
|
2573
|
+
}
|
|
2520
2574
|
export async function listChatGptProjectsWithIds(input = {}) {
|
|
2521
2575
|
const port = resolveCdpPort(input.port);
|
|
2522
2576
|
const page = await findChatGptPage(port, input.timeoutMs ?? 3_000);
|
|
@@ -2530,6 +2584,24 @@ export async function listChatGptProjectsWithIds(input = {}) {
|
|
|
2530
2584
|
return [];
|
|
2531
2585
|
}
|
|
2532
2586
|
}
|
|
2587
|
+
/** Delete one project. Callers must have confirmed intent before calling. */
|
|
2588
|
+
export async function deleteChatGptProject(input) {
|
|
2589
|
+
const port = resolveCdpPort(input.port);
|
|
2590
|
+
const page = await findChatGptPage(port, input.timeoutMs ?? 3_000);
|
|
2591
|
+
if (!page.ok || !page.page) {
|
|
2592
|
+
throw new ChatGptBrowserBlockerError(page.blocker ?? {
|
|
2593
|
+
code: "browser_unreachable",
|
|
2594
|
+
message: `No Chrome DevTools endpoint is reachable on 127.0.0.1:${port}.`,
|
|
2595
|
+
retryable: true,
|
|
2596
|
+
next_step: "Run `prodex pro browser login` to reopen the dedicated window, then retry."
|
|
2597
|
+
});
|
|
2598
|
+
}
|
|
2599
|
+
const result = await evaluateOnPage(page.page, deleteProjectExpression(input.projectId), {
|
|
2600
|
+
timeoutMs: 30_000
|
|
2601
|
+
});
|
|
2602
|
+
if (!result?.ok)
|
|
2603
|
+
throw new Error(`ChatGPT refused to delete the project: ${result?.reason ?? "unknown reason"}`);
|
|
2604
|
+
}
|
|
2533
2605
|
export async function listRecentChatGptConversations(input = {}) {
|
|
2534
2606
|
const port = resolveCdpPort(input.port);
|
|
2535
2607
|
const page = await findChatGptPage(port, input.timeoutMs ?? 3_000);
|
package/dist/cli-help.js
CHANGED
|
@@ -263,6 +263,11 @@ export function printProBrowserHelp(stdout, sourceCli) {
|
|
|
263
263
|
: "prodex pro browser projects [--source-cli /absolute/path/to/dist/cli.js] [--port 9333] [--timeout-ms 15000] # read-only: exact sidebar project names";
|
|
264
264
|
// A send that outlives its budget is not a lost answer, but only if agents
|
|
265
265
|
// know this exists - and this help is where onboarding sends them.
|
|
266
|
+
// Deleting a project takes its chats with it, so the usage line says so and
|
|
267
|
+
// the flag that actually deletes is spelled out rather than implied.
|
|
268
|
+
const projectDeleteUsage = sourceCli
|
|
269
|
+
? `${cli} pro browser project-delete${sourceCliOption} [--name "exact name" | --id g-p-...] [--confirm-delete] # previews unless --confirm-delete; deleting a project takes its chats with it`
|
|
270
|
+
: `prodex pro browser project-delete [--source-cli /absolute/path/to/dist/cli.js] [--name "exact name" | --id g-p-...] [--confirm-delete] # previews unless --confirm-delete; deleting a project takes its chats with it`;
|
|
266
271
|
const recoverUsage = sourceCli
|
|
267
272
|
? `${cli} pro browser recover${sourceCliOption} [--cwd /absolute/path/to/repo] [--port 9333] --target-url <thread-url> [--timeout-ms 60000] # fetch a finished answer (deep research reports too) from a thread whose send timed out`
|
|
268
273
|
: "prodex pro browser recover [--source-cli /absolute/path/to/dist/cli.js] [--cwd /absolute/path/to/repo] [--port 9333] --target-url <thread-url> [--timeout-ms 60000] # fetch a finished answer (deep research reports too) from a thread whose send timed out";
|
|
@@ -274,6 +279,7 @@ Commands:
|
|
|
274
279
|
${smokeUsage}
|
|
275
280
|
${modelsUsage}
|
|
276
281
|
${projectsUsage}
|
|
282
|
+
${projectDeleteUsage}
|
|
277
283
|
${askUsage}
|
|
278
284
|
${recoverUsage}
|
|
279
285
|
|
package/dist/cli-pro.js
CHANGED
|
@@ -2,7 +2,7 @@ import { existsSync, statSync } from "node:fs";
|
|
|
2
2
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { buildDryRunBundle } from "./bundle.js";
|
|
5
|
-
import { DEFAULT_CDP_PORT, resolveCdpPort, chatGptVisibilityBlocker, defaultChatGptProfileDir, formatDurationMs, getChatGptBrowserStatus, listChatGptModelOptions, listChatGptSidebarProjects, normalizeChatGptTargetUrl, openChatGptBrowser, openChatGptTab, parseProMode, parseReasoningEffort, defaultTimeoutForTools, ensureVirtualDisplay, minimizeChatGptWindow, readLastBrowserLoginLaunch, resolveVirtualDisplayPreference, resolveBrowserWindowMode, resolveHeadlessPreference, recordBrowserLoginLaunch, recoverChatGptAnswerFromThread, sendChatGptPrompt } from "./chatgpt-browser.js";
|
|
5
|
+
import { DEFAULT_CDP_PORT, resolveCdpPort, resolveProjectToDelete, chatGptVisibilityBlocker, defaultChatGptProfileDir, formatDurationMs, getChatGptBrowserStatus, listChatGptModelOptions, deleteChatGptProject, listChatGptProjectsWithIds, listChatGptSidebarProjects, normalizeChatGptTargetUrl, openChatGptBrowser, openChatGptTab, parseProMode, parseReasoningEffort, defaultTimeoutForTools, ensureVirtualDisplay, minimizeChatGptWindow, readLastBrowserLoginLaunch, resolveVirtualDisplayPreference, resolveBrowserWindowMode, resolveHeadlessPreference, recordBrowserLoginLaunch, recoverChatGptAnswerFromThread, sendChatGptPrompt } from "./chatgpt-browser.js";
|
|
6
6
|
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, readNonNegativeIntegerFlag, readPositiveIntegerFlag, readRepeatedFlag, resolveCwdFlag, resolveOptionalFileFlag, unknownSubcommandError } from "./cli-args.js";
|
|
7
7
|
import { printProBrowserHelp, printProHelp } from "./cli-help.js";
|
|
8
8
|
import { listRawResultsForInspection, listTasksForInspection } from "./cli-ledger.js";
|
|
@@ -463,8 +463,20 @@ export async function runProCommand(rest, io, runCliFn) {
|
|
|
463
463
|
return 0;
|
|
464
464
|
}
|
|
465
465
|
io.stdout("ChatGPT sidebar projects (read-only; exact names as rendered):");
|
|
466
|
-
|
|
467
|
-
|
|
466
|
+
// The rendered sidebar can lag reality - it still showed a project that
|
|
467
|
+
// had just been deleted - so print the account's own listing when it is
|
|
468
|
+
// readable, and its ids, which `project-delete --id` needs to tell two
|
|
469
|
+
// projects of the same name apart.
|
|
470
|
+
const withIds = await listChatGptProjectsWithIds({
|
|
471
|
+
...(projectsPort !== undefined ? { port: projectsPort } : {}),
|
|
472
|
+
...(projectsTimeoutMs !== undefined ? { timeoutMs: projectsTimeoutMs } : {})
|
|
473
|
+
}).catch(() => []);
|
|
474
|
+
if (withIds.length > 0)
|
|
475
|
+
for (const project of withIds)
|
|
476
|
+
io.stdout(` ${project.name} ${project.id}`);
|
|
477
|
+
else
|
|
478
|
+
for (const name of listed.projects)
|
|
479
|
+
io.stdout(` ${name}`);
|
|
468
480
|
io.stdout("Use with `pro browser ask --project \"<name>\"` or pin one with `prodex setup --project \"<name>\"`.");
|
|
469
481
|
return 0;
|
|
470
482
|
}
|
|
@@ -525,7 +537,51 @@ export async function runProCommand(rest, io, runCliFn) {
|
|
|
525
537
|
io.stderr(`recovered: answer saved to .bridge; re-print with \`prodex pro latest --cwd ${recoverCwd}\``);
|
|
526
538
|
return 0;
|
|
527
539
|
}
|
|
528
|
-
|
|
540
|
+
if (browserSubcommand === "project-delete") {
|
|
541
|
+
if (printProBrowserHelpIfRequested(browserArgs, "pro browser project-delete", io, {
|
|
542
|
+
valueFlags: ["--cwd", "--port", "--timeout-ms", "--name", "--id", "--source-cli"],
|
|
543
|
+
booleanFlags: ["--confirm-delete"]
|
|
544
|
+
})) {
|
|
545
|
+
return 0;
|
|
546
|
+
}
|
|
547
|
+
assertOnlyOptions(browserArgs, "pro browser project-delete", ["--cwd", "--port", "--timeout-ms", "--name", "--id", "--source-cli"], ["--confirm-delete"]);
|
|
548
|
+
const deletePort = readPortFlag(browserArgs, "--port");
|
|
549
|
+
const deleteTimeoutMs = readPositiveIntegerFlag(browserArgs, "--timeout-ms");
|
|
550
|
+
const projects = await listChatGptProjectsWithIds({
|
|
551
|
+
...(deletePort !== undefined ? { port: deletePort } : {}),
|
|
552
|
+
...(deleteTimeoutMs !== undefined ? { timeoutMs: deleteTimeoutMs } : {})
|
|
553
|
+
});
|
|
554
|
+
const target = resolveProjectToDelete(projects, {
|
|
555
|
+
...(readFlag(browserArgs, "--name") !== undefined ? { name: readFlag(browserArgs, "--name") } : {}),
|
|
556
|
+
...(readFlag(browserArgs, "--id") !== undefined ? { id: readFlag(browserArgs, "--id") } : {})
|
|
557
|
+
});
|
|
558
|
+
if (!target.ok)
|
|
559
|
+
throw new Error(target.reason);
|
|
560
|
+
// Deleting a project is not undoable from here, so the default is a
|
|
561
|
+
// preview: say exactly what would go, and delete nothing.
|
|
562
|
+
if (!browserArgs.includes("--confirm-delete")) {
|
|
563
|
+
io.stdout(`Would delete project "${target.name}" (${target.id}) and everything filed under it.`);
|
|
564
|
+
io.stdout("Nothing was deleted. Re-run with --confirm-delete to go ahead.");
|
|
565
|
+
return 0;
|
|
566
|
+
}
|
|
567
|
+
await deleteChatGptProject({
|
|
568
|
+
projectId: target.id,
|
|
569
|
+
...(deletePort !== undefined ? { port: deletePort } : {}),
|
|
570
|
+
...(deleteTimeoutMs !== undefined ? { timeoutMs: deleteTimeoutMs } : {})
|
|
571
|
+
});
|
|
572
|
+
io.stdout(`deleted project "${target.name}" (${target.id})`);
|
|
573
|
+
return 0;
|
|
574
|
+
}
|
|
575
|
+
throw unknownSubcommandError("pro browser", browserSubcommand, [
|
|
576
|
+
"login",
|
|
577
|
+
"ask",
|
|
578
|
+
"smoke",
|
|
579
|
+
"check",
|
|
580
|
+
"models",
|
|
581
|
+
"projects",
|
|
582
|
+
"project-delete",
|
|
583
|
+
"recover"
|
|
584
|
+
]);
|
|
529
585
|
}
|
|
530
586
|
if (subcommand === "open" || subcommand === "status" || subcommand === "smoke" || subcommand === "check" || subcommand === "doctor") {
|
|
531
587
|
// Point at a subcommand that EXISTS: `pro status` used to say "use `pro
|