chatgpt-pro-research 0.2.1 → 0.2.3

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 CHANGED
@@ -34,8 +34,135 @@ var import_react = require("react");
34
34
  var RESEARCH_ICON = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="-40 -76 200 200"><title>Anyvia R</title><defs><mask id="R-mono-black-standard-gap" maskUnits="userSpaceOnUse" x="-100" y="-100" width="400" height="400"><rect x="-100" y="-100" width="400" height="400" fill="white"/><path d="M77.18032292691355 52.74975779269961 A3 3 0 0 1 79.72446721538282 48.16 L108.02474889607315 48.16 A3 3 0 0 1 110.56889318454242 49.57024220730038 L139.21290427606598 95.41024220730039 A3 3 0 0 1 136.6687599875967 100 L108.36847830690638 100 A3 3 0 0 1 105.82433401843711 98.58975779269961 Z" transform="translate(-30 -12)" fill="black" stroke="black" stroke-width="6" stroke-linejoin="round"/></mask></defs><g mask="url(#R-mono-black-standard-gap)"><path d="M3.46 -40 H67 A39 39 0 0 1 67 38 H36.81354211962983 V84.54 Q36.81354211962983 88 33.35354211962983 88 H3.46 Q0 88 0 84.54 V-36.54 Q0 -40 3.46 -40 Z M36.81354211962983 -14 V12 H67 A13 13 0 0 0 67 -14 Z" fill="currentColor" fill-rule="evenodd"/></g><path d="M77.18032292691355 52.74975779269961 A3 3 0 0 1 79.72446721538282 48.16 L108.02474889607315 48.16 A3 3 0 0 1 110.56889318454242 49.57024220730038 L139.21290427606598 95.41024220730039 A3 3 0 0 1 136.6687599875967 100 L108.36847830690638 100 A3 3 0 0 1 105.82433401843711 98.58975779269961 Z" transform="translate(-30 -12)" fill="currentColor"/></svg>';
35
35
  var DESIGN_ICON = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="-22.599456503942804 -56 200 200"><title>Anyvia X</title><defs><mask id="X-mono-black-standard-gap" maskUnits="userSpaceOnUse" x="-100" y="-100" width="400" height="400"><rect x="-100" y="-100" width="400" height="400" fill="white"/><path d="M77.18032292691355 52.74975779269961 A3 3 0 0 1 79.72446721538282 48.16 L108.02474889607315 48.16 A3 3 0 0 1 110.56889318454242 49.57024220730038 L139.21290427606598 95.41024220730039 A3 3 0 0 1 136.6687599875967 100 L108.36847830690638 100 A3 3 0 0 1 105.82433401843711 98.58975779269961 Z" transform="translate(-12 0)" fill="black" stroke="black" stroke-width="6" stroke-linejoin="round"/></mask></defs><g mask="url(#X-mono-black-standard-gap)"><path d="M89.67202319894069 -10.373520654246889 A3.46 3.46 0 0 1 92.60626961164192 -12 L127.85608887910462 -12 A3.46 3.46 0 0 1 130.79033529180586 -6.706479345753113 L65.12906379317373 98.37352065424689 A3.46 3.46 0 0 1 62.19481738047249 100 L26.94499811300981 100 A3.46 3.46 0 0 1 24.010751700308575 94.70647934575311 Z" fill="currentColor"/><path d="M28.027942559241307 -6.706479345753113 A3.46 3.46 0 0 1 30.96218897194254 -12 L58.17762652153973 -12 A3.46 3.46 0 0 1 61.111872934240964 -10.373520654246889 L91.78046072595076 38.70647934575311 A3.46 3.46 0 0 1 88.84621431324952 44 L61.63077676365233 44 A3.46 3.46 0 0 1 58.69653035095109 42.37352065424689 Z" fill="currentColor"/></g><path d="M77.18032292691355 52.74975779269961 A3 3 0 0 1 79.72446721538282 48.16 L108.02474889607315 48.16 A3 3 0 0 1 110.56889318454242 49.57024220730038 L139.21290427606598 95.41024220730039 A3 3 0 0 1 136.6687599875967 100 L108.36847830690638 100 A3 3 0 0 1 105.82433401843711 98.58975779269961 Z" transform="translate(-12 0)" fill="currentColor"/></svg>';
36
36
 
