autokap 1.0.6 → 1.0.7

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.
Files changed (130) hide show
  1. package/assets/chrome/ios-statusbar-comparison-reference.jpg +0 -0
  2. package/assets/chrome/ios-statusbar-dark-reference.jpg +0 -0
  3. package/assets/chrome/ios-statusbar-light-reference.jpg +0 -0
  4. package/assets/devices/ipad-pro-11-m4.json +52 -0
  5. package/assets/devices/iphone-16-pro.json +53 -0
  6. package/assets/devices/macbook-air-13.json +45 -0
  7. package/assets/frames/MacBook Air 13.svg +242 -0
  8. package/assets/frames/Status bar - iPhone.png +0 -0
  9. Menu bar- iPad.png +0 -0
  10. package/assets/frames/iPad Pro M4 11_.png +0 -0
  11. package/assets/frames/iPhone 16 Pro.png +0 -0
  12. package/assets/icons/Cellular Connection.svg +3 -0
  13. package/assets/icons/Union.svg +6 -0
  14. package/assets/icons/Wifi.svg +3 -0
  15. package/assets/icons/battery.svg +5 -0
  16. package/assets/icons/battery_charging.svg +8 -0
  17. package/dist/abort.d.ts +5 -0
  18. package/dist/abort.js +44 -0
  19. package/dist/agent.d.ts +142 -0
  20. package/dist/agent.js +4511 -0
  21. package/dist/billing-operation-logging.d.ts +38 -0
  22. package/dist/billing-operation-logging.js +248 -0
  23. package/dist/browser-bar.d.ts +40 -0
  24. package/dist/browser-bar.js +147 -0
  25. package/dist/browser.d.ts +25 -0
  26. package/dist/browser.js +177 -9
  27. package/dist/capture-alt-text.d.ts +12 -0
  28. package/dist/capture-alt-text.js +51 -0
  29. package/dist/capture-encryption.d.ts +10 -0
  30. package/dist/capture-encryption.js +41 -0
  31. package/dist/capture-language-preflight.d.ts +41 -0
  32. package/dist/capture-language-preflight.js +286 -0
  33. package/dist/capture-llm-page-identity.d.ts +15 -0
  34. package/dist/capture-llm-page-identity.js +116 -0
  35. package/dist/capture-model-resolution.d.ts +9 -0
  36. package/dist/capture-model-resolution.js +21 -0
  37. package/dist/capture-page-identity.d.ts +9 -0
  38. package/dist/capture-page-identity.js +219 -0
  39. package/dist/capture-preset-credentials.d.ts +12 -0
  40. package/dist/capture-preset-credentials.js +57 -0
  41. package/dist/capture-request-plan.d.ts +58 -0
  42. package/dist/capture-request-plan.js +216 -0
  43. package/dist/capture-run-optimizer.d.ts +139 -0
  44. package/dist/capture-run-optimizer.js +848 -0
  45. package/dist/capture-selector-memory.d.ts +26 -0
  46. package/dist/capture-selector-memory.js +327 -0
  47. package/dist/capture-session-profile-encryption.d.ts +2 -0
  48. package/dist/capture-session-profile-encryption.js +22 -0
  49. package/dist/capture-step-timeout.d.ts +10 -0
  50. package/dist/capture-step-timeout.js +30 -0
  51. package/dist/capture-studio-sync.d.ts +22 -0
  52. package/dist/capture-studio-sync.js +166 -0
  53. package/dist/capture-variant-state.d.ts +54 -0
  54. package/dist/capture-variant-state.js +156 -0
  55. package/dist/cli.js +15 -0
  56. package/dist/clip-orchestrator.d.ts +148 -0
  57. package/dist/clip-orchestrator.js +950 -0
  58. package/dist/clip-postprocess.d.ts +42 -0
  59. package/dist/clip-postprocess.js +192 -0
  60. package/dist/cost-logging.d.ts +27 -0
  61. package/dist/cost-logging.js +128 -0
  62. package/dist/credential-templates.d.ts +5 -0
  63. package/dist/credential-templates.js +60 -0
  64. package/dist/element-capture.d.ts +53 -0
  65. package/dist/element-capture.js +766 -0
  66. package/dist/hybrid-navigator.d.ts +138 -0
  67. package/dist/hybrid-navigator.js +468 -0
  68. package/dist/index.d.ts +15 -0
  69. package/dist/index.js +11 -0
  70. package/dist/llm-usage.d.ts +17 -0
  71. package/dist/llm-usage.js +45 -0
  72. package/dist/mockup-html.d.ts +119 -0
  73. package/dist/mockup-html.js +253 -0
  74. package/dist/mockup.d.ts +94 -0
  75. package/dist/mockup.js +608 -0
  76. package/dist/mouse-animation.d.ts +46 -0
  77. package/dist/mouse-animation.js +100 -0
  78. package/dist/overlay-utils.d.ts +14 -0
  79. package/dist/overlay-utils.js +13 -0
  80. package/dist/posthog.d.ts +4 -0
  81. package/dist/posthog.js +26 -0
  82. package/dist/prompt-cache.d.ts +10 -0
  83. package/dist/prompt-cache.js +24 -0
  84. package/dist/prompts.d.ts +167 -0
  85. package/dist/prompts.js +1165 -0
  86. package/dist/remote-browser.d.ts +191 -0
  87. package/dist/remote-browser.js +305 -0
  88. package/dist/security.d.ts +20 -0
  89. package/dist/security.js +569 -0
  90. package/dist/server-capture-runtime.d.ts +123 -0
  91. package/dist/server-capture-runtime.js +638 -0
  92. package/dist/server-credit-usage.d.ts +12 -0
  93. package/dist/server-credit-usage.js +41 -0
  94. package/dist/server-posthog.d.ts +2 -0
  95. package/dist/server-posthog.js +16 -0
  96. package/dist/server-project-webhooks.d.ts +45 -0
  97. package/dist/server-project-webhooks.js +97 -0
  98. package/dist/server-screenshot-watermark.d.ts +7 -0
  99. package/dist/server-screenshot-watermark.js +38 -0
  100. package/dist/session-profile.d.ts +86 -0
  101. package/dist/session-profile.js +1373 -0
  102. package/dist/sf-pro-fonts.d.ts +4 -0
  103. package/dist/sf-pro-fonts.js +7 -0
  104. package/dist/status-bar-l10n.d.ts +14 -0
  105. package/dist/status-bar-l10n.js +177 -0
  106. package/dist/status-bar.d.ts +44 -0
  107. package/dist/status-bar.js +336 -0
  108. package/dist/tools.d.ts +4 -0
  109. package/dist/tools.js +578 -0
  110. package/dist/video-agent.d.ts +143 -0
  111. package/dist/video-agent.js +4783 -0
  112. package/dist/video-observation.d.ts +36 -0
  113. package/dist/video-observation.js +192 -0
  114. package/dist/video-planner.d.ts +12 -0
  115. package/dist/video-planner.js +500 -0
  116. package/dist/video-prompts.d.ts +37 -0
  117. package/dist/video-prompts.js +554 -0
  118. package/dist/video-tools.d.ts +3 -0
  119. package/dist/video-tools.js +59 -0
  120. package/dist/video-variant-state.d.ts +29 -0
  121. package/dist/video-variant-state.js +80 -0
  122. package/dist/vision-model.d.ts +17 -0
  123. package/dist/vision-model.js +74 -0
  124. package/dist/ws-auth.d.ts +20 -0
  125. package/dist/ws-auth.js +67 -0
  126. package/dist/ws-handler.d.ts +10 -0
  127. package/dist/ws-handler.js +1663 -0
  128. package/dist/ws-server.d.ts +9 -0
  129. package/dist/ws-server.js +52 -0
  130. package/package.json +93 -39
