chatgpt-pro-research 0.2.3 → 0.2.5
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/lib/client.js +92 -24
- package/lib/index.js +79 -6
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -343,26 +343,7 @@ function Panel() {
|
|
|
343
343
|
) : void 0
|
|
344
344
|
),
|
|
345
345
|
(0, import_react.createElement)("div", { style: { fontWeight: 600, marginBottom: 4 } }, "\u8F6E\u6B21"),
|
|
346
|
-
(detail.rounds_detail ?? []).length === 0 ? (0, import_react.createElement)("div", { style: { opacity: 0.6 } }, "\u8FD8\u6CA1\u6709\u63D0\u4EA4\u8BB0\u5F55\u3002") : (0, import_react.createElement)(
|
|
347
|
-
"table",
|
|
348
|
-
{ style: { width: "100%", borderCollapse: "collapse", fontSize: 12 } },
|
|
349
|
-
(0, import_react.createElement)("thead", {}, (0, import_react.createElement)(
|
|
350
|
-
"tr",
|
|
351
|
-
{},
|
|
352
|
-
["\u8F6E", "\u88C1\u51B3", "\u6587\u4EF6", "\u63D0\u4EA4\u65F6\u95F4"].map((label) => (0, import_react.createElement)("th", {
|
|
353
|
-
key: label,
|
|
354
|
-
style: { textAlign: "left", padding: "4px 6px", borderBottom: "1px solid rgba(127,127,127,.25)", opacity: 0.7, fontWeight: 500 }
|
|
355
|
-
}, label))
|
|
356
|
-
)),
|
|
357
|
-
(0, import_react.createElement)("tbody", {}, (detail.rounds_detail ?? []).map((round) => (0, import_react.createElement)(
|
|
358
|
-
"tr",
|
|
359
|
-
{ key: round.n },
|
|
360
|
-
(0, import_react.createElement)("td", { style: cellStyle() }, String(round.n)),
|
|
361
|
-
(0, import_react.createElement)("td", { style: cellStyle() }, round.verdict ?? "\u2014"),
|
|
362
|
-
(0, import_react.createElement)("td", { style: cellStyle() }, String(round.files)),
|
|
363
|
-
(0, import_react.createElement)("td", { style: cellStyle() }, round.submittedAt?.slice(0, 16).replace("T", " ") ?? "\u2014")
|
|
364
|
-
)))
|
|
365
|
-
),
|
|
346
|
+
(detail.rounds_detail ?? []).length === 0 ? (0, import_react.createElement)("div", { style: { opacity: 0.6 } }, "\u8FD8\u6CA1\u6709\u63D0\u4EA4\u8BB0\u5F55\u3002") : (0, import_react.createElement)("div", {}, (detail.rounds_detail ?? []).map((round) => renderRound(detail, round))),
|
|
366
347
|
config.designMode && detail.designs != null ? (0, import_react.createElement)("div", { style: { marginTop: 8, opacity: 0.75 } }, `\u6BCF\u8F6E\u65B9\u5411\uFF1A${detail.designs}`) : void 0,
|
|
367
348
|
renderArtifacts(detail)
|
|
368
349
|
)
|
|
@@ -376,7 +357,16 @@ function renderArtifacts(detail) {
|
|
|
376
357
|
const artifacts = detail.artifacts ?? [];
|
|
377
358
|
if (artifacts.length === 0) return void 0;
|
|
378
359
|
if (!config.designMode) {
|
|
379
|
-
return (0, import_react.createElement)(
|
|
360
|
+
return (0, import_react.createElement)(
|
|
361
|
+
"div",
|
|
362
|
+
{ style: { marginTop: 10 } },
|
|
363
|
+
(0, import_react.createElement)("div", { style: { fontWeight: 600, marginBottom: 4 } }, "\u5168\u90E8\u4EA7\u7269"),
|
|
364
|
+
(0, import_react.createElement)(
|
|
365
|
+
"div",
|
|
366
|
+
{ style: { display: "flex", gap: 6, flexWrap: "wrap" } },
|
|
367
|
+
artifacts.map((path) => artifactChip(detail.topic_id, path))
|
|
368
|
+
)
|
|
369
|
+
);
|
|
380
370
|
}
|
|
381
371
|
const images = artifacts.filter((path) => /\.(png|jpe?g|webp|gif|svg)$/i.test(path));
|
|
382
372
|
const demos = artifacts.filter((path) => /\.html?$/i.test(path));
|
|
@@ -417,6 +407,87 @@ function renderArtifacts(detail) {
|
|
|
417
407
|
(0, import_react.createElement)("div", { style: { marginTop: 8, opacity: 0.6, fontSize: 11, wordBreak: "break-all" } }, artifacts.join("\u3001"))
|
|
418
408
|
);
|
|
419
409
|
}
|
|
410
|
+
function renderRound(detail, round) {
|
|
411
|
+
const prefix = `/round-${String(round.n).padStart(2, "0")}/`;
|
|
412
|
+
const artifacts = round.artifacts ?? (detail.artifacts ?? []).filter((path) => path.includes(prefix));
|
|
413
|
+
const attempts = round.attempts ?? 1;
|
|
414
|
+
return (0, import_react.createElement)(
|
|
415
|
+
"div",
|
|
416
|
+
{
|
|
417
|
+
key: round.n,
|
|
418
|
+
style: { padding: "8px 0", borderTop: "1px solid rgba(127,127,127,.18)" }
|
|
419
|
+
},
|
|
420
|
+
(0, import_react.createElement)(
|
|
421
|
+
"div",
|
|
422
|
+
{ style: { display: "flex", gap: 10, flexWrap: "wrap", alignItems: "baseline", fontSize: 12 } },
|
|
423
|
+
(0, import_react.createElement)("strong", {}, `\u7B2C ${round.n} \u8F6E`),
|
|
424
|
+
(0, import_react.createElement)("span", { style: { opacity: 0.72 } }, round.verdict ?? (round.answeredAt !== void 0 ? "\u5DF2\u7B54\u590D" : "\u8FDB\u884C\u4E2D")),
|
|
425
|
+
(0, import_react.createElement)("span", { style: { opacity: 0.6 } }, `${round.files} \u6587\u4EF6`),
|
|
426
|
+
(0, import_react.createElement)("span", { style: { opacity: 0.6 } }, `\u63D0\u4EA4 ${formatTime(round.submittedAt)}`),
|
|
427
|
+
(0, import_react.createElement)("span", { style: { opacity: 0.6 } }, `\u5B8C\u6210 ${formatTime(round.answeredAt)}`),
|
|
428
|
+
attempts > 1 ? (0, import_react.createElement)("span", { style: { color: "#ed6c02" } }, `\u91CD\u8BD5 ${attempts - 1}`) : void 0
|
|
429
|
+
),
|
|
430
|
+
(0, import_react.createElement)(
|
|
431
|
+
"div",
|
|
432
|
+
{ style: { display: "flex", gap: 6, flexWrap: "wrap", marginTop: 6 } },
|
|
433
|
+
(0, import_react.createElement)("button", {
|
|
434
|
+
onClick: () => window.open(`${config.routes}/answer?id=${encodeURIComponent(detail.topic_id)}&round=${round.n}`, "_blank", "noopener,noreferrer"),
|
|
435
|
+
style: chipStyle()
|
|
436
|
+
}, "\u67E5\u770B\u56DE\u7B54"),
|
|
437
|
+
artifacts.map((path) => artifactChip(detail.topic_id, path))
|
|
438
|
+
)
|
|
439
|
+
);
|
|
440
|
+
}
|
|
441
|
+
function artifactChip(topicId, path) {
|
|
442
|
+
const name = path.split("/").pop() ?? path;
|
|
443
|
+
if (/\.(png|jpe?g|webp|gif|svg)$/i.test(path)) {
|
|
444
|
+
return (0, import_react.createElement)("a", {
|
|
445
|
+
key: path,
|
|
446
|
+
href: assetUrl(topicId, path),
|
|
447
|
+
target: "_blank",
|
|
448
|
+
rel: "noreferrer",
|
|
449
|
+
style: chipStyle()
|
|
450
|
+
}, name);
|
|
451
|
+
}
|
|
452
|
+
if (/\.html?$/i.test(path)) {
|
|
453
|
+
return (0, import_react.createElement)("a", {
|
|
454
|
+
key: path,
|
|
455
|
+
href: assetUrl(topicId, path),
|
|
456
|
+
target: "_blank",
|
|
457
|
+
rel: "noreferrer",
|
|
458
|
+
style: chipStyle()
|
|
459
|
+
}, `${name} \u2197`);
|
|
460
|
+
}
|
|
461
|
+
return (0, import_react.createElement)("button", {
|
|
462
|
+
key: path,
|
|
463
|
+
onClick: () => void revealArtifact(topicId, path),
|
|
464
|
+
style: chipStyle()
|
|
465
|
+
}, `${name} \xB7 \u76EE\u5F55`);
|
|
466
|
+
}
|
|
467
|
+
async function revealArtifact(topicId, path) {
|
|
468
|
+
await fetch(`${config.routes}/open`, {
|
|
469
|
+
method: "POST",
|
|
470
|
+
headers: { "content-type": "application/json" },
|
|
471
|
+
body: JSON.stringify({ topic_id: topicId, path })
|
|
472
|
+
}).catch(() => void 0);
|
|
473
|
+
}
|
|
474
|
+
function formatTime(value) {
|
|
475
|
+
return value === void 0 ? "\u2014" : value.slice(0, 16).replace("T", " ");
|
|
476
|
+
}
|
|
477
|
+
function chipStyle() {
|
|
478
|
+
return {
|
|
479
|
+
padding: "2px 8px",
|
|
480
|
+
borderRadius: 999,
|
|
481
|
+
border: "1px solid rgba(127,127,127,.35)",
|
|
482
|
+
background: "transparent",
|
|
483
|
+
color: "inherit",
|
|
484
|
+
cursor: "pointer",
|
|
485
|
+
fontSize: 11,
|
|
486
|
+
textDecoration: "none",
|
|
487
|
+
display: "inline-flex",
|
|
488
|
+
alignItems: "center"
|
|
489
|
+
};
|
|
490
|
+
}
|
|
420
491
|
function buttonStyle() {
|
|
421
492
|
return {
|
|
422
493
|
padding: "4px 10px",
|
|
@@ -428,9 +499,6 @@ function buttonStyle() {
|
|
|
428
499
|
fontSize: 12
|
|
429
500
|
};
|
|
430
501
|
}
|
|
431
|
-
function cellStyle() {
|
|
432
|
-
return { padding: "4px 6px", borderBottom: "1px solid rgba(127,127,127,.12)" };
|
|
433
|
-
}
|
|
434
502
|
function Icon({ kind }) {
|
|
435
503
|
const svg = kind === "design" ? DESIGN_ICON : RESEARCH_ICON;
|
|
436
504
|
return (0, import_react.createElement)("span", {
|
package/lib/index.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
// chatgpt-pro-research — built from packages/research/src/index.ts
|
|
2
2
|
|
|
3
3
|
// src/plugin.ts
|
|
4
|
+
import { spawn } from "node:child_process";
|
|
4
5
|
import { homedir } from "node:os";
|
|
5
6
|
import { dirname as dirname2, extname, isAbsolute, join as join3, resolve as resolve3, sep } from "node:path";
|
|
6
|
-
import { mkdir as mkdir3, readFile as readFile3, writeFile as writeFile2 } from "node:fs/promises";
|
|
7
|
+
import { access, mkdir as mkdir3, readFile as readFile3, writeFile as writeFile2 } from "node:fs/promises";
|
|
7
8
|
import { fileURLToPath } from "node:url";
|
|
8
9
|
import z from "@deepseek-ai/schemastery";
|
|
9
10
|
import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
@@ -649,7 +650,7 @@ var ResearchEngine = class {
|
|
|
649
650
|
}
|
|
650
651
|
let round = pending ?? topic.rounds.find((item) => item.n === n);
|
|
651
652
|
if (round === void 0) {
|
|
652
|
-
round = { n, files: 0 };
|
|
653
|
+
round = { n, files: 0, attempts: 0 };
|
|
653
654
|
topic.rounds.push(round);
|
|
654
655
|
const stored = n === 1 ? await this.options.store.readQuestion(topic, 0) : await this.options.store.readQuestion(topic, n);
|
|
655
656
|
const brief = stored ?? this.followUpBrief(topic);
|
|
@@ -667,7 +668,10 @@ var ResearchEngine = class {
|
|
|
667
668
|
const fetched = await this.waitAnswer(round, signal);
|
|
668
669
|
if (fetched === void 0) return;
|
|
669
670
|
await this.options.store.writeAnswer(topic, n, fetched.answer);
|
|
670
|
-
await this.options.store.writeArtifacts(topic, n, fetched);
|
|
671
|
+
const written = await this.options.store.writeArtifacts(topic, n, fetched);
|
|
672
|
+
if (written.length > 0) {
|
|
673
|
+
round.artifacts = [.../* @__PURE__ */ new Set([...round.artifacts ?? [], ...written])];
|
|
674
|
+
}
|
|
671
675
|
round.answeredAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
672
676
|
await this.transition(topic, "answered");
|
|
673
677
|
this.emit({ type: "answered", topicId: topic.id, round: n });
|
|
@@ -690,6 +694,7 @@ var ResearchEngine = class {
|
|
|
690
694
|
fallbackFiles = allFiles;
|
|
691
695
|
}
|
|
692
696
|
round.files = files.length;
|
|
697
|
+
round.attempts = (round.attempts ?? 0) + 1;
|
|
693
698
|
const result = await this.options.transport.submit({
|
|
694
699
|
topicId: topic.id,
|
|
695
700
|
round: round.n,
|
|
@@ -971,6 +976,8 @@ var ChatGptWebTransport = class {
|
|
|
971
976
|
context;
|
|
972
977
|
launching;
|
|
973
978
|
submissions = /* @__PURE__ */ new Map();
|
|
979
|
+
/** One visible tab per topic so later rounds update the same conversation. */
|
|
980
|
+
topicPages = /* @__PURE__ */ new Map();
|
|
974
981
|
options;
|
|
975
982
|
constructor(options) {
|
|
976
983
|
this.options = options;
|
|
@@ -1042,7 +1049,9 @@ var ChatGptWebTransport = class {
|
|
|
1042
1049
|
}
|
|
1043
1050
|
async submit(request) {
|
|
1044
1051
|
const context = await this.ensureContext();
|
|
1045
|
-
const
|
|
1052
|
+
const existing = this.topicPages.get(request.topicId);
|
|
1053
|
+
const page = existing !== void 0 && !existing.isClosed() ? existing : await context.newPage();
|
|
1054
|
+
this.topicPages.set(request.topicId, page);
|
|
1046
1055
|
try {
|
|
1047
1056
|
const target = request.session?.conversationUrl ?? this.appUrl;
|
|
1048
1057
|
let reused = request.session?.conversationUrl !== void 0;
|
|
@@ -1204,10 +1213,15 @@ ${attachments.join("\n")}`);
|
|
|
1204
1213
|
})).filter((block) => block.text.trim() !== ""));
|
|
1205
1214
|
}
|
|
1206
1215
|
async cancel() {
|
|
1207
|
-
|
|
1208
|
-
|
|
1216
|
+
const pages = /* @__PURE__ */ new Set([
|
|
1217
|
+
...[...this.submissions.values()].map((submission) => submission.page),
|
|
1218
|
+
...this.topicPages.values()
|
|
1219
|
+
]);
|
|
1220
|
+
for (const page of pages) {
|
|
1221
|
+
await page.close().catch(() => void 0);
|
|
1209
1222
|
}
|
|
1210
1223
|
this.submissions.clear();
|
|
1224
|
+
this.topicPages.clear();
|
|
1211
1225
|
}
|
|
1212
1226
|
async close() {
|
|
1213
1227
|
await this.cancel();
|
|
@@ -1624,6 +1638,28 @@ ${input.answer.slice(0, 3e4)}`
|
|
|
1624
1638
|
const bytes = await readFile3(target);
|
|
1625
1639
|
return { bytes, mime: mimeFor(target) };
|
|
1626
1640
|
}
|
|
1641
|
+
/** Raw answer text for one round; shown in a new tab from the panel. */
|
|
1642
|
+
async answerText(id, round) {
|
|
1643
|
+
const entry = await this.entryFor(id);
|
|
1644
|
+
if (entry === void 0) return void 0;
|
|
1645
|
+
const store = this.storeAt(entry.root, entry.mode);
|
|
1646
|
+
const topic = await store.load(id);
|
|
1647
|
+
return await store.readAnswer(topic, round);
|
|
1648
|
+
}
|
|
1649
|
+
/** Reveal a topic-relative file in the OS file manager. */
|
|
1650
|
+
async revealFile(id, relativePath) {
|
|
1651
|
+
const entry = await this.entryFor(id);
|
|
1652
|
+
if (entry === void 0) throw new Error(`unknown topic: ${id}`);
|
|
1653
|
+
const store = this.storeAt(entry.root, entry.mode);
|
|
1654
|
+
const dir = await store.dirOf(id);
|
|
1655
|
+
const target = resolve3(dir, relativePath);
|
|
1656
|
+
if (target !== dir && !target.startsWith(`${dir}${sep}`)) {
|
|
1657
|
+
throw new Error("path escapes the topic");
|
|
1658
|
+
}
|
|
1659
|
+
await access(target);
|
|
1660
|
+
const child = process.platform === "win32" ? spawn("explorer.exe", ["/select,", target], { detached: true, stdio: "ignore" }) : process.platform === "darwin" ? spawn("open", ["-R", target], { detached: true, stdio: "ignore" }) : spawn("xdg-open", [dirname2(target)], { detached: true, stdio: "ignore" });
|
|
1661
|
+
child.unref();
|
|
1662
|
+
}
|
|
1627
1663
|
async act(id, action, conclusion, note) {
|
|
1628
1664
|
const entry = await this.entryFor(id);
|
|
1629
1665
|
if (entry === void 0) throw new Error(`unknown topic: ${id}`);
|
|
@@ -1904,6 +1940,43 @@ function registerRoutes(scoped, service, spec2) {
|
|
|
1904
1940
|
}
|
|
1905
1941
|
}
|
|
1906
1942
|
}), `${spec2.name}: asset route`);
|
|
1943
|
+
scoped.effect(() => webServer.register({
|
|
1944
|
+
kind: "exact",
|
|
1945
|
+
path: `${spec2.routes}/answer`,
|
|
1946
|
+
handler: async (req, res) => {
|
|
1947
|
+
const denied = reject(req);
|
|
1948
|
+
if (denied !== void 0) return sendJson(res, denied, { error: "unauthorized" });
|
|
1949
|
+
const url = new URL(req.url ?? "/", "http://localhost");
|
|
1950
|
+
const id = url.searchParams.get("id") ?? "";
|
|
1951
|
+
const round = Number(url.searchParams.get("round") ?? "0");
|
|
1952
|
+
try {
|
|
1953
|
+
const text = await service.answerText(id, round);
|
|
1954
|
+
if (text === void 0) return sendJson(res, 404, { error: "answer not found" });
|
|
1955
|
+
res.writeHead(200, { "content-type": "text/plain; charset=utf-8", "cache-control": "no-store" });
|
|
1956
|
+
res.end(text);
|
|
1957
|
+
} catch (error) {
|
|
1958
|
+
sendJson(res, 500, { error: error instanceof Error ? error.message : String(error) });
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1961
|
+
}), `${spec2.name}: answer route`);
|
|
1962
|
+
scoped.effect(() => webServer.register({
|
|
1963
|
+
kind: "exact",
|
|
1964
|
+
path: `${spec2.routes}/open`,
|
|
1965
|
+
handler: async (req, res) => {
|
|
1966
|
+
const denied = reject(req);
|
|
1967
|
+
if (denied !== void 0) return sendJson(res, denied, { error: "unauthorized" });
|
|
1968
|
+
if (req.method !== "POST") return sendJson(res, 405, { error: "method not allowed" });
|
|
1969
|
+
try {
|
|
1970
|
+
const body = await readJsonBody(req);
|
|
1971
|
+
const id = typeof body.topic_id === "string" ? body.topic_id : "";
|
|
1972
|
+
const relativePath = typeof body.path === "string" ? body.path : "";
|
|
1973
|
+
await service.revealFile(id, relativePath);
|
|
1974
|
+
sendJson(res, 200, { ok: true });
|
|
1975
|
+
} catch (error) {
|
|
1976
|
+
sendJson(res, 400, { error: error instanceof Error ? error.message : String(error) });
|
|
1977
|
+
}
|
|
1978
|
+
}
|
|
1979
|
+
}), `${spec2.name}: open route`);
|
|
1907
1980
|
}
|
|
1908
1981
|
function createPlugin(spec2) {
|
|
1909
1982
|
return (ctx, config) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chatgpt-pro-research",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.5",
|
|
4
4
|
"description": "ChatGPT Pro Research: unattended multi-round research through a real ChatGPT Pro web session, with local records and a DSH sidebar.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|