37
+ // client/sidebar-status.ts
38
+ var POLL_MS = 5e3;
39
+ function countTopics(topics) {
40
+ const counts = { active: 0, completed: 0, failed: 0 };
41
+ for (const topic of topics) {
42
+ switch (topic.status) {
43
+ case "queued":
44
+ case "uploading":
45
+ case "submitted":
46
+ case "waiting":
47
+ case "answered":
48
+ case "adjudicating":
49
+ counts.active += 1;
50
+ break;
51
+ case "completed":
52
+ counts.completed += 1;
53
+ break;
54
+ case "failed":
55
+ case "stalled":
56
+ counts.failed += 1;
57
+ break;
58
+ default:
59
+ break;
60
+ }
61
+ }
62
+ return counts;
63
+ }
64
+ function badgeText(counts) {
65
+ const parts = [];
66
+ if (counts.active > 0) parts.push(`${counts.active} \u8FDB\u884C`);
67
+ if (counts.completed > 0) parts.push(`${counts.completed} \u5B8C\u6210`);
68
+ if (counts.failed > 0) parts.push(`${counts.failed} \u5931\u8D25`);
69
+ return parts.join(" \xB7 ");
70
+ }
71
+ function mountSidebarStatus(options) {
72
+ if (typeof document === "undefined") return () => {
73
+ };
74
+ let badge;
75
+ let text = "";
76
+ let disposed = false;
77
+ let polling = false;
78
+ let attachTimer;
79
+ let observer;
80
+ const rowFor = () => {
81
+ const buttons = document.querySelectorAll("button[aria-label]");
82
+ for (const button of buttons) {
83
+ if (button.getAttribute("aria-label") === options.label) return button;
84
+ }
85
+ return void 0;
86
+ };
87
+ const applyText = (next) => {
88
+ text = next;
89
+ if (badge !== void 0 && badge.textContent !== text) badge.textContent = text;
90
+ ensureMounted();
91
+ };
92
+ const ensureMounted = () => {
93
+ if (disposed) return;
94
+ if (text === "") {
95
+ badge?.remove();
96
+ badge = void 0;
97
+ return;
98
+ }
99
+ const row = rowFor();
100
+ if (row === void 0) return;
101
+ const title = row.querySelector('[class*="panelTitle"]');
102
+ if (title === null) {
103
+ badge?.remove();
104
+ badge = void 0;
105
+ return;
106
+ }
107
+ if (badge !== void 0 && badge.parentElement === row) {
108
+ if (badge.textContent !== text) badge.textContent = text;
109
+ return;
110
+ }
111
+ badge = document.createElement("span");
112
+ badge.dataset.dshProBadge = options.panelId;
113
+ badge.textContent = text;
114
+ badge.setAttribute("aria-hidden", "true");
115
+ badge.style.cssText = [
116
+ "margin-left:auto",
117
+ "display:inline-flex",
118
+ "align-items:center",
119
+ "flex:0 0 auto",
120
+ "font-size:10px",
121
+ "line-height:1",
122
+ "opacity:.62",
123
+ "white-space:nowrap",
124
+ "font-variant-numeric:tabular-nums",
125
+ "pointer-events:none"
126
+ ].join(";");
127
+ row.appendChild(badge);
128
+ };
129
+ const refresh = async () => {
130
+ if (disposed || polling) return;
131
+ polling = true;
132
+ try {
133
+ const response = await fetch(`${options.routes}/topics`, { headers: { accept: "application/json" } });
134
+ if (!response.ok) return;
135
+ const data = await response.json();
136
+ applyText(badgeText(countTopics(data.topics ?? [])));
137
+ } catch {
138
+ } finally {
139
+ polling = false;
140
+ }
141
+ };
142
+ observer = new MutationObserver(() => {
143
+ if (attachTimer !== void 0) return;
144
+ attachTimer = window.setTimeout(() => {
145
+ attachTimer = void 0;
146
+ ensureMounted();
147
+ }, 100);
148
+ });
149
+ observer.observe(document.body, { childList: true, subtree: true });
150
+ const timer = window.setInterval(() => {
151
+ void refresh();
152
+ }, POLL_MS);
153
+ void refresh();
154
+ return () => {
155
+ disposed = true;
156
+ window.clearInterval(timer);
157
+ if (attachTimer !== void 0) window.clearTimeout(attachTimer);
158
+ observer?.disconnect();
159
+ badge?.remove();
160
+ badge = void 0;
161
+ };
162
+ }
163
+
37
164
  // client/panel.tsx
38
- var POLL_MS = 3e3;
165
+ var POLL_MS2 = 3e3;
39
166
  var config = {
40
167
  routes: "/chatgpt-pro-research",
41
168
  panelId: "chatgpt-pro-research",
@@ -126,7 +253,7 @@ function Panel() {
126
253
  void loadList();
127
254
  const timer = setInterval(() => {
128
255
  void loadList();
129
- }, POLL_MS);
256
+ }, POLL_MS2);
130
257
  return () => clearInterval(timer);
