@spark-apps/quickpeek 1.1.1 → 1.1.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/dist/index.d.mts +308 -0
- package/dist/index.mjs +1857 -0
- package/dist/qp.js +636 -364
- package/dist/voices-BtYZey4Y.d.mts +287 -0
- package/dist/web.d.mts +102 -0
- package/dist/web.mjs +1025 -0
- package/package.json +13 -1
package/dist/qp.js
CHANGED
|
@@ -137,16 +137,7 @@ var init_utils = __esm({
|
|
|
137
137
|
});
|
|
138
138
|
|
|
139
139
|
// src/lib/api-client.ts
|
|
140
|
-
function
|
|
141
|
-
return Boolean(process.env[ENV_SPARK_API_KEY]);
|
|
142
|
-
}
|
|
143
|
-
function getGroqApiKey() {
|
|
144
|
-
return process.env[ENV_SPARK_API_KEY] || null;
|
|
145
|
-
}
|
|
146
|
-
function getStripeUrl() {
|
|
147
|
-
return process.env[ENV_STRIPE_BACKEND] || DEFAULT_STRIPE_BACKEND;
|
|
148
|
-
}
|
|
149
|
-
async function getOrRefreshToken(apiKey) {
|
|
140
|
+
async function getOrRefreshToken() {
|
|
150
141
|
const now = Date.now();
|
|
151
142
|
if (cachedToken && cachedToken.expiresAt > now + 3e5) {
|
|
152
143
|
return cachedToken.token;
|
|
@@ -154,7 +145,7 @@ async function getOrRefreshToken(apiKey) {
|
|
|
154
145
|
try {
|
|
155
146
|
const response = await fetch(SPARK_AI_TOKEN, {
|
|
156
147
|
method: "POST",
|
|
157
|
-
headers: { "X-Api-Key":
|
|
148
|
+
headers: { "X-Api-Key": API_KEY }
|
|
158
149
|
});
|
|
159
150
|
const data = await response.json();
|
|
160
151
|
if (!response.ok || !data.success || !data.token) {
|
|
@@ -163,7 +154,6 @@ async function getOrRefreshToken(apiKey) {
|
|
|
163
154
|
cachedToken = {
|
|
164
155
|
token: data.token,
|
|
165
156
|
expiresAt: data.expiresAt || now + 36e5
|
|
166
|
-
// Default 1 hour
|
|
167
157
|
};
|
|
168
158
|
return data.token;
|
|
169
159
|
} catch (err) {
|
|
@@ -171,16 +161,15 @@ async function getOrRefreshToken(apiKey) {
|
|
|
171
161
|
}
|
|
172
162
|
}
|
|
173
163
|
async function callAI(systemPrompt, userPrompt, tier = "free") {
|
|
174
|
-
|
|
175
|
-
if (!apiKey) {
|
|
164
|
+
if (!API_KEY) {
|
|
176
165
|
return {
|
|
177
166
|
success: false,
|
|
178
|
-
error:
|
|
167
|
+
error: "AI service is not configured. Please report this issue.",
|
|
179
168
|
isAuthError: true
|
|
180
169
|
};
|
|
181
170
|
}
|
|
182
171
|
try {
|
|
183
|
-
const token = await getOrRefreshToken(
|
|
172
|
+
const token = await getOrRefreshToken();
|
|
184
173
|
let lastRateLimitError = null;
|
|
185
174
|
for (let attempt = 1; attempt <= 3; attempt++) {
|
|
186
175
|
try {
|
|
@@ -207,7 +196,7 @@ async function callAI(systemPrompt, userPrompt, tier = "free") {
|
|
|
207
196
|
success: false,
|
|
208
197
|
error,
|
|
209
198
|
isRateLimit: true,
|
|
210
|
-
|
|
199
|
+
...retryAfter ? { retryAfterSeconds: parseInt(retryAfter, 10) } : {}
|
|
211
200
|
};
|
|
212
201
|
await new Promise((resolve9) => setTimeout(resolve9, Math.min(delayMs, 3e4)));
|
|
213
202
|
continue;
|
|
@@ -232,7 +221,7 @@ async function callAI(systemPrompt, userPrompt, tier = "free") {
|
|
|
232
221
|
used: data.limits?.used ?? 0,
|
|
233
222
|
limit: data.limits?.limit ?? 100,
|
|
234
223
|
tier,
|
|
235
|
-
resetAt: data.limits
|
|
224
|
+
...data.limits?.resetAt ? { resetAt: data.limits.resetAt } : {}
|
|
236
225
|
}
|
|
237
226
|
};
|
|
238
227
|
} catch (err) {
|
|
@@ -246,14 +235,32 @@ async function callAI(systemPrompt, userPrompt, tier = "free") {
|
|
|
246
235
|
return { success: false, error: `Spark AI error: ${getErrorMessage(err)}` };
|
|
247
236
|
}
|
|
248
237
|
}
|
|
249
|
-
function
|
|
250
|
-
|
|
238
|
+
function parseAIPlanResponse(content) {
|
|
239
|
+
try {
|
|
240
|
+
let processedContent = content;
|
|
241
|
+
const codeBlockMatch = processedContent.match(/```(?:json)?\s*([\s\S]*?)```/);
|
|
242
|
+
if (codeBlockMatch?.[1]) {
|
|
243
|
+
processedContent = codeBlockMatch[1].trim();
|
|
244
|
+
}
|
|
245
|
+
const jsonMatch = processedContent.match(/\{[\s\S]*\}/);
|
|
246
|
+
if (!jsonMatch) {
|
|
247
|
+
return { success: false, error: "No valid JSON found in AI response" };
|
|
248
|
+
}
|
|
249
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
250
|
+
return { success: true, plan: parsed };
|
|
251
|
+
} catch (err) {
|
|
252
|
+
return {
|
|
253
|
+
success: false,
|
|
254
|
+
error: `Failed to parse AI response: ${err instanceof Error ? err.message : String(err)}`
|
|
255
|
+
};
|
|
256
|
+
}
|
|
251
257
|
}
|
|
252
|
-
function
|
|
253
|
-
|
|
254
|
-
|
|
258
|
+
function normalizeUrl(input) {
|
|
259
|
+
let url = input.trim();
|
|
260
|
+
url = url.replace(/^(https?:\/\/)?(www\.)?/i, "");
|
|
261
|
+
return `https://${url}`;
|
|
255
262
|
}
|
|
256
|
-
var SPARK_AI_BASE, SPARK_AI_CHAT, SPARK_AI_TOKEN,
|
|
263
|
+
var SPARK_AI_BASE, SPARK_AI_CHAT, SPARK_AI_TOKEN, API_KEY, cachedToken;
|
|
257
264
|
var init_api_client = __esm({
|
|
258
265
|
"src/lib/api-client.ts"() {
|
|
259
266
|
"use strict";
|
|
@@ -261,29 +268,95 @@ var init_api_client = __esm({
|
|
|
261
268
|
SPARK_AI_BASE = "https://sparkbrain.app";
|
|
262
269
|
SPARK_AI_CHAT = `${SPARK_AI_BASE}/api/chat`;
|
|
263
270
|
SPARK_AI_TOKEN = `${SPARK_AI_BASE}/api/models/token`;
|
|
264
|
-
|
|
265
|
-
ENV_STRIPE_BACKEND = "QP_STRIPE_BACKEND";
|
|
266
|
-
DEFAULT_STRIPE_BACKEND = "https://sparkstripe.com";
|
|
271
|
+
API_KEY = "spark_ai-8eefdbe9be3069cad2566cb5753dcff9";
|
|
267
272
|
cachedToken = null;
|
|
268
273
|
}
|
|
269
274
|
});
|
|
270
275
|
|
|
276
|
+
// src/lib/SparkStripe-feature-gating.ts
|
|
277
|
+
async function _fetch(email, init) {
|
|
278
|
+
const r = await fetch(`${BASE}/api/public/subscription/status-public?email=${encodeURIComponent(email)}&app_id=${APP_ID}`, init);
|
|
279
|
+
if (!r.ok) throw new Error(`spark-stripe: ${r.status}`);
|
|
280
|
+
return r.json();
|
|
281
|
+
}
|
|
282
|
+
async function getStatusCached(email) {
|
|
283
|
+
const k = email.toLowerCase(), hit = _cache.get(k);
|
|
284
|
+
if (hit && Date.now() < hit.exp) return hit.data;
|
|
285
|
+
const data = await getStatus(email);
|
|
286
|
+
_cache.set(k, { data, exp: Date.now() + 6e4 });
|
|
287
|
+
return data;
|
|
288
|
+
}
|
|
289
|
+
function getTier(d) {
|
|
290
|
+
if (!d.subscription || !d.registered) return "free";
|
|
291
|
+
if (d.subscription.status === "canceled" || d.subscription.status === "past_due") return "free";
|
|
292
|
+
return TIERS[d.subscription.price_id] ?? "free";
|
|
293
|
+
}
|
|
294
|
+
async function getTierByEmail(email) {
|
|
295
|
+
try {
|
|
296
|
+
return getTier(await getStatusCached(email));
|
|
297
|
+
} catch {
|
|
298
|
+
return "free";
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
async function isVerified(email) {
|
|
302
|
+
try {
|
|
303
|
+
return (await getStatusCached(email)).verified;
|
|
304
|
+
} catch {
|
|
305
|
+
return false;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
function pricingUrl(opts) {
|
|
309
|
+
const u = new URL(`${BASE}/pricing/${APP_ID}`);
|
|
310
|
+
if (opts?.email) u.searchParams.set("email", opts.email);
|
|
311
|
+
if (opts?.returnUrl) u.searchParams.set("return_url", opts.returnUrl);
|
|
312
|
+
return u.toString();
|
|
313
|
+
}
|
|
314
|
+
var APP_ID, BASE, TIERS, RANK, FEATURES, getStatus, _cache, isPaid, hasAccess, hasTrialRemaining, showPaywall, isCanceling;
|
|
315
|
+
var init_SparkStripe_feature_gating = __esm({
|
|
316
|
+
"src/lib/SparkStripe-feature-gating.ts"() {
|
|
317
|
+
"use strict";
|
|
318
|
+
APP_ID = process.env.SPARK_APP_ID ?? "quickpeek";
|
|
319
|
+
BASE = "https://sparkstripe.com";
|
|
320
|
+
TIERS = {
|
|
321
|
+
"price_1TADnPEf9opkuWid9NtqOYON": "pro",
|
|
322
|
+
"price_1TADnPEf9opkuWidgbvvX9ma": "pro",
|
|
323
|
+
"price_1TADm7Ef9opkuWid3I9tquIB": "pro",
|
|
324
|
+
"price_1TADm8Ef9opkuWid1RSA2wCa": "pro",
|
|
325
|
+
"price_1TCREmEf9opkuWidvhuqzqDM": "pro",
|
|
326
|
+
"price_1TCREmEf9opkuWidacf1tiC7": "pro"
|
|
327
|
+
};
|
|
328
|
+
RANK = {
|
|
329
|
+
"free": 0,
|
|
330
|
+
"pro": 1
|
|
331
|
+
};
|
|
332
|
+
FEATURES = { "free": [], "pro": [
|
|
333
|
+
"Everything in Free",
|
|
334
|
+
"No watermark",
|
|
335
|
+
"Full video composition (voiceover + captions + music + transitions)",
|
|
336
|
+
"Unlimited videos",
|
|
337
|
+
"Priority support"
|
|
338
|
+
] };
|
|
339
|
+
getStatus = (email) => _fetch(email);
|
|
340
|
+
_cache = /* @__PURE__ */ new Map();
|
|
341
|
+
isPaid = (d) => getTier(d) !== "free";
|
|
342
|
+
hasAccess = (d, min) => (RANK[getTier(d)] ?? 0) >= (RANK[min] ?? 0);
|
|
343
|
+
hasTrialRemaining = (d) => isPaid(d) || !!d.trial && !d.trial.is_expired && d.trial.uses_remaining > 0;
|
|
344
|
+
showPaywall = (d) => !isPaid(d) && !hasTrialRemaining(d);
|
|
345
|
+
isCanceling = (d) => !!d.subscription && d.subscription.cancel_at_period_end && d.subscription.status !== "canceled";
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
|
|
271
349
|
// src/lib/billing.ts
|
|
272
350
|
function shouldWatermark(tier, videoDurationSeconds) {
|
|
273
351
|
return tier === "free" && videoDurationSeconds > 20;
|
|
274
352
|
}
|
|
275
353
|
function getUpgradeUrl(userEmail) {
|
|
276
|
-
|
|
277
|
-
let url = `${stripe}/pricing/quickpeek`;
|
|
278
|
-
if (userEmail) {
|
|
279
|
-
url += `?email=${encodeURIComponent(userEmail)}`;
|
|
280
|
-
}
|
|
281
|
-
return url;
|
|
354
|
+
return pricingUrl(userEmail ? { email: userEmail } : void 0);
|
|
282
355
|
}
|
|
283
356
|
var init_billing = __esm({
|
|
284
357
|
"src/lib/billing.ts"() {
|
|
285
358
|
"use strict";
|
|
286
|
-
|
|
359
|
+
init_SparkStripe_feature_gating();
|
|
287
360
|
}
|
|
288
361
|
});
|
|
289
362
|
|
|
@@ -340,7 +413,22 @@ var init_config = __esm({
|
|
|
340
413
|
});
|
|
341
414
|
|
|
342
415
|
// src/lib/scraping.ts
|
|
343
|
-
|
|
416
|
+
function isExcludedLink(text, href = "") {
|
|
417
|
+
const lowerText = text.toLowerCase();
|
|
418
|
+
const lowerHref = href.toLowerCase();
|
|
419
|
+
return EXCLUDED_LINK_PATTERNS.some((p) => lowerText.includes(p) || lowerHref.includes(p));
|
|
420
|
+
}
|
|
421
|
+
function shouldSkipLink(text, href, currentPath) {
|
|
422
|
+
if (!href || href === "#") return true;
|
|
423
|
+
try {
|
|
424
|
+
const linkPath = new URL(href).pathname;
|
|
425
|
+
if (linkPath === "/" || linkPath === currentPath) return true;
|
|
426
|
+
} catch {
|
|
427
|
+
return true;
|
|
428
|
+
}
|
|
429
|
+
return isExcludedLink(text, href);
|
|
430
|
+
}
|
|
431
|
+
var EXCLUDED_LINK_PATTERNS, INTERACTIVE_SELECTORS;
|
|
344
432
|
var init_scraping = __esm({
|
|
345
433
|
"src/lib/scraping.ts"() {
|
|
346
434
|
"use strict";
|
|
@@ -407,6 +495,22 @@ var init_scraping = __esm({
|
|
|
407
495
|
"feed",
|
|
408
496
|
"home"
|
|
409
497
|
];
|
|
498
|
+
INTERACTIVE_SELECTORS = [
|
|
499
|
+
"button",
|
|
500
|
+
"input",
|
|
501
|
+
"select",
|
|
502
|
+
"textarea",
|
|
503
|
+
"a[href]",
|
|
504
|
+
'[role="button"]',
|
|
505
|
+
'[role="checkbox"]',
|
|
506
|
+
'[role="radio"]',
|
|
507
|
+
'[role="switch"]',
|
|
508
|
+
'[role="slider"]',
|
|
509
|
+
'[role="combobox"]',
|
|
510
|
+
'[role="menuitem"]',
|
|
511
|
+
'[type="submit"]',
|
|
512
|
+
"[onclick]"
|
|
513
|
+
];
|
|
410
514
|
}
|
|
411
515
|
});
|
|
412
516
|
|
|
@@ -1273,49 +1377,13 @@ var init_styles = __esm({
|
|
|
1273
1377
|
});
|
|
1274
1378
|
|
|
1275
1379
|
// src/lib/subscription-client.ts
|
|
1276
|
-
async function fetchStatus(email) {
|
|
1277
|
-
const cacheKey = `${email}:${APP_ID}`;
|
|
1278
|
-
const cached = responseCache.get(cacheKey);
|
|
1279
|
-
if (cached && Date.now() - cached.timestamp < CACHE_TTL_MS) return cached.data;
|
|
1280
|
-
const stripe = getStripeUrl();
|
|
1281
|
-
try {
|
|
1282
|
-
const params = new URLSearchParams({ email, app_id: APP_ID });
|
|
1283
|
-
const response = await fetch(`${stripe}/api/subscription/status-public?${params}`);
|
|
1284
|
-
if (!response.ok) return null;
|
|
1285
|
-
const data = await response.json();
|
|
1286
|
-
responseCache.set(cacheKey, { data, timestamp: Date.now() });
|
|
1287
|
-
return data;
|
|
1288
|
-
} catch {
|
|
1289
|
-
return null;
|
|
1290
|
-
}
|
|
1291
|
-
}
|
|
1292
|
-
function resolveTier(status) {
|
|
1293
|
-
return status && PRO_STATUSES.includes(status) ? "pro" : "free";
|
|
1294
|
-
}
|
|
1295
|
-
async function getTier(userEmail) {
|
|
1296
|
-
const data = await fetchStatus(userEmail);
|
|
1297
|
-
if (!data) return "free";
|
|
1298
|
-
const status = data.subscription?.status;
|
|
1299
|
-
const tier = resolveTier(status);
|
|
1300
|
-
if (tier === "pro" && status === "trialing" && data.subscription?.current_period_end) {
|
|
1301
|
-
if (new Date(data.subscription.current_period_end) <= /* @__PURE__ */ new Date()) return "free";
|
|
1302
|
-
}
|
|
1303
|
-
return tier;
|
|
1304
|
-
}
|
|
1305
|
-
async function checkVerificationStatus(email) {
|
|
1306
|
-
const data = await fetchStatus(email);
|
|
1307
|
-
if (!data) return { verified: false, tier: "free" };
|
|
1308
|
-
const verified = data.verified === true || data.user?.verified === true || data.user?.email_verified === true;
|
|
1309
|
-
const tier = resolveTier(data.subscription?.status);
|
|
1310
|
-
return { verified, tier };
|
|
1311
|
-
}
|
|
1312
1380
|
async function registerFreeUser(email) {
|
|
1313
|
-
const
|
|
1381
|
+
const APP_ID2 = process.env.SPARK_APP_ID ?? "quickpeek";
|
|
1314
1382
|
try {
|
|
1315
|
-
const response = await fetch(
|
|
1383
|
+
const response = await fetch(`https://sparkstripe.com/api/register-free`, {
|
|
1316
1384
|
method: "POST",
|
|
1317
1385
|
headers: { "Content-Type": "application/json" },
|
|
1318
|
-
body: JSON.stringify({ email, app_id:
|
|
1386
|
+
body: JSON.stringify({ email, app_id: APP_ID2, app_name: "QuickPeek" })
|
|
1319
1387
|
});
|
|
1320
1388
|
const text = await response.text();
|
|
1321
1389
|
let data = {};
|
|
@@ -1340,15 +1408,9 @@ async function registerFreeUser(email) {
|
|
|
1340
1408
|
return { success: false, error: err instanceof Error ? err.message : "Network error" };
|
|
1341
1409
|
}
|
|
1342
1410
|
}
|
|
1343
|
-
var APP_ID, PRO_STATUSES, CACHE_TTL_MS, responseCache;
|
|
1344
1411
|
var init_subscription_client = __esm({
|
|
1345
1412
|
"src/lib/subscription-client.ts"() {
|
|
1346
1413
|
"use strict";
|
|
1347
|
-
init_api_client();
|
|
1348
|
-
APP_ID = "quickpeek";
|
|
1349
|
-
PRO_STATUSES = ["active", "trialing", "lifetime"];
|
|
1350
|
-
CACHE_TTL_MS = 6e4;
|
|
1351
|
-
responseCache = /* @__PURE__ */ new Map();
|
|
1352
1414
|
}
|
|
1353
1415
|
});
|
|
1354
1416
|
|
|
@@ -1869,6 +1931,8 @@ __export(lib_exports, {
|
|
|
1869
1931
|
DEFAULT_CAPTIONS: () => DEFAULT_CAPTIONS,
|
|
1870
1932
|
DEFAULT_CONFIG: () => DEFAULT_CONFIG,
|
|
1871
1933
|
EXCLUDED_LINK_PATTERNS: () => EXCLUDED_LINK_PATTERNS,
|
|
1934
|
+
FEATURES: () => FEATURES,
|
|
1935
|
+
INTERACTIVE_SELECTORS: () => INTERACTIVE_SELECTORS,
|
|
1872
1936
|
LANGUAGE_VOICES: () => LANGUAGE_VOICES,
|
|
1873
1937
|
VERSION: () => VERSION,
|
|
1874
1938
|
alignVoiceoverToRecording: () => alignVoiceoverToRecording,
|
|
@@ -1880,7 +1944,6 @@ __export(lib_exports, {
|
|
|
1880
1944
|
buildWatermarkFilters: () => buildWatermarkFilters,
|
|
1881
1945
|
callAI: () => callAI,
|
|
1882
1946
|
canReuseVoiceover: () => canReuseVoiceover,
|
|
1883
|
-
checkVerificationStatus: () => checkVerificationStatus,
|
|
1884
1947
|
composeVideo: () => composeVideo,
|
|
1885
1948
|
concatMedia: () => concatMedia,
|
|
1886
1949
|
concatVideoWithOutro: () => concatVideoWithOutro,
|
|
@@ -1889,25 +1952,33 @@ __export(lib_exports, {
|
|
|
1889
1952
|
generateASSHeader: () => generateASSHeader,
|
|
1890
1953
|
generateSilence: () => generateSilence,
|
|
1891
1954
|
generateVoiceover: () => generateVoiceover,
|
|
1892
|
-
getBackendUrls: () => getBackendUrls,
|
|
1893
|
-
getCredentials: () => getCredentials,
|
|
1894
1955
|
getErrorMessage: () => getErrorMessage,
|
|
1895
|
-
getGroqApiKey: () => getGroqApiKey,
|
|
1896
1956
|
getLanguageName: () => getLanguageName,
|
|
1897
1957
|
getMediaDuration: () => getMediaDuration,
|
|
1898
|
-
|
|
1899
|
-
|
|
1958
|
+
getStatus: () => getStatus,
|
|
1959
|
+
getStatusCached: () => getStatusCached,
|
|
1960
|
+
getTierByEmail: () => getTierByEmail,
|
|
1900
1961
|
getUpgradeUrl: () => getUpgradeUrl,
|
|
1901
|
-
|
|
1962
|
+
hasAccess: () => hasAccess,
|
|
1963
|
+
hasTrialRemaining: () => hasTrialRemaining,
|
|
1902
1964
|
hexToASS: () => hexToASS,
|
|
1965
|
+
isCanceling: () => isCanceling,
|
|
1966
|
+
isExcludedLink: () => isExcludedLink,
|
|
1967
|
+
isPaid: () => isPaid,
|
|
1968
|
+
isVerified: () => isVerified,
|
|
1903
1969
|
loadExistingVoiceover: () => loadExistingVoiceover,
|
|
1904
1970
|
loadVoiceoverMeta: () => loadVoiceoverMeta,
|
|
1905
1971
|
mapTimestampsAfterCompression: () => mapTimestampsAfterCompression,
|
|
1972
|
+
normalizeUrl: () => normalizeUrl,
|
|
1973
|
+
parseAIPlanResponse: () => parseAIPlanResponse,
|
|
1906
1974
|
parsePartial: () => parsePartial,
|
|
1975
|
+
pricingUrl: () => pricingUrl,
|
|
1907
1976
|
registerFreeUser: () => registerFreeUser,
|
|
1908
1977
|
resolveVoice: () => resolveVoice,
|
|
1909
1978
|
rgbaToHex: () => rgbaToHex,
|
|
1979
|
+
shouldSkipLink: () => shouldSkipLink,
|
|
1910
1980
|
shouldWatermark: () => shouldWatermark,
|
|
1981
|
+
showPaywall: () => showPaywall,
|
|
1911
1982
|
speedUpVideo: () => speedUpVideo,
|
|
1912
1983
|
spliceVideos: () => spliceVideos,
|
|
1913
1984
|
synthesizeSpeech: () => synthesizeSpeech,
|
|
@@ -1926,6 +1997,7 @@ var init_lib = __esm({
|
|
|
1926
1997
|
init_prompts();
|
|
1927
1998
|
init_scraping();
|
|
1928
1999
|
init_styles();
|
|
2000
|
+
init_SparkStripe_feature_gating();
|
|
1929
2001
|
init_subscription_client();
|
|
1930
2002
|
init_tts2();
|
|
1931
2003
|
init_types3();
|
|
@@ -1937,19 +2009,98 @@ var init_lib = __esm({
|
|
|
1937
2009
|
|
|
1938
2010
|
// src/qp.ts
|
|
1939
2011
|
var import_dotenv = __toESM(require("dotenv"));
|
|
1940
|
-
var
|
|
2012
|
+
var import_chalk18 = __toESM(require("chalk"));
|
|
1941
2013
|
|
|
1942
2014
|
// src/commands/capture-cmd.ts
|
|
1943
2015
|
var import_node_path19 = require("path");
|
|
1944
|
-
var
|
|
2016
|
+
var import_chalk9 = __toESM(require("chalk"));
|
|
1945
2017
|
|
|
1946
2018
|
// src/ai/narrate.ts
|
|
1947
2019
|
init_lib();
|
|
1948
2020
|
|
|
1949
2021
|
// src/ai/client.ts
|
|
1950
2022
|
var import_node_readline = require("readline");
|
|
1951
|
-
var
|
|
2023
|
+
var import_chalk2 = __toESM(require("chalk"));
|
|
1952
2024
|
init_lib();
|
|
2025
|
+
|
|
2026
|
+
// src/utils/browser-auth.ts
|
|
2027
|
+
var import_node_http = require("http");
|
|
2028
|
+
var import_node_url = require("url");
|
|
2029
|
+
var import_chalk = __toESM(require("chalk"));
|
|
2030
|
+
init_SparkStripe_feature_gating();
|
|
2031
|
+
var TIMEOUT_MS = 12e4;
|
|
2032
|
+
async function openBrowser(url) {
|
|
2033
|
+
const { exec: exec2 } = await import("child_process");
|
|
2034
|
+
const isWSL2 = process.platform === "linux" && Boolean(process.env.WSL_DISTRO_NAME);
|
|
2035
|
+
const command = isWSL2 ? `cmd.exe /c start "" "${url}"` : process.platform === "darwin" ? `open "${url}"` : `xdg-open "${url}"`;
|
|
2036
|
+
return new Promise((resolve9) => {
|
|
2037
|
+
exec2(command, () => resolve9());
|
|
2038
|
+
});
|
|
2039
|
+
}
|
|
2040
|
+
async function browserLogin() {
|
|
2041
|
+
return new Promise((resolve9) => {
|
|
2042
|
+
let server;
|
|
2043
|
+
let timer;
|
|
2044
|
+
const cleanup = (email) => {
|
|
2045
|
+
clearTimeout(timer);
|
|
2046
|
+
server?.close();
|
|
2047
|
+
resolve9(email);
|
|
2048
|
+
};
|
|
2049
|
+
server = (0, import_node_http.createServer)((req, res) => {
|
|
2050
|
+
const url = new import_node_url.URL(req.url || "/", `http://localhost`);
|
|
2051
|
+
const path = url.pathname;
|
|
2052
|
+
if (path === "/callback" || path === "/") {
|
|
2053
|
+
const email = url.searchParams.get("email");
|
|
2054
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
2055
|
+
res.end(`
|
|
2056
|
+
<!DOCTYPE html>
|
|
2057
|
+
<html>
|
|
2058
|
+
<head><title>QuickPeek</title><style>
|
|
2059
|
+
body { font-family: system-ui; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background: #0a0a0a; color: #fff; }
|
|
2060
|
+
.card { text-align: center; padding: 2rem; }
|
|
2061
|
+
h1 { font-size: 1.5rem; margin-bottom: 0.5rem; }
|
|
2062
|
+
p { color: #888; }
|
|
2063
|
+
</style></head>
|
|
2064
|
+
<body><div class="card">
|
|
2065
|
+
<h1>${email ? "Logged in!" : "Return to your terminal"}</h1>
|
|
2066
|
+
<p>${email ? `Signed in as ${email}` : "Complete setup in your terminal."}</p>
|
|
2067
|
+
<p style="margin-top: 1rem; font-size: 0.875rem;">You can close this tab.</p>
|
|
2068
|
+
</div></body>
|
|
2069
|
+
</html>
|
|
2070
|
+
`);
|
|
2071
|
+
if (email) {
|
|
2072
|
+
cleanup(email);
|
|
2073
|
+
}
|
|
2074
|
+
} else {
|
|
2075
|
+
res.writeHead(404);
|
|
2076
|
+
res.end();
|
|
2077
|
+
}
|
|
2078
|
+
});
|
|
2079
|
+
server.listen(0, "127.0.0.1", () => {
|
|
2080
|
+
const addr = server.address();
|
|
2081
|
+
if (!addr || typeof addr === "string") {
|
|
2082
|
+
cleanup(null);
|
|
2083
|
+
return;
|
|
2084
|
+
}
|
|
2085
|
+
const port = addr.port;
|
|
2086
|
+
const returnUrl = `http://localhost:${port}/callback`;
|
|
2087
|
+
const url = pricingUrl({ returnUrl });
|
|
2088
|
+
console.log();
|
|
2089
|
+
console.log(import_chalk.default.cyan(" Opening browser to sign in..."));
|
|
2090
|
+
console.log(import_chalk.default.dim(` ${url}`));
|
|
2091
|
+
console.log();
|
|
2092
|
+
console.log(import_chalk.default.dim(" Waiting for sign-in (press Ctrl+C to cancel)..."));
|
|
2093
|
+
openBrowser(url);
|
|
2094
|
+
});
|
|
2095
|
+
timer = setTimeout(() => {
|
|
2096
|
+
console.log();
|
|
2097
|
+
console.log(import_chalk.default.yellow(" Sign-in timed out."));
|
|
2098
|
+
cleanup(null);
|
|
2099
|
+
}, TIMEOUT_MS);
|
|
2100
|
+
});
|
|
2101
|
+
}
|
|
2102
|
+
|
|
2103
|
+
// src/ai/client.ts
|
|
1953
2104
|
var lastRateLimitInfo = null;
|
|
1954
2105
|
function getLastRateLimitInfo() {
|
|
1955
2106
|
return lastRateLimitInfo;
|
|
@@ -1965,78 +2116,73 @@ function prompt(question) {
|
|
|
1965
2116
|
}
|
|
1966
2117
|
async function setupUserEmail() {
|
|
1967
2118
|
console.log();
|
|
1968
|
-
console.log(
|
|
1969
|
-
console.log(
|
|
2119
|
+
console.log(import_chalk2.default.yellow(" \u2709 Account Setup"));
|
|
2120
|
+
console.log(import_chalk2.default.dim(" \u2500".repeat(20)));
|
|
1970
2121
|
console.log();
|
|
1971
|
-
console.log(
|
|
1972
|
-
console.log(
|
|
2122
|
+
console.log(import_chalk2.default.white(" Sign in with your Google account to track usage"));
|
|
2123
|
+
console.log(import_chalk2.default.white(" and unlock Pro features."));
|
|
2124
|
+
const browserEmail = await browserLogin();
|
|
2125
|
+
if (browserEmail) {
|
|
2126
|
+
console.log();
|
|
2127
|
+
console.log(import_chalk2.default.green(` \u2713 Signed in as ${browserEmail}`));
|
|
2128
|
+
console.log();
|
|
2129
|
+
return browserEmail;
|
|
2130
|
+
}
|
|
1973
2131
|
console.log();
|
|
1974
|
-
|
|
2132
|
+
console.log(import_chalk2.default.dim(" Or enter your email manually:"));
|
|
2133
|
+
const email = await prompt(import_chalk2.default.white(" Email: "));
|
|
1975
2134
|
if (!email) {
|
|
1976
2135
|
console.log();
|
|
1977
|
-
console.log(
|
|
2136
|
+
console.log(import_chalk2.default.red(" No email provided. Exiting."));
|
|
1978
2137
|
process.exit(1);
|
|
1979
2138
|
}
|
|
1980
2139
|
if (!email.includes("@") || !email.includes(".")) {
|
|
1981
2140
|
console.log();
|
|
1982
|
-
console.log(
|
|
1983
|
-
const proceed = await prompt(
|
|
2141
|
+
console.log(import_chalk2.default.yellow(" \u26A0 That doesn't look like a valid email address."));
|
|
2142
|
+
const proceed = await prompt(import_chalk2.default.white(" Continue anyway? ") + import_chalk2.default.dim("[y/N] "));
|
|
1984
2143
|
if (proceed.toLowerCase() !== "y") {
|
|
1985
2144
|
process.exit(1);
|
|
1986
2145
|
}
|
|
1987
2146
|
}
|
|
1988
2147
|
console.log();
|
|
1989
|
-
console.log(
|
|
2148
|
+
console.log(import_chalk2.default.green(" \u2713 Email registered"));
|
|
1990
2149
|
console.log();
|
|
1991
2150
|
return email;
|
|
1992
2151
|
}
|
|
1993
|
-
function checkCredentials() {
|
|
1994
|
-
if (!hasCredentials()) {
|
|
1995
|
-
console.log();
|
|
1996
|
-
console.log(import_chalk.default.red(" \u26A0 Spark AI API key not configured"));
|
|
1997
|
-
console.log();
|
|
1998
|
-
console.log(import_chalk.default.dim(" Set the following environment variable:"));
|
|
1999
|
-
console.log(import_chalk.default.white(" SPARK_API_KEY=your-spark-api-key"));
|
|
2000
|
-
console.log();
|
|
2001
|
-
console.log(import_chalk.default.dim(" Get a key at: https://sparkbrain.app/dashboard"));
|
|
2002
|
-
console.log();
|
|
2003
|
-
process.exit(1);
|
|
2004
|
-
}
|
|
2005
|
-
}
|
|
2006
2152
|
async function callGroq(systemPrompt, userPrompt, logError2, _userEmail, tier = "free") {
|
|
2007
2153
|
const result = await callAI(systemPrompt, userPrompt, tier);
|
|
2008
2154
|
if (!result.success) {
|
|
2009
2155
|
await logError2("AI API", new Error(result.error));
|
|
2010
2156
|
if (result.isRateLimit) {
|
|
2011
2157
|
console.log();
|
|
2012
|
-
console.log(
|
|
2158
|
+
console.log(import_chalk2.default.red(" \u26A0 Rate Limit Reached (after 3 retries)"));
|
|
2013
2159
|
console.log();
|
|
2014
|
-
console.log(
|
|
2015
|
-
console.log(
|
|
2160
|
+
console.log(import_chalk2.default.dim(" Spark AI daily limit exceeded. You may have hit your quota."));
|
|
2161
|
+
console.log(import_chalk2.default.dim(" Options:"));
|
|
2016
2162
|
if (result.retryAfterSeconds) {
|
|
2017
2163
|
const minutes = Math.ceil(result.retryAfterSeconds / 60);
|
|
2018
|
-
console.log(
|
|
2164
|
+
console.log(import_chalk2.default.dim(` \u2022 Wait ${minutes} minutes until quota resets`));
|
|
2019
2165
|
} else {
|
|
2020
|
-
console.log(
|
|
2166
|
+
console.log(import_chalk2.default.dim(" \u2022 Wait until tomorrow (limits reset at midnight UTC)"));
|
|
2021
2167
|
}
|
|
2022
|
-
console.log(
|
|
2023
|
-
console.log(
|
|
2168
|
+
console.log(import_chalk2.default.dim(" \u2022 Check your usage at: https://sparkbrain.app/dashboard"));
|
|
2169
|
+
console.log(import_chalk2.default.dim(" \u2022 Upgrade to a higher tier for more requests"));
|
|
2024
2170
|
console.log();
|
|
2025
2171
|
process.exit(1);
|
|
2026
2172
|
}
|
|
2027
2173
|
if (result.isAuthError) {
|
|
2028
2174
|
console.log();
|
|
2029
|
-
console.log(
|
|
2175
|
+
console.log(import_chalk2.default.red(" \u26A0 Authentication Error"));
|
|
2030
2176
|
console.log();
|
|
2031
|
-
console.log(
|
|
2177
|
+
console.log(import_chalk2.default.dim(` ${result.error}`));
|
|
2032
2178
|
console.log();
|
|
2033
2179
|
process.exit(1);
|
|
2034
2180
|
}
|
|
2035
2181
|
console.log();
|
|
2036
|
-
console.log(
|
|
2182
|
+
console.log(import_chalk2.default.red(" \u26A0 AI Error"));
|
|
2037
2183
|
console.log();
|
|
2038
2184
|
const firstLine = result.error.split("\n")[0] ?? result.error;
|
|
2039
|
-
console.log(
|
|
2185
|
+
console.log(import_chalk2.default.dim(` ${firstLine.slice(0, 80)}`));
|
|
2040
2186
|
console.log();
|
|
2041
2187
|
process.exit(1);
|
|
2042
2188
|
}
|
|
@@ -2162,11 +2308,11 @@ function eventsToSteps(events, url) {
|
|
|
2162
2308
|
let prevClickSelector;
|
|
2163
2309
|
let prevClickTime = 0;
|
|
2164
2310
|
let lastNavigatedUrl;
|
|
2165
|
-
const normalizedOriginUrl =
|
|
2311
|
+
const normalizedOriginUrl = normalizeUrl2(url);
|
|
2166
2312
|
for (const event of events) {
|
|
2167
2313
|
if (event.type === "navigate") {
|
|
2168
2314
|
if (!event.url) continue;
|
|
2169
|
-
const normalized =
|
|
2315
|
+
const normalized = normalizeUrl2(event.url);
|
|
2170
2316
|
if (normalized === normalizedOriginUrl) continue;
|
|
2171
2317
|
if (lastNavigatedUrl !== void 0 && normalized === lastNavigatedUrl) continue;
|
|
2172
2318
|
lastNavigatedUrl = normalized;
|
|
@@ -2216,7 +2362,7 @@ function eventsToSteps(events, url) {
|
|
|
2216
2362
|
}
|
|
2217
2363
|
return steps;
|
|
2218
2364
|
}
|
|
2219
|
-
function
|
|
2365
|
+
function normalizeUrl2(url) {
|
|
2220
2366
|
try {
|
|
2221
2367
|
const u = new URL(url);
|
|
2222
2368
|
return u.origin + u.pathname.replace(/\/$/, "");
|
|
@@ -2227,14 +2373,14 @@ function normalizeUrl(url) {
|
|
|
2227
2373
|
|
|
2228
2374
|
// src/capture.ts
|
|
2229
2375
|
var import_node_child_process4 = require("child_process");
|
|
2230
|
-
var
|
|
2376
|
+
var import_node_http3 = require("http");
|
|
2231
2377
|
var import_node_readline2 = require("readline");
|
|
2232
2378
|
var import_playwright3 = require("playwright");
|
|
2233
2379
|
|
|
2234
2380
|
// src/browser/wsl.ts
|
|
2235
2381
|
var import_node_child_process3 = require("child_process");
|
|
2236
2382
|
var import_node_fs3 = require("fs");
|
|
2237
|
-
var
|
|
2383
|
+
var import_node_http2 = require("http");
|
|
2238
2384
|
var import_playwright2 = require("playwright");
|
|
2239
2385
|
function isWSL() {
|
|
2240
2386
|
if (process.platform !== "linux") return false;
|
|
@@ -2312,7 +2458,7 @@ function waitForChrome(host, port) {
|
|
|
2312
2458
|
reject(new Error("Chrome debug port did not open within 20 s"));
|
|
2313
2459
|
return;
|
|
2314
2460
|
}
|
|
2315
|
-
const req = (0,
|
|
2461
|
+
const req = (0, import_node_http2.request)({ host, port, path: "/json/version", method: "GET" }, (res) => {
|
|
2316
2462
|
res.resume();
|
|
2317
2463
|
resolve9();
|
|
2318
2464
|
});
|
|
@@ -2502,7 +2648,7 @@ async function launchCaptureChrome() {
|
|
|
2502
2648
|
const windowsHost = getWindowsHostIP();
|
|
2503
2649
|
const probe = await startRelay(nodePath, windowsHost, debugPort);
|
|
2504
2650
|
const alreadyUp = await new Promise((resolve9) => {
|
|
2505
|
-
const req = (0,
|
|
2651
|
+
const req = (0, import_node_http3.request)({ host: windowsHost, port: probe.port, path: "/json/version", method: "GET" }, (res) => {
|
|
2506
2652
|
res.resume();
|
|
2507
2653
|
resolve9(true);
|
|
2508
2654
|
});
|
|
@@ -2831,7 +2977,7 @@ async function logStep(stepNum, action, details) {
|
|
|
2831
2977
|
// src/utils/ui.ts
|
|
2832
2978
|
var import_node_path8 = require("path");
|
|
2833
2979
|
var import_node_readline3 = require("readline");
|
|
2834
|
-
var
|
|
2980
|
+
var import_chalk3 = __toESM(require("chalk"));
|
|
2835
2981
|
var import_ora = __toESM(require("ora"));
|
|
2836
2982
|
var INDENT = " ";
|
|
2837
2983
|
var autoYes = false;
|
|
@@ -2843,16 +2989,16 @@ function task(text) {
|
|
|
2843
2989
|
}
|
|
2844
2990
|
function header(text) {
|
|
2845
2991
|
console.log();
|
|
2846
|
-
console.log(
|
|
2992
|
+
console.log(import_chalk3.default.bold.cyan(`\u25A0 ${text}`));
|
|
2847
2993
|
}
|
|
2848
2994
|
function info(text) {
|
|
2849
|
-
console.log(`${INDENT}${
|
|
2995
|
+
console.log(`${INDENT}${import_chalk3.default.dim("\u2502")} ${import_chalk3.default.dim(text)}`);
|
|
2850
2996
|
}
|
|
2851
2997
|
function confirm(prompt2) {
|
|
2852
2998
|
if (autoYes) return Promise.resolve(true);
|
|
2853
2999
|
const rl = (0, import_node_readline3.createInterface)({ input: process.stdin, output: process.stdout });
|
|
2854
3000
|
return new Promise((res) => {
|
|
2855
|
-
rl.question(
|
|
3001
|
+
rl.question(import_chalk3.default.yellow(`${prompt2} [Y/n] `), (answer) => {
|
|
2856
3002
|
rl.close();
|
|
2857
3003
|
res(answer.toLowerCase() !== "n");
|
|
2858
3004
|
});
|
|
@@ -2862,7 +3008,7 @@ function promptInput(prompt2) {
|
|
|
2862
3008
|
if (autoYes) return Promise.resolve("");
|
|
2863
3009
|
const rl = (0, import_node_readline3.createInterface)({ input: process.stdin, output: process.stdout });
|
|
2864
3010
|
return new Promise((res) => {
|
|
2865
|
-
rl.question(
|
|
3011
|
+
rl.question(import_chalk3.default.cyan(`${INDENT}${prompt2}`), (answer) => {
|
|
2866
3012
|
rl.close();
|
|
2867
3013
|
res(answer.trim());
|
|
2868
3014
|
});
|
|
@@ -2897,7 +3043,7 @@ init_lib();
|
|
|
2897
3043
|
var import_node_fs9 = require("fs");
|
|
2898
3044
|
var import_promises9 = require("fs/promises");
|
|
2899
3045
|
var import_node_path14 = require("path");
|
|
2900
|
-
var
|
|
3046
|
+
var import_chalk4 = __toESM(require("chalk"));
|
|
2901
3047
|
|
|
2902
3048
|
// src/browser/actions.ts
|
|
2903
3049
|
var import_node_fs6 = require("fs");
|
|
@@ -4072,9 +4218,9 @@ async function recordSession(plan, config, stepDurations, workDir, baseDir, task
|
|
|
4072
4218
|
actualStepDurations.push(actualDuration);
|
|
4073
4219
|
}
|
|
4074
4220
|
if (failures.length > 0) {
|
|
4075
|
-
console.log(
|
|
4221
|
+
console.log(import_chalk4.default.yellow(` \u26A0 ${failures.length} step(s) skipped:`));
|
|
4076
4222
|
for (const f of failures) {
|
|
4077
|
-
console.log(
|
|
4223
|
+
console.log(import_chalk4.default.yellow(` - ${f}`));
|
|
4078
4224
|
}
|
|
4079
4225
|
}
|
|
4080
4226
|
const saveTask = task2("Saving video");
|
|
@@ -4245,10 +4391,10 @@ async function composeAndFinish(videoPath, audioPath, assPath, config, userTier
|
|
|
4245
4391
|
async function speedUpToFitLimit(finalPath, config, limitSecs = 60) {
|
|
4246
4392
|
const duration = await getMediaDuration(finalPath);
|
|
4247
4393
|
if (duration <= limitSecs * 1e3) return finalPath;
|
|
4248
|
-
const
|
|
4394
|
+
const chalk19 = require("chalk");
|
|
4249
4395
|
const speedFactor = Math.ceil(duration / (limitSecs * 1e3) * 10) / 10;
|
|
4250
4396
|
console.log();
|
|
4251
|
-
console.log(
|
|
4397
|
+
console.log(chalk19.yellow(` Video is ${(duration / 1e3).toFixed(0)}s - over ${limitSecs}s`));
|
|
4252
4398
|
const speedUp = await confirm(` Speed up ${speedFactor}x to fit under ${limitSecs}s?`);
|
|
4253
4399
|
if (!speedUp) return finalPath;
|
|
4254
4400
|
const speedTask = task(`Speeding up ${speedFactor}x`);
|
|
@@ -4270,13 +4416,142 @@ async function speedUpToFitLimit(finalPath, config, limitSecs = 60) {
|
|
|
4270
4416
|
}
|
|
4271
4417
|
|
|
4272
4418
|
// src/commands/utils/record-helpers.ts
|
|
4419
|
+
var import_chalk7 = __toESM(require("chalk"));
|
|
4420
|
+
init_lib();
|
|
4421
|
+
|
|
4422
|
+
// src/help.ts
|
|
4273
4423
|
var import_chalk5 = __toESM(require("chalk"));
|
|
4274
4424
|
init_lib();
|
|
4275
4425
|
|
|
4426
|
+
// src/logo.ts
|
|
4427
|
+
var import_figlet = __toESM(require("figlet"));
|
|
4428
|
+
var import_gradient_string = __toESM(require("gradient-string"));
|
|
4429
|
+
var DEFAULT_LOGO_CONFIG = {
|
|
4430
|
+
text: "QuickPeek",
|
|
4431
|
+
font: "Slant",
|
|
4432
|
+
horizontalLayout: "default",
|
|
4433
|
+
gradientColors: ["#00ffff", "#00d4ff", "#a855f7", "#ec4899"],
|
|
4434
|
+
suffix: " \u{1F440}"
|
|
4435
|
+
};
|
|
4436
|
+
function renderLogo(config = {}) {
|
|
4437
|
+
const resolved = { ...DEFAULT_LOGO_CONFIG, ...config };
|
|
4438
|
+
const ascii = import_figlet.default.textSync(resolved.text, {
|
|
4439
|
+
font: resolved.font,
|
|
4440
|
+
horizontalLayout: resolved.horizontalLayout
|
|
4441
|
+
});
|
|
4442
|
+
const colored = (0, import_gradient_string.default)(resolved.gradientColors)(ascii);
|
|
4443
|
+
return resolved.suffix ? `${colored}${resolved.suffix}` : colored;
|
|
4444
|
+
}
|
|
4445
|
+
|
|
4446
|
+
// src/help.ts
|
|
4447
|
+
var cmd = import_chalk5.default.cyan;
|
|
4448
|
+
var arg = import_chalk5.default.green;
|
|
4449
|
+
var opt = import_chalk5.default.magenta;
|
|
4450
|
+
var desc = import_chalk5.default.white;
|
|
4451
|
+
var ex = import_chalk5.default.dim;
|
|
4452
|
+
function pad2(colored, visible, width) {
|
|
4453
|
+
return colored + " ".repeat(Math.max(0, width - visible.length));
|
|
4454
|
+
}
|
|
4455
|
+
function showLogo() {
|
|
4456
|
+
const logo = renderLogo();
|
|
4457
|
+
console.log(logo);
|
|
4458
|
+
console.log(import_chalk5.default.dim(` v${VERSION} - Auto-generate demo videos with AI`));
|
|
4459
|
+
console.log(import_chalk5.default.italic.dim(" Because Tutorial Videos are Overrated!\n"));
|
|
4460
|
+
}
|
|
4461
|
+
function formatUsageLines(status, tier) {
|
|
4462
|
+
const lines = [];
|
|
4463
|
+
lines.push(`Videos generated: ${status.usage_count}`);
|
|
4464
|
+
if (tier !== "pro" && status.trial) {
|
|
4465
|
+
const { uses_remaining, max_uses, days_remaining, is_expired } = status.trial;
|
|
4466
|
+
const used = max_uses - uses_remaining;
|
|
4467
|
+
lines.push(`Trial: ${used}/${max_uses} uses${is_expired ? " (expired)" : ` (${uses_remaining} remaining)`}`);
|
|
4468
|
+
if (!is_expired && days_remaining > 0) {
|
|
4469
|
+
lines.push(`Trial ends in ${days_remaining} day${days_remaining === 1 ? "" : "s"}`);
|
|
4470
|
+
}
|
|
4471
|
+
}
|
|
4472
|
+
if (tier === "pro" && status.subscription) {
|
|
4473
|
+
const end = status.subscription.current_period_end;
|
|
4474
|
+
if (status.subscription.is_lifetime) {
|
|
4475
|
+
lines.push("Lifetime access");
|
|
4476
|
+
} else if (isCanceling(status)) {
|
|
4477
|
+
const date = new Date(end).toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" });
|
|
4478
|
+
lines.push(`Cancels: ${date}`);
|
|
4479
|
+
} else if (end) {
|
|
4480
|
+
const date = new Date(end).toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" });
|
|
4481
|
+
lines.push(`Renews: ${date}`);
|
|
4482
|
+
}
|
|
4483
|
+
}
|
|
4484
|
+
return lines;
|
|
4485
|
+
}
|
|
4486
|
+
async function showAccount() {
|
|
4487
|
+
const userData = await loadUserData();
|
|
4488
|
+
if (!userData.email) {
|
|
4489
|
+
console.log(import_chalk5.default.dim(" Not signed in. Run ") + cmd("qp login") + import_chalk5.default.dim(" to sign in."));
|
|
4490
|
+
console.log();
|
|
4491
|
+
return;
|
|
4492
|
+
}
|
|
4493
|
+
const email = userData.email;
|
|
4494
|
+
const tier = userData.tier === "pro" ? "pro" : "free";
|
|
4495
|
+
const tierLabel = tier === "pro" ? import_chalk5.default.green("Pro") : "Free";
|
|
4496
|
+
console.log(` ${tierLabel} plan ${import_chalk5.default.dim("\xB7")} ${import_chalk5.default.dim(email)}`);
|
|
4497
|
+
try {
|
|
4498
|
+
const status = await getStatusCached(email);
|
|
4499
|
+
for (const line of formatUsageLines(status, tier)) {
|
|
4500
|
+
console.log(import_chalk5.default.dim(` ${line}`));
|
|
4501
|
+
}
|
|
4502
|
+
} catch {
|
|
4503
|
+
}
|
|
4504
|
+
console.log();
|
|
4505
|
+
}
|
|
4506
|
+
async function showHelp() {
|
|
4507
|
+
showLogo();
|
|
4508
|
+
await showAccount();
|
|
4509
|
+
const W = 28;
|
|
4510
|
+
const usage = [
|
|
4511
|
+
[`${cmd("qp")} ${arg("<url>")}`, `qp <url>`, "AI-generated demo video"],
|
|
4512
|
+
[`${cmd("qp")} ${arg("<url>")} ${opt("--script")} ${arg("<file>")}`, `qp <url> --script <file>`, "Use your own narration script"],
|
|
4513
|
+
[`${cmd("qp capture")} ${arg("<url>")}`, `qp capture <url>`, "Record interactions manually"],
|
|
4514
|
+
[`${cmd("qp login")}`, `qp login`, "Sign in with your Google account"],
|
|
4515
|
+
[`${cmd("qp logout")}`, `qp logout`, "Sign out"],
|
|
4516
|
+
[`${cmd("qp init")}`, `qp init`, "Create config file"],
|
|
4517
|
+
[`${cmd("qp config")} ${arg("<key>")} ${arg("<value>")}`, `qp config <key> <value>`, "Update a config option"]
|
|
4518
|
+
];
|
|
4519
|
+
const options = [
|
|
4520
|
+
[`${opt("lang=")}${arg("xx")}`, `lang=xx`, "Voice language (en, es, ar, zh, fr, de, ja...)"],
|
|
4521
|
+
[`${opt("-d")}, ${opt("--description")} ${arg('"..."')}`, `-d, --description "..."`, "Guide AI with an app description"],
|
|
4522
|
+
[`${opt("-s")}, ${opt("--script")} ${arg("<file>")}`, `-s, --script <file>`, "Narration script (one caption per line)"],
|
|
4523
|
+
[`${opt("-y")}, ${opt("--yes")}`, `-y, --yes`, "Skip confirmation prompts"],
|
|
4524
|
+
[`${opt("--skip-voice")}, ${opt("-sv")}`, `--skip-voice, -sv`, "Reuse existing voiceover"],
|
|
4525
|
+
[`${opt("-f")}, ${opt("--fresh")}`, `-f, --fresh`, "Clear cached voiceover and recording"]
|
|
4526
|
+
];
|
|
4527
|
+
const examples = [
|
|
4528
|
+
`${ex("$")} ${cmd("qp")} ${arg("localhost:3000")}`,
|
|
4529
|
+
`${ex("$")} ${cmd("qp")} ${arg("myapp.com")} ${opt("lang=")}${arg("es")} ${opt("-d")} ${arg('"E-commerce checkout flow"')}`,
|
|
4530
|
+
`${ex("$")} ${cmd("qp")} ${arg("myapp.com")} ${opt("--script")} ${arg("narration.txt")}`,
|
|
4531
|
+
`${ex("$")} ${cmd("qp capture")} ${arg("localhost:3000")}`
|
|
4532
|
+
];
|
|
4533
|
+
console.log();
|
|
4534
|
+
console.log(import_chalk5.default.yellow.bold("USAGE"));
|
|
4535
|
+
for (const [colored, plain, d] of usage) {
|
|
4536
|
+
console.log(` ${pad2(colored, plain, W)}${desc(d)}`);
|
|
4537
|
+
}
|
|
4538
|
+
console.log();
|
|
4539
|
+
console.log(import_chalk5.default.yellow.bold("OPTIONS"));
|
|
4540
|
+
for (const [colored, plain, d] of options) {
|
|
4541
|
+
console.log(` ${pad2(colored, plain, W)}${desc(d)}`);
|
|
4542
|
+
}
|
|
4543
|
+
console.log();
|
|
4544
|
+
console.log(import_chalk5.default.yellow.bold("EXAMPLES"));
|
|
4545
|
+
for (const e of examples) {
|
|
4546
|
+
console.log(` ${e}`);
|
|
4547
|
+
}
|
|
4548
|
+
console.log();
|
|
4549
|
+
}
|
|
4550
|
+
|
|
4276
4551
|
// src/plan.ts
|
|
4277
4552
|
var import_promises11 = require("fs/promises");
|
|
4278
4553
|
var import_node_path16 = require("path");
|
|
4279
|
-
var
|
|
4554
|
+
var import_chalk6 = __toESM(require("chalk"));
|
|
4280
4555
|
var import_playwright5 = require("playwright");
|
|
4281
4556
|
init_lib();
|
|
4282
4557
|
async function crawlExtension(url, extensionPath, workDir, task2) {
|
|
@@ -4302,7 +4577,7 @@ async function crawlExtension(url, extensionPath, workDir, task2) {
|
|
|
4302
4577
|
const contexts = chromeSession.browser.contexts();
|
|
4303
4578
|
context = contexts[0] || await chromeSession.browser.newContext({ viewport: { width: 400, height: 600 } });
|
|
4304
4579
|
} catch (_err) {
|
|
4305
|
-
console.log(
|
|
4580
|
+
console.log(import_chalk6.default.yellow(" \u26A0 Windows Chrome unavailable, using Chromium"));
|
|
4306
4581
|
context = await launchPersistent();
|
|
4307
4582
|
}
|
|
4308
4583
|
} else {
|
|
@@ -4412,7 +4687,6 @@ async function validateAndFixPlan(planPath, task2, logError2, userEmail, tier =
|
|
|
4412
4687
|
plan = JSON.parse(content);
|
|
4413
4688
|
} catch (_parseError) {
|
|
4414
4689
|
const fixTask = task2("Fixing malformed JSON");
|
|
4415
|
-
checkCredentials();
|
|
4416
4690
|
try {
|
|
4417
4691
|
const fixed = await callGroq(
|
|
4418
4692
|
"You fix malformed JSON. Return ONLY valid JSON, no explanation.",
|
|
@@ -4499,31 +4773,41 @@ function displayRateLimitInfo() {
|
|
|
4499
4773
|
const percentage = Math.round(limits.used / limits.limit * 100);
|
|
4500
4774
|
if (percentage >= 90) {
|
|
4501
4775
|
console.log();
|
|
4502
|
-
console.log(
|
|
4776
|
+
console.log(import_chalk7.default.yellow(` \u26A0 ${remaining} requests remaining (${limits.tier} tier)`));
|
|
4503
4777
|
if (limits.tier === "free") {
|
|
4504
|
-
console.log(
|
|
4778
|
+
console.log(import_chalk7.default.dim(` Upgrade: ${getUpgradeUrl()}`));
|
|
4505
4779
|
}
|
|
4506
4780
|
} else if (percentage >= 50) {
|
|
4507
|
-
console.log(
|
|
4781
|
+
console.log(import_chalk7.default.dim(` ${remaining}/${limits.limit} requests remaining`));
|
|
4508
4782
|
}
|
|
4509
4783
|
}
|
|
4510
|
-
function displayUsageStatus(userEmail, userTier,
|
|
4511
|
-
const
|
|
4512
|
-
const
|
|
4513
|
-
console.log(
|
|
4784
|
+
async function displayUsageStatus(userEmail, userTier, _localVideoCount = 0) {
|
|
4785
|
+
const tier = userTier === "pro" ? "pro" : "free";
|
|
4786
|
+
const tierLabel = tier === "pro" ? import_chalk7.default.green("Pro") : "Free";
|
|
4787
|
+
console.log(import_chalk7.default.cyan(` \u2714 ${tierLabel} plan`));
|
|
4788
|
+
console.log(import_chalk7.default.dim(` ${userEmail}`));
|
|
4789
|
+
if (userEmail && userEmail !== "anonymous") {
|
|
4790
|
+
try {
|
|
4791
|
+
const status = await getStatusCached(userEmail);
|
|
4792
|
+
for (const line of formatUsageLines(status, tier)) {
|
|
4793
|
+
console.log(import_chalk7.default.dim(` ${line}`));
|
|
4794
|
+
}
|
|
4795
|
+
} catch {
|
|
4796
|
+
}
|
|
4797
|
+
}
|
|
4514
4798
|
if (userTier === "free") {
|
|
4515
4799
|
const upgradeUrl = getUpgradeUrl(userEmail !== "anonymous" ? userEmail : void 0);
|
|
4516
|
-
console.log(
|
|
4800
|
+
console.log(import_chalk7.default.dim(` Upgrade: ${upgradeUrl}`));
|
|
4517
4801
|
}
|
|
4518
4802
|
}
|
|
4519
4803
|
function displayPlanSummary(plan) {
|
|
4520
4804
|
console.log();
|
|
4521
|
-
console.log(
|
|
4522
|
-
console.log(
|
|
4805
|
+
console.log(import_chalk7.default.dim(` ${plan.title || "Untitled"}`));
|
|
4806
|
+
console.log(import_chalk7.default.dim(` ${plan.steps?.length || 0} steps`));
|
|
4523
4807
|
for (const step of plan.steps || []) {
|
|
4524
4808
|
const text = step.caption || step.action || "";
|
|
4525
4809
|
const caption = text.length > 50 ? `${text.slice(0, 47)}...` : text;
|
|
4526
|
-
console.log(
|
|
4810
|
+
console.log(import_chalk7.default.dim(` ${step.id}. ${caption}`));
|
|
4527
4811
|
}
|
|
4528
4812
|
}
|
|
4529
4813
|
function getOutputDirFromUrl(url) {
|
|
@@ -4753,7 +5037,7 @@ async function runVideoPhase(params) {
|
|
|
4753
5037
|
// src/commands/phases/voiceover-phase.ts
|
|
4754
5038
|
var import_node_fs12 = require("fs");
|
|
4755
5039
|
var import_node_path18 = require("path");
|
|
4756
|
-
var
|
|
5040
|
+
var import_chalk8 = __toESM(require("chalk"));
|
|
4757
5041
|
init_lib();
|
|
4758
5042
|
var INDENT5 = " ";
|
|
4759
5043
|
async function runVoiceoverPhase(plan, config, workDir, skipVoice, fresh) {
|
|
@@ -4795,9 +5079,9 @@ async function runVoiceoverPhase(plan, config, workDir, skipVoice, fresh) {
|
|
|
4795
5079
|
const reuseCheck = canReuseVoiceover(plan.steps, voice, existingMeta);
|
|
4796
5080
|
if (reuseCheck.canReuse) {
|
|
4797
5081
|
console.log();
|
|
4798
|
-
console.log(
|
|
5082
|
+
console.log(import_chalk8.default.dim(` Found existing voiceover in ${config.output.dir}`));
|
|
4799
5083
|
if (reuseCheck.reason) {
|
|
4800
|
-
console.log(
|
|
5084
|
+
console.log(import_chalk8.default.dim(` ${reuseCheck.reason}`));
|
|
4801
5085
|
}
|
|
4802
5086
|
const reuse = await confirm(`${INDENT5}Reuse existing voiceover?`);
|
|
4803
5087
|
if (reuse) {
|
|
@@ -4837,7 +5121,7 @@ async function captureCommand(url, lang) {
|
|
|
4837
5121
|
const captureResult = await captureUserSession(url, workDir);
|
|
4838
5122
|
info(`Captured ${captureResult.events.length} interactions over ${(captureResult.durationMs / 1e3).toFixed(0)}s`);
|
|
4839
5123
|
if (captureResult.events.length === 0) {
|
|
4840
|
-
console.log(
|
|
5124
|
+
console.log(import_chalk9.default.yellow(" No interactions captured. Exiting."));
|
|
4841
5125
|
return;
|
|
4842
5126
|
}
|
|
4843
5127
|
header("Convert");
|
|
@@ -4845,7 +5129,7 @@ async function captureCommand(url, lang) {
|
|
|
4845
5129
|
const rawSteps = eventsToSteps(captureResult.events, url);
|
|
4846
5130
|
if (rawSteps.length === 0) {
|
|
4847
5131
|
convertTask.fail("No actionable steps found");
|
|
4848
|
-
console.log(
|
|
5132
|
+
console.log(import_chalk9.default.yellow(" No actionable steps could be derived from the captured events."));
|
|
4849
5133
|
return;
|
|
4850
5134
|
}
|
|
4851
5135
|
convertTask.succeed(`${rawSteps.length} steps extracted`);
|
|
@@ -4880,13 +5164,13 @@ async function captureCommand(url, lang) {
|
|
|
4880
5164
|
const planPath = (0, import_node_path19.join)(config.output.dir, `plan-${lang ?? "en"}.json`);
|
|
4881
5165
|
await savePlan(plan, planPath);
|
|
4882
5166
|
console.log();
|
|
4883
|
-
console.log(
|
|
4884
|
-
console.log(
|
|
5167
|
+
console.log(import_chalk9.default.bold.green(` Plan saved: ${planPath}`));
|
|
5168
|
+
console.log(import_chalk9.default.dim(` Run: qp ${url} to replay and create a polished demo video`));
|
|
4885
5169
|
console.log();
|
|
4886
5170
|
const makeVideo = await confirm(" Create polished demo video now?");
|
|
4887
5171
|
if (!makeVideo) {
|
|
4888
5172
|
console.log();
|
|
4889
|
-
console.log(
|
|
5173
|
+
console.log(import_chalk9.default.dim(` Run: qp ${url} anytime to create a video from this plan.`));
|
|
4890
5174
|
return;
|
|
4891
5175
|
}
|
|
4892
5176
|
header("Voiceover");
|
|
@@ -4902,20 +5186,20 @@ async function captureCommand(url, lang) {
|
|
|
4902
5186
|
userTier
|
|
4903
5187
|
});
|
|
4904
5188
|
console.log();
|
|
4905
|
-
console.log(
|
|
4906
|
-
console.log(
|
|
5189
|
+
console.log(import_chalk9.default.bold.green(" Done!"));
|
|
5190
|
+
console.log(import_chalk9.default.cyan(` ${finalPath}`));
|
|
4907
5191
|
}
|
|
4908
5192
|
|
|
4909
5193
|
// src/commands/init-cmd.ts
|
|
4910
5194
|
var import_node_fs13 = require("fs");
|
|
4911
5195
|
var import_promises13 = require("fs/promises");
|
|
4912
5196
|
var import_node_path20 = require("path");
|
|
4913
|
-
var
|
|
5197
|
+
var import_chalk10 = __toESM(require("chalk"));
|
|
4914
5198
|
init_lib();
|
|
4915
5199
|
async function initCommand() {
|
|
4916
5200
|
const configPath = (0, import_node_path20.join)(process.cwd(), CONFIG_FILE);
|
|
4917
5201
|
if ((0, import_node_fs13.existsSync)(configPath)) {
|
|
4918
|
-
console.log(
|
|
5202
|
+
console.log(import_chalk10.default.yellow(` \u26A0 ${CONFIG_FILE} already exists`));
|
|
4919
5203
|
return;
|
|
4920
5204
|
}
|
|
4921
5205
|
const defaultConfig = {
|
|
@@ -4940,24 +5224,24 @@ async function initCommand() {
|
|
|
4940
5224
|
};
|
|
4941
5225
|
await (0, import_promises13.writeFile)(configPath, `${JSON.stringify(defaultConfig, null, 2)}
|
|
4942
5226
|
`);
|
|
4943
|
-
console.log(
|
|
4944
|
-
console.log(
|
|
5227
|
+
console.log(import_chalk10.default.green(` \u2714 Created ${CONFIG_FILE}`));
|
|
5228
|
+
console.log(import_chalk10.default.dim(` Edit it or use: qp config <key> <value>`));
|
|
4945
5229
|
}
|
|
4946
5230
|
async function configCommand(key, value) {
|
|
4947
5231
|
const configPath = (0, import_node_path20.join)(process.cwd(), CONFIG_FILE);
|
|
4948
5232
|
if (!key) {
|
|
4949
5233
|
if (!(0, import_node_fs13.existsSync)(configPath)) {
|
|
4950
|
-
console.log(
|
|
5234
|
+
console.log(import_chalk10.default.yellow(` No ${CONFIG_FILE} found. Run: qp init`));
|
|
4951
5235
|
return;
|
|
4952
5236
|
}
|
|
4953
5237
|
const content = await (0, import_promises13.readFile)(configPath, "utf-8");
|
|
4954
|
-
console.log(
|
|
5238
|
+
console.log(import_chalk10.default.cyan(` ${CONFIG_FILE}:`));
|
|
4955
5239
|
console.log(content);
|
|
4956
5240
|
return;
|
|
4957
5241
|
}
|
|
4958
5242
|
if (!value) {
|
|
4959
|
-
console.log(
|
|
4960
|
-
console.log(
|
|
5243
|
+
console.log(import_chalk10.default.red(` Missing value for ${key}`));
|
|
5244
|
+
console.log(import_chalk10.default.dim(` Usage: qp config ${key} <value>`));
|
|
4961
5245
|
return;
|
|
4962
5246
|
}
|
|
4963
5247
|
let config = {};
|
|
@@ -4987,122 +5271,99 @@ async function configCommand(key, value) {
|
|
|
4987
5271
|
}
|
|
4988
5272
|
await (0, import_promises13.writeFile)(configPath, `${JSON.stringify(config, null, 2)}
|
|
4989
5273
|
`);
|
|
4990
|
-
console.log(
|
|
5274
|
+
console.log(import_chalk10.default.green(` \u2714 Set ${key} = ${value}`));
|
|
4991
5275
|
}
|
|
4992
5276
|
|
|
4993
|
-
// src/commands/
|
|
4994
|
-
var
|
|
4995
|
-
var import_promises16 = require("fs/promises");
|
|
4996
|
-
var import_node_path23 = require("path");
|
|
4997
|
-
var import_chalk14 = __toESM(require("chalk"));
|
|
4998
|
-
|
|
4999
|
-
// src/help.ts
|
|
5000
|
-
var import_chalk9 = __toESM(require("chalk"));
|
|
5277
|
+
// src/commands/login-cmd.ts
|
|
5278
|
+
var import_chalk11 = __toESM(require("chalk"));
|
|
5001
5279
|
init_lib();
|
|
5002
|
-
|
|
5003
|
-
// src/logo.ts
|
|
5004
|
-
var import_figlet = __toESM(require("figlet"));
|
|
5005
|
-
var import_gradient_string = __toESM(require("gradient-string"));
|
|
5006
|
-
var DEFAULT_LOGO_CONFIG = {
|
|
5007
|
-
text: "QuickPeek",
|
|
5008
|
-
font: "Slant",
|
|
5009
|
-
horizontalLayout: "default",
|
|
5010
|
-
gradientColors: ["#00ffff", "#00d4ff", "#a855f7", "#ec4899"],
|
|
5011
|
-
suffix: " \u{1F440}"
|
|
5012
|
-
};
|
|
5013
|
-
function renderLogo(config = {}) {
|
|
5014
|
-
const resolved = { ...DEFAULT_LOGO_CONFIG, ...config };
|
|
5015
|
-
const ascii = import_figlet.default.textSync(resolved.text, {
|
|
5016
|
-
font: resolved.font,
|
|
5017
|
-
horizontalLayout: resolved.horizontalLayout
|
|
5018
|
-
});
|
|
5019
|
-
const colored = (0, import_gradient_string.default)(resolved.gradientColors)(ascii);
|
|
5020
|
-
return resolved.suffix ? `${colored}${resolved.suffix}` : colored;
|
|
5021
|
-
}
|
|
5022
|
-
|
|
5023
|
-
// src/help.ts
|
|
5024
|
-
var cmd = import_chalk9.default.cyan;
|
|
5025
|
-
var arg = import_chalk9.default.green;
|
|
5026
|
-
var opt = import_chalk9.default.magenta;
|
|
5027
|
-
var desc = import_chalk9.default.white;
|
|
5028
|
-
var ex = import_chalk9.default.dim;
|
|
5029
|
-
function pad2(colored, visible, width) {
|
|
5030
|
-
return colored + " ".repeat(Math.max(0, width - visible.length));
|
|
5031
|
-
}
|
|
5032
|
-
function showLogo() {
|
|
5033
|
-
const logo = renderLogo();
|
|
5034
|
-
console.log(logo);
|
|
5035
|
-
console.log(import_chalk9.default.dim(` v${VERSION} - Auto-generate demo videos with AI`));
|
|
5036
|
-
console.log(import_chalk9.default.italic.dim(" Because Tutorial Videos are Overrated!\n"));
|
|
5037
|
-
}
|
|
5038
|
-
function showHelp() {
|
|
5039
|
-
showLogo();
|
|
5040
|
-
const W = 28;
|
|
5041
|
-
const usage = [
|
|
5042
|
-
[`${cmd("qp")} ${arg("<url>")}`, `qp <url>`, "AI-generated demo video"],
|
|
5043
|
-
[`${cmd("qp")} ${arg("<url>")} ${opt("--script")} ${arg("<file>")}`, `qp <url> --script <file>`, "Use your own narration script"],
|
|
5044
|
-
[`${cmd("qp capture")} ${arg("<url>")}`, `qp capture <url>`, "Record interactions manually"],
|
|
5045
|
-
[`${cmd("qp init")}`, `qp init`, "Create config file"],
|
|
5046
|
-
[`${cmd("qp config")} ${arg("<key>")} ${arg("<value>")}`, `qp config <key> <value>`, "Update a config option"]
|
|
5047
|
-
];
|
|
5048
|
-
const options = [
|
|
5049
|
-
[`${opt("lang=")}${arg("xx")}`, `lang=xx`, "Voice language (en, es, ar, zh, fr, de, ja...)"],
|
|
5050
|
-
[`${opt("-d")}, ${opt("--description")} ${arg('"..."')}`, `-d, --description "..."`, "Guide AI with an app description"],
|
|
5051
|
-
[`${opt("-s")}, ${opt("--script")} ${arg("<file>")}`, `-s, --script <file>`, "Narration script (one caption per line)"],
|
|
5052
|
-
[`${opt("-y")}, ${opt("--yes")}`, `-y, --yes`, "Skip confirmation prompts"],
|
|
5053
|
-
[`${opt("--skip-voice")}, ${opt("-sv")}`, `--skip-voice, -sv`, "Reuse existing voiceover"],
|
|
5054
|
-
[`${opt("-f")}, ${opt("--fresh")}`, `-f, --fresh`, "Clear cached voiceover and recording"]
|
|
5055
|
-
];
|
|
5056
|
-
const examples = [
|
|
5057
|
-
`${ex("$")} ${cmd("qp")} ${arg("localhost:3000")}`,
|
|
5058
|
-
`${ex("$")} ${cmd("qp")} ${arg("myapp.com")} ${opt("lang=")}${arg("es")} ${opt("-d")} ${arg('"E-commerce checkout flow"')}`,
|
|
5059
|
-
`${ex("$")} ${cmd("qp")} ${arg("myapp.com")} ${opt("--script")} ${arg("narration.txt")}`,
|
|
5060
|
-
`${ex("$")} ${cmd("qp capture")} ${arg("localhost:3000")}`
|
|
5061
|
-
];
|
|
5280
|
+
async function loginCommand() {
|
|
5062
5281
|
console.log();
|
|
5063
|
-
|
|
5064
|
-
|
|
5065
|
-
|
|
5282
|
+
showLogo();
|
|
5283
|
+
const existing = await loadUserData();
|
|
5284
|
+
if (existing.email && existing.verified) {
|
|
5285
|
+
console.log(import_chalk11.default.dim(` Already signed in as ${existing.email}`));
|
|
5286
|
+
console.log(import_chalk11.default.dim(" Run ") + import_chalk11.default.cyan("qp logout") + import_chalk11.default.dim(" first to switch accounts."));
|
|
5287
|
+
console.log();
|
|
5288
|
+
try {
|
|
5289
|
+
const status = await getStatusCached(existing.email);
|
|
5290
|
+
const tier2 = await getTierByEmail(existing.email);
|
|
5291
|
+
const tierLabel2 = tier2 === "pro" ? import_chalk11.default.green("Pro") : "Free";
|
|
5292
|
+
console.log(` ${tierLabel2} plan`);
|
|
5293
|
+
for (const line of formatUsageLines(status, tier2)) {
|
|
5294
|
+
console.log(import_chalk11.default.dim(` ${line}`));
|
|
5295
|
+
}
|
|
5296
|
+
} catch {
|
|
5297
|
+
}
|
|
5298
|
+
console.log();
|
|
5299
|
+
return;
|
|
5066
5300
|
}
|
|
5067
|
-
console.log();
|
|
5068
|
-
console.log(
|
|
5069
|
-
|
|
5070
|
-
|
|
5301
|
+
console.log(import_chalk11.default.white(" Sign in with your Google account to track usage"));
|
|
5302
|
+
console.log(import_chalk11.default.white(" and unlock Pro features."));
|
|
5303
|
+
const email = await browserLogin();
|
|
5304
|
+
if (!email) {
|
|
5305
|
+
console.log();
|
|
5306
|
+
console.log(import_chalk11.default.yellow(" Sign-in cancelled or timed out."));
|
|
5307
|
+
console.log(import_chalk11.default.dim(" Run ") + import_chalk11.default.cyan("qp login") + import_chalk11.default.dim(" to try again."));
|
|
5308
|
+
console.log();
|
|
5309
|
+
return;
|
|
5071
5310
|
}
|
|
5072
|
-
|
|
5073
|
-
|
|
5074
|
-
|
|
5075
|
-
|
|
5311
|
+
let tier = "free";
|
|
5312
|
+
try {
|
|
5313
|
+
tier = await getTierByEmail(email);
|
|
5314
|
+
} catch {
|
|
5076
5315
|
}
|
|
5316
|
+
await saveUserData({ email, verified: true, tier });
|
|
5317
|
+
console.log();
|
|
5318
|
+
const tierLabel = tier === "pro" ? import_chalk11.default.green("Pro") : "Free";
|
|
5319
|
+
console.log(import_chalk11.default.green(` \u2713 Signed in as ${email}`));
|
|
5320
|
+
console.log(` ${tierLabel} plan`);
|
|
5077
5321
|
console.log();
|
|
5078
|
-
|
|
5079
|
-
|
|
5322
|
+
}
|
|
5323
|
+
async function logoutCommand() {
|
|
5324
|
+
console.log();
|
|
5325
|
+
const userData = await loadUserData();
|
|
5326
|
+
if (!userData.email) {
|
|
5327
|
+
console.log(import_chalk11.default.dim(" Not signed in."));
|
|
5328
|
+
console.log();
|
|
5329
|
+
return;
|
|
5330
|
+
}
|
|
5331
|
+
const email = userData.email;
|
|
5332
|
+
await saveUserData({ email: "", verified: false, tier: "free" });
|
|
5333
|
+
console.log(import_chalk11.default.green(` \u2713 Signed out (${email})`));
|
|
5080
5334
|
console.log();
|
|
5081
5335
|
}
|
|
5082
5336
|
|
|
5083
5337
|
// src/commands/record-cmd.ts
|
|
5338
|
+
var import_node_fs16 = require("fs");
|
|
5339
|
+
var import_promises16 = require("fs/promises");
|
|
5340
|
+
var import_node_path23 = require("path");
|
|
5341
|
+
var import_chalk16 = __toESM(require("chalk"));
|
|
5084
5342
|
init_lib();
|
|
5085
5343
|
|
|
5086
5344
|
// src/commands/phases/auth-phase.ts
|
|
5087
|
-
var
|
|
5345
|
+
var import_chalk13 = __toESM(require("chalk"));
|
|
5088
5346
|
init_lib();
|
|
5089
5347
|
|
|
5090
5348
|
// src/commands/utils/auth-helpers.ts
|
|
5091
|
-
var
|
|
5349
|
+
var import_chalk12 = __toESM(require("chalk"));
|
|
5092
5350
|
init_lib();
|
|
5093
5351
|
async function handleRegistrationGate() {
|
|
5094
5352
|
console.log();
|
|
5095
|
-
console.log(
|
|
5096
|
-
console.log(
|
|
5353
|
+
console.log(import_chalk12.default.yellow(" \u{1F389} You've made 5 free videos!"));
|
|
5354
|
+
console.log(import_chalk12.default.white(" Sign in to continue creating videos."));
|
|
5097
5355
|
console.log();
|
|
5098
|
-
console.log(
|
|
5099
|
-
console.log(
|
|
5100
|
-
console.log(
|
|
5101
|
-
console.log(
|
|
5102
|
-
|
|
5103
|
-
|
|
5356
|
+
console.log(import_chalk12.default.dim(" Benefits of signing in:"));
|
|
5357
|
+
console.log(import_chalk12.default.dim(" \u2022 20 more videos per month"));
|
|
5358
|
+
console.log(import_chalk12.default.dim(" \u2022 Keep your video history"));
|
|
5359
|
+
console.log(import_chalk12.default.dim(" \u2022 Upgrade to Pro anytime"));
|
|
5360
|
+
let email = await browserLogin();
|
|
5361
|
+
if (!email) {
|
|
5362
|
+
console.log();
|
|
5363
|
+
email = await promptInput("Email: ");
|
|
5364
|
+
}
|
|
5104
5365
|
if (!email || !email.includes("@")) {
|
|
5105
|
-
console.log(
|
|
5366
|
+
console.log(import_chalk12.default.red(" Invalid email. Run qp again to try again."));
|
|
5106
5367
|
return null;
|
|
5107
5368
|
}
|
|
5108
5369
|
const registerTask = task("Sending verification email");
|
|
@@ -5117,23 +5378,23 @@ async function handleRegistrationGate() {
|
|
|
5117
5378
|
}
|
|
5118
5379
|
registerTask.succeed("Check your email!");
|
|
5119
5380
|
console.log();
|
|
5120
|
-
console.log(
|
|
5121
|
-
console.log(
|
|
5381
|
+
console.log(import_chalk12.default.cyan(" \u{1F4E7} Verification email sent to:"));
|
|
5382
|
+
console.log(import_chalk12.default.white(` ${email}`));
|
|
5122
5383
|
console.log();
|
|
5123
|
-
console.log(
|
|
5384
|
+
console.log(import_chalk12.default.dim(" Click the link in the email, then press Enter to continue..."));
|
|
5124
5385
|
await promptInput("");
|
|
5125
5386
|
const verifyTask = task("Checking verification");
|
|
5126
|
-
const
|
|
5127
|
-
if (
|
|
5387
|
+
const verified = await isVerified(email);
|
|
5388
|
+
if (verified) {
|
|
5128
5389
|
verifyTask.succeed("Email verified!");
|
|
5129
5390
|
return { email, verified: true };
|
|
5130
5391
|
}
|
|
5131
5392
|
verifyTask.fail("Not verified yet");
|
|
5132
5393
|
console.log();
|
|
5133
|
-
console.log(
|
|
5134
|
-
console.log(
|
|
5394
|
+
console.log(import_chalk12.default.yellow(" Email not verified. Check your inbox and spam folder."));
|
|
5395
|
+
console.log(import_chalk12.default.dim(" Run qp again after clicking the verification link."));
|
|
5135
5396
|
console.log();
|
|
5136
|
-
console.log(
|
|
5397
|
+
console.log(import_chalk12.default.cyan(` Or register/upgrade at: ${getUpgradeUrl(email)}`));
|
|
5137
5398
|
console.log();
|
|
5138
5399
|
return { email, verified: false };
|
|
5139
5400
|
}
|
|
@@ -5141,27 +5402,27 @@ async function handleRegistrationGate() {
|
|
|
5141
5402
|
// src/commands/phases/auth-phase.ts
|
|
5142
5403
|
async function runAuthPhase(input) {
|
|
5143
5404
|
let { userEmail, userTier, localVideoCount } = input;
|
|
5144
|
-
const { isVerified } = input;
|
|
5145
|
-
if (userEmail &&
|
|
5405
|
+
const { isVerified: isVerified2 } = input;
|
|
5406
|
+
if (userEmail && isVerified2) {
|
|
5146
5407
|
const tierTask = task("Checking subscription");
|
|
5147
|
-
userTier = await
|
|
5408
|
+
userTier = await getTierByEmail(userEmail);
|
|
5148
5409
|
tierTask.stop();
|
|
5149
|
-
displayUsageStatus(userEmail, userTier, localVideoCount);
|
|
5410
|
+
await displayUsageStatus(userEmail, userTier, localVideoCount);
|
|
5150
5411
|
if (userTier !== input.userTier) {
|
|
5151
5412
|
await saveUserData({ tier: userTier });
|
|
5152
5413
|
}
|
|
5153
|
-
} else if (userEmail && !
|
|
5414
|
+
} else if (userEmail && !isVerified2) {
|
|
5154
5415
|
const verifyTask = task("Checking verification");
|
|
5155
|
-
const
|
|
5156
|
-
if (
|
|
5416
|
+
const verified = await isVerified(userEmail);
|
|
5417
|
+
if (verified) {
|
|
5157
5418
|
verifyTask.stop();
|
|
5158
|
-
await
|
|
5159
|
-
|
|
5160
|
-
displayUsageStatus(userEmail, userTier, localVideoCount);
|
|
5419
|
+
userTier = await getTierByEmail(userEmail);
|
|
5420
|
+
await saveUserData({ verified: true, tier: userTier });
|
|
5421
|
+
await displayUsageStatus(userEmail, userTier, localVideoCount);
|
|
5161
5422
|
} else {
|
|
5162
5423
|
verifyTask.fail("Email not verified");
|
|
5163
5424
|
console.log();
|
|
5164
|
-
console.log(
|
|
5425
|
+
console.log(import_chalk13.default.yellow(" Check your inbox for the verification email."));
|
|
5165
5426
|
console.log();
|
|
5166
5427
|
const input2 = await promptInput(`New email or Enter to retry (${userEmail}): `);
|
|
5167
5428
|
if (input2.toLowerCase() === "exit") {
|
|
@@ -5181,29 +5442,29 @@ async function runAuthPhase(input) {
|
|
|
5181
5442
|
registerTask.stop();
|
|
5182
5443
|
userEmail = newEmail;
|
|
5183
5444
|
await saveUserData({ email: newEmail, verified: true });
|
|
5184
|
-
displayUsageStatus(userEmail, userTier, localVideoCount);
|
|
5445
|
+
await displayUsageStatus(userEmail, userTier, localVideoCount);
|
|
5185
5446
|
} else {
|
|
5186
5447
|
registerTask.succeed("Check your email!");
|
|
5187
5448
|
await saveUserData({ email: newEmail, verified: false });
|
|
5188
5449
|
console.log();
|
|
5189
|
-
console.log(
|
|
5190
|
-
console.log(
|
|
5450
|
+
console.log(import_chalk13.default.cyan(` \u{1F4E7} Verification sent to: ${newEmail}`));
|
|
5451
|
+
console.log(import_chalk13.default.dim(" Click the link in the email, then run qp again."));
|
|
5191
5452
|
console.log();
|
|
5192
5453
|
gracefulExit();
|
|
5193
5454
|
return null;
|
|
5194
5455
|
}
|
|
5195
5456
|
} else {
|
|
5196
5457
|
const recheckTask = task("Rechecking verification");
|
|
5197
|
-
const
|
|
5198
|
-
if (
|
|
5458
|
+
const recheckVerified = await isVerified(userEmail);
|
|
5459
|
+
if (recheckVerified) {
|
|
5199
5460
|
recheckTask.stop();
|
|
5200
|
-
await
|
|
5201
|
-
|
|
5202
|
-
displayUsageStatus(userEmail, userTier, localVideoCount);
|
|
5461
|
+
userTier = await getTierByEmail(userEmail);
|
|
5462
|
+
await saveUserData({ verified: true, tier: userTier });
|
|
5463
|
+
await displayUsageStatus(userEmail, userTier, localVideoCount);
|
|
5203
5464
|
} else {
|
|
5204
5465
|
recheckTask.fail("Still not verified");
|
|
5205
5466
|
console.log();
|
|
5206
|
-
console.log(
|
|
5467
|
+
console.log(import_chalk13.default.cyan(` Register/upgrade at: ${getUpgradeUrl(userEmail)}`));
|
|
5207
5468
|
console.log();
|
|
5208
5469
|
gracefulExit();
|
|
5209
5470
|
return null;
|
|
@@ -5226,12 +5487,12 @@ async function runAuthPhase(input) {
|
|
|
5226
5487
|
gracefulExit();
|
|
5227
5488
|
return null;
|
|
5228
5489
|
}
|
|
5229
|
-
displayUsageStatus(userEmail, userTier, localVideoCount);
|
|
5490
|
+
await displayUsageStatus(userEmail, userTier, localVideoCount);
|
|
5230
5491
|
} else {
|
|
5231
5492
|
const remaining = FREE_VIDEO_LIMIT - localVideoCount;
|
|
5232
5493
|
console.log();
|
|
5233
|
-
console.log(
|
|
5234
|
-
console.log(
|
|
5494
|
+
console.log(import_chalk13.default.cyan(` \u{1F3AC} Video ${localVideoCount + 1}/${FREE_VIDEO_LIMIT} free`));
|
|
5495
|
+
console.log(import_chalk13.default.dim(` ${remaining - 1} more after this \u2022 Sign up for unlimited`));
|
|
5235
5496
|
userEmail = await setupUserEmail();
|
|
5236
5497
|
await saveUserData({ email: userEmail, verified: false, localVideoCount });
|
|
5237
5498
|
}
|
|
@@ -5239,7 +5500,7 @@ async function runAuthPhase(input) {
|
|
|
5239
5500
|
userEmail: userEmail ?? "anonymous",
|
|
5240
5501
|
userTier,
|
|
5241
5502
|
localVideoCount,
|
|
5242
|
-
isVerified
|
|
5503
|
+
isVerified: isVerified2
|
|
5243
5504
|
};
|
|
5244
5505
|
}
|
|
5245
5506
|
|
|
@@ -5247,7 +5508,7 @@ async function runAuthPhase(input) {
|
|
|
5247
5508
|
var import_node_fs14 = require("fs");
|
|
5248
5509
|
var import_promises14 = require("fs/promises");
|
|
5249
5510
|
var import_node_path21 = require("path");
|
|
5250
|
-
var
|
|
5511
|
+
var import_chalk14 = __toESM(require("chalk"));
|
|
5251
5512
|
|
|
5252
5513
|
// src/ai/fix.ts
|
|
5253
5514
|
init_lib();
|
|
@@ -5443,8 +5704,8 @@ async function runPlanPhase(params) {
|
|
|
5443
5704
|
} catch {
|
|
5444
5705
|
}
|
|
5445
5706
|
}
|
|
5446
|
-
const
|
|
5447
|
-
const urlMatches = existingPlan &&
|
|
5707
|
+
const normalizeUrl4 = (u) => u.replace(/\/+$/, "").toLowerCase();
|
|
5708
|
+
const urlMatches = existingPlan && normalizeUrl4(existingPlan.url) === normalizeUrl4(url);
|
|
5448
5709
|
let plan;
|
|
5449
5710
|
const doTranslate = async (failMsg) => {
|
|
5450
5711
|
const translateTask = task(`Translating to ${targetLang}`);
|
|
@@ -5461,23 +5722,23 @@ async function runPlanPhase(params) {
|
|
|
5461
5722
|
};
|
|
5462
5723
|
if (existingPlan && existingPlanPath) {
|
|
5463
5724
|
console.log();
|
|
5464
|
-
console.log(
|
|
5465
|
-
console.log(
|
|
5725
|
+
console.log(import_chalk14.default.dim(` Found existing plan: `) + import_chalk14.default.cyan(formatClickablePath(existingPlanPath)));
|
|
5726
|
+
console.log(import_chalk14.default.dim(` ${existingPlan.steps.length} steps for ${existingPlan.url}`));
|
|
5466
5727
|
if (!urlMatches) {
|
|
5467
|
-
console.log(
|
|
5468
|
-
console.log(
|
|
5469
|
-
console.log(
|
|
5728
|
+
console.log(import_chalk14.default.yellow(` \u26A0 Plan URL differs from argument:`));
|
|
5729
|
+
console.log(import_chalk14.default.yellow(` Plan: ${existingPlan.url}`));
|
|
5730
|
+
console.log(import_chalk14.default.yellow(` Arg: ${url}`));
|
|
5470
5731
|
}
|
|
5471
5732
|
const existingLang = existingPlan.lang || "en";
|
|
5472
5733
|
if (existingLang !== targetLang) {
|
|
5473
|
-
console.log(
|
|
5734
|
+
console.log(import_chalk14.default.yellow(` \u26A0 Plan is in ${existingLang}, config is ${targetLang}`));
|
|
5474
5735
|
}
|
|
5475
5736
|
const reuse = await confirm(`${INDENT6}Use existing plan?`);
|
|
5476
5737
|
if (reuse) {
|
|
5477
5738
|
plan = await validateAndFixPlan(existingPlanPath, task, logError, userEmail, userTier);
|
|
5478
5739
|
if (existingPlanPath !== planPath) {
|
|
5479
5740
|
await savePlan(plan, planPath);
|
|
5480
|
-
console.log(
|
|
5741
|
+
console.log(import_chalk14.default.dim(` Migrated to: ${formatClickablePath(planPath)}`));
|
|
5481
5742
|
}
|
|
5482
5743
|
if (existingLang !== targetLang) {
|
|
5483
5744
|
const translate = await confirm(`${INDENT6}Translate to ${targetLang}?`);
|
|
@@ -5512,7 +5773,7 @@ async function runPlanPhase(params) {
|
|
|
5512
5773
|
uploadIdx++;
|
|
5513
5774
|
}
|
|
5514
5775
|
}
|
|
5515
|
-
console.log(
|
|
5776
|
+
console.log(import_chalk14.default.dim(` Preserved upload paths from previous plan`));
|
|
5516
5777
|
}
|
|
5517
5778
|
await savePlan(plan, planPath);
|
|
5518
5779
|
plan = await validateAndFixPlan(planPath, task, logError, userEmail, userTier);
|
|
@@ -5536,8 +5797,8 @@ async function runPlanPhase(params) {
|
|
|
5536
5797
|
}
|
|
5537
5798
|
displayPlanSummary(plan);
|
|
5538
5799
|
console.log();
|
|
5539
|
-
console.log(
|
|
5540
|
-
console.log(
|
|
5800
|
+
console.log(import_chalk14.default.dim(` Plan: ${formatClickablePath(planPath)}`));
|
|
5801
|
+
console.log(import_chalk14.default.dim(` Enter a prompt to refine, or press Enter to continue.`));
|
|
5541
5802
|
const MAX_REFINEMENTS = 10;
|
|
5542
5803
|
let refinementCount = 0;
|
|
5543
5804
|
while (true) {
|
|
@@ -5555,15 +5816,15 @@ async function runPlanPhase(params) {
|
|
|
5555
5816
|
refineTask.succeed("Plan refined");
|
|
5556
5817
|
displayRateLimitInfo();
|
|
5557
5818
|
if (plan.steps.length > maxSteps) {
|
|
5558
|
-
console.log(
|
|
5819
|
+
console.log(import_chalk14.default.yellow(` \u26A0 ${plan.steps.length} steps exceeds limit of ${maxSteps}`));
|
|
5559
5820
|
}
|
|
5560
5821
|
displayPlanSummary(plan);
|
|
5561
5822
|
console.log();
|
|
5562
|
-
console.log(
|
|
5823
|
+
console.log(import_chalk14.default.dim(` Plan: ${formatClickablePath(planPath)}`));
|
|
5563
5824
|
if (refinementCount >= MAX_REFINEMENTS) {
|
|
5564
5825
|
console.log();
|
|
5565
|
-
console.log(
|
|
5566
|
-
console.log(
|
|
5826
|
+
console.log(import_chalk14.default.yellow(` \u26A0 You've refined the plan ${MAX_REFINEMENTS} times.`));
|
|
5827
|
+
console.log(import_chalk14.default.dim(` Further refinements may incur additional costs.`));
|
|
5567
5828
|
const continueRefining = await confirm(`${INDENT6}Continue refining?`);
|
|
5568
5829
|
if (!continueRefining) break;
|
|
5569
5830
|
}
|
|
@@ -5580,7 +5841,7 @@ async function runPlanPhase(params) {
|
|
|
5580
5841
|
const translatedPlan = JSON.parse(await (0, import_promises14.readFile)(planPath, "utf-8"));
|
|
5581
5842
|
if (translatedPlan.steps?.length === plan.steps.length && translatedPlan.lang === targetLang) {
|
|
5582
5843
|
console.log();
|
|
5583
|
-
console.log(
|
|
5844
|
+
console.log(import_chalk14.default.dim(` Found translated plan: ${formatClickablePath(planPath)}`));
|
|
5584
5845
|
const useTranslated = await confirm(`${INDENT6}Use ${targetLang} translation?`);
|
|
5585
5846
|
if (useTranslated) {
|
|
5586
5847
|
plan = translatedPlan;
|
|
@@ -5592,7 +5853,7 @@ async function runPlanPhase(params) {
|
|
|
5592
5853
|
}
|
|
5593
5854
|
if (!useExistingTranslation) {
|
|
5594
5855
|
console.log();
|
|
5595
|
-
console.log(
|
|
5856
|
+
console.log(import_chalk14.default.yellow(` Plan is in ${planLang}, but voice is set to ${targetLang}`));
|
|
5596
5857
|
const translate = await confirm(`${INDENT6}Translate captions to ${targetLang}?`);
|
|
5597
5858
|
if (translate) await doTranslate("Translation failed, using original");
|
|
5598
5859
|
}
|
|
@@ -5602,7 +5863,7 @@ async function runPlanPhase(params) {
|
|
|
5602
5863
|
|
|
5603
5864
|
// src/commands/phases/validation-phase.ts
|
|
5604
5865
|
var import_promises15 = require("fs/promises");
|
|
5605
|
-
var
|
|
5866
|
+
var import_chalk15 = __toESM(require("chalk"));
|
|
5606
5867
|
async function runValidationPhase(params) {
|
|
5607
5868
|
let { plan } = params;
|
|
5608
5869
|
const { config, planPath, isExtensionUrl, userEmail, userTier = "free" } = params;
|
|
@@ -5653,21 +5914,21 @@ async function runValidationPhase(params) {
|
|
|
5653
5914
|
const stepNum = result.stepIndex + 1;
|
|
5654
5915
|
const step = plan.steps[result.stepIndex];
|
|
5655
5916
|
console.log();
|
|
5656
|
-
console.log(
|
|
5917
|
+
console.log(import_chalk15.default.red(` \u2717 Step ${stepNum}: ${step.action}`));
|
|
5657
5918
|
const caption = step.caption || "";
|
|
5658
|
-
if (caption) console.log(
|
|
5659
|
-
console.log(
|
|
5660
|
-
if (step.value) console.log(
|
|
5661
|
-
console.log(
|
|
5919
|
+
if (caption) console.log(import_chalk15.default.dim(` Caption: ${caption.slice(0, 50)}${caption.length > 50 ? "..." : ""}`));
|
|
5920
|
+
console.log(import_chalk15.default.dim(` Target: ${step.target || "none"}`));
|
|
5921
|
+
if (step.value) console.log(import_chalk15.default.dim(` Value: ${step.value}`));
|
|
5922
|
+
console.log(import_chalk15.default.dim(` Error: ${result.error}`));
|
|
5662
5923
|
if (result.suggestion) {
|
|
5663
|
-
console.log(
|
|
5924
|
+
console.log(import_chalk15.default.yellow(` Hint: ${result.suggestion}`));
|
|
5664
5925
|
}
|
|
5665
5926
|
console.log();
|
|
5666
5927
|
const isUploadMissingFile = step.action === "upload" && (!step.value || step.value.startsWith("[") || step.value.includes("example") || step.value.includes("/path/to"));
|
|
5667
5928
|
if (isUploadMissingFile) {
|
|
5668
5929
|
const filePath = await promptInput('Enter file path to upload (or "skip"): ');
|
|
5669
5930
|
if (filePath.toLowerCase() === "skip") {
|
|
5670
|
-
console.log(
|
|
5931
|
+
console.log(import_chalk15.default.yellow(` Skipping step ${stepNum}`));
|
|
5671
5932
|
continue;
|
|
5672
5933
|
}
|
|
5673
5934
|
if (filePath) {
|
|
@@ -5676,13 +5937,13 @@ async function runValidationPhase(params) {
|
|
|
5676
5937
|
const revalidation2 = await validateStepsOnPage(plan, config, logError, config.output.dir);
|
|
5677
5938
|
const newResult2 = revalidation2.results[result.stepIndex];
|
|
5678
5939
|
if (newResult2?.success) {
|
|
5679
|
-
console.log(
|
|
5940
|
+
console.log(import_chalk15.default.green(` \u2713 Step ${stepNum} now passes`));
|
|
5680
5941
|
validationResult = revalidation2;
|
|
5681
5942
|
continue;
|
|
5682
5943
|
}
|
|
5683
5944
|
result.error = newResult2?.error;
|
|
5684
5945
|
result.suggestion = newResult2?.suggestion;
|
|
5685
|
-
console.log(
|
|
5946
|
+
console.log(import_chalk15.default.red(` Still failing: ${result.error}`));
|
|
5686
5947
|
}
|
|
5687
5948
|
}
|
|
5688
5949
|
const fixTask = task(`Fixing step ${stepNum} with AI`);
|
|
@@ -5704,8 +5965,8 @@ async function runValidationPhase(params) {
|
|
|
5704
5965
|
await savePlan(plan, planPath);
|
|
5705
5966
|
fixTask.succeed("AI suggested a fix");
|
|
5706
5967
|
const fixedStep = plan.steps[result.stepIndex];
|
|
5707
|
-
console.log(
|
|
5708
|
-
if (fixedStep.value) console.log(
|
|
5968
|
+
console.log(import_chalk15.default.dim(` New target: ${fixedStep.target || "none"}`));
|
|
5969
|
+
if (fixedStep.value) console.log(import_chalk15.default.dim(` New value: ${fixedStep.value}`));
|
|
5709
5970
|
} catch {
|
|
5710
5971
|
fixTask.fail("AI could not fix automatically");
|
|
5711
5972
|
}
|
|
@@ -5721,11 +5982,11 @@ async function runValidationPhase(params) {
|
|
|
5721
5982
|
result.error = newResult?.error;
|
|
5722
5983
|
result.suggestion = newResult?.suggestion;
|
|
5723
5984
|
console.log();
|
|
5724
|
-
console.log(
|
|
5985
|
+
console.log(import_chalk15.default.yellow(` Edit step ${stepNum} in: `) + import_chalk15.default.cyan(formatClickablePath(planPath)));
|
|
5725
5986
|
console.log();
|
|
5726
5987
|
const choice = await promptInput('Press Enter after editing, or type "skip" to skip this step: ');
|
|
5727
5988
|
if (choice.toLowerCase() === "skip") {
|
|
5728
|
-
console.log(
|
|
5989
|
+
console.log(import_chalk15.default.yellow(` Skipping step ${stepNum} - may fail during recording`));
|
|
5729
5990
|
continue;
|
|
5730
5991
|
}
|
|
5731
5992
|
plan = JSON.parse(await (0, import_promises15.readFile)(planPath, "utf-8"));
|
|
@@ -5823,23 +6084,22 @@ async function recordCommand(url, langOverride, description, skipVoice, fresh, a
|
|
|
5823
6084
|
if (autoYes2) setAutoYes(true);
|
|
5824
6085
|
console.log();
|
|
5825
6086
|
showLogo();
|
|
5826
|
-
checkCredentials();
|
|
5827
6087
|
const { config, created: configCreated } = await loadConfig(url, langOverride);
|
|
5828
6088
|
let chromeExtension = null;
|
|
5829
6089
|
if (url.startsWith("chrome:")) {
|
|
5830
6090
|
const extensionId = url.slice(7);
|
|
5831
6091
|
chromeExtension = resolveChromeExtension(extensionId);
|
|
5832
6092
|
if (!chromeExtension) {
|
|
5833
|
-
console.log(
|
|
6093
|
+
console.log(import_chalk16.default.red(`
|
|
5834
6094
|
\u2717 Chrome extension not found: ${extensionId}`));
|
|
5835
|
-
console.log(
|
|
6095
|
+
console.log(import_chalk16.default.dim(` Make sure the extension is installed in Chrome
|
|
5836
6096
|
`));
|
|
5837
6097
|
process.exit(1);
|
|
5838
6098
|
}
|
|
5839
6099
|
config.browser = { extension: chromeExtension.path };
|
|
5840
6100
|
url = chromeExtension.popupUrl;
|
|
5841
|
-
console.log(
|
|
5842
|
-
console.log(
|
|
6101
|
+
console.log(import_chalk16.default.dim(` Extension: ${chromeExtension.name}`));
|
|
6102
|
+
console.log(import_chalk16.default.dim(` Path: ${chromeExtension.path}`));
|
|
5843
6103
|
}
|
|
5844
6104
|
config.output.dir = chromeExtension ? `./${chromeExtension.name.toLowerCase().replace(/[^a-z0-9]+/g, "-")}-demo` : getOutputDirFromUrl(url);
|
|
5845
6105
|
const userData = await loadUserData();
|
|
@@ -5854,18 +6114,18 @@ async function recordCommand(url, langOverride, description, skipVoice, fresh, a
|
|
|
5854
6114
|
if (configCreated) {
|
|
5855
6115
|
const configPath = (0, import_node_path23.join)(process.cwd(), CONFIG_FILE);
|
|
5856
6116
|
console.log();
|
|
5857
|
-
console.log(
|
|
5858
|
-
console.log(
|
|
6117
|
+
console.log(import_chalk16.default.green(` \u2714 Created ${CONFIG_FILE}`));
|
|
6118
|
+
console.log(import_chalk16.default.dim(` ${configPath}`));
|
|
5859
6119
|
console.log();
|
|
5860
|
-
console.log(
|
|
5861
|
-
console.log(
|
|
5862
|
-
console.log(
|
|
5863
|
-
console.log(
|
|
5864
|
-
console.log(
|
|
6120
|
+
console.log(import_chalk16.default.cyan(" Review the config file to customize:"));
|
|
6121
|
+
console.log(import_chalk16.default.dim(" \u2022 lang: Voice language (en, es, ar, zh...)"));
|
|
6122
|
+
console.log(import_chalk16.default.dim(" \u2022 video: Resolution, quality, effects"));
|
|
6123
|
+
console.log(import_chalk16.default.dim(" \u2022 music: Background music path and volume"));
|
|
6124
|
+
console.log(import_chalk16.default.dim(" \u2022 output: Output directory"));
|
|
5865
6125
|
console.log();
|
|
5866
6126
|
const proceed = await confirm(`${INDENT3}Proceed with default settings?`);
|
|
5867
6127
|
if (!proceed) {
|
|
5868
|
-
console.log(
|
|
6128
|
+
console.log(import_chalk16.default.yellow(`
|
|
5869
6129
|
Edit ${CONFIG_FILE} and run again.
|
|
5870
6130
|
`));
|
|
5871
6131
|
process.exit(0);
|
|
@@ -5876,14 +6136,14 @@ async function recordCommand(url, langOverride, description, skipVoice, fresh, a
|
|
|
5876
6136
|
const musicPath = (0, import_node_path23.isAbsolute)(config.music.path) ? config.music.path : (0, import_node_path23.resolve)(config.output.dir, config.music.path);
|
|
5877
6137
|
if (!(0, import_node_fs16.existsSync)(musicPath)) {
|
|
5878
6138
|
console.log();
|
|
5879
|
-
console.log(
|
|
5880
|
-
console.log(
|
|
6139
|
+
console.log(import_chalk16.default.yellow(` \u26A0 Music file not found: ${config.music.path}`));
|
|
6140
|
+
console.log(import_chalk16.default.dim(` Config: ${(0, import_node_path23.join)(process.cwd(), CONFIG_FILE)}`));
|
|
5881
6141
|
console.log();
|
|
5882
6142
|
const proceed = await confirm(`${INDENT3}Continue without background music?`);
|
|
5883
6143
|
if (!proceed) {
|
|
5884
|
-
console.log(
|
|
6144
|
+
console.log(import_chalk16.default.yellow(`
|
|
5885
6145
|
Edit music.path in config and run again.`));
|
|
5886
|
-
console.log(
|
|
6146
|
+
console.log(import_chalk16.default.dim(` ${(0, import_node_path23.join)(process.cwd(), CONFIG_FILE)}
|
|
5887
6147
|
`));
|
|
5888
6148
|
process.exit(0);
|
|
5889
6149
|
}
|
|
@@ -5932,19 +6192,19 @@ async function recordCommand(url, langOverride, description, skipVoice, fresh, a
|
|
|
5932
6192
|
await saveUserData({ localVideoCount: newCount });
|
|
5933
6193
|
const remaining = FREE_VIDEO_LIMIT - newCount;
|
|
5934
6194
|
if (remaining > 0) {
|
|
5935
|
-
console.log(
|
|
6195
|
+
console.log(import_chalk16.default.dim(` ${remaining} free video${remaining === 1 ? "" : "s"} remaining`));
|
|
5936
6196
|
}
|
|
5937
6197
|
}
|
|
5938
6198
|
console.log();
|
|
5939
|
-
console.log(
|
|
6199
|
+
console.log(import_chalk16.default.bold.green(" \u2713 Done!"));
|
|
5940
6200
|
console.log();
|
|
5941
|
-
console.log(
|
|
6201
|
+
console.log(import_chalk16.default.cyan(` ${formatClickablePath(finalPath)}`));
|
|
5942
6202
|
console.log();
|
|
5943
6203
|
} catch (err) {
|
|
5944
6204
|
await logError("Record Command", err);
|
|
5945
6205
|
console.log();
|
|
5946
|
-
console.log(
|
|
5947
|
-
console.log(
|
|
6206
|
+
console.log(import_chalk16.default.red(` Error: ${err instanceof Error ? err.message : "Unknown error"}`));
|
|
6207
|
+
console.log(import_chalk16.default.dim(` See ${logFilePath} for details`));
|
|
5948
6208
|
process.exit(1);
|
|
5949
6209
|
}
|
|
5950
6210
|
}
|
|
@@ -5952,7 +6212,7 @@ async function recordCommand(url, langOverride, description, skipVoice, fresh, a
|
|
|
5952
6212
|
// src/commands/script-cmd.ts
|
|
5953
6213
|
var import_promises17 = require("fs/promises");
|
|
5954
6214
|
var import_node_path24 = require("path");
|
|
5955
|
-
var
|
|
6215
|
+
var import_chalk17 = __toESM(require("chalk"));
|
|
5956
6216
|
init_lib();
|
|
5957
6217
|
async function scriptCommand(url, scriptPath, lang) {
|
|
5958
6218
|
const { config, workDir, captions } = await initCommandEnv(url, lang);
|
|
@@ -5960,7 +6220,7 @@ async function scriptCommand(url, scriptPath, lang) {
|
|
|
5960
6220
|
const raw = await (0, import_promises17.readFile)(scriptPath, "utf-8");
|
|
5961
6221
|
const lines = raw.split("\n").map((l) => l.trim()).filter((l) => l.length > 0 && !l.startsWith("#"));
|
|
5962
6222
|
if (lines.length === 0) {
|
|
5963
|
-
console.log(
|
|
6223
|
+
console.log(import_chalk17.default.red(" Script file is empty or has no non-comment lines"));
|
|
5964
6224
|
process.exit(1);
|
|
5965
6225
|
}
|
|
5966
6226
|
info(`${lines.length} captions from script`);
|
|
@@ -5992,15 +6252,15 @@ async function scriptCommand(url, scriptPath, lang) {
|
|
|
5992
6252
|
const assPath = await generateASS(plan, stepAudio, workDir, config, captions);
|
|
5993
6253
|
const finalPath = await composeAndFinish(videoPath, voiceoverPath, assPath, config, "free");
|
|
5994
6254
|
console.log();
|
|
5995
|
-
console.log(
|
|
5996
|
-
console.log(
|
|
6255
|
+
console.log(import_chalk17.default.bold.green(" Done!"));
|
|
6256
|
+
console.log(import_chalk17.default.cyan(` ${finalPath}`));
|
|
5997
6257
|
}
|
|
5998
6258
|
|
|
5999
6259
|
// src/qp.ts
|
|
6000
6260
|
init_lib();
|
|
6001
6261
|
import_dotenv.default.config({ path: ".env.local", quiet: true });
|
|
6002
6262
|
import_dotenv.default.config({ quiet: true });
|
|
6003
|
-
function
|
|
6263
|
+
function normalizeUrl3(input) {
|
|
6004
6264
|
if (input.startsWith("chrome-extension://") || input.startsWith("chrome:")) {
|
|
6005
6265
|
return input;
|
|
6006
6266
|
}
|
|
@@ -6032,6 +6292,12 @@ function parseArgs(args) {
|
|
|
6032
6292
|
return { ...base, command: "help" };
|
|
6033
6293
|
}
|
|
6034
6294
|
const first = args[0];
|
|
6295
|
+
if (first === "login") {
|
|
6296
|
+
return { ...base, command: "login" };
|
|
6297
|
+
}
|
|
6298
|
+
if (first === "logout") {
|
|
6299
|
+
return { ...base, command: "logout" };
|
|
6300
|
+
}
|
|
6035
6301
|
if (first === "init") {
|
|
6036
6302
|
return { ...base, command: "init" };
|
|
6037
6303
|
}
|
|
@@ -6055,7 +6321,7 @@ function parseArgs(args) {
|
|
|
6055
6321
|
if (arg2.startsWith("lang=")) {
|
|
6056
6322
|
lang2 = arg2.slice(5);
|
|
6057
6323
|
} else if (!arg2.startsWith("-") && !url2 && !arg2.includes("=")) {
|
|
6058
|
-
url2 =
|
|
6324
|
+
url2 = normalizeUrl3(arg2);
|
|
6059
6325
|
}
|
|
6060
6326
|
}
|
|
6061
6327
|
if (!url2) return { ...base, command: "help" };
|
|
@@ -6088,7 +6354,7 @@ function parseArgs(args) {
|
|
|
6088
6354
|
if (arg2.endsWith(".js") || arg2.endsWith(".ts") || arg2.startsWith("./") || arg2.startsWith("/")) {
|
|
6089
6355
|
continue;
|
|
6090
6356
|
}
|
|
6091
|
-
url =
|
|
6357
|
+
url = normalizeUrl3(arg2);
|
|
6092
6358
|
}
|
|
6093
6359
|
}
|
|
6094
6360
|
if (!url) {
|
|
@@ -6104,7 +6370,13 @@ async function main() {
|
|
|
6104
6370
|
console.log(`quickpeek v${VERSION}`);
|
|
6105
6371
|
break;
|
|
6106
6372
|
case "help":
|
|
6107
|
-
showHelp();
|
|
6373
|
+
await showHelp();
|
|
6374
|
+
break;
|
|
6375
|
+
case "login":
|
|
6376
|
+
await loginCommand();
|
|
6377
|
+
break;
|
|
6378
|
+
case "logout":
|
|
6379
|
+
await logoutCommand();
|
|
6108
6380
|
break;
|
|
6109
6381
|
case "init":
|
|
6110
6382
|
await initCommand();
|
|
@@ -6114,8 +6386,8 @@ async function main() {
|
|
|
6114
6386
|
break;
|
|
6115
6387
|
case "record":
|
|
6116
6388
|
if (!parsed.url) {
|
|
6117
|
-
console.log(
|
|
6118
|
-
console.log(
|
|
6389
|
+
console.log(import_chalk18.default.red(" Missing URL"));
|
|
6390
|
+
console.log(import_chalk18.default.dim(" Run: qp --help"));
|
|
6119
6391
|
process.exit(1);
|
|
6120
6392
|
}
|
|
6121
6393
|
setAutoYes(parsed.yes);
|
|
@@ -6129,8 +6401,8 @@ async function main() {
|
|
|
6129
6401
|
break;
|
|
6130
6402
|
case "capture":
|
|
6131
6403
|
if (!parsed.url) {
|
|
6132
|
-
console.log(
|
|
6133
|
-
console.log(
|
|
6404
|
+
console.log(import_chalk18.default.red(" Missing URL"));
|
|
6405
|
+
console.log(import_chalk18.default.dim(" Run: qp --help"));
|
|
6134
6406
|
process.exit(1);
|
|
6135
6407
|
}
|
|
6136
6408
|
console.log();
|