@@ -0,0 +1,116 @@
1
+ import { inferCapturePageIdentity } from "./capture-page-identity.js";
2
+ /**
3
+ * Use a cheap LLM call to classify all pages in a capture run.
4
+ * Falls back to regex heuristics if the LLM call fails.
5
+ */
6
+ export async function inferPageIdentitiesWithLLM(pageRuns, model, apiKey, providerPrefs) {
7
+ // Regex fallback for all pages
8
+ const regexFallback = Object.fromEntries(pageRuns.map((pr) => [pr.pageId ?? "main", inferCapturePageIdentity(pr)]));
9
+ if (pageRuns.length === 0) {
10
+ return { identities: regexFallback, usage: null };
11
+ }
12
+ const pagesDescription = pageRuns.map((pr, i) => {
13
+ const pageId = pr.pageId ?? "main";
14
+ return `${i + 1}. pageId="${pageId}" | url="${pr.url}" | prompt="${pr.prompt}"`;
15
+ }).join("\n");
16
+ const systemPrompt = `You classify web page capture targets for a screenshot automation tool.
17
+
18
+ IMPORTANT: The "pageId" is a user-defined identifier — it is NOT a technical description. Do NOT infer the page type from the pageId name alone. A page named "preset_creation_modal" might actually be a sidebar panel, not a DOM dialog. Base your classification on the PROMPT text and URL, not the pageId.
19
+
20
+ For each page, determine:
21
+ - "kind": one of "gallery", "modal_selection", "modal_configuration", "editor_route", "detail_route", "unknown"
22
+ - "gallery": a grid/list overview of screenshots, captures, or presets
23
+ - "modal_selection": the prompt explicitly describes opening a dialog/modal/popup to pick or select something
24
+ - "modal_configuration": the prompt explicitly describes opening a dialog/modal/popup to configure/edit something
25
+ - "editor_route": a dedicated full-page editor (e.g., /presets/abc-123/edit)
26
+ - "detail_route": a dedicated detail page that is NOT a modal
27
+ - "unknown": none of the above
28
+ - "dialogTarget": true ONLY if the prompt explicitly says to open an actual DOM dialog, modal, or popup overlay. Set to FALSE for: sidebar panels, drawers, slide-over panels, full pages, dropdown menus, popovers, or any UI that is part of the main page layout. When in doubt, set to false.
29
+ - "dedicatedRoute": true if the capture target has its own URL route (not just a modal on top of another page)
30
+
31
+ Respond with a JSON array of objects: [{ "pageId": "...", "kind": "...", "dialogTarget": true/false, "dedicatedRoute": true/false }]
32
+ Output ONLY valid JSON, nothing else.`;
33
+ try {
34
+ const providerBody = { provider: { ...providerPrefs?.[model], zdr: true } };
35
+ const res = await fetch("https://openrouter.ai/api/v1/chat/completions", {
36
+ method: "POST",
37
+ headers: {
38
+ Authorization: `Bearer ${apiKey}`,
39
+ "Content-Type": "application/json",
40
+ "HTTP-Referer": "https://autokap.app",
41
+ "X-Title": "Screenshot Agent",
42
+ },
43
+ body: JSON.stringify({
44
+ model,
45
+ max_tokens: 512,
46
+ stream: false,
47
+ response_format: { type: "json_object" },
48
+ messages: [
49
+ { role: "system", content: systemPrompt },
50
+ { role: "user", content: `Classify these ${pageRuns.length} page capture targets:\n\n${pagesDescription}` },
51
+ ],
52
+ ...providerBody,
53
+ }),
54
+ signal: AbortSignal.timeout(15_000),
55
+ });
56
+ if (!res.ok) {
57
+ console.warn(`[llm-page-identity] LLM call failed: HTTP ${res.status}`);
58
+ return { identities: regexFallback, usage: null };
59
+ }
60
+ const json = await res.json();
61
+ const content = json.choices?.[0]?.message?.content?.trim();
62
+ const usage = {
63
+ stepNumber: 0,
64
+ stepType: "page_identity_classification",
65
+ generationId: json.id ?? null,
66
+ modelRequested: model,
67
+ modelUsed: json.model ?? null,
68
+ promptTokens: json.usage?.prompt_tokens ?? null,
69
+ completionTokens: json.usage?.completion_tokens ?? null,
70
+ totalTokens: json.usage?.total_tokens ?? null,
71
+ imagesInPrompt: 0,
72
+ };
73
+ if (!content)
74
+ return { identities: regexFallback, usage };
75
+ // Parse — handle both array and { pages: [...] } wrapper
76
+ let classifications;
77
+ const parsed = JSON.parse(content);
78
+ if (Array.isArray(parsed)) {
79
+ classifications = parsed;
80
+ }
81
+ else if (Array.isArray(parsed.pages)) {
82
+ classifications = parsed.pages;
83
+ }
84
+ else if (Array.isArray(parsed.classifications)) {
85
+ classifications = parsed.classifications;
86
+ }
87
+ else {
88
+ console.warn("[llm-page-identity] Unexpected LLM response shape:", content.slice(0, 200));
89
+ return { identities: regexFallback, usage };
90
+ }
91
+ // Merge LLM results with regex fallback (LLM wins, regex fills gaps)
92
+ const result = { ...regexFallback };
93
+ for (const cls of classifications) {
94
+ const pageId = cls.pageId;
95
+ if (!pageId || !result[pageId])
96
+ continue;
97
+ const validKinds = [
98
+ "gallery", "modal_selection", "modal_configuration", "editor_route", "detail_route", "unknown",
99
+ ];
100
+ const kind = validKinds.includes(cls.kind) ? cls.kind : result[pageId].kind;
101
+ result[pageId] = {
102
+ ...result[pageId],
103
+ kind,
104
+ dialogTarget: typeof cls.dialogTarget === "boolean" ? cls.dialogTarget : result[pageId].dialogTarget,
105
+ dedicatedRoute: typeof cls.dedicatedRoute === "boolean" ? cls.dedicatedRoute : result[pageId].dedicatedRoute,
106
+ summary: `${kind}:${result[pageId].subjectTokens.slice(0, 4).join(",")}@llm`,
107
+ };
108
+ }
109
+ return { identities: result, usage };
110
+ }
111
+ catch (err) {
112
+ console.warn(`[llm-page-identity] LLM classification failed, using regex fallback:`, err instanceof Error ? err.message : err);
113
+ return { identities: regexFallback, usage: null };
114
+ }
115
+ }
116
+ //# sourceMappingURL=capture-llm-page-identity.js.map
@@ -0,0 +1,9 @@
1
+ export declare function resolveRunModels(params: {
2
+ requestModel?: string | null;
3
+ defaultModel?: string | null;
4
+ fallbackModel?: string | null;
5
+ presetId?: string | null;
6
+ }): {
7
+ model: string;
8
+ fallbackModel?: string;
9
+ };
@@ -0,0 +1,21 @@
1
+ function normalizeModelId(value) {
2
+ if (typeof value !== 'string')
3
+ return null;
4
+ const trimmed = value.trim();
5
+ return trimmed.length > 0 ? trimmed : null;
6
+ }
7
+ export function resolveRunModels(params) {
8
+ const requestModel = normalizeModelId(params.requestModel);
9
+ const defaultModel = normalizeModelId(params.defaultModel);
10
+ const fallbackModel = normalizeModelId(params.fallbackModel);
11
+ const preferAdminModel = Boolean(params.presetId && (defaultModel || fallbackModel));
12
+ const model = (preferAdminModel
13
+ ? defaultModel ?? fallbackModel ?? requestModel
14
+ : requestModel ?? defaultModel ?? fallbackModel)
15
+ ?? '';
16
+ return {
17
+ model,
18
+ fallbackModel: fallbackModel && fallbackModel !== model ? fallbackModel : undefined,
19
+ };
20
+ }
21
+ //# sourceMappingURL=capture-model-resolution.js.map
@@ -0,0 +1,9 @@
1
+ import type { CapturePageIdentity } from "./types.js";
2
+ import type { ScreenshotPageRunInput } from "./capture-run-optimizer.js";
3
+ export declare function inferCapturePageIdentity(pageRun: ScreenshotPageRunInput): CapturePageIdentity;
4
+ export interface VariantPageDefinitionIssue {
5
+ pageId: string;
6
+ duplicateOfPageId: string;
7
+ reason: string;
8
+ }
9
+ export declare function findVariantPageDefinitionIssues(pageRuns: ScreenshotPageRunInput[]): VariantPageDefinitionIssue[];
@@ -0,0 +1,219 @@
1
+ const TOKEN_STOP_WORDS = new Set([
2
+ "the",
3
+ "and",
4
+ "for",
5
+ "with",
6
+ "from",
7
+ "that",
8
+ "this",
9
+ "your",
10
+ "into",
11
+ "onto",
12
+ "page",
13
+ "pages",
14
+ "state",
15
+ "screen",
16
+ "capture",
17
+ "captures",
18
+ "screenshot",
19
+ "screenshots",
20
+ "open",
21
+ "opened",
22
+ "currently",
23
+ "current",
24
+ "already",
25
+ "visible",
26
+ "show",
27
+ "showing",
28
+ "display",
29
+ "displayed",
30
+ "view",
31
+ "interface",
32
+ "workflow",
33
+ "project",
34
+ "projects",
35
+ "user",
36
+ "step",
37
+ "steps",
38
+ "then",
39
+ "after",
40
+ "before",
41
+ "dans",
42
+ "pour",
43
+ "avec",
44
+ "page",
45
+ "pages",
46
+ "etat",
47
+ "état",
48
+ "capture",
49
+ "captures",
50
+ "ecran",
51
+ "écran",
52
+ "ouverte",
53
+ "ouvert",
54
+ "affiche",
55
+ "affiché",
56
+ "affichee",
57
+ "affichée",
58
+ "courante",
59
+ "actuelle",
60
+ "deja",
61
+ "déjà",
62
+ "interface",
63
+ "etape",
64
+ "étape",
65
+ ]);
66
+ const GALLERY_RE = /\b(gallery|screenshots|captures|overview|dashboard|list|galerie)\b/i;
67
+ const MODAL_RE = /\b(modal|dialog|popup|drawer|overlay)\b/i;
68
+ const TEMPLATE_RE = /\b(template|templates|picker|selection|select|choose|search templates|start from scratch|new preset|nouveau preset)\b/i;
69
+ const CONFIGURATION_RE = /\b(edit|editor|editing|config|configuration|configure|settings|details|detail|instructions|textarea|form|modifier|modification|modifier|edition)\b/i;
70
+ const STRONG_ROUTE_PROMPT_RE = /\b(route|dedicated page|full page|editor page|edit page|preset editor|preset edit page|page d[‘’]edition du preset|page de preset)\b/i;
71
+ const PASSIVE_STATE_RE = /\b(currently open|already open|currently displayed|already displayed|without launching|ne la lance pas|sans la lancer|deja ouverte|ouverte actuellement)\b/i;
72
+ const DISTINCT_TRANSITION_RE = /\b(save|saved|submitted|created|results|preview|confirmation|pricing|homepage|hero|scratch|blank|empty|different|another|autre|resultat|apercu|confirmation|vide)\b/i;
73
+ function uniqueInOrder(values) {
74
+ const seen = new Set();
75
+ const out = [];
76
+ for (const value of values) {
77
+ if (seen.has(value))
78
+ continue;
79
+ seen.add(value);
80
+ out.push(value);
81
+ }
82
+ return out;
83
+ }
84
+ function tokenize(value) {
85
+ return value
86
+ .toLowerCase()
87
+ .normalize("NFD")
88
+ .replace(/[\u0300-\u036f]/g, "")
89
+ .split(/[^a-z0-9]+/g)
90
+ .map((part) => part.trim())
91
+ .filter((part) => part.length >= 3 && !TOKEN_STOP_WORDS.has(part));
92
+ }
93
+ function normalizeUrlPath(rawUrl) {
94
+ try {
95
+ return new URL(rawUrl).pathname.toLowerCase();
96
+ }
97
+ catch {
98
+ return rawUrl.toLowerCase();
99
+ }
100
+ }
101
+ function summarizeIdentity(kind, tokens, urlPath) {
102
+ const tokenSummary = tokens.slice(0, 4).join(",");
103
+ const pathSummary = urlPath.replace(/\/+/g, "/").slice(0, 80) || "/";
104
+ return `${kind}:${tokenSummary || "none"}@${pathSummary}`;
105
+ }
106
+ function normalizeForMatching(value) {
107
+ return value
108
+ .normalize("NFD")
109
+ .replace(/[\u0300-\u036f]/g, "")
110
+ .toLowerCase();
111
+ }
112
+ export function inferCapturePageIdentity(pageRun) {
113
+ const rawPrompt = pageRun.prompt.trim();
114
+ const prompt = normalizeForMatching(rawPrompt);
115
+ const pageId = pageRun.pageId ?? "main";
116
+ const urlPath = normalizeUrlPath(pageRun.url);
117
+ const context = normalizeForMatching(`${pageId} ${rawPrompt} ${urlPath}`);
118
+ // For dialog/modal detection, only use the prompt and URL — page IDs are user-defined
119
+ // identifiers that often contain "modal" or "dialog" as naming convention, not as DOM intent.
120
+ const promptAndUrl = normalizeForMatching(`${rawPrompt} ${urlPath}`);
121
+ const hasPresetRoute = /\/projects\/[^/]+\/presets\/[^/?#]+/i.test(pageRun.url) || /\/presets\/[^/?#]+/i.test(pageRun.url);
122
+ const mentionsGallery = GALLERY_RE.test(context);
123
+ const mentionsModal = MODAL_RE.test(promptAndUrl);
124
+ const mentionsTemplate = TEMPLATE_RE.test(promptAndUrl);
125
+ const mentionsConfiguration = CONFIGURATION_RE.test(context);
126
+ const passiveState = PASSIVE_STATE_RE.test(prompt);
127
+ const mentionsDedicatedRoute = hasPresetRoute || (STRONG_ROUTE_PROMPT_RE.test(prompt) && !passiveState);
128
+ let kind = "unknown";
129
+ if (mentionsGallery && !mentionsConfiguration && !mentionsModal && !mentionsTemplate) {
130
+ kind = "gallery";
131
+ }
132
+ else if (mentionsConfiguration && mentionsDedicatedRoute && !mentionsModal) {
133
+ kind = "editor_route";
134
+ }
135
+ else if (mentionsConfiguration && (mentionsModal || mentionsTemplate || passiveState)) {
136
+ kind = "modal_configuration";
137
+ }
138
+ else if (mentionsModal || mentionsTemplate) {
139
+ kind = "modal_selection";
140
+ }
141
+ else if (mentionsConfiguration) {
142
+ kind = "detail_route";
143
+ }
144
+ else if (mentionsGallery) {
145
+ kind = "gallery";
146
+ }
147
+ const subjectTokens = uniqueInOrder([
148
+ ...tokenize(pageId),
149
+ ...tokenize(prompt),
150
+ ...tokenize(urlPath),
151
+ ]).slice(0, 10);
152
+ return {
153
+ kind,
154
+ summary: summarizeIdentity(kind, subjectTokens, urlPath),
155
+ subjectTokens,
156
+ dialogTarget: kind === "modal_selection" || kind === "modal_configuration",
157
+ dedicatedRoute: kind === "editor_route" || (kind === "detail_route" && hasPresetRoute),
158
+ };
159
+ }
160
+ function countTokenOverlap(left, right) {
161
+ if (left.length === 0 || right.length === 0)
162
+ return 0;
163
+ const rightSet = new Set(right);
164
+ return left.filter((token) => rightSet.has(token)).length;
165
+ }
166
+ function urlsPointToSameState(leftUrl, rightUrl) {
167
+ try {
168
+ const left = new URL(leftUrl);
169
+ const right = new URL(rightUrl);
170
+ return left.origin === right.origin && left.pathname === right.pathname;
171
+ }
172
+ catch {
173
+ return leftUrl === rightUrl;
174
+ }
175
+ }
176
+ function promptDeclaresDistinctTransition(prompt) {
177
+ return DISTINCT_TRANSITION_RE.test(normalizeForMatching(prompt));
178
+ }
179
+ export function findVariantPageDefinitionIssues(pageRuns) {
180
+ const issues = [];
181
+ for (let index = 1; index < pageRuns.length; index += 1) {
182
+ const previous = pageRuns[index - 1];
183
+ const current = pageRuns[index];
184
+ const previousIdentity = inferCapturePageIdentity(previous);
185
+ const currentIdentity = inferCapturePageIdentity(current);
186
+ const sameUrl = urlsPointToSameState(previous.url, current.url);
187
+ const overlap = countTokenOverlap(previousIdentity.subjectTokens, currentIdentity.subjectTokens);
188
+ const sameKind = previousIdentity.kind === currentIdentity.kind;
189
+ const passiveState = PASSIVE_STATE_RE.test(normalizeForMatching(current.prompt));
190
+ const lacksDistinctTransition = !promptDeclaresDistinctTransition(current.prompt);
191
+ if (sameUrl
192
+ && sameKind
193
+ && previousIdentity.kind !== "unknown"
194
+ && currentIdentity.kind !== "unknown"
195
+ && (overlap >= 2 || passiveState)
196
+ && lacksDistinctTransition) {
197
+ issues.push({
198
+ pageId: current.pageId ?? "main",
199
+ duplicateOfPageId: previous.pageId ?? "main",
200
+ reason: `Named capture "${current.pageId ?? "main"}" is ambiguous with "${previous.pageId ?? "main"}": both target the same ${currentIdentity.kind.replace(/_/g, " ")} state at ${current.url}. ` +
201
+ "Rewrite one prompt to describe a materially different state, or add a URL override for the later page.",
202
+ });
203
+ continue;
204
+ }
205
+ if (currentIdentity.dedicatedRoute
206
+ && previousIdentity.dialogTarget
207
+ && sameUrl
208
+ && !promptDeclaresDistinctTransition(current.prompt)) {
209
+ issues.push({
210
+ pageId: current.pageId ?? "main",
211
+ duplicateOfPageId: previous.pageId ?? "main",
212
+ reason: `Named capture "${current.pageId ?? "main"}" expects a dedicated editor/detail route, but it reuses the same URL/state as "${previous.pageId ?? "main"}" without any explicit navigation step. ` +
213
+ "Add a distinct URL override or rewrite the prompt so the target route is unambiguous.",
214
+ });
215
+ }
216
+ }
217
+ return issues;
218
+ }
219
+ //# sourceMappingURL=capture-page-identity.js.map
@@ -0,0 +1,12 @@
1
+ import { type EncryptedEnvelope } from './capture-encryption.js';
2
+ export interface StoredPresetCredentials {
3
+ loginUrl?: string;
4
+ email?: string;
5
+ password?: string;
6
+ }
7
+ export declare function normalizePresetCredentials(credentials?: Partial<StoredPresetCredentials> | null): StoredPresetCredentials | undefined;
8
+ export declare function hasStoredCredentials(raw: unknown): boolean;
9
+ export declare function encryptPresetCredentials(credentials: StoredPresetCredentials): EncryptedEnvelope;
10
+ export declare function decryptPresetCredentials(value: unknown): StoredPresetCredentials | undefined;
11
+ export declare function preparePresetConfigForStorage<T extends Record<string, unknown>>(config: T): T;
12
+ export declare function hydratePresetConfigFromStorage<T extends Record<string, unknown>>(raw: unknown): T;
@@ -0,0 +1,57 @@
1
+ import { decrypt, encrypt, isEncryptedEnvelope } from './capture-encryption.js';
2
+ function getSecret() {
3
+ const secret = process.env.PRESET_CREDENTIALS_ENCRYPTION_SECRET;
4
+ if (!secret) {
5
+ throw new Error('Missing PRESET_CREDENTIALS_ENCRYPTION_SECRET for preset credential encryption');
6
+ }
7
+ return secret;
8
+ }
9
+ export function normalizePresetCredentials(credentials) {
10
+ if (!credentials)
11
+ return undefined;
12
+ const loginUrl = credentials.loginUrl?.trim() || undefined;
13
+ const email = credentials.email?.trim() || undefined;
14
+ const password = credentials.password || undefined;
15
+ if (!loginUrl && !email && !password)
16
+ return undefined;
17
+ return {
18
+ ...(loginUrl ? { loginUrl } : {}),
19
+ ...(email ? { email } : {}),
20
+ ...(password ? { password } : {}),
21
+ };
22
+ }
23
+ export function hasStoredCredentials(raw) {
24
+ if (!raw || typeof raw !== 'object')
25
+ return false;
26
+ return raw.credentials != null;
27
+ }
28
+ export function encryptPresetCredentials(credentials) {
29
+ return encrypt(JSON.stringify(credentials), getSecret());
30
+ }
31
+ export function decryptPresetCredentials(value) {
32
+ if (!value)
33
+ return undefined;
34
+ if (isEncryptedEnvelope(value)) {
35
+ const decrypted = decrypt(value, getSecret());
36
+ return normalizePresetCredentials(JSON.parse(decrypted));
37
+ }
38
+ return normalizePresetCredentials(value);
39
+ }
40
+ export function preparePresetConfigForStorage(config) {
41
+ const credentials = normalizePresetCredentials(config.credentials);
42
+ return {
43
+ ...config,
44
+ ...(credentials
45
+ ? { credentials: encryptPresetCredentials(credentials) }
46
+ : { credentials: undefined }),
47
+ };
48
+ }
49
+ export function hydratePresetConfigFromStorage(raw) {
50
+ const config = (raw && typeof raw === 'object' ? raw : {});
51
+ const credentials = decryptPresetCredentials(config.credentials);
52
+ return {
53
+ ...config,
54
+ ...(credentials ? { credentials } : { credentials: undefined }),
55
+ };
56
+ }
57
+ //# sourceMappingURL=capture-preset-credentials.js.map
@@ -0,0 +1,58 @@
1
+ import { type ScreenshotPageRunInput, type ScreenshotVariantExecution } from "./capture-run-optimizer.js";
2
+ import { findVariantPageDefinitionIssues } from "./capture-page-identity.js";
3
+ export type CaptureTheme = "light" | "dark";
4
+ export interface CaptureViewport {
5
+ width: number;
6
+ height: number;
7
+ }
8
+ export interface CaptureTargetLike {
9
+ id: string;
10
+ label: string;
11
+ viewport: CaptureViewport;
12
+ deviceFrame?: string;
13
+ mockupOptions?: {
14
+ orientation?: "portrait" | "landscape" | string;
15
+ } | null;
16
+ }
17
+ export interface CapturePageDefinitionLike {
18
+ id?: string | null;
19
+ prompt?: string | null;
20
+ url?: string | null;
21
+ }
22
+ export interface CaptureRequestPlanInput<TTarget extends CaptureTargetLike = CaptureTargetLike, TElement = unknown> {
23
+ url: string;
24
+ prompt?: string | null;
25
+ targets?: TTarget[] | null;
26
+ viewports?: CaptureViewport[] | null;
27
+ deviceFrame?: string | null;
28
+ mockupOptions?: TTarget["mockupOptions"];
29
+ outputScale?: number | null;
30
+ langs?: string[] | null;
31
+ themes?: Array<CaptureTheme> | null;
32
+ elements?: TElement[] | null;
33
+ pages?: CapturePageDefinitionLike[] | null;
34
+ }
35
+ export interface CaptureRequestPlan<TTarget extends CaptureTargetLike = CaptureTargetLike, TElement = unknown> {
36
+ targets: TTarget[];
37
+ outputScale: number;
38
+ langs: string[];
39
+ themes: Array<CaptureTheme>;
40
+ elements: TElement[];
41
+ pageRuns: ScreenshotPageRunInput[];
42
+ variantPlan: ScreenshotVariantExecution[];
43
+ pageDefinitionIssues: ReturnType<typeof findVariantPageDefinitionIssues>;
44
+ requestedCaptureCount: number;
45
+ totalCaptures: number;
46
+ }
47
+ export declare function hasNonEmptyCapturePrompt(prompt: string | null | undefined): boolean;
48
+ export declare function getCapturePromptValidationError(input: Pick<CaptureRequestPlanInput, "url" | "prompt" | "pages">): string | null;
49
+ export declare function normalizeOutputScale(scale?: number | null): number;
50
+ export declare function resolveCaptureLangs(raw: unknown): string[];
51
+ export declare function resolveCaptureThemes(raw: unknown): Array<CaptureTheme>;
52
+ export declare function buildCapturePageRuns(input: Pick<CaptureRequestPlanInput, "url" | "prompt" | "pages">): ScreenshotPageRunInput[];
53
+ export declare function normalizeCaptureTargets<TTarget extends CaptureTargetLike = CaptureTargetLike>(input: Pick<CaptureRequestPlanInput<TTarget>, "targets" | "viewports" | "deviceFrame" | "mockupOptions">): Promise<TTarget[]>;
54
+ export declare function sanitizeStorageSegment(value: string | null | undefined): string;
55
+ export declare function getCaptureUrlHostname(value: string | null | undefined): string;
56
+ export declare function normalizeComparableUrl(value: string | null | undefined): string;
57
+ export declare function urlsPointToSameCaptureState(left: string | null | undefined, right: string | null | undefined): boolean;
58
+ export declare function resolveCaptureRequestPlan<TTarget extends CaptureTargetLike = CaptureTargetLike, TElement = unknown>(input: CaptureRequestPlanInput<TTarget, TElement>): Promise<CaptureRequestPlan<TTarget, TElement>>;