@spark-apps/quickpeek 1.2.5 → 1.2.6
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/README.md +7 -14
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +147 -122
- package/dist/mcp-tools.mjs +207 -171
- package/dist/qp.js +208 -172
- package/dist/{voices-ufga0oOn.d.mts → voices-5AFt1GDp.d.mts} +45 -3
- package/dist/web.d.mts +2 -2
- package/dist/web.mjs +146 -19
- package/package.json +3 -6
package/dist/mcp-tools.mjs
CHANGED
|
@@ -296,6 +296,139 @@ var init_image_kind = __esm({
|
|
|
296
296
|
}
|
|
297
297
|
});
|
|
298
298
|
|
|
299
|
+
// src/browser/headless.ts
|
|
300
|
+
import { chromium } from "playwright";
|
|
301
|
+
async function launchHeadlessChromium(options = {}) {
|
|
302
|
+
const opts = { ...options, headless: true };
|
|
303
|
+
try {
|
|
304
|
+
return await chromium.launch(opts);
|
|
305
|
+
} catch (bundledError) {
|
|
306
|
+
try {
|
|
307
|
+
return await chromium.launch({ ...opts, channel: "chrome" });
|
|
308
|
+
} catch {
|
|
309
|
+
const detail = bundledError instanceof Error ? bundledError.message : String(bundledError);
|
|
310
|
+
throw new Error(`${NO_BROWSER_MESSAGE}
|
|
311
|
+
|
|
312
|
+
Playwright reported: ${detail}`);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
var NO_BROWSER_MESSAGE;
|
|
317
|
+
var init_headless = __esm({
|
|
318
|
+
"src/browser/headless.ts"() {
|
|
319
|
+
"use strict";
|
|
320
|
+
NO_BROWSER_MESSAGE = "QuickPeek needs a browser to record and could not find one.\n Run this once, then run QuickPeek again:\n npx playwright install chromium";
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
// src/lib/json-repair.ts
|
|
325
|
+
function repairUnicodeEscapes(content) {
|
|
326
|
+
return content.replace(/\\u\{([0-9a-fA-F]{1,6})\}/g, (match, hex) => {
|
|
327
|
+
const code = Number.parseInt(hex, 16);
|
|
328
|
+
return code <= 1114111 ? String.fromCodePoint(code) : match;
|
|
329
|
+
}).replace(/\\u(?![0-9a-fA-F]{4})/g, "\\\\u");
|
|
330
|
+
}
|
|
331
|
+
function salvageTruncatedJson(content) {
|
|
332
|
+
const stack = [];
|
|
333
|
+
const cuts = [];
|
|
334
|
+
let inString = false;
|
|
335
|
+
let escaped = false;
|
|
336
|
+
const record = (index, closesContainer) => cuts.push({ index, open: stack.join(""), closesContainer });
|
|
337
|
+
for (let i = 0; i < content.length; i++) {
|
|
338
|
+
const ch = content[i];
|
|
339
|
+
if (inString) {
|
|
340
|
+
if (escaped) escaped = false;
|
|
341
|
+
else if (ch === "\\") escaped = true;
|
|
342
|
+
else if (ch === '"') {
|
|
343
|
+
inString = false;
|
|
344
|
+
record(i + 1, false);
|
|
345
|
+
}
|
|
346
|
+
continue;
|
|
347
|
+
}
|
|
348
|
+
if (ch === '"') {
|
|
349
|
+
inString = true;
|
|
350
|
+
} else if (ch === "{" || ch === "[") {
|
|
351
|
+
stack.push(ch);
|
|
352
|
+
} else if (ch === "}" || ch === "]") {
|
|
353
|
+
stack.pop();
|
|
354
|
+
record(i + 1, true);
|
|
355
|
+
} else if (ch === ",") {
|
|
356
|
+
record(i, content[i - 1] === "}" || content[i - 1] === "]");
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
const tryCut = (cut) => {
|
|
360
|
+
const head = content.slice(0, cut.index).replace(/[\s,]+$/, "");
|
|
361
|
+
if (head.endsWith(":")) return null;
|
|
362
|
+
let closed = head;
|
|
363
|
+
for (let d = cut.open.length - 1; d >= 0; d--) {
|
|
364
|
+
closed += CLOSERS[cut.open[d]];
|
|
365
|
+
}
|
|
366
|
+
try {
|
|
367
|
+
JSON.parse(closed);
|
|
368
|
+
return closed;
|
|
369
|
+
} catch {
|
|
370
|
+
return null;
|
|
371
|
+
}
|
|
372
|
+
};
|
|
373
|
+
const attempts = Math.min(cuts.length, MAX_SALVAGE_ATTEMPTS);
|
|
374
|
+
for (const containerOnly of [true, false]) {
|
|
375
|
+
for (let n = 0; n < attempts; n++) {
|
|
376
|
+
const cut = cuts[cuts.length - 1 - n];
|
|
377
|
+
if (containerOnly && !cut.closesContainer) continue;
|
|
378
|
+
const salvaged = tryCut(cut);
|
|
379
|
+
if (salvaged !== null) return salvaged;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
return null;
|
|
383
|
+
}
|
|
384
|
+
function extractJsonCandidates(content) {
|
|
385
|
+
let text = content.trim();
|
|
386
|
+
const fence = text.match(/```(?:json)?\s*([\s\S]*?)(?:```|$)/);
|
|
387
|
+
if (fence?.[1]?.trim()) text = fence[1].trim();
|
|
388
|
+
const start = text.search(/[{[]/);
|
|
389
|
+
if (start === -1) return [];
|
|
390
|
+
const closer = text[start] === "{" ? "}" : "]";
|
|
391
|
+
const end = text.lastIndexOf(closer);
|
|
392
|
+
const candidates = [text.slice(start)];
|
|
393
|
+
if (end > start && end < text.length - 1) candidates.unshift(text.slice(start, end + 1));
|
|
394
|
+
return candidates;
|
|
395
|
+
}
|
|
396
|
+
function parseAIJson(content) {
|
|
397
|
+
const candidates = extractJsonCandidates(content);
|
|
398
|
+
if (candidates.length === 0) return null;
|
|
399
|
+
for (const text of candidates) {
|
|
400
|
+
try {
|
|
401
|
+
return { value: JSON.parse(text), repaired: false };
|
|
402
|
+
} catch {
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
for (const text of candidates) {
|
|
406
|
+
try {
|
|
407
|
+
return { value: JSON.parse(repairUnicodeEscapes(text)), repaired: true };
|
|
408
|
+
} catch {
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
for (const text of candidates) {
|
|
412
|
+
for (const variant of [repairUnicodeEscapes(text), text]) {
|
|
413
|
+
const salvaged = salvageTruncatedJson(variant);
|
|
414
|
+
if (salvaged === null) continue;
|
|
415
|
+
try {
|
|
416
|
+
return { value: JSON.parse(salvaged), repaired: true };
|
|
417
|
+
} catch {
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
return null;
|
|
422
|
+
}
|
|
423
|
+
var MAX_SALVAGE_ATTEMPTS, CLOSERS;
|
|
424
|
+
var init_json_repair = __esm({
|
|
425
|
+
"src/lib/json-repair.ts"() {
|
|
426
|
+
"use strict";
|
|
427
|
+
MAX_SALVAGE_ATTEMPTS = 500;
|
|
428
|
+
CLOSERS = { "{": "}", "[": "]" };
|
|
429
|
+
}
|
|
430
|
+
});
|
|
431
|
+
|
|
299
432
|
// src/lib/api-client.ts
|
|
300
433
|
function apiKey() {
|
|
301
434
|
return process.env.SPARK_AI_KEY || "";
|
|
@@ -494,24 +627,26 @@ async function callAIViaRelay(systemPrompt, userPrompt, email, tier = "free", op
|
|
|
494
627
|
}
|
|
495
628
|
}
|
|
496
629
|
function parseAIPlanResponse(content) {
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
}
|
|
503
|
-
const jsonMatch = processedContent.match(/\{[\s\S]*\}/);
|
|
504
|
-
if (!jsonMatch) {
|
|
505
|
-
return { success: false, error: "No valid JSON found in AI response" };
|
|
506
|
-
}
|
|
507
|
-
const parsed = JSON.parse(jsonMatch[0]);
|
|
508
|
-
return { success: true, plan: parsed };
|
|
509
|
-
} catch (err) {
|
|
510
|
-
return {
|
|
511
|
-
success: false,
|
|
512
|
-
error: `Failed to parse AI response: ${err instanceof Error ? err.message : String(err)}`
|
|
513
|
-
};
|
|
630
|
+
const parsed = parseAIJson(content);
|
|
631
|
+
if (!parsed) {
|
|
632
|
+
return { success: false, error: "No valid JSON found in AI response" };
|
|
633
|
+
}
|
|
634
|
+
if (!Array.isArray(parsed.value.steps) || parsed.value.steps.length === 0) {
|
|
635
|
+
return { success: false, error: "AI response has no steps" };
|
|
514
636
|
}
|
|
637
|
+
return { success: true, plan: { ...parsed.value, steps: parsed.value.steps } };
|
|
638
|
+
}
|
|
639
|
+
async function callAIForPlan(systemPrompt, userPrompt, tier = "free", opts = {}) {
|
|
640
|
+
let lastParseError = "No valid JSON found in AI response";
|
|
641
|
+
for (let attempt = 1; attempt <= 2; attempt++) {
|
|
642
|
+
const aiResult = await callAI(systemPrompt, userPrompt, tier, opts);
|
|
643
|
+
if (!aiResult.success) return aiResult;
|
|
644
|
+
const parsed = parseAIPlanResponse(aiResult.content);
|
|
645
|
+
if (parsed.success) return { success: true, plan: parsed.plan, limits: aiResult.limits };
|
|
646
|
+
lastParseError = parsed.error;
|
|
647
|
+
console.error(`Plan parse failed (attempt ${attempt}/2):`, parsed.error);
|
|
648
|
+
}
|
|
649
|
+
return { success: false, error: lastParseError, isParseError: true };
|
|
515
650
|
}
|
|
516
651
|
function normalizeUrl(input) {
|
|
517
652
|
let url = input.trim();
|
|
@@ -522,6 +657,7 @@ var PLAN_MAX_TOKENS, SERVER_BACKOFF_MS, CLI_BACKOFF_MS, SPARK_AI_BASE, SPARK_AI_
|
|
|
522
657
|
var init_api_client = __esm({
|
|
523
658
|
"src/lib/api-client.ts"() {
|
|
524
659
|
"use strict";
|
|
660
|
+
init_json_repair();
|
|
525
661
|
init_utils();
|
|
526
662
|
PLAN_MAX_TOKENS = 1500;
|
|
527
663
|
SERVER_BACKOFF_MS = [2e3, 4e3];
|
|
@@ -692,7 +828,7 @@ var VERSION, CONFIG_FILE, SIZE_PRESETS, RATE_FACTOR, NO_CLIP_OUTRO, DEFAULT_CONF
|
|
|
692
828
|
var init_config = __esm({
|
|
693
829
|
"src/lib/config.ts"() {
|
|
694
830
|
"use strict";
|
|
695
|
-
VERSION = "1.2.
|
|
831
|
+
VERSION = "1.2.6";
|
|
696
832
|
CONFIG_FILE = "quickpeek.config.json";
|
|
697
833
|
SIZE_PRESETS = {
|
|
698
834
|
wide: { width: 1920, height: 1080, zoom: 1 },
|
|
@@ -884,7 +1020,6 @@ var init_scraping = __esm({
|
|
|
884
1020
|
});
|
|
885
1021
|
|
|
886
1022
|
// src/lib/crawl.ts
|
|
887
|
-
import { chromium as chromium4 } from "playwright";
|
|
888
1023
|
async function waitForDomStable(page, cap = SETTLE_CAP_MS) {
|
|
889
1024
|
const deadline = Date.now() + cap;
|
|
890
1025
|
let previous = -1;
|
|
@@ -1004,7 +1139,13 @@ async function crawlPage(url, task2 = noopTask, storageStatePath, viewport) {
|
|
|
1004
1139
|
return { title: cached.title, elements: [...cached.elements], scrollable: cached.scrollable };
|
|
1005
1140
|
}
|
|
1006
1141
|
const browserTask = task2("Launching browser");
|
|
1007
|
-
|
|
1142
|
+
let browser;
|
|
1143
|
+
try {
|
|
1144
|
+
browser = await launchHeadlessChromium();
|
|
1145
|
+
} catch (err) {
|
|
1146
|
+
browserTask.succeed("Could not launch a browser");
|
|
1147
|
+
throw err;
|
|
1148
|
+
}
|
|
1008
1149
|
try {
|
|
1009
1150
|
const context = await browser.newContext({
|
|
1010
1151
|
...storageStatePath ? { storageState: storageStatePath } : {},
|
|
@@ -1184,6 +1325,7 @@ var SETTLE_POLL_MS, SETTLE_STABLE_POLLS, SETTLE_CAP_MS, PLACEHOLDER_POLL_MS, PLA
|
|
|
1184
1325
|
var init_crawl = __esm({
|
|
1185
1326
|
"src/lib/crawl.ts"() {
|
|
1186
1327
|
"use strict";
|
|
1328
|
+
init_headless();
|
|
1187
1329
|
init_scraping();
|
|
1188
1330
|
SETTLE_POLL_MS = 400;
|
|
1189
1331
|
SETTLE_STABLE_POLLS = 2;
|
|
@@ -2617,114 +2759,6 @@ var init_ffmpeg2 = __esm({
|
|
|
2617
2759
|
}
|
|
2618
2760
|
});
|
|
2619
2761
|
|
|
2620
|
-
// src/lib/json-repair.ts
|
|
2621
|
-
function repairUnicodeEscapes(content) {
|
|
2622
|
-
return content.replace(/\\u\{([0-9a-fA-F]{1,6})\}/g, (match, hex) => {
|
|
2623
|
-
const code = Number.parseInt(hex, 16);
|
|
2624
|
-
return code <= 1114111 ? String.fromCodePoint(code) : match;
|
|
2625
|
-
}).replace(/\\u(?![0-9a-fA-F]{4})/g, "\\\\u");
|
|
2626
|
-
}
|
|
2627
|
-
function salvageTruncatedJson(content) {
|
|
2628
|
-
const stack = [];
|
|
2629
|
-
const cuts = [];
|
|
2630
|
-
let inString = false;
|
|
2631
|
-
let escaped = false;
|
|
2632
|
-
const record = (index, closesContainer) => cuts.push({ index, open: stack.join(""), closesContainer });
|
|
2633
|
-
for (let i = 0; i < content.length; i++) {
|
|
2634
|
-
const ch = content[i];
|
|
2635
|
-
if (inString) {
|
|
2636
|
-
if (escaped) escaped = false;
|
|
2637
|
-
else if (ch === "\\") escaped = true;
|
|
2638
|
-
else if (ch === '"') {
|
|
2639
|
-
inString = false;
|
|
2640
|
-
record(i + 1, false);
|
|
2641
|
-
}
|
|
2642
|
-
continue;
|
|
2643
|
-
}
|
|
2644
|
-
if (ch === '"') {
|
|
2645
|
-
inString = true;
|
|
2646
|
-
} else if (ch === "{" || ch === "[") {
|
|
2647
|
-
stack.push(ch);
|
|
2648
|
-
} else if (ch === "}" || ch === "]") {
|
|
2649
|
-
stack.pop();
|
|
2650
|
-
record(i + 1, true);
|
|
2651
|
-
} else if (ch === ",") {
|
|
2652
|
-
record(i, content[i - 1] === "}" || content[i - 1] === "]");
|
|
2653
|
-
}
|
|
2654
|
-
}
|
|
2655
|
-
const tryCut = (cut) => {
|
|
2656
|
-
const head = content.slice(0, cut.index).replace(/[\s,]+$/, "");
|
|
2657
|
-
if (head.endsWith(":")) return null;
|
|
2658
|
-
let closed = head;
|
|
2659
|
-
for (let d = cut.open.length - 1; d >= 0; d--) {
|
|
2660
|
-
closed += CLOSERS[cut.open[d]];
|
|
2661
|
-
}
|
|
2662
|
-
try {
|
|
2663
|
-
JSON.parse(closed);
|
|
2664
|
-
return closed;
|
|
2665
|
-
} catch {
|
|
2666
|
-
return null;
|
|
2667
|
-
}
|
|
2668
|
-
};
|
|
2669
|
-
const attempts = Math.min(cuts.length, MAX_SALVAGE_ATTEMPTS);
|
|
2670
|
-
for (const containerOnly of [true, false]) {
|
|
2671
|
-
for (let n = 0; n < attempts; n++) {
|
|
2672
|
-
const cut = cuts[cuts.length - 1 - n];
|
|
2673
|
-
if (containerOnly && !cut.closesContainer) continue;
|
|
2674
|
-
const salvaged = tryCut(cut);
|
|
2675
|
-
if (salvaged !== null) return salvaged;
|
|
2676
|
-
}
|
|
2677
|
-
}
|
|
2678
|
-
return null;
|
|
2679
|
-
}
|
|
2680
|
-
function extractJsonCandidates(content) {
|
|
2681
|
-
let text = content.trim();
|
|
2682
|
-
const fence = text.match(/```(?:json)?\s*([\s\S]*?)(?:```|$)/);
|
|
2683
|
-
if (fence?.[1]?.trim()) text = fence[1].trim();
|
|
2684
|
-
const start = text.search(/[{[]/);
|
|
2685
|
-
if (start === -1) return [];
|
|
2686
|
-
const closer = text[start] === "{" ? "}" : "]";
|
|
2687
|
-
const end = text.lastIndexOf(closer);
|
|
2688
|
-
const candidates = [text.slice(start)];
|
|
2689
|
-
if (end > start && end < text.length - 1) candidates.unshift(text.slice(start, end + 1));
|
|
2690
|
-
return candidates;
|
|
2691
|
-
}
|
|
2692
|
-
function parseAIJson(content) {
|
|
2693
|
-
const candidates = extractJsonCandidates(content);
|
|
2694
|
-
if (candidates.length === 0) return null;
|
|
2695
|
-
for (const text of candidates) {
|
|
2696
|
-
try {
|
|
2697
|
-
return { value: JSON.parse(text), repaired: false };
|
|
2698
|
-
} catch {
|
|
2699
|
-
}
|
|
2700
|
-
}
|
|
2701
|
-
for (const text of candidates) {
|
|
2702
|
-
try {
|
|
2703
|
-
return { value: JSON.parse(repairUnicodeEscapes(text)), repaired: true };
|
|
2704
|
-
} catch {
|
|
2705
|
-
}
|
|
2706
|
-
}
|
|
2707
|
-
for (const text of candidates) {
|
|
2708
|
-
for (const variant of [repairUnicodeEscapes(text), text]) {
|
|
2709
|
-
const salvaged = salvageTruncatedJson(variant);
|
|
2710
|
-
if (salvaged === null) continue;
|
|
2711
|
-
try {
|
|
2712
|
-
return { value: JSON.parse(salvaged), repaired: true };
|
|
2713
|
-
} catch {
|
|
2714
|
-
}
|
|
2715
|
-
}
|
|
2716
|
-
}
|
|
2717
|
-
return null;
|
|
2718
|
-
}
|
|
2719
|
-
var MAX_SALVAGE_ATTEMPTS, CLOSERS;
|
|
2720
|
-
var init_json_repair = __esm({
|
|
2721
|
-
"src/lib/json-repair.ts"() {
|
|
2722
|
-
"use strict";
|
|
2723
|
-
MAX_SALVAGE_ATTEMPTS = 500;
|
|
2724
|
-
CLOSERS = { "{": "}", "[": "]" };
|
|
2725
|
-
}
|
|
2726
|
-
});
|
|
2727
|
-
|
|
2728
2762
|
// src/lib/prompts.ts
|
|
2729
2763
|
function buildSystemPrompt(maxSteps, profile) {
|
|
2730
2764
|
let scope;
|
|
@@ -4104,6 +4138,7 @@ __export(lib_exports, {
|
|
|
4104
4138
|
buildUserPrompt: () => buildUserPrompt,
|
|
4105
4139
|
buildWatermarkFilters: () => buildWatermarkFilters,
|
|
4106
4140
|
callAI: () => callAI,
|
|
4141
|
+
callAIForPlan: () => callAIForPlan,
|
|
4107
4142
|
callAIViaRelay: () => callAIViaRelay,
|
|
4108
4143
|
canReuseVoiceover: () => canReuseVoiceover,
|
|
4109
4144
|
captionCanvas: () => captionCanvas,
|
|
@@ -5445,7 +5480,7 @@ function resolveStartUrl(url) {
|
|
|
5445
5480
|
// src/browser/launch.ts
|
|
5446
5481
|
init_lifecycle();
|
|
5447
5482
|
import { mkdir as mkdir2 } from "fs/promises";
|
|
5448
|
-
import { chromium as
|
|
5483
|
+
import { chromium as chromium3 } from "playwright";
|
|
5449
5484
|
|
|
5450
5485
|
// src/browser/brand-watermark.ts
|
|
5451
5486
|
init_image_kind();
|
|
@@ -5538,11 +5573,14 @@ function clampOpacity(value) {
|
|
|
5538
5573
|
return Math.min(1, Math.max(0.35, value));
|
|
5539
5574
|
}
|
|
5540
5575
|
|
|
5576
|
+
// src/browser/launch.ts
|
|
5577
|
+
init_headless();
|
|
5578
|
+
|
|
5541
5579
|
// src/browser/wsl.ts
|
|
5542
5580
|
import { execSync, spawn as spawn2 } from "child_process";
|
|
5543
5581
|
import { existsSync as existsSync3, readFileSync as readFileSync4 } from "fs";
|
|
5544
5582
|
import { request } from "http";
|
|
5545
|
-
import { chromium } from "playwright";
|
|
5583
|
+
import { chromium as chromium2 } from "playwright";
|
|
5546
5584
|
function isWSL() {
|
|
5547
5585
|
if (process.platform !== "linux") return false;
|
|
5548
5586
|
try {
|
|
@@ -5661,7 +5699,7 @@ async function launchWindowsChrome(extensionPath, userDataDir, options = {}) {
|
|
|
5661
5699
|
const { proc: relay, port: proxyPort } = await startRelay(nodePath, windowsHost, debugPort);
|
|
5662
5700
|
try {
|
|
5663
5701
|
await waitForChrome(windowsHost, proxyPort);
|
|
5664
|
-
const browser = await
|
|
5702
|
+
const browser = await chromium2.connectOverCDP(`http://${windowsHost}:${proxyPort}`, { timeout: 5e3 });
|
|
5665
5703
|
return {
|
|
5666
5704
|
browser,
|
|
5667
5705
|
cleanup: async () => {
|
|
@@ -5718,7 +5756,7 @@ async function launchBrowserContext(config, userDataDir, recordVideo, storageSta
|
|
|
5718
5756
|
acceptDownloads: true
|
|
5719
5757
|
});
|
|
5720
5758
|
} catch {
|
|
5721
|
-
context = await
|
|
5759
|
+
context = await chromium3.launchPersistentContext(userDataDir, {
|
|
5722
5760
|
headless: false,
|
|
5723
5761
|
args: persistentArgs,
|
|
5724
5762
|
viewport,
|
|
@@ -5729,7 +5767,7 @@ async function launchBrowserContext(config, userDataDir, recordVideo, storageSta
|
|
|
5729
5767
|
});
|
|
5730
5768
|
}
|
|
5731
5769
|
} else {
|
|
5732
|
-
context = await
|
|
5770
|
+
context = await chromium3.launchPersistentContext(userDataDir, {
|
|
5733
5771
|
headless: false,
|
|
5734
5772
|
args: persistentArgs,
|
|
5735
5773
|
viewport,
|
|
@@ -5747,12 +5785,12 @@ async function launchBrowserContext(config, userDataDir, recordVideo, storageSta
|
|
|
5747
5785
|
ignoreDefaultArgs: ["--enable-automation"]
|
|
5748
5786
|
};
|
|
5749
5787
|
try {
|
|
5750
|
-
browser = await
|
|
5788
|
+
browser = await chromium3.launch({ ...headedOpts, channel: "chrome" });
|
|
5751
5789
|
} catch {
|
|
5752
|
-
browser = await
|
|
5790
|
+
browser = await chromium3.launch(headedOpts);
|
|
5753
5791
|
}
|
|
5754
5792
|
} else {
|
|
5755
|
-
browser = await
|
|
5793
|
+
browser = await launchHeadlessChromium();
|
|
5756
5794
|
}
|
|
5757
5795
|
context = await browser.newContext({
|
|
5758
5796
|
viewport,
|
|
@@ -5789,9 +5827,9 @@ async function launchSignInBrowser() {
|
|
|
5789
5827
|
ignoreDefaultArgs: ["--enable-automation"]
|
|
5790
5828
|
};
|
|
5791
5829
|
try {
|
|
5792
|
-
return await
|
|
5830
|
+
return await chromium3.launch({ ...opts, channel: "chrome" });
|
|
5793
5831
|
} catch {
|
|
5794
|
-
return await
|
|
5832
|
+
return await chromium3.launch(opts);
|
|
5795
5833
|
}
|
|
5796
5834
|
}
|
|
5797
5835
|
async function cleanupBrowserSession(session) {
|
|
@@ -5863,7 +5901,7 @@ async function cancelSignIn() {
|
|
|
5863
5901
|
import { copyFileSync, existsSync as existsSync4, mkdirSync, mkdtempSync, rmSync as rmSync2 } from "fs";
|
|
5864
5902
|
import { homedir as homedir2, tmpdir as tmpdir2 } from "os";
|
|
5865
5903
|
import { dirname, join as join3 } from "path";
|
|
5866
|
-
import { chromium as
|
|
5904
|
+
import { chromium as chromium4 } from "playwright";
|
|
5867
5905
|
var BROWSERS = [
|
|
5868
5906
|
{
|
|
5869
5907
|
key: "chrome",
|
|
@@ -5973,7 +6011,7 @@ async function readState(profileDir, channel, store2) {
|
|
|
5973
6011
|
];
|
|
5974
6012
|
if (store2) args.push(`--password-store=${store2}`);
|
|
5975
6013
|
try {
|
|
5976
|
-
const context = await
|
|
6014
|
+
const context = await chromium4.launchPersistentContext(profileDir, { headless: true, channel, args });
|
|
5977
6015
|
try {
|
|
5978
6016
|
return await context.storageState();
|
|
5979
6017
|
} finally {
|
|
@@ -6590,35 +6628,23 @@ async function setupUserEmail() {
|
|
|
6590
6628
|
console.log(chalk4.white(" needs an address to meter. No card, and no verification"));
|
|
6591
6629
|
console.log(chalk4.white(" needed for your first 5 videos."));
|
|
6592
6630
|
console.log(chalk4.dim(" After that, signing in gets you 10 videos a day."));
|
|
6593
|
-
const browserEmail = await browserLogin();
|
|
6594
|
-
if (browserEmail) {
|
|
6595
|
-
console.log();
|
|
6596
|
-
console.log(chalk4.green(` \u2713 Signed in as ${browserEmail}`));
|
|
6597
|
-
console.log();
|
|
6598
|
-
return browserEmail;
|
|
6599
|
-
}
|
|
6600
6631
|
console.log();
|
|
6601
|
-
console.log(chalk4.dim("
|
|
6632
|
+
console.log(chalk4.dim(" Type your email, or 'b' to sign in through a browser."));
|
|
6602
6633
|
const email = await prompt(chalk4.white(" Email: "));
|
|
6603
|
-
if (!email)
|
|
6634
|
+
if (!email) return null;
|
|
6635
|
+
if (email.toLowerCase() === "b" || email.toLowerCase() === "browser") {
|
|
6636
|
+
const browserEmail = await browserLogin();
|
|
6637
|
+
if (!browserEmail) return null;
|
|
6604
6638
|
console.log();
|
|
6605
|
-
console.log(chalk4.
|
|
6606
|
-
console.log(chalk4.dim(" Nothing has been recorded. Run qp again when you are ready."));
|
|
6607
|
-
console.log(chalk4.dim(" To record with no account at all, use the MCP server:"));
|
|
6608
|
-
console.log(chalk4.dim(" https://quickpeek.co/mcp"));
|
|
6639
|
+
console.log(chalk4.green(` \u2713 Signed in as ${browserEmail}`));
|
|
6609
6640
|
console.log();
|
|
6610
|
-
|
|
6641
|
+
return browserEmail;
|
|
6611
6642
|
}
|
|
6612
6643
|
if (!email.includes("@") || !email.includes(".")) {
|
|
6613
6644
|
console.log();
|
|
6614
6645
|
console.log(chalk4.yellow(" \u26A0 That doesn't look like a valid email address."));
|
|
6615
6646
|
const proceed = await prompt(chalk4.white(" Continue anyway? ") + chalk4.dim("[y/N] "));
|
|
6616
|
-
if (proceed.toLowerCase() !== "y")
|
|
6617
|
-
console.log();
|
|
6618
|
-
console.log(chalk4.dim(" Nothing has been recorded. Run qp again when you are ready."));
|
|
6619
|
-
console.log();
|
|
6620
|
-
process.exit(1);
|
|
6621
|
-
}
|
|
6647
|
+
if (proceed.toLowerCase() !== "y") return null;
|
|
6622
6648
|
}
|
|
6623
6649
|
console.log();
|
|
6624
6650
|
console.log(chalk4.green(" \u2713 Email registered"));
|
|
@@ -7132,10 +7158,11 @@ async function handleRegistrationGate() {
|
|
|
7132
7158
|
console.log(chalk8.dim(" \u2022 10 videos a day instead of 5 in total"));
|
|
7133
7159
|
console.log(chalk8.dim(" \u2022 Keep your video history"));
|
|
7134
7160
|
console.log(chalk8.dim(" \u2022 Upgrade to Pro anytime"));
|
|
7135
|
-
|
|
7136
|
-
|
|
7137
|
-
|
|
7138
|
-
|
|
7161
|
+
console.log();
|
|
7162
|
+
console.log(chalk8.dim(" Type your email, or 'b' to sign in through a browser."));
|
|
7163
|
+
let email = await promptInput("Email: ");
|
|
7164
|
+
if (email.toLowerCase() === "b" || email.toLowerCase() === "browser") {
|
|
7165
|
+
email = await browserLogin() ?? "";
|
|
7139
7166
|
}
|
|
7140
7167
|
if (!email || !email.includes("@")) {
|
|
7141
7168
|
console.log(chalk8.red(" Invalid email. Run qp again to try again."));
|
|
@@ -7210,7 +7237,7 @@ async function runAuthPhase(input) {
|
|
|
7210
7237
|
console.log();
|
|
7211
7238
|
const input2 = await promptInput(`New email or Enter to retry (${userEmail}): `);
|
|
7212
7239
|
if (input2.toLowerCase() === "exit") {
|
|
7213
|
-
gracefulExit();
|
|
7240
|
+
gracefulExit(1);
|
|
7214
7241
|
return null;
|
|
7215
7242
|
}
|
|
7216
7243
|
if (input2?.includes("@")) {
|
|
@@ -7219,7 +7246,7 @@ async function runAuthPhase(input) {
|
|
|
7219
7246
|
const result = await registerFreeUser(newEmail);
|
|
7220
7247
|
if (!result.success) {
|
|
7221
7248
|
registerTask.fail(result.error || "Registration failed");
|
|
7222
|
-
gracefulExit();
|
|
7249
|
+
gracefulExit(1);
|
|
7223
7250
|
return null;
|
|
7224
7251
|
}
|
|
7225
7252
|
if (result.alreadyVerified) {
|
|
@@ -7234,7 +7261,7 @@ async function runAuthPhase(input) {
|
|
|
7234
7261
|
console.log(chalk9.cyan(` \u{1F4E7} Verification sent to: ${newEmail}`));
|
|
7235
7262
|
console.log(chalk9.dim(" Click the link in the email, then run qp again."));
|
|
7236
7263
|
console.log();
|
|
7237
|
-
gracefulExit();
|
|
7264
|
+
gracefulExit(1);
|
|
7238
7265
|
return null;
|
|
7239
7266
|
}
|
|
7240
7267
|
} else {
|
|
@@ -7250,7 +7277,7 @@ async function runAuthPhase(input) {
|
|
|
7250
7277
|
console.log();
|
|
7251
7278
|
console.log(chalk9.cyan(` Register/upgrade at: ${getUpgradeUrl(userEmail)}`));
|
|
7252
7279
|
console.log();
|
|
7253
|
-
gracefulExit();
|
|
7280
|
+
gracefulExit(1);
|
|
7254
7281
|
return null;
|
|
7255
7282
|
}
|
|
7256
7283
|
}
|
|
@@ -7258,7 +7285,7 @@ async function runAuthPhase(input) {
|
|
|
7258
7285
|
} else if (localVideoCount >= FREE_VIDEO_LIMIT) {
|
|
7259
7286
|
const registration = await handleRegistrationGate();
|
|
7260
7287
|
if (!registration) {
|
|
7261
|
-
gracefulExit();
|
|
7288
|
+
gracefulExit(1);
|
|
7262
7289
|
return null;
|
|
7263
7290
|
}
|
|
7264
7291
|
userEmail = registration.email;
|
|
@@ -7268,7 +7295,7 @@ async function runAuthPhase(input) {
|
|
|
7268
7295
|
localVideoCount
|
|
7269
7296
|
});
|
|
7270
7297
|
if (!registration.verified) {
|
|
7271
|
-
gracefulExit();
|
|
7298
|
+
gracefulExit(1);
|
|
7272
7299
|
return null;
|
|
7273
7300
|
}
|
|
7274
7301
|
await displayUsageStatus(userEmail, userTier, localVideoCount);
|
|
@@ -7277,7 +7304,16 @@ async function runAuthPhase(input) {
|
|
|
7277
7304
|
console.log();
|
|
7278
7305
|
console.log(chalk9.cyan(` \u{1F3AC} Video ${localVideoCount + 1}/${FREE_VIDEO_LIMIT} free`));
|
|
7279
7306
|
console.log(chalk9.dim(` ${remaining2 - 1} more after this \u2022 Sign up for 10 a day`));
|
|
7280
|
-
|
|
7307
|
+
const enteredEmail = await setupUserEmail();
|
|
7308
|
+
if (!enteredEmail) {
|
|
7309
|
+
console.log();
|
|
7310
|
+
console.log(chalk9.yellow(" No email, so no video was recorded."));
|
|
7311
|
+
console.log(chalk9.white(" Next: run `qp login` to sign in, then run qp again."));
|
|
7312
|
+
console.log();
|
|
7313
|
+
gracefulExit(1);
|
|
7314
|
+
return null;
|
|
7315
|
+
}
|
|
7316
|
+
userEmail = enteredEmail;
|
|
7281
7317
|
let registeredVerified = false;
|
|
7282
7318
|
try {
|
|
7283
7319
|
const reg = await registerFreeUser(userEmail);
|