131
258
  }, [loadList]);
132
259
  (0, import_react.useEffect)(() => {
@@ -134,7 +261,7 @@ function Panel() {
134
261
  void loadDetail(selected);
135
262
  const timer = setInterval(() => {
136
263
  void loadDetail(selected);
137
- }, POLL_MS);
264
+ }, POLL_MS2);
138
265
  return () => clearInterval(timer);
139
266
  }, [selected, loadDetail, topics]);
140
267
  const act = (0, import_react.useCallback)(async (action, conclusion) => {
@@ -315,6 +442,8 @@ function Icon({ kind }) {
315
442
  function apply(ctx) {
316
443
  ctx.slots.inject("main", () => ctx.slots.register({ name: "main", key: config.panelId }, () => (0, import_react.createElement)(Panel)));
317
444
  ctx.slots.inject("sidebar.panellist", () => ctx.slots.register({ name: "sidebar.panellist", id: config.panelId, order: 21, label: () => config.label }, () => (0, import_react.createElement)(Icon, { kind: config.icon })));
445
+ const disposeBadge = mountSidebarStatus({ label: config.label, routes: config.routes, panelId: config.panelId });
446
+ if (typeof ctx.effect === "function") ctx.effect(() => disposeBadge, `${config.panelId}: sidebar status badge`);
318
447
  }
319
448
 
320
449
  // client/index.tsx
package/lib/index.js CHANGED
@@ -733,7 +733,9 @@ var ResearchEngine = class {
733
733
  try {
734
734
  return await this.options.transport.fetch({ submissionId });
735
735
  } catch (error) {
736
- if (polled.answer !== void 0) return { answer: polled.answer };
736
+ if (polled.answer !== void 0) {
737
+ return { answer: polled.answer.startsWith("image:") ? "" : polled.answer };
738
+ }
737
739
  throw error;
738
740
  }
739
741
  }
@@ -932,6 +934,16 @@ function advanceCompletion(previous, text, streaming) {
932
934
  return { lastText: text, stable: 1, answered: false };
933
935
  }
934
936
 
937
+ // ../transport/src/assets.ts
938
+ function parseDataUrlImage(source) {
939
+ const match = /^data:([^;,]+);base64,(.*)$/s.exec(source);
940
+ if (match === null) return void 0;
941
+ const mime = match[1];
942
+ const base64 = match[2];
943
+ if (mime === void 0 || base64 === void 0) return void 0;
944
+ return { mime, base64: base64.replace(/\s/g, "") };
945
+ }
946
+
935
947
  // ../transport/src/chatgpt.ts
936
948
  function sha256(value) {
937
949
  return createHash3("sha256").update(value, "utf8").digest("hex");
@@ -1140,6 +1152,17 @@ ${attachments.join("\n")}`);
1140
1152
  const images = [];
1141
1153
  for (const [index, source] of sources.slice(0, 8).entries()) {
1142
1154
  try {
1155
+ if (source.startsWith("data:")) {
1156
+ const parsed = parseDataUrlImage(source);
1157
+ if (parsed === void 0) continue;
1158
+ if (Buffer.from(parsed.base64, "base64").byteLength < 2e4) continue;
1159
+ images.push({
1160
+ name: `image-${String(index + 1).padStart(2, "0")}.${extensionFor(parsed.mime)}`,
1161
+ mime: parsed.mime,
1162
+ base64: parsed.base64
1163
+ });
1164
+ continue;
1165
+ }
1143
1166
  if (source.startsWith("blob:")) {
1144
1167
  const dataUrl = await page.evaluate(async (src) => {
1145
1168
  const response = await fetch(src);
@@ -1237,8 +1260,8 @@ ${attachments.join("\n")}`);
1237
1260
  async richAnswerText(page) {
1238
1261
  const text = await this.lastAssistantText(page);
1239
1262
  if (text !== "") return text;
1240
- const images = await page.evaluate(() => [...document.querySelectorAll("main img")].filter((image) => image.naturalWidth >= 300).map((image) => image.src));
1241
- return images.length > 0 ? `image:${images.join("|")}` : "";
1263
+ const images = await page.evaluate(() => [...document.querySelectorAll("main img")].filter((image) => image.naturalWidth >= 300).map((image) => `${image.naturalWidth}x${image.naturalHeight}`));
1264
+ return images.length > 0 ? `image:${images.length}:${images.join(",")}` : "";
1242
1265
  }
1243
1266
  async lastAssistantText(page) {
1244
1267
  const messages = page.locator(SELECTORS.assistantMessage);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chatgpt-pro-research",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
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",