@apmantza/greedysearch-pi 1.6.2 → 1.6.4
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/CHANGELOG.md +11 -0
- package/LICENSE +21 -0
- package/index.ts +421 -421
- package/package.json +8 -1
- package/src/formatters/results.ts +207 -207
- package/cdp.mjs +0 -1004
- package/coding-task.mjs +0 -392
- package/extractors/bing-copilot.mjs +0 -167
- package/extractors/common.mjs +0 -237
- package/extractors/consent.mjs +0 -273
- package/extractors/gemini.mjs +0 -163
- package/extractors/google-ai.mjs +0 -156
- package/extractors/perplexity.mjs +0 -128
- package/extractors/selectors.mjs +0 -52
- package/launch.mjs +0 -288
- package/search.mjs +0 -1242
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@apmantza/greedysearch-pi",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.4",
|
|
4
4
|
"description": "Pi extension: multi-engine AI search (Perplexity, Bing Copilot, Google AI) via browser automation — NO API KEYS needed. Extracts answers with sources, optional Gemini synthesis. Grounded AI answers from real browser interactions.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
@@ -11,6 +11,13 @@
|
|
|
11
11
|
"url": "git+https://github.com/apmantza/GreedySearch-pi.git"
|
|
12
12
|
},
|
|
13
13
|
"license": "MIT",
|
|
14
|
+
"files": [
|
|
15
|
+
"index.ts",
|
|
16
|
+
"src/",
|
|
17
|
+
"skills/",
|
|
18
|
+
"CHANGELOG.md",
|
|
19
|
+
"README.md"
|
|
20
|
+
],
|
|
14
21
|
"pi": {
|
|
15
22
|
"extensions": [
|
|
16
23
|
"./index.ts"
|
|
@@ -1,207 +1,207 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Search results formatters
|
|
3
|
-
* Extracted from index.ts
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { formatEngineName, humanizeSourceType } from "../utils/helpers.js";
|
|
7
|
-
import { renderSynthesis } from "./synthesis.js";
|
|
8
|
-
import { formatSourceLine, renderSourceEvidence } from "./sources.js";
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Format search results based on engine type
|
|
12
|
-
*/
|
|
13
|
-
export function formatResults(
|
|
14
|
-
engine: string,
|
|
15
|
-
data: Record<string, unknown>,
|
|
16
|
-
): string {
|
|
17
|
-
const lines: string[] = [];
|
|
18
|
-
|
|
19
|
-
if (engine === "all") {
|
|
20
|
-
return formatAllEnginesResult(data, lines);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
return formatSingleEngineResult(data, lines);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Format multi-engine results with synthesis
|
|
28
|
-
*/
|
|
29
|
-
function formatAllEnginesResult(
|
|
30
|
-
data: Record<string, unknown>,
|
|
31
|
-
lines: string[],
|
|
32
|
-
): string {
|
|
33
|
-
const synthesis = data._synthesis as Record<string, unknown> | undefined;
|
|
34
|
-
const dedupedSources = data._sources as
|
|
35
|
-
| Array<Record<string, unknown>>
|
|
36
|
-
| undefined;
|
|
37
|
-
|
|
38
|
-
// If we have a synthesis answer, render it
|
|
39
|
-
if (synthesis?.answer) {
|
|
40
|
-
renderSynthesis(lines, synthesis, dedupedSources || [], 6);
|
|
41
|
-
lines.push("*Synthesized from Perplexity, Bing Copilot, and Google AI*\n");
|
|
42
|
-
return lines.join("\n").trim();
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// Fallback: render individual engine results
|
|
46
|
-
for (const [eng, result] of Object.entries(data)) {
|
|
47
|
-
if (eng.startsWith("_")) continue;
|
|
48
|
-
lines.push(`\n## ${formatEngineName(eng)}`);
|
|
49
|
-
formatEngineResult(result as Record<string, unknown>, lines, 3);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return lines.join("\n").trim();
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Format single engine result
|
|
57
|
-
*/
|
|
58
|
-
function formatSingleEngineResult(
|
|
59
|
-
data: Record<string, unknown>,
|
|
60
|
-
lines: string[],
|
|
61
|
-
): string {
|
|
62
|
-
formatEngineResult(data, lines, 5);
|
|
63
|
-
return lines.join("\n").trim();
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Format a single engine's result (answer + sources)
|
|
68
|
-
*/
|
|
69
|
-
function formatEngineResult(
|
|
70
|
-
data: Record<string, unknown>,
|
|
71
|
-
lines: string[],
|
|
72
|
-
maxSources: number,
|
|
73
|
-
): void {
|
|
74
|
-
if (data.error) {
|
|
75
|
-
lines.push(`Error: ${data.error}`);
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
if (data.answer) {
|
|
80
|
-
lines.push(String(data.answer));
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const sources = data.sources as Array<Record<string, string>> | undefined;
|
|
84
|
-
if (Array.isArray(sources) && sources.length > 0) {
|
|
85
|
-
lines.push("\nSources:");
|
|
86
|
-
for (const s of sources.slice(0, maxSources)) {
|
|
87
|
-
lines.push(`- [${s.title || s.url}](${s.url})`);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Format deep research results with confidence metrics
|
|
94
|
-
*/
|
|
95
|
-
export function formatDeepResearch(data: Record<string, unknown>): string {
|
|
96
|
-
const lines: string[] = [];
|
|
97
|
-
const confidence = data._confidence as Record<string, unknown> | undefined;
|
|
98
|
-
const dedupedSources = data._sources as
|
|
99
|
-
| Array<Record<string, unknown>>
|
|
100
|
-
| undefined;
|
|
101
|
-
const synthesis = data._synthesis as Record<string, unknown> | undefined;
|
|
102
|
-
|
|
103
|
-
lines.push("# Deep Research Report\n");
|
|
104
|
-
|
|
105
|
-
if (confidence) {
|
|
106
|
-
formatConfidenceSection(lines, confidence);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
if (synthesis?.answer) {
|
|
110
|
-
renderSynthesis(lines, synthesis, dedupedSources || [], 8);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
formatEnginePerspectives(lines, data);
|
|
114
|
-
formatSourceRegistry(lines, dedupedSources || []);
|
|
115
|
-
|
|
116
|
-
return lines.join("\n").trim();
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Format confidence section with metrics
|
|
121
|
-
*/
|
|
122
|
-
function formatConfidenceSection(
|
|
123
|
-
lines: string[],
|
|
124
|
-
confidence: Record<string, unknown>,
|
|
125
|
-
): void {
|
|
126
|
-
const enginesResponded = (confidence.enginesResponded as string[]) || [];
|
|
127
|
-
const enginesFailed = (confidence.enginesFailed as string[]) || [];
|
|
128
|
-
const agreementLevel = String(confidence.agreementLevel || "mixed");
|
|
129
|
-
const firstPartySourceCount = Number(confidence.firstPartySourceCount || 0);
|
|
130
|
-
const sourceTypeBreakdown = confidence.sourceTypeBreakdown as
|
|
131
|
-
| Record<string, number>
|
|
132
|
-
| undefined;
|
|
133
|
-
|
|
134
|
-
lines.push("## Confidence\n");
|
|
135
|
-
lines.push(`- Agreement: ${formatEngineName(agreementLevel)}`);
|
|
136
|
-
lines.push(
|
|
137
|
-
`- Engines responded: ${enginesResponded.map(formatEngineName).join(", ") || "none"}`,
|
|
138
|
-
);
|
|
139
|
-
|
|
140
|
-
if (enginesFailed.length > 0) {
|
|
141
|
-
lines.push(
|
|
142
|
-
`- Engines failed: ${enginesFailed.map(formatEngineName).join(", ")}`,
|
|
143
|
-
);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
lines.push(
|
|
147
|
-
`- Top source consensus: ${confidence.topSourceConsensus || 0}/3 engines`,
|
|
148
|
-
);
|
|
149
|
-
lines.push(`- Total unique sources: ${confidence.sourcesCount || 0}`);
|
|
150
|
-
lines.push(`- Official sources: ${confidence.officialSourceCount || 0}`);
|
|
151
|
-
lines.push(`- First-party sources: ${firstPartySourceCount}`);
|
|
152
|
-
lines.push(
|
|
153
|
-
`- Fetch success rate: ${confidence.fetchedSourceSuccessRate || 0}`,
|
|
154
|
-
);
|
|
155
|
-
|
|
156
|
-
if (sourceTypeBreakdown && Object.keys(sourceTypeBreakdown).length > 0) {
|
|
157
|
-
lines.push(
|
|
158
|
-
`- Source mix: ${Object.entries(sourceTypeBreakdown)
|
|
159
|
-
.map(([type, count]) => `${humanizeSourceType(type)} ${count}`)
|
|
160
|
-
.join(", ")}`,
|
|
161
|
-
);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
lines.push("");
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* Format engine perspectives section
|
|
169
|
-
*/
|
|
170
|
-
function formatEnginePerspectives(
|
|
171
|
-
lines: string[],
|
|
172
|
-
data: Record<string, unknown>,
|
|
173
|
-
): void {
|
|
174
|
-
lines.push("## Engine Perspectives\n");
|
|
175
|
-
|
|
176
|
-
for (const engine of ["perplexity", "bing", "google"]) {
|
|
177
|
-
const r = data[engine] as Record<string, unknown> | undefined;
|
|
178
|
-
if (!r) continue;
|
|
179
|
-
|
|
180
|
-
lines.push(`### ${formatEngineName(engine)}`);
|
|
181
|
-
|
|
182
|
-
if (r.error) {
|
|
183
|
-
lines.push(`⚠️ Error: ${r.error}`);
|
|
184
|
-
} else if (r.answer) {
|
|
185
|
-
lines.push(String(r.answer).slice(0, 2000));
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
lines.push("");
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
/**
|
|
193
|
-
* Format source registry section
|
|
194
|
-
*/
|
|
195
|
-
function formatSourceRegistry(
|
|
196
|
-
lines: string[],
|
|
197
|
-
sources: Array<Record<string, unknown>>,
|
|
198
|
-
): void {
|
|
199
|
-
if (sources.length === 0) return;
|
|
200
|
-
|
|
201
|
-
lines.push("## Source Registry\n");
|
|
202
|
-
for (const source of sources) {
|
|
203
|
-
lines.push(formatSourceLine(source));
|
|
204
|
-
renderSourceEvidence(lines, source);
|
|
205
|
-
}
|
|
206
|
-
lines.push("");
|
|
207
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Search results formatters
|
|
3
|
+
* Extracted from index.ts
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { formatEngineName, humanizeSourceType } from "../utils/helpers.js";
|
|
7
|
+
import { renderSynthesis } from "./synthesis.js";
|
|
8
|
+
import { formatSourceLine, renderSourceEvidence } from "./sources.js";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Format search results based on engine type
|
|
12
|
+
*/
|
|
13
|
+
export function formatResults(
|
|
14
|
+
engine: string,
|
|
15
|
+
data: Record<string, unknown>,
|
|
16
|
+
): string {
|
|
17
|
+
const lines: string[] = [];
|
|
18
|
+
|
|
19
|
+
if (engine === "all") {
|
|
20
|
+
return formatAllEnginesResult(data, lines);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return formatSingleEngineResult(data, lines);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Format multi-engine results with synthesis
|
|
28
|
+
*/
|
|
29
|
+
function formatAllEnginesResult(
|
|
30
|
+
data: Record<string, unknown>,
|
|
31
|
+
lines: string[],
|
|
32
|
+
): string {
|
|
33
|
+
const synthesis = data._synthesis as Record<string, unknown> | undefined;
|
|
34
|
+
const dedupedSources = data._sources as
|
|
35
|
+
| Array<Record<string, unknown>>
|
|
36
|
+
| undefined;
|
|
37
|
+
|
|
38
|
+
// If we have a synthesis answer, render it
|
|
39
|
+
if (synthesis?.answer) {
|
|
40
|
+
renderSynthesis(lines, synthesis, dedupedSources || [], 6);
|
|
41
|
+
lines.push("*Synthesized from Perplexity, Bing Copilot, and Google AI*\n");
|
|
42
|
+
return lines.join("\n").trim();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Fallback: render individual engine results
|
|
46
|
+
for (const [eng, result] of Object.entries(data)) {
|
|
47
|
+
if (eng.startsWith("_")) continue;
|
|
48
|
+
lines.push(`\n## ${formatEngineName(eng)}`);
|
|
49
|
+
formatEngineResult(result as Record<string, unknown>, lines, 3);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return lines.join("\n").trim();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Format single engine result
|
|
57
|
+
*/
|
|
58
|
+
function formatSingleEngineResult(
|
|
59
|
+
data: Record<string, unknown>,
|
|
60
|
+
lines: string[],
|
|
61
|
+
): string {
|
|
62
|
+
formatEngineResult(data, lines, 5);
|
|
63
|
+
return lines.join("\n").trim();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Format a single engine's result (answer + sources)
|
|
68
|
+
*/
|
|
69
|
+
function formatEngineResult(
|
|
70
|
+
data: Record<string, unknown>,
|
|
71
|
+
lines: string[],
|
|
72
|
+
maxSources: number,
|
|
73
|
+
): void {
|
|
74
|
+
if (data.error) {
|
|
75
|
+
lines.push(`Error: ${data.error}`);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (data.answer) {
|
|
80
|
+
lines.push(String(data.answer));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const sources = data.sources as Array<Record<string, string>> | undefined;
|
|
84
|
+
if (Array.isArray(sources) && sources.length > 0) {
|
|
85
|
+
lines.push("\nSources:");
|
|
86
|
+
for (const s of sources.slice(0, maxSources)) {
|
|
87
|
+
lines.push(`- [${s.title || s.url}](${s.url})`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Format deep research results with confidence metrics
|
|
94
|
+
*/
|
|
95
|
+
export function formatDeepResearch(data: Record<string, unknown>): string {
|
|
96
|
+
const lines: string[] = [];
|
|
97
|
+
const confidence = data._confidence as Record<string, unknown> | undefined;
|
|
98
|
+
const dedupedSources = data._sources as
|
|
99
|
+
| Array<Record<string, unknown>>
|
|
100
|
+
| undefined;
|
|
101
|
+
const synthesis = data._synthesis as Record<string, unknown> | undefined;
|
|
102
|
+
|
|
103
|
+
lines.push("# Deep Research Report\n");
|
|
104
|
+
|
|
105
|
+
if (confidence) {
|
|
106
|
+
formatConfidenceSection(lines, confidence);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (synthesis?.answer) {
|
|
110
|
+
renderSynthesis(lines, synthesis, dedupedSources || [], 8);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
formatEnginePerspectives(lines, data);
|
|
114
|
+
formatSourceRegistry(lines, dedupedSources || []);
|
|
115
|
+
|
|
116
|
+
return lines.join("\n").trim();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Format confidence section with metrics
|
|
121
|
+
*/
|
|
122
|
+
function formatConfidenceSection(
|
|
123
|
+
lines: string[],
|
|
124
|
+
confidence: Record<string, unknown>,
|
|
125
|
+
): void {
|
|
126
|
+
const enginesResponded = (confidence.enginesResponded as string[]) || [];
|
|
127
|
+
const enginesFailed = (confidence.enginesFailed as string[]) || [];
|
|
128
|
+
const agreementLevel = String(confidence.agreementLevel || "mixed");
|
|
129
|
+
const firstPartySourceCount = Number(confidence.firstPartySourceCount || 0);
|
|
130
|
+
const sourceTypeBreakdown = confidence.sourceTypeBreakdown as
|
|
131
|
+
| Record<string, number>
|
|
132
|
+
| undefined;
|
|
133
|
+
|
|
134
|
+
lines.push("## Confidence\n");
|
|
135
|
+
lines.push(`- Agreement: ${formatEngineName(agreementLevel)}`);
|
|
136
|
+
lines.push(
|
|
137
|
+
`- Engines responded: ${enginesResponded.map(formatEngineName).join(", ") || "none"}`,
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
if (enginesFailed.length > 0) {
|
|
141
|
+
lines.push(
|
|
142
|
+
`- Engines failed: ${enginesFailed.map(formatEngineName).join(", ")}`,
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
lines.push(
|
|
147
|
+
`- Top source consensus: ${confidence.topSourceConsensus || 0}/3 engines`,
|
|
148
|
+
);
|
|
149
|
+
lines.push(`- Total unique sources: ${confidence.sourcesCount || 0}`);
|
|
150
|
+
lines.push(`- Official sources: ${confidence.officialSourceCount || 0}`);
|
|
151
|
+
lines.push(`- First-party sources: ${firstPartySourceCount}`);
|
|
152
|
+
lines.push(
|
|
153
|
+
`- Fetch success rate: ${confidence.fetchedSourceSuccessRate || 0}`,
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
if (sourceTypeBreakdown && Object.keys(sourceTypeBreakdown).length > 0) {
|
|
157
|
+
lines.push(
|
|
158
|
+
`- Source mix: ${Object.entries(sourceTypeBreakdown)
|
|
159
|
+
.map(([type, count]) => `${humanizeSourceType(type)} ${count}`)
|
|
160
|
+
.join(", ")}`,
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
lines.push("");
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Format engine perspectives section
|
|
169
|
+
*/
|
|
170
|
+
function formatEnginePerspectives(
|
|
171
|
+
lines: string[],
|
|
172
|
+
data: Record<string, unknown>,
|
|
173
|
+
): void {
|
|
174
|
+
lines.push("## Engine Perspectives\n");
|
|
175
|
+
|
|
176
|
+
for (const engine of ["perplexity", "bing", "google"]) {
|
|
177
|
+
const r = data[engine] as Record<string, unknown> | undefined;
|
|
178
|
+
if (!r) continue;
|
|
179
|
+
|
|
180
|
+
lines.push(`### ${formatEngineName(engine)}`);
|
|
181
|
+
|
|
182
|
+
if (r.error) {
|
|
183
|
+
lines.push(`⚠️ Error: ${r.error}`);
|
|
184
|
+
} else if (r.answer) {
|
|
185
|
+
lines.push(String(r.answer).slice(0, 2000));
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
lines.push("");
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Format source registry section
|
|
194
|
+
*/
|
|
195
|
+
function formatSourceRegistry(
|
|
196
|
+
lines: string[],
|
|
197
|
+
sources: Array<Record<string, unknown>>,
|
|
198
|
+
): void {
|
|
199
|
+
if (sources.length === 0) return;
|
|
200
|
+
|
|
201
|
+
lines.push("## Source Registry\n");
|
|
202
|
+
for (const source of sources) {
|
|
203
|
+
lines.push(formatSourceLine(source));
|
|
204
|
+
renderSourceEvidence(lines, source);
|
|
205
|
+
}
|
|
206
|
+
lines.push("");
|
|
207
|
+
}
|