mcp-scraper 0.97.1 → 0.99.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/CHANGELOG.md +11 -2434
- package/README.md +16 -412
- package/dist/bin/api-server.js +3 -2
- package/dist/bin/mcp-scraper-cli.js +33 -27
- package/dist/bin/mcp-scraper-core.js +9 -1
- package/dist/bin/mcp-scraper-install.js +17 -2
- package/dist/bin/mcp-stdio-server.js +9 -1
- package/dist/bin/paa-harvest.js +8 -1
- package/dist/index.cjs +5 -21830
- package/dist/index.d.ts +14 -607
- package/dist/index.js +5 -39
- package/dist/transport.d.ts +35 -0
- package/package.json +11 -51
- package/THIRD_PARTY_NOTICES.html +0 -203
- package/dist/actions-5X5CQAKO.js +0 -1
- package/dist/artifacts-4ZDNP2JW.js +0 -1
- package/dist/artifacts-N62OPIT5.js +0 -1
- package/dist/artifacts-PSADD5OC.js +0 -1
- package/dist/artifacts-RUR3OHAK.js +0 -1
- package/dist/blob-store-GIH26EDO.js +0 -1
- package/dist/bundle-GS657HXG.js +0 -1
- package/dist/chunk-24YDQ3EO.js +0 -5
- package/dist/chunk-2L7CHN5N.js +0 -4
- package/dist/chunk-3F7AFM53.js +0 -1
- package/dist/chunk-4AEW53RU.js +0 -954
- package/dist/chunk-4HZXATHB.js +0 -27
- package/dist/chunk-4YZ74SJ6.js +0 -1
- package/dist/chunk-4ZVO6GX4.js +0 -1
- package/dist/chunk-55ZANTHL.js +0 -3
- package/dist/chunk-5RQWFYXY.js +0 -930
- package/dist/chunk-5TQINRZN.js +0 -12
- package/dist/chunk-5UQAL7HV.js +0 -41
- package/dist/chunk-6D25Q5BR.js +0 -21728
- package/dist/chunk-6N5EC7ZO.js +0 -1
- package/dist/chunk-7POHNTVW.js +0 -1
- package/dist/chunk-7SR7MEU5.js +0 -62
- package/dist/chunk-ANQ4JJVI.js +0 -71
- package/dist/chunk-B3JRL45T.js +0 -1
- package/dist/chunk-B4ZKPLSP.js +0 -1
- package/dist/chunk-B5Y26E3A.js +0 -1
- package/dist/chunk-C3JWX6KM.js +0 -10
- package/dist/chunk-C7MGJUTU.js +0 -1
- package/dist/chunk-CNSVZTTT.js +0 -13
- package/dist/chunk-EYGA6LOU.js +0 -1
- package/dist/chunk-F3M7INSN.js +0 -41
- package/dist/chunk-FLAWC5PF.js +0 -5
- package/dist/chunk-FM7BX2LU.js +0 -159
- package/dist/chunk-HCK6PLM3.js +0 -3
- package/dist/chunk-HNNYXMSI.js +0 -1
- package/dist/chunk-IFPION5D.js +0 -121
- package/dist/chunk-IPQOBOFY.js +0 -1
- package/dist/chunk-J7XDXIB6.js +0 -163
- package/dist/chunk-JHDD6R3O.js +0 -8
- package/dist/chunk-JMZIDHYQ.js +0 -2
- package/dist/chunk-K3VDGJNK.js +0 -1
- package/dist/chunk-KBZY2OGP.js +0 -11
- package/dist/chunk-KJQXUZ4Y.js +0 -4
- package/dist/chunk-KXJCGV3H.js +0 -1
- package/dist/chunk-NALXKTUU.js +0 -1
- package/dist/chunk-NNOX3FYG.js +0 -198
- package/dist/chunk-NQFTETVC.js +0 -189
- package/dist/chunk-OILOHQNL.js +0 -2
- package/dist/chunk-OLYPLX5C.js +0 -1
- package/dist/chunk-P6CDR3RM.js +0 -1
- package/dist/chunk-PKQHHCO4.js +0 -20
- package/dist/chunk-QCBCOEH5.js +0 -1
- package/dist/chunk-QER2ELZP.js +0 -1
- package/dist/chunk-RDHGQ2WZ.js +0 -1
- package/dist/chunk-SDQW7IJN.js +0 -1
- package/dist/chunk-SEZETSB3.js +0 -1
- package/dist/chunk-SJNSTO3C.js +0 -42
- package/dist/chunk-TZYITYL4.js +0 -2
- package/dist/chunk-UIOSADPB.js +0 -1718
- package/dist/chunk-UNEJYZ4T.js +0 -102
- package/dist/chunk-UUJYICAJ.js +0 -1
- package/dist/chunk-VVSHZV5S.js +0 -73
- package/dist/chunk-WNUXMPVX.js +0 -1
- package/dist/chunk-XNTAHMPA.js +0 -218
- package/dist/chunk-ZDECILF6.js +0 -16
- package/dist/chunk-ZNPGM3XE.js +0 -4
- package/dist/compatibility-UPPKZ4KY.js +0 -1
- package/dist/contracts-GUXGFCVV.js +0 -1
- package/dist/core-schema-Y47YFKH2.js +0 -899
- package/dist/direct-completion-ZVPPEBDU.js +0 -1
- package/dist/dispatch-GRMMPKQD.js +0 -1
- package/dist/editorial-reading-room/assets/app.js +0 -433
- package/dist/editorial-reading-room/assets/index.html +0 -139
- package/dist/editorial-reading-room/assets/styles.css +0 -1091
- package/dist/index.d.cts +0 -607
- package/dist/jobs-NRZHGQ7I.js +0 -1
- package/dist/media-artifacts-67CJWI7O.js +0 -1
- package/dist/metering-YHZ46GV4.js +0 -1
- package/dist/migrations-3AKEC5QE.js +0 -1
- package/dist/queue-2VKW7SGH.js +0 -1
- package/dist/reconciliation-QC6SILLR.js +0 -1
- package/dist/report-artifact-offload-PQNFDNPL.js +0 -1
- package/dist/repository-CYXVMYAI.js +0 -1
- package/dist/repository-UKDA4IWP.js +0 -1
- package/dist/server-EYMJROPX.js +0 -5544
- package/dist/settlement-47EUTSGX.js +0 -1
- package/dist/vendor-errors-647LSH6H.js +0 -1
- package/dist/worker-AWJF7UYR.js +0 -1
- package/public/.well-known/xray-install.json +0 -183
- package/public/agent-install.md +0 -200
package/dist/index.d.ts
CHANGED
|
@@ -1,607 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
interface VideoResult {
|
|
16
|
-
type: 'video' | 'short_video';
|
|
17
|
-
title: string;
|
|
18
|
-
channel: string;
|
|
19
|
-
platform: string;
|
|
20
|
-
duration: string;
|
|
21
|
-
url: string;
|
|
22
|
-
}
|
|
23
|
-
interface ForumResult {
|
|
24
|
-
title: string;
|
|
25
|
-
source: string;
|
|
26
|
-
url: string;
|
|
27
|
-
}
|
|
28
|
-
interface WhatPeopleSayingCard {
|
|
29
|
-
type: 'reddit' | 'facebook' | 'instagram' | 'tiktok' | 'youtube' | 'news' | 'unknown';
|
|
30
|
-
title: string;
|
|
31
|
-
url: string;
|
|
32
|
-
source: string;
|
|
33
|
-
platform: string;
|
|
34
|
-
popularComment: string | null;
|
|
35
|
-
engagement: string;
|
|
36
|
-
date: string;
|
|
37
|
-
duration: string | null;
|
|
38
|
-
authorNote: string | null;
|
|
39
|
-
}
|
|
40
|
-
interface AICitation {
|
|
41
|
-
text: string;
|
|
42
|
-
href: string;
|
|
43
|
-
rawUrl: string;
|
|
44
|
-
resolvedUrl: string | null;
|
|
45
|
-
linkType: SerpLinkType;
|
|
46
|
-
resolutionStatus: SerpLinkResolutionStatus;
|
|
47
|
-
}
|
|
48
|
-
interface AIOverviewResult {
|
|
49
|
-
detected: boolean;
|
|
50
|
-
text: string | null;
|
|
51
|
-
citations: AICitation[];
|
|
52
|
-
expanded?: boolean;
|
|
53
|
-
fullyExpanded?: boolean;
|
|
54
|
-
sections?: string[];
|
|
55
|
-
shareUrl?: string | null;
|
|
56
|
-
}
|
|
57
|
-
interface AIModeResult {
|
|
58
|
-
detected: boolean;
|
|
59
|
-
text: string | null;
|
|
60
|
-
citations: AICitation[];
|
|
61
|
-
}
|
|
62
|
-
type GoogleSurface = 'web' | 'aim' | 'unknown';
|
|
63
|
-
interface OrganicResult {
|
|
64
|
-
position: number;
|
|
65
|
-
title: string;
|
|
66
|
-
url: string;
|
|
67
|
-
rawUrl: string;
|
|
68
|
-
resolvedUrl: string | null;
|
|
69
|
-
linkType: SerpLinkType;
|
|
70
|
-
resolutionStatus: SerpLinkResolutionStatus;
|
|
71
|
-
domain: string;
|
|
72
|
-
cite: string | null;
|
|
73
|
-
snippet: string | null;
|
|
74
|
-
isRedditStyle: boolean;
|
|
75
|
-
inlineRating: {
|
|
76
|
-
value: string;
|
|
77
|
-
count: string;
|
|
78
|
-
} | null;
|
|
79
|
-
}
|
|
80
|
-
interface LocalPackBusiness {
|
|
81
|
-
position: number;
|
|
82
|
-
name: string;
|
|
83
|
-
cid: string | null;
|
|
84
|
-
rating: string | null;
|
|
85
|
-
reviewCount: string | null;
|
|
86
|
-
metadata: string[];
|
|
87
|
-
websiteUrl: string | null;
|
|
88
|
-
directionsUrl: string | null;
|
|
89
|
-
}
|
|
90
|
-
interface EntityRecord {
|
|
91
|
-
name: string;
|
|
92
|
-
kgId: string | null;
|
|
93
|
-
cid: string | null;
|
|
94
|
-
gcid: string | null;
|
|
95
|
-
}
|
|
96
|
-
interface EntityIds {
|
|
97
|
-
entities: EntityRecord[];
|
|
98
|
-
kgIds: string[];
|
|
99
|
-
cids: string[];
|
|
100
|
-
gcids: string[];
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
interface SerpPreviewQuestion {
|
|
104
|
-
question: string;
|
|
105
|
-
answer: string | null;
|
|
106
|
-
url: string | null;
|
|
107
|
-
sourceTitle?: string | null;
|
|
108
|
-
sourceName?: string | null;
|
|
109
|
-
displayLink?: string | null;
|
|
110
|
-
}
|
|
111
|
-
interface SerpFeatureItem {
|
|
112
|
-
title: string | null;
|
|
113
|
-
url: string | null;
|
|
114
|
-
description: string | null;
|
|
115
|
-
position: number | null;
|
|
116
|
-
}
|
|
117
|
-
interface SerpFeatureGroup {
|
|
118
|
-
name: string;
|
|
119
|
-
attributes: Record<string, string | number | boolean | null>;
|
|
120
|
-
items: SerpFeatureItem[];
|
|
121
|
-
}
|
|
122
|
-
interface FullSerpPageFeatures {
|
|
123
|
-
page: 1 | 2;
|
|
124
|
-
status: Record<string, SerpFeatureCompletenessState>;
|
|
125
|
-
paaPreview: SerpPreviewQuestion[];
|
|
126
|
-
additional: SerpFeatureGroup[];
|
|
127
|
-
localPack: LocalPackBusiness[];
|
|
128
|
-
forums: ForumResult[];
|
|
129
|
-
videos: VideoResult[];
|
|
130
|
-
whatPeopleSaying: WhatPeopleSayingCard[];
|
|
131
|
-
aiOverview: AIOverviewResult;
|
|
132
|
-
aiMode: AIModeResult;
|
|
133
|
-
entityIds: EntityIds;
|
|
134
|
-
}
|
|
135
|
-
interface FullSerpFeatures {
|
|
136
|
-
status: Record<string, SerpFeatureCompletenessState>;
|
|
137
|
-
paaPreview: SerpPreviewQuestion[];
|
|
138
|
-
additional: SerpFeatureGroup[];
|
|
139
|
-
pages: FullSerpPageFeatures[];
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
declare const HarvestOptionsSchema: z.ZodObject<{
|
|
143
|
-
query: z.ZodString;
|
|
144
|
-
location: z.ZodOptional<z.ZodString>;
|
|
145
|
-
gl: z.ZodDefault<z.ZodString>;
|
|
146
|
-
hl: z.ZodDefault<z.ZodString>;
|
|
147
|
-
device: z.ZodDefault<z.ZodEnum<{
|
|
148
|
-
desktop: "desktop";
|
|
149
|
-
mobile: "mobile";
|
|
150
|
-
}>>;
|
|
151
|
-
proxyMode: z.ZodDefault<z.ZodEnum<{
|
|
152
|
-
location: "location";
|
|
153
|
-
configured: "configured";
|
|
154
|
-
none: "none";
|
|
155
|
-
}>>;
|
|
156
|
-
proxyZip: z.ZodOptional<z.ZodString>;
|
|
157
|
-
keepDefaultProxy: z.ZodOptional<z.ZodBoolean>;
|
|
158
|
-
requireUsEgress: z.ZodOptional<z.ZodBoolean>;
|
|
159
|
-
maxAttempts: z.ZodOptional<z.ZodNumber>;
|
|
160
|
-
debug: z.ZodDefault<z.ZodBoolean>;
|
|
161
|
-
depth: z.ZodDefault<z.ZodNumber>;
|
|
162
|
-
maxQuestions: z.ZodDefault<z.ZodNumber>;
|
|
163
|
-
headless: z.ZodDefault<z.ZodBoolean>;
|
|
164
|
-
profileDir: z.ZodOptional<z.ZodString>;
|
|
165
|
-
proxy: z.ZodOptional<z.ZodString>;
|
|
166
|
-
\u006b\u0065\u0072\u006e\u0065\u006cApiKey: z.ZodOptional<z.ZodString>;
|
|
167
|
-
\u006b\u0065\u0072\u006e\u0065\u006cProxyId: z.ZodOptional<z.ZodString>;
|
|
168
|
-
\u006b\u0065\u0072\u006e\u0065\u006cProfileName: z.ZodOptional<z.ZodString>;
|
|
169
|
-
\u006b\u0065\u0072\u006e\u0065\u006cProfileSaveChanges: z.ZodOptional<z.ZodBoolean>;
|
|
170
|
-
\u006b\u0065\u0072\u006e\u0065\u006cStealth: z.ZodOptional<z.ZodBoolean>;
|
|
171
|
-
serpIdentity: z.ZodOptional<z.ZodString>;
|
|
172
|
-
\u006b\u0065\u0072\u006e\u0065\u006cProxyResolution: z.ZodOptional<z.ZodUnknown>;
|
|
173
|
-
outputDir: z.ZodDefault<z.ZodString>;
|
|
174
|
-
format: z.ZodDefault<z.ZodEnum<{
|
|
175
|
-
json: "json";
|
|
176
|
-
csv: "csv";
|
|
177
|
-
both: "both";
|
|
178
|
-
}>>;
|
|
179
|
-
serpOnly: z.ZodDefault<z.ZodBoolean>;
|
|
180
|
-
questionsOnly: z.ZodDefault<z.ZodBoolean>;
|
|
181
|
-
questionGrowthRecoveryRounds: z.ZodDefault<z.ZodNumber>;
|
|
182
|
-
pages: z.ZodDefault<z.ZodNumber>;
|
|
183
|
-
includeAllSerpFeatures: z.ZodDefault<z.ZodBoolean>;
|
|
184
|
-
includeLocalPack: z.ZodDefault<z.ZodBoolean>;
|
|
185
|
-
includeForums: z.ZodDefault<z.ZodBoolean>;
|
|
186
|
-
includeVideos: z.ZodDefault<z.ZodBoolean>;
|
|
187
|
-
includeAiOverview: z.ZodDefault<z.ZodBoolean>;
|
|
188
|
-
includeWhatPeopleSaying: z.ZodDefault<z.ZodBoolean>;
|
|
189
|
-
includeShortVideos: z.ZodDefault<z.ZodBoolean>;
|
|
190
|
-
recency: z.ZodOptional<z.ZodEnum<{
|
|
191
|
-
day: "day";
|
|
192
|
-
week: "week";
|
|
193
|
-
month: "month";
|
|
194
|
-
year: "year";
|
|
195
|
-
}>>;
|
|
196
|
-
softDeadlineMs: z.ZodOptional<z.ZodNumber>;
|
|
197
|
-
}, z.core.$strip>;
|
|
198
|
-
|
|
199
|
-
type PaaDiscoveryStatus = 'in_progress' | 'target_reached' | 'frontier_exhausted' | 'no_paa_observed' | 'interrupted' | 'recovery_exhausted';
|
|
200
|
-
type PaaTargetStatus = 'met' | 'not_met';
|
|
201
|
-
type PaaMaterialStatus = 'complete' | 'partial';
|
|
202
|
-
interface PaaRetryBreakdown {
|
|
203
|
-
controlRetries: number;
|
|
204
|
-
browserRetries: number;
|
|
205
|
-
providerFallbacks: number;
|
|
206
|
-
runtimeRecoveries: number;
|
|
207
|
-
idempotentReplays: number;
|
|
208
|
-
}
|
|
209
|
-
interface PaaLifecycleDiagnostics {
|
|
210
|
-
requestedQuestions: number;
|
|
211
|
-
questionsFound: number;
|
|
212
|
-
targetStatus: PaaTargetStatus;
|
|
213
|
-
discoveryStatus: PaaDiscoveryStatus;
|
|
214
|
-
materialStatus: PaaMaterialStatus;
|
|
215
|
-
completionReason: string;
|
|
216
|
-
automaticRetries: number;
|
|
217
|
-
retryBreakdown: PaaRetryBreakdown;
|
|
218
|
-
knownQuestions: number;
|
|
219
|
-
processedQuestions: number;
|
|
220
|
-
failedInteractions: number;
|
|
221
|
-
noGrowthConfirmations: number;
|
|
222
|
-
visibleControls?: number;
|
|
223
|
-
eligibleControls?: number;
|
|
224
|
-
dispatchedInteractions?: number;
|
|
225
|
-
confirmedInteractions?: number;
|
|
226
|
-
unconfirmedInteractions?: number;
|
|
227
|
-
}
|
|
228
|
-
type PaaInteractionOutcome = 'confirmed' | 'unconfirmed' | 'dispatch_failed';
|
|
229
|
-
type PaaInteractionSignal = 'expanded' | 'answer' | 'source' | 'question_growth';
|
|
230
|
-
/** One bounded, auditable interaction with a rendered PAA control. Question
|
|
231
|
-
* growth is recorded separately from control-local answer/expanded signals so
|
|
232
|
-
* a multi-control batch never pretends it can attribute shared DOM growth to a
|
|
233
|
-
* specific click with more certainty than the browser exposed. */
|
|
234
|
-
interface PaaInteractionObservation {
|
|
235
|
-
id: string;
|
|
236
|
-
questionId: string;
|
|
237
|
-
question: string;
|
|
238
|
-
batchNumber: number;
|
|
239
|
-
controlAttempt: number;
|
|
240
|
-
observedAt: string;
|
|
241
|
-
dispatched: boolean;
|
|
242
|
-
outcome: PaaInteractionOutcome;
|
|
243
|
-
confirmationSignals: PaaInteractionSignal[];
|
|
244
|
-
confirmationCheckpointMs: 700 | 1000 | 1400 | null;
|
|
245
|
-
confirmedAtMs: number | null;
|
|
246
|
-
dispatchDurationMs: number;
|
|
247
|
-
totalDurationMs: number;
|
|
248
|
-
beforeQuestionCount: number;
|
|
249
|
-
afterQuestionCount: number;
|
|
250
|
-
newQuestionCount: number;
|
|
251
|
-
expandedBefore: boolean;
|
|
252
|
-
expandedAfter: boolean;
|
|
253
|
-
answerObserved: boolean;
|
|
254
|
-
sourceCount: number;
|
|
255
|
-
errorCode: 'click_failed' | 'click_ack_timeout' | 'control_missing' | 'confirmation_timeout' | null;
|
|
256
|
-
}
|
|
257
|
-
type PaaProgressPhase = 'serp_captured' | 'discovered' | 'click_observed' | 'answer_captured' | 'fill_incomplete' | 'expansion_finished' | 'attempt_ending';
|
|
258
|
-
/** Material already observed before PAA expansion completes. It is checkpointed
|
|
259
|
-
* so a worker interruption cannot discard the SERP that led to the PAA graph. */
|
|
260
|
-
interface PaaProgressMaterial {
|
|
261
|
-
pagination?: HarvestPaginationDiagnostics;
|
|
262
|
-
surface: GoogleSurface;
|
|
263
|
-
aiOverview: AIOverviewResult;
|
|
264
|
-
aiMode: AIModeResult;
|
|
265
|
-
whatPeopleSaying: WhatPeopleSayingCard[];
|
|
266
|
-
videos: VideoResult[];
|
|
267
|
-
forums: ForumResult[];
|
|
268
|
-
organicResults: OrganicResult[];
|
|
269
|
-
localPack: LocalPackBusiness[];
|
|
270
|
-
entityIds: EntityIds;
|
|
271
|
-
}
|
|
272
|
-
interface PaaProgressSnapshot {
|
|
273
|
-
sequence: number;
|
|
274
|
-
observedAt: string;
|
|
275
|
-
clickedQuestions: string[];
|
|
276
|
-
phase: PaaProgressPhase;
|
|
277
|
-
records: FlatRow[];
|
|
278
|
-
lifecycle?: PaaLifecycleDiagnostics;
|
|
279
|
-
material?: PaaProgressMaterial;
|
|
280
|
-
interactions?: PaaInteractionObservation[];
|
|
281
|
-
}
|
|
282
|
-
interface PaaSource extends SerpOutboundLink {
|
|
283
|
-
title: string | null;
|
|
284
|
-
site: string | null;
|
|
285
|
-
/** Description displayed in the Google source card, when observed. */
|
|
286
|
-
description?: string | null;
|
|
287
|
-
}
|
|
288
|
-
interface PAANode {
|
|
289
|
-
questionId: string;
|
|
290
|
-
googleLinkId: string | null;
|
|
291
|
-
googleEvidenceId: string | null;
|
|
292
|
-
question: string;
|
|
293
|
-
answer: string | null;
|
|
294
|
-
/** A remaining inner answer control could not be expanded within the capture budget. */
|
|
295
|
-
answerExpansionIncomplete?: boolean;
|
|
296
|
-
sourceTitle: string | null;
|
|
297
|
-
sourceSite: string | null;
|
|
298
|
-
sourceCite: string | null;
|
|
299
|
-
sources: PaaSource[];
|
|
300
|
-
depth: number;
|
|
301
|
-
parentQuestion: string | null;
|
|
302
|
-
children: PAANode[];
|
|
303
|
-
}
|
|
304
|
-
interface FlatRow {
|
|
305
|
-
seed_query: string;
|
|
306
|
-
question_id: string;
|
|
307
|
-
google_link_id: string;
|
|
308
|
-
google_evidence_id: string;
|
|
309
|
-
question: string;
|
|
310
|
-
answer: string;
|
|
311
|
-
answerExpansionIncomplete?: boolean;
|
|
312
|
-
source_title: string;
|
|
313
|
-
source_site: string;
|
|
314
|
-
source_cite: string;
|
|
315
|
-
sources: PaaSource[];
|
|
316
|
-
depth: number;
|
|
317
|
-
parent_question: string;
|
|
318
|
-
extracted_at: string;
|
|
319
|
-
}
|
|
320
|
-
type HarvestOptions = z.infer<typeof HarvestOptionsSchema>;
|
|
321
|
-
|
|
322
|
-
type CanonicalLocationName = string;
|
|
323
|
-
type SerpDevice = 'desktop' | 'mobile';
|
|
324
|
-
type ProxyMode = 'location' | 'configured' | 'none';
|
|
325
|
-
|
|
326
|
-
interface \u004b\u0065\u0072\u006e\u0065\u006cLocationProxyTarget {
|
|
327
|
-
canonicalLocation: CanonicalLocationName;
|
|
328
|
-
level: 'zip' | 'city' | 'state';
|
|
329
|
-
country: 'US';
|
|
330
|
-
state: string;
|
|
331
|
-
city: string;
|
|
332
|
-
zip?: string;
|
|
333
|
-
cityCandidates: string[];
|
|
334
|
-
proxyName: string;
|
|
335
|
-
config: {
|
|
336
|
-
country: 'US';
|
|
337
|
-
state: string;
|
|
338
|
-
city?: string;
|
|
339
|
-
zip?: string;
|
|
340
|
-
};
|
|
341
|
-
}
|
|
342
|
-
type \u004b\u0065\u0072\u006e\u0065\u006cProxyResolutionSource = 'disabled' | 'location_reused' | 'location_created' | 'mobile_created' | 'isp_created' | 'backup_custom' | 'configured_fallback' | 'unavailable';
|
|
343
|
-
interface \u004b\u0065\u0072\u006e\u0065\u006cProxyResolutionDebug {
|
|
344
|
-
source: \u004b\u0065\u0072\u006e\u0065\u006cProxyResolutionSource;
|
|
345
|
-
proxyType: string;
|
|
346
|
-
proxyMode: ProxyMode;
|
|
347
|
-
proxyIdPresent: boolean;
|
|
348
|
-
proxyIdSuffix: string | null;
|
|
349
|
-
target: \u004b\u0065\u0072\u006e\u0065\u006cLocationProxyTarget | null;
|
|
350
|
-
error: string | null;
|
|
351
|
-
disposable?: boolean;
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
interface BrowserNetworkLocationDebug {
|
|
355
|
-
source: 'ipapi.co' | 'ipwho.is' | 'api64.ipify.org';
|
|
356
|
-
ip: string | null;
|
|
357
|
-
city: string | null;
|
|
358
|
-
region: string | null;
|
|
359
|
-
country: string | null;
|
|
360
|
-
org: string | null;
|
|
361
|
-
timezone: string | null;
|
|
362
|
-
error: string | null;
|
|
363
|
-
}
|
|
364
|
-
interface \u004b\u0065\u0072\u006e\u0065\u006cSessionDebug {
|
|
365
|
-
sessionId: string | null;
|
|
366
|
-
proxyMode: ProxyMode;
|
|
367
|
-
requestedProxyIdPresent: boolean;
|
|
368
|
-
requestedProxyIdSuffix: string | null;
|
|
369
|
-
createdProxyIdPresent: boolean | null;
|
|
370
|
-
createdProxyIdSuffix: string | null;
|
|
371
|
-
retrievedProxyIdPresent: boolean | null;
|
|
372
|
-
retrievedProxyIdSuffix: string | null;
|
|
373
|
-
retrievedProxyIdMatchesRequested: boolean | null;
|
|
374
|
-
defaultProxyDisabled: boolean | null;
|
|
375
|
-
defaultProxyDisableError: string | null;
|
|
376
|
-
proxyResolution: \u004b\u0065\u0072\u006e\u0065\u006cProxyResolutionDebug | null;
|
|
377
|
-
timeoutSeconds: number | null;
|
|
378
|
-
stealth: boolean | null;
|
|
379
|
-
profilePresent: boolean | null;
|
|
380
|
-
poolPresent: boolean | null;
|
|
381
|
-
retrieveError: string | null;
|
|
382
|
-
}
|
|
383
|
-
interface BrowserContextDebug {
|
|
384
|
-
viewport: {
|
|
385
|
-
width: number;
|
|
386
|
-
height: number;
|
|
387
|
-
} | null;
|
|
388
|
-
locale: string | null;
|
|
389
|
-
device: SerpDevice;
|
|
390
|
-
requestedDevice?: SerpDevice;
|
|
391
|
-
userAgent: string | null;
|
|
392
|
-
deviceScaleFactor: number | null;
|
|
393
|
-
isMobile: boolean;
|
|
394
|
-
hasTouch: boolean;
|
|
395
|
-
}
|
|
396
|
-
interface SerpNavigationDebug {
|
|
397
|
-
requestedUrl: string;
|
|
398
|
-
finalUrl: string | null;
|
|
399
|
-
title: string | null;
|
|
400
|
-
bodySnippet: string | null;
|
|
401
|
-
hasPaa: boolean | null;
|
|
402
|
-
captchaDetected: boolean | null;
|
|
403
|
-
googleSorryUrl: boolean | null;
|
|
404
|
-
redirected: boolean | null;
|
|
405
|
-
}
|
|
406
|
-
interface BrowserDebugSnapshot {
|
|
407
|
-
\u006b\u0065\u0072\u006e\u0065\u006c: \u004b\u0065\u0072\u006e\u0065\u006cSessionDebug | null;
|
|
408
|
-
context: BrowserContextDebug | null;
|
|
409
|
-
networkLocation: BrowserNetworkLocationDebug | null;
|
|
410
|
-
serpNavigation: SerpNavigationDebug | null;
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
interface SerpLocationCandidate {
|
|
414
|
-
city: string;
|
|
415
|
-
regionCode: string;
|
|
416
|
-
count: number;
|
|
417
|
-
examples: string[];
|
|
418
|
-
}
|
|
419
|
-
interface SerpLocationEvidence {
|
|
420
|
-
status: 'not_requested' | 'matched' | 'mismatch' | 'unknown';
|
|
421
|
-
expected: {
|
|
422
|
-
city: string;
|
|
423
|
-
regionCode: string | null;
|
|
424
|
-
canonicalLocation: string;
|
|
425
|
-
} | null;
|
|
426
|
-
candidates: SerpLocationCandidate[];
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
interface HarvestStats {
|
|
430
|
-
seed: string;
|
|
431
|
-
totalQuestions: number;
|
|
432
|
-
maxDepthReached: number;
|
|
433
|
-
durationMs: number;
|
|
434
|
-
errorCount: number;
|
|
435
|
-
}
|
|
436
|
-
type HarvestCompletionStatus = 'paa_found' | 'no_paa' | 'serp_only';
|
|
437
|
-
/**
|
|
438
|
-
* A completed browser attempt can still be unsuitable for programmatic use.
|
|
439
|
-
* This is deliberately separate from completionStatus so a valid no-PAA
|
|
440
|
-
* response with organic results is not confused with an unreadable SERP.
|
|
441
|
-
*/
|
|
442
|
-
type HarvestResultQuality = 'complete' | 'partial' | 'degraded';
|
|
443
|
-
type HarvestDegradationReason = 'empty_primary_serp' | 'page_two_missing';
|
|
444
|
-
type HarvestDiagnosticWarningCode = 'short_videos_captcha_skipped' | 'paa_partial';
|
|
445
|
-
type HarvestDiagnosticWarningSurface = 'short_videos' | 'paa';
|
|
446
|
-
interface HarvestDiagnosticWarning {
|
|
447
|
-
code: HarvestDiagnosticWarningCode;
|
|
448
|
-
surface: HarvestDiagnosticWarningSurface;
|
|
449
|
-
message: string;
|
|
450
|
-
retryable: boolean;
|
|
451
|
-
}
|
|
452
|
-
interface HarvestCompleteness {
|
|
453
|
-
paaWithoutAnswer: number;
|
|
454
|
-
paaWithoutSource: number;
|
|
455
|
-
paaIncompleteAnswers?: number;
|
|
456
|
-
paaAnswersRecovered: number;
|
|
457
|
-
aioShareCaptured: boolean | null;
|
|
458
|
-
paaRequested?: number;
|
|
459
|
-
paaReturned?: number;
|
|
460
|
-
paaUnique?: number;
|
|
461
|
-
}
|
|
462
|
-
type HarvestInterruptionCode = 'browser_session_interrupted' | 'request_aborted' | 'timeout';
|
|
463
|
-
type HarvestInterruptionReasonSource = 'client_runtime' | 'extractor';
|
|
464
|
-
interface HarvestInterruptionDiagnostic {
|
|
465
|
-
code: HarvestInterruptionCode;
|
|
466
|
-
reasonSource: HarvestInterruptionReasonSource;
|
|
467
|
-
message: string | null;
|
|
468
|
-
}
|
|
469
|
-
/** Observed organic pagination outcome; page two never expands a separate PAA graph. */
|
|
470
|
-
interface HarvestPaginationDiagnostics {
|
|
471
|
-
requestedPages: 1 | 2;
|
|
472
|
-
capturedPages: 1 | 2;
|
|
473
|
-
page2Status: 'not_requested' | 'not_attempted' | 'captured' | 'unavailable' | 'failed';
|
|
474
|
-
page1OrganicCount: number;
|
|
475
|
-
page2OrganicCount: number;
|
|
476
|
-
failureCode?: 'missing_next' | 'invalid_next' | 'empty_page' | 'captcha' | 'timeout' | 'navigation_error' | 'unsupported_driver';
|
|
477
|
-
}
|
|
478
|
-
interface HarvestDiagnostics {
|
|
479
|
-
phaseTimings?: {
|
|
480
|
-
providerConnectMs?: number;
|
|
481
|
-
navigationMs?: number;
|
|
482
|
-
serpParseMs?: number;
|
|
483
|
-
paaExpansionMs?: number;
|
|
484
|
-
linkResolutionMs?: number;
|
|
485
|
-
totalServerMs: number;
|
|
486
|
-
};
|
|
487
|
-
pagination?: HarvestPaginationDiagnostics;
|
|
488
|
-
completionStatus: HarvestCompletionStatus;
|
|
489
|
-
/** Set only when the extractor directly observed a readable SERP without a PAA block. */
|
|
490
|
-
noPaaObserved?: boolean;
|
|
491
|
-
problem: null;
|
|
492
|
-
resultQuality?: HarvestResultQuality;
|
|
493
|
-
degradedResult?: boolean;
|
|
494
|
-
degradationReasons?: HarvestDegradationReason[];
|
|
495
|
-
retryRecommended?: boolean;
|
|
496
|
-
interruption?: HarvestInterruptionDiagnostic;
|
|
497
|
-
warnings?: HarvestDiagnosticWarning[];
|
|
498
|
-
links?: HarvestLinkDiagnostics;
|
|
499
|
-
completeness?: HarvestCompleteness;
|
|
500
|
-
paaLifecycle?: PaaLifecycleDiagnostics;
|
|
501
|
-
debug?: HarvestDebugSnapshot;
|
|
502
|
-
}
|
|
503
|
-
interface HarvestLinkDiagnostics {
|
|
504
|
-
linkCandidatesObserved: number;
|
|
505
|
-
plainLinksObserved: number;
|
|
506
|
-
googleUrlRedirectsObserved: number;
|
|
507
|
-
googleGotoRedirectsObserved: number;
|
|
508
|
-
redirectsResolved: number;
|
|
509
|
-
redirectsUnresolved: number;
|
|
510
|
-
redirectsRejected: number;
|
|
511
|
-
resolverNonGoogleRequestsObserved: number;
|
|
512
|
-
paaSourcesPreserved: number;
|
|
513
|
-
aiOverviewLabelValidated: boolean;
|
|
514
|
-
}
|
|
515
|
-
interface HarvestDebugSnapshot {
|
|
516
|
-
enabled: true;
|
|
517
|
-
request: {
|
|
518
|
-
query: string;
|
|
519
|
-
locationInput: string | null;
|
|
520
|
-
canonicalLocation: string | null;
|
|
521
|
-
locationResolved: boolean | null;
|
|
522
|
-
locationMatchSource: string | null;
|
|
523
|
-
locationAmbiguousCandidates: number | null;
|
|
524
|
-
uule: string | null;
|
|
525
|
-
gl: string;
|
|
526
|
-
hl: string;
|
|
527
|
-
device: SerpDevice;
|
|
528
|
-
proxyMode: ProxyMode;
|
|
529
|
-
proxyZip: string | null;
|
|
530
|
-
serpIdentity: string | null;
|
|
531
|
-
serpOnly: boolean;
|
|
532
|
-
pages: number;
|
|
533
|
-
};
|
|
534
|
-
browser: BrowserDebugSnapshot;
|
|
535
|
-
locationEvidence?: SerpLocationEvidence;
|
|
536
|
-
}
|
|
537
|
-
interface HarvestResult {
|
|
538
|
-
seed: string;
|
|
539
|
-
location: string | null;
|
|
540
|
-
extractedAt: string;
|
|
541
|
-
diagnostics: HarvestDiagnostics;
|
|
542
|
-
totalQuestions: number;
|
|
543
|
-
surface: GoogleSurface;
|
|
544
|
-
aiOverview: AIOverviewResult;
|
|
545
|
-
aiMode: AIModeResult;
|
|
546
|
-
whatPeopleSaying: WhatPeopleSayingCard[];
|
|
547
|
-
tree: PAANode[];
|
|
548
|
-
flat: FlatRow[];
|
|
549
|
-
videos: VideoResult[];
|
|
550
|
-
forums: ForumResult[];
|
|
551
|
-
organicResults: OrganicResult[];
|
|
552
|
-
localPack: LocalPackBusiness[];
|
|
553
|
-
entityIds: EntityIds;
|
|
554
|
-
fullSerpFeatures?: FullSerpFeatures;
|
|
555
|
-
stats: HarvestStats;
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
declare function harvest(rawOptions: unknown): Promise<HarvestResult>;
|
|
559
|
-
|
|
560
|
-
interface EpisodeBrief {
|
|
561
|
-
episodeNumber: number;
|
|
562
|
-
episodeCount: number;
|
|
563
|
-
slug: string;
|
|
564
|
-
sectionTitle: string;
|
|
565
|
-
storyMoment: string;
|
|
566
|
-
characterName: string;
|
|
567
|
-
clip1: string;
|
|
568
|
-
clip2: string;
|
|
569
|
-
voiceover: string;
|
|
570
|
-
audioMood: string;
|
|
571
|
-
depositionVisualTell: string;
|
|
572
|
-
continuityNote: string;
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
interface ClipPairOptions {
|
|
576
|
-
resolution?: '480p' | '720p' | '1080p';
|
|
577
|
-
aspectRatio?: '16:9' | '9:16' | '1:1';
|
|
578
|
-
clipDurationSeconds?: number;
|
|
579
|
-
generateAudio?: boolean;
|
|
580
|
-
seed?: number;
|
|
581
|
-
outputDir?: string;
|
|
582
|
-
}
|
|
583
|
-
interface ClipPairResult {
|
|
584
|
-
clip1Url: string;
|
|
585
|
-
clip2Url: string;
|
|
586
|
-
finalVideoPath: string;
|
|
587
|
-
seed: number;
|
|
588
|
-
promptClip1: string;
|
|
589
|
-
promptClip2: string;
|
|
590
|
-
voiceover: string;
|
|
591
|
-
audioMood: string;
|
|
592
|
-
}
|
|
593
|
-
declare class VideoGenerator {
|
|
594
|
-
constructor(apiKey?: string);
|
|
595
|
-
generateClipPair(question: string, answer: string, opts?: ClipPairOptions): Promise<ClipPairResult>;
|
|
596
|
-
generateEpisode(brief: EpisodeBrief, opts?: ClipPairOptions): Promise<ClipPairResult>;
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
interface ClipPromptPair {
|
|
600
|
-
clip1: string;
|
|
601
|
-
clip2: string;
|
|
602
|
-
voiceover: string;
|
|
603
|
-
audioMood: string;
|
|
604
|
-
}
|
|
605
|
-
declare function buildClipPrompts(question: string, answer: string): Promise<ClipPromptPair>;
|
|
606
|
-
|
|
607
|
-
export { type AICitation, type AIModeResult, type AIOverviewResult, type ClipPairOptions, type ClipPairResult, type FlatRow, type GoogleSurface, type HarvestCompletionStatus, type HarvestDiagnostics, type HarvestOptions, type HarvestResult, type HarvestStats, type PAANode, type PaaDiscoveryStatus, type PaaLifecycleDiagnostics, type PaaMaterialStatus, type PaaProgressMaterial, type PaaProgressSnapshot, type PaaTargetStatus, VideoGenerator, type WhatPeopleSayingCard, buildClipPrompts, harvest };
|
|
1
|
+
export { McpScraperClient } from './transport.js';
|
|
2
|
+
export type { PublicClientOptions, RpcMessage, ToolResult, } from './transport.js';
|
|
3
|
+
import { type PublicClientOptions } from './transport.js';
|
|
4
|
+
export interface HarvestInput {
|
|
5
|
+
query: string;
|
|
6
|
+
location?: string;
|
|
7
|
+
gl?: string;
|
|
8
|
+
hl?: string;
|
|
9
|
+
maxQuestions?: number;
|
|
10
|
+
depth?: number;
|
|
11
|
+
idempotencyKey?: string;
|
|
12
|
+
}
|
|
13
|
+
/** Hosted harvest; returns the public MCP result and its full-result delivery handles. */
|
|
14
|
+
export declare function harvest(input: HarvestInput, options?: PublicClientOptions): Promise<import("./transport.js").ToolResult>;
|
package/dist/index.js
CHANGED
|
@@ -1,39 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
Produce four things:
|
|
8
|
-
|
|
9
|
-
1. clip1 (~8s): Show a real person experiencing the situation the question describes. End on a specific frozen moment \u2014 a held expression, a paused action \u2014 that clip 2 continues from.
|
|
10
|
-
|
|
11
|
-
2. clip2 (~8s): Continue from that exact frozen frame. Deliver the informational payoff from the answer. Show specific facts playing out visually. Describe motion continuing from clip 1's ending frame, not a new scene.
|
|
12
|
-
|
|
13
|
-
3. voiceover: A spoken narration that fits in 10 seconds of TTS audio. HARD LIMIT: 22 words maximum. Count every word before finalizing \u2014 if it exceeds 22 words, cut it. Starts with the problem, ends with the answer. No filler, no "in conclusion". Informational and direct.
|
|
14
|
-
|
|
15
|
-
4. audioMood: 6-10 words describing INSTRUMENTAL background music only \u2014 no vocals, no voice, no lyrics. Describe instruments and mood (e.g. "warm acoustic guitar, uplifting, professional home service, no vocals"). Will be passed to an AI audio model.
|
|
16
|
-
|
|
17
|
-
Rules for visuals:
|
|
18
|
-
- No text, captions, graphics, or overlays
|
|
19
|
-
- Photorealistic, natural lighting, specific details
|
|
20
|
-
- Describe exactly what the camera sees
|
|
21
|
-
|
|
22
|
-
Respond with JSON only:
|
|
23
|
-
{"clip1": "...", "clip2": "...", "voiceover": "...", "audioMood": "..."}`;async function B(i,t,e,o){let n=await fetch(t,{method:"POST",headers:{Authorization:`Bearer ${i}`,"Content-Type":"application/json"},body:JSON.stringify({model:A,temperature:.7,messages:[{role:"system",content:Z},{role:"user",content:`Question: ${e}
|
|
24
|
-
|
|
25
|
-
Answer: ${o.slice(0,500)}`}]})});if(!n.ok)throw new Error(`LLM call failed (${n.status}): ${await n.text()}`);let r=await n.json();t===L?M({vendor:"openrouter",model:A,units:r.usage?.cost??0,unitType:"usd"}):M({vendor:"deepinfra_qwen",model:A,units:r.usage?.completion_tokens??0,unitType:"tokens"});let a=r.choices[0]?.message?.content?.trim()??"",c=a.match(/\{[\s\S]*\}/);if(!c)throw new Error(`No JSON in QWEN response: ${a.slice(0,200)}`);let s=JSON.parse(c[0]);if(!s.clip1||!s.clip2||!s.voiceover||!s.audioMood)throw new Error(`QWEN response missing fields: ${a.slice(0,200)}`);return s}async function T(i,t){let e=process.env.DEEPINFRA_API_KEY,o=process.env.OPENROUTER_API_KEY;if(e)try{return await B(e,X,i,t)}catch(n){console.warn("[promptBuilder] DeepInfra failed, trying OpenRouter:",n.message)}if(o)return await B(o,L,i,t);throw new Error("No LLM key \u2014 set DEEPINFRA_API_KEY or OPENROUTER_API_KEY")}function V(i){if(!i.clip1||!i.clip2||!i.voiceover||!i.audioMood)throw new Error("Episode brief is missing prompt fields \u2014 run blog-to-video skill to regenerate");return{clip1:i.clip1,clip2:i.clip2,voiceover:i.voiceover,audioMood:i.audioMood}}import{writeFileSync as U,mkdirSync as ee}from"fs";import{join as F}from"path";import{tmpdir as oe}from"os";import{fal as x}from"@fal-ai/client";var te="fal-ai/mmaudio-v2",ie="fal-ai/elevenlabs/tts",re="fal-ai/google/gemini-2.5-flash-preview-tts";async function j(i){let t=await fetch(i);if(!t.ok)throw new Error(`Failed to download TTS audio (${t.status})`);return Buffer.from(await t.arrayBuffer())}async function O(i){console.log("[AudioGenerator] Generating voiceover...");let t=F(oe(),`tts-${Date.now()}`);ee(t,{recursive:!0});let e=F(t,"voiceover.mp3");try{let r=process.env.ELEVENLABS_VOICE_ID??"pNInz6obpgDQGcFmaJgB",a=await x.run(ie,{input:{text:i,voice_id:r,model_id:"eleven_v3"}});return U(e,await j(a.audio.url)),console.log("[AudioGenerator] TTS: ElevenLabs via fal"),e}catch(r){console.warn("[AudioGenerator] ElevenLabs via fal failed, trying Gemini:",r.message)}let o=process.env.GEMINI_TTS_VOICE??"Kore",n=await x.run(re,{input:{text:i,voice:o}});return U(e,await j(n.audio.url)),console.log("[AudioGenerator] TTS: Gemini via fal"),e}async function $(i,t,e){return console.log("[AudioGenerator] Adding background audio via MMAudio V2..."),(await x.run(te,{input:{video_url:i,prompt:t,negative_prompt:"speech, voice, talking, dialogue, narration, vocals, singing, human voice, conversation, words, lyrics, announcer, commentary",duration:e,cfg_strength:4.5}})).video.url}import{execSync as K}from"child_process";import{writeFileSync as ne,mkdirSync as ae}from"fs";import{join as u}from"path";import{fal as se}from"@fal-ai/client";async function I(i,t){let e=await fetch(i);if(!e.ok)throw new Error(`Download failed (${e.status}): ${i}`);ne(t,Buffer.from(await e.arrayBuffer()))}async function N(i,t,e){ae(e,{recursive:!0});let o=Date.now(),n=u(e,`clip1-${o}.mp4`),r=u(e,`clip2-${o}.mp4`),a=u(e,`combined-${o}.mp4`);return console.log("[VideoMixer] Downloading clips..."),await Promise.all([I(i,n),I(t,r)]),console.log("[VideoMixer] Concatenating..."),K(`ffmpeg -i "${n}" -i "${r}" -filter_complex "[0:v][1:v]concat=n=2:v=1:a=0[v]" -map "[v]" -y "${a}" -loglevel error`),a}async function b(i){let{readFileSync:t}=await import("fs"),e=new Blob([t(i)],{type:"video/mp4"}),o=await se.storage.upload(e);return console.log("[VideoMixer] Uploaded to fal:",o),o}async function R(i,t,e){let o=Date.now(),n=u(e,`voiceover-${o}.wav`),r=u(e,`final-${o}.mp4`);return console.log("[VideoMixer] Downloading voiceover..."),await I(t,n),console.log("[VideoMixer] Mixing voiceover over background audio..."),K(`ffmpeg -i "${i}" -i "${n}" -filter_complex "[0:a]volume=0.2[bg];[1:a]volume=1.0[vo];[bg][vo]amix=inputs=2:duration=first[a]" -map 0:v -map "[a]" -c:v copy -y "${r}" -loglevel error`),r}var q="bytedance/seedance-2.0/text-to-video",J="bytedance/seedance-2.0/image-to-video";function v(i,t,e,o){return{prompt:i,resolution:t.resolution??"720p",duration:t.clipDurationSeconds??8,aspect_ratio:t.aspectRatio??"16:9",generate_audio:!1,...e!==void 0?{seed:e}:{},...o!==void 0?{image_url:o}:{}}}async function w(i,t){let{request_id:e}=await p.queue.submit(i,{input:t});for(console.log(`[fal] submitted ${i} \u2192 ${e}`);;){await new Promise(r=>setTimeout(r,5e3));let o=await p.queue.status(i,{requestId:e,logs:!1});if(console.log(`[fal] ${e} \u2192 ${o.status}`),o.status==="FAILED")throw new Error(`Request ${e} failed`);if(o.status!=="COMPLETED")continue;return(await p.queue.result(i,{requestId:e})).data}}async function H(i,t){let e=Date.now(),o=l(t,`clip1-raw-${e}.mp4`),n=l(t,`last-frame-${e}.jpg`),r=await fetch(i);if(!r.ok)throw new Error(`Failed to download clip 1 (${r.status})`);_(o,Buffer.from(await r.arrayBuffer()));try{ce(`ffmpeg -sseof -0.1 -i "${o}" -vframes 1 -y "${n}" -loglevel error`)}finally{try{D(o)}catch{}}return n}var k=class{constructor(t){let e=t??process.env.FAL_KEY;if(!e)throw new Error("FAL_KEY is required");p.config({credentials:e})}async generateClipPair(t,e,o={}){let n=o.outputDir??l(W(),`paa-video-${Date.now()}`);Y(n,{recursive:!0}),console.log(`
|
|
26
|
-
[1/7] Generating prompts via QWEN 3.6...`);let r=await T(t,e);console.log(" Voiceover:",r.voiceover),console.log(" Audio mood:",r.audioMood),console.log(`
|
|
27
|
-
[2/7] Generating clip 1 (text-to-video)...`);let a=await w(q,v(r.clip1,o,o.seed));console.log(`
|
|
28
|
-
[3/7] Extracting last frame \u2192 clip 2 start...`);let c=await H(a.video.url,n),s=new Blob([G(c)],{type:"image/jpeg"}),h=await p.storage.upload(s);try{D(c)}catch{}console.log(`
|
|
29
|
-
[4/7] Generating clip 2 (image-to-video from last frame)...`);let d=o.seed!==void 0?o.seed+1:void 0,m=await w(J,v(r.clip2,o,d,h));console.log(`
|
|
30
|
-
[5/7] Concatenating clips + generating voiceover (parallel)...`);let[P,y]=await Promise.all([N(a.video.url,m.video.url,n),O(r.voiceover)]);console.log(`
|
|
31
|
-
[6/7] Adding background audio via MMAudio V2...`);let E=await b(P),S=(o.clipDurationSeconds??8)*2,g=await $(E,r.audioMood,S);console.log(`
|
|
32
|
-
[7/7] Overlaying voiceover on final video...`);let f=l(n,`with-bg-audio-${Date.now()}.mp4`),C=await fetch(g);_(f,Buffer.from(await C.arrayBuffer()));let Q=await R(f,y,n);return{clip1Url:a.video.url,clip2Url:m.video.url,finalVideoPath:Q,seed:a.seed,promptClip1:r.clip1,promptClip2:r.clip2,voiceover:r.voiceover,audioMood:r.audioMood}}async generateEpisode(t,e={}){let o=e.outputDir??l(W(),`episode-${t.episodeNumber}-${Date.now()}`);Y(o,{recursive:!0});let n=V(t);console.log(`
|
|
33
|
-
[Episode ${t.episodeNumber}/${t.episodeCount}] ${t.sectionTitle}`),console.log(" Voiceover:",n.voiceover),console.log(`
|
|
34
|
-
[2/7] Generating clip 1 (text-to-video)...`);let r=await w(q,v(n.clip1,e,e.seed));console.log(`
|
|
35
|
-
[3/7] Extracting last frame \u2192 clip 2 start...`);let a=await H(r.video.url,o),c=new Blob([G(a)],{type:"image/jpeg"}),s=await p.storage.upload(c);try{D(a)}catch{}console.log(`
|
|
36
|
-
[4/7] Generating clip 2 (image-to-video from last frame)...`);let h=e.seed!==void 0?e.seed+1:void 0,d=await w(J,v(n.clip2,e,h,s));console.log(`
|
|
37
|
-
[5/7] Concatenating clips + generating voiceover (parallel)...`);let[m,P]=await Promise.all([N(r.video.url,d.video.url,o),O(n.voiceover)]);console.log(`
|
|
38
|
-
[6/7] Adding background audio via MMAudio V2...`);let y=await b(m),E=(e.clipDurationSeconds??8)*2,S=await $(y,n.audioMood,E);console.log(`
|
|
39
|
-
[7/7] Overlaying voiceover on final video...`);let g=l(o,`with-bg-audio-${Date.now()}.mp4`),f=await fetch(S);_(g,Buffer.from(await f.arrayBuffer()));let C=await R(g,P,o);return{clip1Url:r.video.url,clip2Url:d.video.url,finalVideoPath:C,seed:r.seed,promptClip1:n.clip1,promptClip2:n.clip2,voiceover:n.voiceover,audioMood:n.audioMood}}};export{k as VideoGenerator,T as buildClipPrompts,z as harvest};
|
|
1
|
+
var a=class{#t;endpoint;fetchImpl;#e;protocolVersion;nextId=0;constructor(e={}){if(this.#t=e.apiKey?.trim()||process.env.MCP_SCRAPER_API_KEY?.trim()||"",!this.#t)throw new Error("MCP_SCRAPER_API_KEY is required");let s=e.baseUrl?.trim()||process.env.MCP_SCRAPER_BASE_URL?.trim()||process.env.MCP_BASE_URL?.trim()||"https://mcpscraper.dev",t=new URL(s);if(!["http:","https:"].includes(t.protocol)||t.username||t.password||t.search||t.hash)throw new Error("Invalid MCP Scraper endpoint");t.pathname=t.pathname.replace(/\/$/,"")+(t.pathname.replace(/\/$/,"").endsWith("/mcp")?"":"/mcp"),this.endpoint=t.href,this.fetchImpl=e.fetchImpl??fetch}async relay(e,s,t){if(e.method==="initialize"&&typeof e.params?.protocolVersion=="string"&&(this.protocolVersion=e.params.protocolVersion),e.method==="server/discover"){let r=e.params?._meta?.["io.modelcontextprotocol/protocolVersion"];typeof r=="string"&&(this.protocolVersion=r)}let n=await this.fetchImpl(this.endpoint,{method:"POST",headers:{"content-type":"application/json",accept:"application/json, text/event-stream","x-api-key":this.#t,...e.method?{"mcp-method":e.method}:{},...this.#e?{"mcp-session-id":this.#e}:{},...this.protocolVersion?{"mcp-protocol-version":this.protocolVersion}:{}},body:JSON.stringify(e),signal:t});if(!n.ok)throw new Error(`MCP Scraper HTTP ${n.status}`);if(this.#e=n.headers.get("mcp-session-id")??this.#e,e.method==="initialize"&&typeof e.params?.protocolVersion=="string"&&(this.protocolVersion=e.params.protocolVersion),n.status===202||n.status===204)return null;if(!n.headers.get("content-type")?.includes("text/event-stream"))return await n.json();if(!n.body)throw new Error("MCP Scraper returned an empty event stream");let p=n.body.getReader(),m=new TextDecoder,o="",c=[],d=()=>{if(!c.length)return null;let r=JSON.parse(c.join(`
|
|
2
|
+
`));return c=[],r.id===e.id&&("result"in r||"error"in r)?r:(s?.(r),null)};try{for(;;){let{done:r,value:f}=await p.read();o+=m.decode(f,{stream:!r}),r&&o&&!o.endsWith(`
|
|
3
|
+
`)&&(o+=`
|
|
4
|
+
`);let l;for(;(l=o.indexOf(`
|
|
5
|
+
`))>=0;){let i=o.slice(0,l).replace(/\r$/,"");if(o=o.slice(l+1),i===""){let h=d();if(h)return h}else i.startsWith("data:")&&c.push(i.slice(5).replace(/^ /,""))}if(r){let i=d();if(i)return i;break}}}finally{await p.cancel().catch(()=>{}),p.releaseLock()}throw new Error("MCP Scraper event stream ended without a response")}async request(e,s){let t=await this.relay({jsonrpc:"2.0",id:++this.nextId,method:e,params:s});if(!t)throw new Error("MCP Scraper returned no response");if(t.error)throw new Error(typeof t.error.message=="string"?t.error.message:"MCP Scraper request failed");return t.result}callTool(e,s={}){return this.request("tools/call",{name:e,arguments:s})}};function y(u,e){return new a(e).callTool("harvest_paa",{...u})}export{a as McpScraperClient,y as harvest};
|