@spark-apps/quickpeek 1.2.3 → 1.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/assets/music/calm-drifting-piano.mp3 +0 -0
- package/dist/assets/music/lofi-roof-tops.mp3 +0 -0
- package/dist/assets/music/manifest.json +32 -0
- package/dist/assets/music/upbeat-spring-on-the-horizon.mp3 +0 -0
- package/dist/highlight.css +8 -7
- package/dist/index.d.mts +561 -36
- package/dist/index.mjs +2287 -398
- package/dist/mcp-tools.mjs +6964 -1865
- package/dist/qp.js +6280 -1565
- package/dist/voices-CPpnWn39.d.mts +613 -0
- package/dist/web.d.mts +5 -3
- package/dist/web.mjs +398 -164
- package/mcp.mjs +94 -21
- package/package.json +5 -2
- package/dist/scraping-C334VxX5.d.mts +0 -335
package/mcp.mjs
CHANGED
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
// Deliberately NO delete tools: outputs are new files and the server never
|
|
14
14
|
// removes anything a user recorded.
|
|
15
15
|
|
|
16
|
-
import { readFileSync } from 'node:fs';
|
|
16
|
+
import { readdirSync, readFileSync, statSync } from 'node:fs';
|
|
17
|
+
import { fileURLToPath } from 'node:url';
|
|
17
18
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
18
19
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
19
20
|
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
@@ -38,8 +39,16 @@ const protocolStdout = new Proxy(process.stdout, {
|
|
|
38
39
|
},
|
|
39
40
|
});
|
|
40
41
|
|
|
42
|
+
// icons: MCP's Implementation schema (SDK 1.29+). A client that renders it
|
|
43
|
+
// shows QuickPeek's own mark beside the server instead of a generic plug; one
|
|
44
|
+
// that does not simply ignores the field.
|
|
41
45
|
const server = new Server(
|
|
42
|
-
{
|
|
46
|
+
{
|
|
47
|
+
name: 'quickpeek',
|
|
48
|
+
version: pkg.version,
|
|
49
|
+
title: 'QuickPeek',
|
|
50
|
+
icons: [{ src: 'https://quickpeek.co/icons/icon-512x512.png', mimeType: 'image/png', sizes: ['512x512'] }],
|
|
51
|
+
},
|
|
43
52
|
{ capabilities: { tools: {} } }
|
|
44
53
|
);
|
|
45
54
|
|
|
@@ -64,36 +73,100 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
64
73
|
}
|
|
65
74
|
});
|
|
66
75
|
|
|
76
|
+
/**
|
|
77
|
+
* Say so when the bundle is older than the sources it was built from.
|
|
78
|
+
*
|
|
79
|
+
* dist/mcp-tools.mjs is what actually answers every tool call, and nothing
|
|
80
|
+
* rebuilds it on the way in. A checkout whose src/ has moved on therefore
|
|
81
|
+
* serves an older QuickPeek than the code in front of you, and the difference
|
|
82
|
+
* shows up as missing output rather than an error - a stale bundle once
|
|
83
|
+
* produced demos with no intro card, no outro card and no length limit, and
|
|
84
|
+
* the three read as three separate regressions in features that were simply
|
|
85
|
+
* not compiled in yet.
|
|
86
|
+
*
|
|
87
|
+
* Warning only, and on stderr: a published install has no src/ to compare
|
|
88
|
+
* against, and no diagnostic is worth refusing to start over.
|
|
89
|
+
*/
|
|
90
|
+
function warnIfBundleStale() {
|
|
91
|
+
try {
|
|
92
|
+
const srcDir = fileURLToPath(new URL('./src', import.meta.url));
|
|
93
|
+
const bundle = fileURLToPath(new URL('./dist/mcp-tools.mjs', import.meta.url));
|
|
94
|
+
const builtAt = statSync(bundle).mtimeMs;
|
|
95
|
+
let newest = 0;
|
|
96
|
+
const walk = (dir) => {
|
|
97
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
98
|
+
const full = `${dir}/${entry.name}`;
|
|
99
|
+
if (entry.isDirectory()) walk(full);
|
|
100
|
+
else if (entry.name.endsWith('.ts')) newest = Math.max(newest, statSync(full).mtimeMs);
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
walk(srcDir);
|
|
104
|
+
if (newest > builtAt) {
|
|
105
|
+
// Seconds under a minute: the usual case is "edited a file, restarted",
|
|
106
|
+
// and rounding that to "0 minute(s) older" reads as no gap at all.
|
|
107
|
+
const gapSecs = Math.round((newest - builtAt) / 1000);
|
|
108
|
+
const age = gapSecs < 60 ? `${gapSecs}s` : `${Math.round(gapSecs / 60)}min`;
|
|
109
|
+
console.error(
|
|
110
|
+
`quickpeek-mcp: dist/mcp-tools.mjs is ${age} older than src/ - ` +
|
|
111
|
+
`this server is running stale code. Run \`bun run build\` to rebuild it.`,
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
} catch {
|
|
115
|
+
// No src/ (a published install), or an unreadable tree: nothing to check.
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
67
119
|
async function main() {
|
|
120
|
+
warnIfBundleStale();
|
|
121
|
+
|
|
68
122
|
const transport = new StdioServerTransport(process.stdin, protocolStdout);
|
|
69
123
|
await server.connect(transport);
|
|
70
124
|
|
|
71
125
|
// Elicitation is optional in MCP. The capability is checked per call, not
|
|
72
126
|
// here: the client only declares it in `initialize`, which arrives after
|
|
73
|
-
// connect() resolves. null means "cannot prompt" and
|
|
74
|
-
//
|
|
75
|
-
setElicit(async (message) => {
|
|
127
|
+
// connect() resolves. null means "cannot prompt" and the tools fall back
|
|
128
|
+
// to a two-call flow (status result now, follow-up call later).
|
|
129
|
+
setElicit(async (message, fields) => {
|
|
76
130
|
if (!server.getClientCapabilities()?.elicitation) return null;
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
type
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
131
|
+
const properties = {};
|
|
132
|
+
for (const f of fields) {
|
|
133
|
+
properties[f.name] =
|
|
134
|
+
f.type === 'enum'
|
|
135
|
+
? {
|
|
136
|
+
type: 'string',
|
|
137
|
+
title: f.title,
|
|
138
|
+
...(f.description ? { description: f.description } : {}),
|
|
139
|
+
enum: f.options,
|
|
140
|
+
...(f.default ? { default: f.default } : {}),
|
|
141
|
+
}
|
|
142
|
+
: { type: 'boolean', title: f.title, ...(f.description ? { description: f.description } : {}) };
|
|
143
|
+
}
|
|
144
|
+
try {
|
|
145
|
+
const result = await server.elicitInput(
|
|
146
|
+
{
|
|
147
|
+
message,
|
|
148
|
+
requestedSchema: { type: 'object', properties, required: fields.map((f) => f.name) },
|
|
87
149
|
},
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
150
|
+
// The SDK's default request timeout is 60s. These prompts wait on a
|
|
151
|
+
// human signing into a website or reading a plan; give them 15 minutes.
|
|
152
|
+
{ timeout: 15 * 60 * 1000 },
|
|
153
|
+
);
|
|
154
|
+
if (result.action === 'accept') return { action: 'accept', content: result.content ?? {} };
|
|
155
|
+
return { action: result.action === 'decline' ? 'decline' : 'cancel' };
|
|
156
|
+
} catch {
|
|
157
|
+
// A client may declare the capability yet choke on the form (or the
|
|
158
|
+
// request may time out unanswered). Treat it as "cannot prompt" so the
|
|
159
|
+
// tool degrades to the two-call flow instead of erroring the call.
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
92
162
|
});
|
|
93
163
|
|
|
94
164
|
// Read per call for the same reason as above: `initialize` has not arrived
|
|
95
|
-
// yet. Lets tools skip opening a sign-in window no prompt could ever
|
|
96
|
-
|
|
165
|
+
// yet. Lets tools skip opening a sign-in window no prompt could ever
|
|
166
|
+
// confirm. Checks `.form` specifically — elicitInput throws for url-only
|
|
167
|
+
// clients, and the SDK normalizes a legacy bare `elicitation: {}` to
|
|
168
|
+
// include form, so this stays true for older clients too.
|
|
169
|
+
setCanElicit(() => !!server.getClientCapabilities()?.elicitation?.form);
|
|
97
170
|
}
|
|
98
171
|
|
|
99
172
|
main().catch((e) => {
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spark-apps/quickpeek",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.4",
|
|
4
4
|
"description": "Auto-generate demo videos for your web app with AI",
|
|
5
|
+
"mcpName": "co.quickpeek/quickpeek",
|
|
5
6
|
"main": "dist/qp.js",
|
|
6
7
|
"bin": {
|
|
7
8
|
"quickpeek": "dist/qp.js",
|
|
@@ -28,6 +29,7 @@
|
|
|
28
29
|
"scripts": {
|
|
29
30
|
"dev": "npx tsx src/qp.ts",
|
|
30
31
|
"build": "tsup",
|
|
32
|
+
"prepare": "tsup",
|
|
31
33
|
"build:tsc": "tsc --noEmit",
|
|
32
34
|
"lint": "biome check src",
|
|
33
35
|
"lint:fix": "biome check --write src",
|
|
@@ -51,7 +53,7 @@
|
|
|
51
53
|
"license": "MIT",
|
|
52
54
|
"dependencies": {
|
|
53
55
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
54
|
-
"@spark-apps/video-kit": "^0.1.
|
|
56
|
+
"@spark-apps/video-kit": "^0.1.1",
|
|
55
57
|
"chalk": "^5.6.2",
|
|
56
58
|
"dotenv": "^17.3.1",
|
|
57
59
|
"figlet": "^1.11.0",
|
|
@@ -63,6 +65,7 @@
|
|
|
63
65
|
"devDependencies": {
|
|
64
66
|
"@biomejs/biome": "^2.4.8",
|
|
65
67
|
"@types/node": "^20.19.37",
|
|
68
|
+
"next": "^15.5.14",
|
|
66
69
|
"tsup": "^8.5.1",
|
|
67
70
|
"tsx": "^4.21.0",
|
|
68
71
|
"typescript": "^5.9.3",
|
|
@@ -1,335 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Configuration types and defaults for QuickPeek
|
|
3
|
-
*/
|
|
4
|
-
declare const VERSION = "1.2.3";
|
|
5
|
-
declare const CONFIG_FILE = "quickpeek.config.json";
|
|
6
|
-
type UserTier = 'free' | 'pro';
|
|
7
|
-
interface Config {
|
|
8
|
-
lang?: string;
|
|
9
|
-
plan?: {
|
|
10
|
-
maxSteps: number;
|
|
11
|
-
};
|
|
12
|
-
browser?: {
|
|
13
|
-
extension?: string;
|
|
14
|
-
};
|
|
15
|
-
video: {
|
|
16
|
-
width: number;
|
|
17
|
-
height: number;
|
|
18
|
-
format: string;
|
|
19
|
-
preset: 'ultrafast' | 'superfast' | 'veryfast' | 'faster' | 'fast' | 'medium' | 'slow' | 'slower' | 'veryslow';
|
|
20
|
-
quality: number;
|
|
21
|
-
fps: number;
|
|
22
|
-
cursor: boolean;
|
|
23
|
-
highlight: false | 'outline' | 'full';
|
|
24
|
-
fadeIn: number;
|
|
25
|
-
fadeOut: number;
|
|
26
|
-
transitions: 'none' | 'fade' | 'blend';
|
|
27
|
-
contrast: number;
|
|
28
|
-
tempo: number;
|
|
29
|
-
stepDelay: number;
|
|
30
|
-
outro?: string;
|
|
31
|
-
zoom?: number;
|
|
32
|
-
};
|
|
33
|
-
audio?: {
|
|
34
|
-
voice?: string;
|
|
35
|
-
format: string;
|
|
36
|
-
bitrate: string;
|
|
37
|
-
};
|
|
38
|
-
music?: {
|
|
39
|
-
path: string;
|
|
40
|
-
volume: number;
|
|
41
|
-
};
|
|
42
|
-
timing: {
|
|
43
|
-
minStepDuration: number;
|
|
44
|
-
};
|
|
45
|
-
output: {
|
|
46
|
-
dir: string;
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
interface CaptionWordStyle {
|
|
50
|
-
color: string;
|
|
51
|
-
size: number;
|
|
52
|
-
bold: boolean;
|
|
53
|
-
}
|
|
54
|
-
interface CaptionStyle {
|
|
55
|
-
font: string;
|
|
56
|
-
outline: string;
|
|
57
|
-
outlineWidth: number;
|
|
58
|
-
shadow: number;
|
|
59
|
-
marginBottom: number;
|
|
60
|
-
regular: CaptionWordStyle;
|
|
61
|
-
highlight: CaptionWordStyle;
|
|
62
|
-
}
|
|
63
|
-
declare const DEFAULT_CONFIG: Config;
|
|
64
|
-
declare const DEFAULT_CAPTIONS: CaptionStyle;
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* AI API client — Spark AI communication
|
|
68
|
-
* Handles token management, rate limiting, and retries
|
|
69
|
-
*
|
|
70
|
-
* Two transports:
|
|
71
|
-
* - Relay (default for the published CLI): quickpeek-web mints a short-lived
|
|
72
|
-
* per-user token from a registered email and proxies the chat server-side.
|
|
73
|
-
* No credential ships in the npm bundle.
|
|
74
|
-
* - Direct (dev/server only): when SPARK_AI_KEY is present in the runtime
|
|
75
|
-
* environment (never baked at build time), call Spark AI directly. This is
|
|
76
|
-
* the path quickpeek-web itself uses server-side.
|
|
77
|
-
*/
|
|
78
|
-
|
|
79
|
-
interface RateLimitInfo {
|
|
80
|
-
used: number;
|
|
81
|
-
limit: number;
|
|
82
|
-
tier: UserTier;
|
|
83
|
-
resetAt?: string;
|
|
84
|
-
}
|
|
85
|
-
interface AIResponse {
|
|
86
|
-
success: true;
|
|
87
|
-
content: string;
|
|
88
|
-
limits: RateLimitInfo;
|
|
89
|
-
}
|
|
90
|
-
interface AIError {
|
|
91
|
-
success: false;
|
|
92
|
-
error: string;
|
|
93
|
-
isRateLimit?: boolean;
|
|
94
|
-
isAuthError?: boolean;
|
|
95
|
-
retryAfterSeconds?: number;
|
|
96
|
-
/** Upstream HTTP status, when the failure was an HTTP response. */
|
|
97
|
-
status?: number;
|
|
98
|
-
/** Host that produced the failure, so relay vs backend is distinguishable. */
|
|
99
|
-
host?: string;
|
|
100
|
-
/** Raw response body (truncated), for the error log. */
|
|
101
|
-
detail?: string;
|
|
102
|
-
}
|
|
103
|
-
type AIResult = AIResponse | AIError;
|
|
104
|
-
/** Called before each backoff so the CLI can explain a long pause. */
|
|
105
|
-
type RetryNotice = (info: {
|
|
106
|
-
attempt: number;
|
|
107
|
-
status: number;
|
|
108
|
-
delayMs: number;
|
|
109
|
-
host: string;
|
|
110
|
-
}) => void;
|
|
111
|
-
interface ChatOptions {
|
|
112
|
-
onRetry?: RetryNotice;
|
|
113
|
-
/**
|
|
114
|
-
* Backoff before each 5xx retry. Defaults to SERVER_BACKOFF_MS, which is
|
|
115
|
-
* sized to fit inside a serverless function budget — callAI runs inside the
|
|
116
|
-
* quickpeek-web routes as well as the CLI. Interactive callers should pass
|
|
117
|
-
* CLI_BACKOFF_MS, which can afford to outwait the backend's closed window.
|
|
118
|
-
*/
|
|
119
|
-
backoffMs?: number[];
|
|
120
|
-
}
|
|
121
|
-
/** Short enough to stay well inside a serverless maxDuration. */
|
|
122
|
-
declare const SERVER_BACKOFF_MS: number[];
|
|
123
|
-
/**
|
|
124
|
-
* Long enough to outlast the AI backend's ~45-60s closed window (measured
|
|
125
|
-
* 16 Aug 2026). Only safe where a human is waiting on a spinner.
|
|
126
|
-
*/
|
|
127
|
-
declare const CLI_BACKOFF_MS: number[];
|
|
128
|
-
/**
|
|
129
|
-
* Direct Spark AI call using the runtime SPARK_AI_KEY (dev + server-side use).
|
|
130
|
-
*/
|
|
131
|
-
declare function callAI(systemPrompt: string, userPrompt: string, tier?: UserTier, opts?: ChatOptions): Promise<AIResult>;
|
|
132
|
-
/**
|
|
133
|
-
* Relayed Spark AI call for the published CLI: authenticates with the user's
|
|
134
|
-
* registered email via quickpeek-web, which holds the real key server-side
|
|
135
|
-
* and enforces per-user daily limits.
|
|
136
|
-
*/
|
|
137
|
-
declare function callAIViaRelay(systemPrompt: string, userPrompt: string, email: string, tier?: UserTier, opts?: ChatOptions): Promise<AIResult>;
|
|
138
|
-
/**
|
|
139
|
-
* Parse AI-generated plan JSON from response text
|
|
140
|
-
*/
|
|
141
|
-
declare function parseAIPlanResponse(content: string): {
|
|
142
|
-
success: true;
|
|
143
|
-
plan: {
|
|
144
|
-
title?: string;
|
|
145
|
-
description?: string;
|
|
146
|
-
steps: Array<{
|
|
147
|
-
id?: number;
|
|
148
|
-
action?: string;
|
|
149
|
-
caption?: string;
|
|
150
|
-
target?: string;
|
|
151
|
-
value?: string;
|
|
152
|
-
}>;
|
|
153
|
-
};
|
|
154
|
-
} | {
|
|
155
|
-
success: false;
|
|
156
|
-
error: string;
|
|
157
|
-
};
|
|
158
|
-
/**
|
|
159
|
-
* Normalize a URL by adding https:// and stripping www prefix
|
|
160
|
-
*/
|
|
161
|
-
declare function normalizeUrl(input: string): string;
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* Shared types for QuickPeek core
|
|
165
|
-
*/
|
|
166
|
-
/** Action type suggested for an element based on its type */
|
|
167
|
-
type SuggestedAction = 'click' | 'type' | 'upload' | 'drag' | 'select' | 'check';
|
|
168
|
-
/** Element type discovered on a page */
|
|
169
|
-
type ElementType = 'button' | 'input' | 'link' | 'text' | 'file' | 'select' | 'range' | 'checkbox';
|
|
170
|
-
/** Discovered element from page crawl */
|
|
171
|
-
interface ElementInfo {
|
|
172
|
-
/** Element type */
|
|
173
|
-
type: ElementType | string;
|
|
174
|
-
/** Display text/label for the element */
|
|
175
|
-
text: string;
|
|
176
|
-
/** CSS selector to target the element */
|
|
177
|
-
selector: string;
|
|
178
|
-
/** Suggested action for this element */
|
|
179
|
-
action?: SuggestedAction;
|
|
180
|
-
/** Simplified target name (just the text, for AI use) */
|
|
181
|
-
target?: string;
|
|
182
|
-
}
|
|
183
|
-
/** Highlight mode for UI elements */
|
|
184
|
-
type HighlightMode = false | 'outline' | 'full';
|
|
185
|
-
/** Step in a demo plan */
|
|
186
|
-
interface Step {
|
|
187
|
-
id: number | string;
|
|
188
|
-
action: string;
|
|
189
|
-
target?: string;
|
|
190
|
-
value?: string;
|
|
191
|
-
caption?: string;
|
|
192
|
-
description?: string;
|
|
193
|
-
duration?: number;
|
|
194
|
-
zoom?: number | string;
|
|
195
|
-
partial?: string;
|
|
196
|
-
transition?: 'fade' | 'blend';
|
|
197
|
-
highlight?: HighlightMode;
|
|
198
|
-
mediaFile?: string;
|
|
199
|
-
mediaType?: 'image' | 'video';
|
|
200
|
-
}
|
|
201
|
-
/** Demo plan structure */
|
|
202
|
-
interface Plan {
|
|
203
|
-
url: string;
|
|
204
|
-
title: string;
|
|
205
|
-
description: string;
|
|
206
|
-
lang?: string;
|
|
207
|
-
steps: Step[];
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
/**
|
|
211
|
-
* Page crawling and interactive element extraction
|
|
212
|
-
*/
|
|
213
|
-
|
|
214
|
-
/**
|
|
215
|
-
* A selector matching one id.
|
|
216
|
-
*
|
|
217
|
-
* HTML ids are far more permissive than CSS identifiers: `description-(optional)`
|
|
218
|
-
* is a perfectly legal id, but `#description-(optional)` is invalid CSS and
|
|
219
|
-
* Playwright rejects it, so the step silently finds nothing. Anything that is
|
|
220
|
-
* not identifier-safe gets the attribute form instead.
|
|
221
|
-
*/
|
|
222
|
-
declare function idSelector(id: string): string;
|
|
223
|
-
/**
|
|
224
|
-
* Crawl a page and extract interactive elements with smart selectors
|
|
225
|
-
* Results are cached in-memory for a few minutes to avoid duplicate browser launches
|
|
226
|
-
* @param url - URL to crawl
|
|
227
|
-
* @param task - Optional progress reporter (CLI uses ora spinners, web can omit)
|
|
228
|
-
*/
|
|
229
|
-
declare function crawlPage(url: string, task?: (text: string) => {
|
|
230
|
-
succeed: (text?: string) => void;
|
|
231
|
-
}, storageStatePath?: string, viewport?: {
|
|
232
|
-
width: number;
|
|
233
|
-
height: number;
|
|
234
|
-
}): Promise<{
|
|
235
|
-
title: string;
|
|
236
|
-
elements: ElementInfo[];
|
|
237
|
-
scrollable: number;
|
|
238
|
-
}>;
|
|
239
|
-
|
|
240
|
-
/**
|
|
241
|
-
* Utility functions for QuickPeek
|
|
242
|
-
*/
|
|
243
|
-
/**
|
|
244
|
-
* Get full language name from ISO code
|
|
245
|
-
*/
|
|
246
|
-
declare function getLanguageName(lang?: string): string | null;
|
|
247
|
-
|
|
248
|
-
/**
|
|
249
|
-
* Convert rgba() or rgb() color string to hex
|
|
250
|
-
*/
|
|
251
|
-
declare function rgbaToHex(color: string): string;
|
|
252
|
-
|
|
253
|
-
/**
|
|
254
|
-
* Parse partial field "start-end" into trim values
|
|
255
|
-
* e.g., "0-3.5" -> { trimStart: 0, trimEnd: 3.5 }
|
|
256
|
-
* e.g., "2-" -> { trimStart: 2, trimEnd: undefined }
|
|
257
|
-
*/
|
|
258
|
-
declare function parsePartial(partial: string | undefined): {
|
|
259
|
-
trimStart?: number;
|
|
260
|
-
trimEnd?: number;
|
|
261
|
-
};
|
|
262
|
-
/**
|
|
263
|
-
* Extract a message string from an unknown error value
|
|
264
|
-
*/
|
|
265
|
-
declare function getErrorMessage(err: unknown): string;
|
|
266
|
-
/**
|
|
267
|
-
* Convert text to Title Case, keeping prepositions/articles lowercase
|
|
268
|
-
* First word is always capitalized
|
|
269
|
-
*/
|
|
270
|
-
declare function toTitleCase(text: string): string;
|
|
271
|
-
|
|
272
|
-
/**
|
|
273
|
-
* AI prompts for demo plan generation
|
|
274
|
-
*/
|
|
275
|
-
|
|
276
|
-
declare function buildSystemPrompt(maxSteps: number): string;
|
|
277
|
-
declare function buildUserPrompt(url: string, title: string, elements: ElementInfo[], langName: string | null, description: string | undefined, maxSteps: number, scrollable?: number): string;
|
|
278
|
-
|
|
279
|
-
type Tier = 'free' | 'pro';
|
|
280
|
-
declare const FEATURES: Record<string, string[]>;
|
|
281
|
-
interface SparkSubscription {
|
|
282
|
-
payment_type: 'subscription' | 'one_time';
|
|
283
|
-
status: 'active' | 'trialing' | 'past_due' | 'canceled' | 'lifetime';
|
|
284
|
-
is_lifetime: boolean;
|
|
285
|
-
current_period_end: string;
|
|
286
|
-
cancel_at_period_end: boolean;
|
|
287
|
-
billing_interval: 'monthly' | 'yearly' | 'one_time';
|
|
288
|
-
price_id: string;
|
|
289
|
-
created_at: string;
|
|
290
|
-
}
|
|
291
|
-
interface SparkTrial {
|
|
292
|
-
is_expired: boolean;
|
|
293
|
-
days_remaining: number;
|
|
294
|
-
uses_remaining: number;
|
|
295
|
-
max_days: number;
|
|
296
|
-
max_uses: number;
|
|
297
|
-
}
|
|
298
|
-
interface SparkStatus {
|
|
299
|
-
subscription: SparkSubscription | null;
|
|
300
|
-
verified: boolean;
|
|
301
|
-
registered: boolean;
|
|
302
|
-
usage_count: number;
|
|
303
|
-
trial: SparkTrial | null;
|
|
304
|
-
message?: string;
|
|
305
|
-
}
|
|
306
|
-
declare const getStatus: (email: string) => Promise<SparkStatus>;
|
|
307
|
-
declare function getStatusCached(email: string): Promise<SparkStatus>;
|
|
308
|
-
declare function getTier(d: SparkStatus): Tier;
|
|
309
|
-
declare const isPaid: (d: SparkStatus) => boolean;
|
|
310
|
-
declare const hasAccess: (d: SparkStatus, min: Tier) => boolean;
|
|
311
|
-
declare const hasTrialRemaining: (d: SparkStatus) => boolean;
|
|
312
|
-
declare const showPaywall: (d: SparkStatus) => boolean;
|
|
313
|
-
declare const isCanceling: (d: SparkStatus) => boolean;
|
|
314
|
-
/** Resolve tier directly from an email — the most common consumer call. Falls back to 'free' on error. */
|
|
315
|
-
declare function getTierByEmail(email: string): Promise<Tier>;
|
|
316
|
-
/** Check whether an email is verified. Falls back to false on error. */
|
|
317
|
-
declare function isVerified(email: string): Promise<boolean>;
|
|
318
|
-
declare function pricingUrl(opts?: {
|
|
319
|
-
email?: string;
|
|
320
|
-
returnUrl?: string;
|
|
321
|
-
}): string;
|
|
322
|
-
|
|
323
|
-
/**
|
|
324
|
-
* Shared constants for page scraping and element filtering
|
|
325
|
-
* Used by both CLI and web versions
|
|
326
|
-
*/
|
|
327
|
-
declare const EXCLUDED_LINK_PATTERNS: string[];
|
|
328
|
-
/** CSS selectors for interactive page elements */
|
|
329
|
-
declare const INTERACTIVE_SELECTORS: string[];
|
|
330
|
-
/** Check if link text or href matches an excluded pattern */
|
|
331
|
-
declare function isExcludedLink(text: string, href?: string): boolean;
|
|
332
|
-
/** Check if a link should be skipped during crawling */
|
|
333
|
-
declare function shouldSkipLink(text: string, href: string, currentPath: string): boolean;
|
|
334
|
-
|
|
335
|
-
export { toTitleCase as $, type AIError as A, hasAccess as B, type Config as C, DEFAULT_CAPTIONS as D, EXCLUDED_LINK_PATTERNS as E, FEATURES as F, hasTrialRemaining as G, type HighlightMode as H, INTERACTIVE_SELECTORS as I, idSelector as J, isCanceling as K, isExcludedLink as L, isPaid as M, isVerified as N, normalizeUrl as O, type Plan as P, parseAIPlanResponse as Q, type RateLimitInfo as R, SERVER_BACKOFF_MS as S, type Tier as T, type UserTier as U, VERSION as V, parsePartial as W, pricingUrl as X, rgbaToHex as Y, shouldSkipLink as Z, showPaywall as _, type AIResponse as a, getTier as a0, type AIResult as b, CLI_BACKOFF_MS as c, CONFIG_FILE as d, type CaptionStyle as e, type CaptionWordStyle as f, type ChatOptions as g, DEFAULT_CONFIG as h, type ElementInfo as i, type ElementType as j, type RetryNotice as k, type SparkStatus as l, type SparkSubscription as m, type SparkTrial as n, type Step as o, type SuggestedAction as p, buildSystemPrompt as q, buildUserPrompt as r, callAI as s, callAIViaRelay as t, crawlPage as u, getErrorMessage as v, getLanguageName as w, getStatus as x, getStatusCached as y, getTierByEmail as z };
|