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.
- package/assets/chrome/ios-statusbar-comparison-reference.jpg +0 -0
- package/assets/chrome/ios-statusbar-dark-reference.jpg +0 -0
- package/assets/chrome/ios-statusbar-light-reference.jpg +0 -0
- package/assets/devices/ipad-pro-11-m4.json +52 -0
- package/assets/devices/iphone-16-pro.json +53 -0
- package/assets/devices/macbook-air-13.json +45 -0
- package/assets/frames/MacBook Air 13.svg +242 -0
- package/assets/frames/Status bar - iPhone.png +0 -0
- Menu bar- iPad.png +0 -0
- package/assets/frames/iPad Pro M4 11_.png +0 -0
- package/assets/frames/iPhone 16 Pro.png +0 -0
- package/assets/icons/Cellular Connection.svg +3 -0
- package/assets/icons/Union.svg +6 -0
- package/assets/icons/Wifi.svg +3 -0
- package/assets/icons/battery.svg +5 -0
- package/assets/icons/battery_charging.svg +8 -0
- package/dist/abort.d.ts +5 -0
- package/dist/abort.js +44 -0
- package/dist/agent.d.ts +142 -0
- package/dist/agent.js +4511 -0
- package/dist/billing-operation-logging.d.ts +38 -0
- package/dist/billing-operation-logging.js +248 -0
- package/dist/browser-bar.d.ts +40 -0
- package/dist/browser-bar.js +147 -0
- package/dist/browser.d.ts +25 -0
- package/dist/browser.js +177 -9
- package/dist/capture-alt-text.d.ts +12 -0
- package/dist/capture-alt-text.js +51 -0
- package/dist/capture-encryption.d.ts +10 -0
- package/dist/capture-encryption.js +41 -0
- package/dist/capture-language-preflight.d.ts +41 -0
- package/dist/capture-language-preflight.js +286 -0
- package/dist/capture-llm-page-identity.d.ts +15 -0
- package/dist/capture-llm-page-identity.js +116 -0
- package/dist/capture-model-resolution.d.ts +9 -0
- package/dist/capture-model-resolution.js +21 -0
- package/dist/capture-page-identity.d.ts +9 -0
- package/dist/capture-page-identity.js +219 -0
- package/dist/capture-preset-credentials.d.ts +12 -0
- package/dist/capture-preset-credentials.js +57 -0
- package/dist/capture-request-plan.d.ts +58 -0
- package/dist/capture-request-plan.js +216 -0
- package/dist/capture-run-optimizer.d.ts +139 -0
- package/dist/capture-run-optimizer.js +848 -0
- package/dist/capture-selector-memory.d.ts +26 -0
- package/dist/capture-selector-memory.js +327 -0
- package/dist/capture-session-profile-encryption.d.ts +2 -0
- package/dist/capture-session-profile-encryption.js +22 -0
- package/dist/capture-step-timeout.d.ts +10 -0
- package/dist/capture-step-timeout.js +30 -0
- package/dist/capture-studio-sync.d.ts +22 -0
- package/dist/capture-studio-sync.js +166 -0
- package/dist/capture-variant-state.d.ts +54 -0
- package/dist/capture-variant-state.js +156 -0
- package/dist/cli.js +15 -0
- package/dist/clip-orchestrator.d.ts +148 -0
- package/dist/clip-orchestrator.js +950 -0
- package/dist/clip-postprocess.d.ts +42 -0
- package/dist/clip-postprocess.js +192 -0
- package/dist/cost-logging.d.ts +27 -0
- package/dist/cost-logging.js +128 -0
- package/dist/credential-templates.d.ts +5 -0
- package/dist/credential-templates.js +60 -0
- package/dist/element-capture.d.ts +53 -0
- package/dist/element-capture.js +766 -0
- package/dist/hybrid-navigator.d.ts +138 -0
- package/dist/hybrid-navigator.js +468 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +11 -0
- package/dist/llm-usage.d.ts +17 -0
- package/dist/llm-usage.js +45 -0
- package/dist/mockup-html.d.ts +119 -0
- package/dist/mockup-html.js +253 -0
- package/dist/mockup.d.ts +94 -0
- package/dist/mockup.js +608 -0
- package/dist/mouse-animation.d.ts +46 -0
- package/dist/mouse-animation.js +100 -0
- package/dist/overlay-utils.d.ts +14 -0
- package/dist/overlay-utils.js +13 -0
- package/dist/posthog.d.ts +4 -0
- package/dist/posthog.js +26 -0
- package/dist/prompt-cache.d.ts +10 -0
- package/dist/prompt-cache.js +24 -0
- package/dist/prompts.d.ts +167 -0
- package/dist/prompts.js +1165 -0
- package/dist/remote-browser.d.ts +191 -0
- package/dist/remote-browser.js +305 -0
- package/dist/security.d.ts +20 -0
- package/dist/security.js +569 -0
- package/dist/server-capture-runtime.d.ts +123 -0
- package/dist/server-capture-runtime.js +638 -0
- package/dist/server-credit-usage.d.ts +12 -0
- package/dist/server-credit-usage.js +41 -0
- package/dist/server-posthog.d.ts +2 -0
- package/dist/server-posthog.js +16 -0
- package/dist/server-project-webhooks.d.ts +45 -0
- package/dist/server-project-webhooks.js +97 -0
- package/dist/server-screenshot-watermark.d.ts +7 -0
- package/dist/server-screenshot-watermark.js +38 -0
- package/dist/session-profile.d.ts +86 -0
- package/dist/session-profile.js +1373 -0
- package/dist/sf-pro-fonts.d.ts +4 -0
- package/dist/sf-pro-fonts.js +7 -0
- package/dist/status-bar-l10n.d.ts +14 -0
- package/dist/status-bar-l10n.js +177 -0
- package/dist/status-bar.d.ts +44 -0
- package/dist/status-bar.js +336 -0
- package/dist/tools.d.ts +4 -0
- package/dist/tools.js +578 -0
- package/dist/video-agent.d.ts +143 -0
- package/dist/video-agent.js +4783 -0
- package/dist/video-observation.d.ts +36 -0
- package/dist/video-observation.js +192 -0
- package/dist/video-planner.d.ts +12 -0
- package/dist/video-planner.js +500 -0
- package/dist/video-prompts.d.ts +37 -0
- package/dist/video-prompts.js +554 -0
- package/dist/video-tools.d.ts +3 -0
- package/dist/video-tools.js +59 -0
- package/dist/video-variant-state.d.ts +29 -0
- package/dist/video-variant-state.js +80 -0
- package/dist/vision-model.d.ts +17 -0
- package/dist/vision-model.js +74 -0
- package/dist/ws-auth.d.ts +20 -0
- package/dist/ws-auth.js +67 -0
- package/dist/ws-handler.d.ts +10 -0
- package/dist/ws-handler.js +1663 -0
- package/dist/ws-server.d.ts +9 -0
- package/dist/ws-server.js +52 -0
- package/package.json +93 -39
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { SupabaseClient } from '@supabase/supabase-js';
|
|
2
|
+
export interface ScreenshotSelectorMemoryUpdate {
|
|
3
|
+
stepSignature: string;
|
|
4
|
+
selector: string;
|
|
5
|
+
source: 'agent' | 'deterministic' | 'manual';
|
|
6
|
+
success: boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare function loadScreenshotSelectorMemory(supabase: SupabaseClient, params: {
|
|
9
|
+
projectId: string | null;
|
|
10
|
+
presetId?: string | null;
|
|
11
|
+
domain: string;
|
|
12
|
+
lang: string;
|
|
13
|
+
theme: 'light' | 'dark';
|
|
14
|
+
}): Promise<Record<string, string[]>>;
|
|
15
|
+
export declare function persistScreenshotSelectorMemoryUpdates(supabase: SupabaseClient, params: {
|
|
16
|
+
projectId: string | null;
|
|
17
|
+
presetId?: string | null;
|
|
18
|
+
domain: string;
|
|
19
|
+
lang: string;
|
|
20
|
+
theme: 'light' | 'dark';
|
|
21
|
+
}, updates: ScreenshotSelectorMemoryUpdate[]): Promise<void>;
|
|
22
|
+
export declare function extractSelectorUpdates(actions: Array<{
|
|
23
|
+
action: string;
|
|
24
|
+
params: Record<string, unknown>;
|
|
25
|
+
success: boolean;
|
|
26
|
+
}>): ScreenshotSelectorMemoryUpdate[];
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
const GENERIC_NAV_TOKENS = new Set([
|
|
2
|
+
'page',
|
|
3
|
+
'pages',
|
|
4
|
+
'open',
|
|
5
|
+
'view',
|
|
6
|
+
'link',
|
|
7
|
+
'button',
|
|
8
|
+
'click',
|
|
9
|
+
'menu',
|
|
10
|
+
'nav',
|
|
11
|
+
'navigation',
|
|
12
|
+
'section',
|
|
13
|
+
'screen',
|
|
14
|
+
'capture',
|
|
15
|
+
'prepare',
|
|
16
|
+
'show',
|
|
17
|
+
'the',
|
|
18
|
+
'and',
|
|
19
|
+
'for',
|
|
20
|
+
'with',
|
|
21
|
+
]);
|
|
22
|
+
const INTERNAL_AUTOMATION_SELECTOR_RE = /\[data-ak-[^\]]+\]|data-ak-interactive-index/i;
|
|
23
|
+
function containsInternalAutomationSelector(selector) {
|
|
24
|
+
return !!selector && INTERNAL_AUTOMATION_SELECTOR_RE.test(selector);
|
|
25
|
+
}
|
|
26
|
+
function uniqueInOrder(values) {
|
|
27
|
+
const seen = new Set();
|
|
28
|
+
const result = [];
|
|
29
|
+
for (const value of values) {
|
|
30
|
+
if (seen.has(value))
|
|
31
|
+
continue;
|
|
32
|
+
seen.add(value);
|
|
33
|
+
result.push(value);
|
|
34
|
+
}
|
|
35
|
+
return result;
|
|
36
|
+
}
|
|
37
|
+
function normalizeNavigationTokens(values) {
|
|
38
|
+
const tokens = [];
|
|
39
|
+
for (const value of values) {
|
|
40
|
+
if (!value)
|
|
41
|
+
continue;
|
|
42
|
+
const parts = value
|
|
43
|
+
.toLowerCase()
|
|
44
|
+
.split(/[^a-z0-9]+/i)
|
|
45
|
+
.map((part) => part.trim())
|
|
46
|
+
.filter((part) => part.length >= 3 && part.length <= 32 && !GENERIC_NAV_TOKENS.has(part));
|
|
47
|
+
tokens.push(...parts);
|
|
48
|
+
}
|
|
49
|
+
return uniqueInOrder(tokens).slice(0, 8);
|
|
50
|
+
}
|
|
51
|
+
function extractPathTokens(rawUrl) {
|
|
52
|
+
if (!rawUrl)
|
|
53
|
+
return [];
|
|
54
|
+
try {
|
|
55
|
+
const url = new URL(rawUrl);
|
|
56
|
+
return normalizeNavigationTokens(url.pathname.split('/'));
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return normalizeNavigationTokens(rawUrl.split('/'));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
export async function loadScreenshotSelectorMemory(supabase, params) {
|
|
63
|
+
const selectFields = 'step_signature, selector, confidence, last_success_at';
|
|
64
|
+
const grouped = {};
|
|
65
|
+
const appendRows = (rows) => {
|
|
66
|
+
for (const row of rows) {
|
|
67
|
+
const signature = String(row.step_signature ?? '').trim();
|
|
68
|
+
const selector = String(row.selector ?? '').trim();
|
|
69
|
+
if (!signature || !selector)
|
|
70
|
+
continue;
|
|
71
|
+
if (!grouped[signature])
|
|
72
|
+
grouped[signature] = [];
|
|
73
|
+
if (!grouped[signature].includes(selector)) {
|
|
74
|
+
grouped[signature].push(selector);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
if (params.presetId) {
|
|
79
|
+
let query = supabase
|
|
80
|
+
.from('screenshot_step_memory')
|
|
81
|
+
.select(selectFields)
|
|
82
|
+
.eq('domain', params.domain)
|
|
83
|
+
.eq('preset_id', params.presetId)
|
|
84
|
+
.eq('lang', params.lang)
|
|
85
|
+
.eq('theme', params.theme)
|
|
86
|
+
.order('confidence', { ascending: false })
|
|
87
|
+
.order('last_success_at', { ascending: false })
|
|
88
|
+
.limit(200);
|
|
89
|
+
if (params.projectId) {
|
|
90
|
+
query = query.eq('project_id', params.projectId);
|
|
91
|
+
}
|
|
92
|
+
const { data } = await query;
|
|
93
|
+
if (data && data.length > 0) {
|
|
94
|
+
appendRows(data);
|
|
95
|
+
return grouped;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
let fallbackQuery = supabase
|
|
99
|
+
.from('screenshot_step_memory')
|
|
100
|
+
.select(selectFields)
|
|
101
|
+
.eq('domain', params.domain)
|
|
102
|
+
.order('confidence', { ascending: false })
|
|
103
|
+
.order('last_success_at', { ascending: false })
|
|
104
|
+
.limit(200);
|
|
105
|
+
if (params.projectId) {
|
|
106
|
+
fallbackQuery = fallbackQuery.eq('project_id', params.projectId);
|
|
107
|
+
}
|
|
108
|
+
const { data: fallbackData } = await fallbackQuery;
|
|
109
|
+
if (fallbackData) {
|
|
110
|
+
appendRows(fallbackData);
|
|
111
|
+
}
|
|
112
|
+
return grouped;
|
|
113
|
+
}
|
|
114
|
+
export async function persistScreenshotSelectorMemoryUpdates(supabase, params, updates) {
|
|
115
|
+
const nowIso = new Date().toISOString();
|
|
116
|
+
const uniqueUpdates = new Map();
|
|
117
|
+
for (const update of updates) {
|
|
118
|
+
if (!update.stepSignature || !update.selector)
|
|
119
|
+
continue;
|
|
120
|
+
const key = `${update.stepSignature}|${update.selector}|${update.source}`;
|
|
121
|
+
uniqueUpdates.set(key, update);
|
|
122
|
+
}
|
|
123
|
+
for (const update of uniqueUpdates.values()) {
|
|
124
|
+
const baseQuery = supabase
|
|
125
|
+
.from('screenshot_step_memory')
|
|
126
|
+
.select('id, success_count, fail_count, confidence')
|
|
127
|
+
.eq('domain', params.domain)
|
|
128
|
+
.eq('step_signature', update.stepSignature)
|
|
129
|
+
.eq('selector', update.selector)
|
|
130
|
+
.eq('lang', params.lang)
|
|
131
|
+
.eq('theme', params.theme)
|
|
132
|
+
.limit(1);
|
|
133
|
+
const scopedQuery = params.presetId
|
|
134
|
+
? baseQuery.eq('preset_id', params.presetId)
|
|
135
|
+
: baseQuery.is('preset_id', null);
|
|
136
|
+
const projectScopedQuery = params.projectId
|
|
137
|
+
? scopedQuery.eq('project_id', params.projectId)
|
|
138
|
+
: scopedQuery.is('project_id', null);
|
|
139
|
+
const { data: existingRows } = await projectScopedQuery;
|
|
140
|
+
const existing = existingRows?.[0];
|
|
141
|
+
if (!existing) {
|
|
142
|
+
await supabase.from('screenshot_step_memory').insert({
|
|
143
|
+
project_id: params.projectId,
|
|
144
|
+
preset_id: params.presetId ?? null,
|
|
145
|
+
domain: params.domain,
|
|
146
|
+
lang: params.lang,
|
|
147
|
+
theme: params.theme,
|
|
148
|
+
step_signature: update.stepSignature,
|
|
149
|
+
selector: update.selector,
|
|
150
|
+
source: update.source,
|
|
151
|
+
success_count: update.success ? 1 : 0,
|
|
152
|
+
fail_count: update.success ? 0 : 1,
|
|
153
|
+
confidence: update.success ? 0.65 : 0.35,
|
|
154
|
+
last_success_at: update.success ? nowIso : null,
|
|
155
|
+
last_failure_at: update.success ? null : nowIso,
|
|
156
|
+
});
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
const successCount = (existing.success_count ?? 0) + (update.success ? 1 : 0);
|
|
160
|
+
const failCount = (existing.fail_count ?? 0) + (update.success ? 0 : 1);
|
|
161
|
+
const currentConfidence = existing.confidence ?? 0.5;
|
|
162
|
+
const confidence = update.success
|
|
163
|
+
? Math.min(0.98, currentConfidence + 0.08)
|
|
164
|
+
: Math.max(0.05, currentConfidence - 0.12);
|
|
165
|
+
await supabase
|
|
166
|
+
.from('screenshot_step_memory')
|
|
167
|
+
.update({
|
|
168
|
+
success_count: successCount,
|
|
169
|
+
fail_count: failCount,
|
|
170
|
+
confidence,
|
|
171
|
+
last_success_at: update.success ? nowIso : undefined,
|
|
172
|
+
last_failure_at: update.success ? undefined : nowIso,
|
|
173
|
+
})
|
|
174
|
+
.eq('id', existing.id);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
export function extractSelectorUpdates(actions) {
|
|
178
|
+
const updates = [];
|
|
179
|
+
const languageAliases = {
|
|
180
|
+
en: ['en', 'english'],
|
|
181
|
+
fr: ['fr', 'french', 'francais', 'français'],
|
|
182
|
+
de: ['de', 'german', 'deutsch'],
|
|
183
|
+
es: ['es', 'spanish', 'espanol', 'español'],
|
|
184
|
+
it: ['it', 'italian', 'italiano'],
|
|
185
|
+
pt: ['pt', 'portuguese', 'portugues', 'português'],
|
|
186
|
+
};
|
|
187
|
+
for (const action of actions) {
|
|
188
|
+
if (action.action !== 'click'
|
|
189
|
+
&& action.action !== 'type_text'
|
|
190
|
+
&& action.action !== 'safe_expand'
|
|
191
|
+
&& action.action !== 'select_option') {
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
const selector = action.params.selector
|
|
195
|
+
?? action.params.resolvedSelector
|
|
196
|
+
?? (action.params.index !== undefined ? `[index:${action.params.index}]` : null);
|
|
197
|
+
if (!selector || selector.startsWith('[index:') || containsInternalAutomationSelector(selector)) {
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
const text = String(action.params.text ?? '').toLowerCase();
|
|
201
|
+
const label = String(action.params.elementLabel ?? '').toLowerCase();
|
|
202
|
+
const optionLabel = String(action.params.optionLabel ?? '').toLowerCase();
|
|
203
|
+
const optionValue = String(action.params.optionValue ?? '').toLowerCase();
|
|
204
|
+
const href = String(action.params.href ?? '').toLowerCase();
|
|
205
|
+
const selectorLower = selector.toLowerCase();
|
|
206
|
+
const combined = `${selectorLower} ${text} ${label} ${optionLabel} ${optionValue} ${href}`;
|
|
207
|
+
if (combined.includes('login')
|
|
208
|
+
|| combined.includes('sign in')
|
|
209
|
+
|| combined.includes('signin')
|
|
210
|
+
|| combined.includes('password')
|
|
211
|
+
|| combined.includes('email')
|
|
212
|
+
|| combined.includes('auth')) {
|
|
213
|
+
updates.push({
|
|
214
|
+
stepSignature: action.action === 'type_text'
|
|
215
|
+
? combined.includes('password')
|
|
216
|
+
? 'login:password_field'
|
|
217
|
+
: 'login:email_field'
|
|
218
|
+
: 'login:submit',
|
|
219
|
+
selector,
|
|
220
|
+
source: 'agent',
|
|
221
|
+
success: action.success,
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
if (action.action === 'type_text'
|
|
225
|
+
&& (combined.includes('search') || combined.includes('query'))) {
|
|
226
|
+
updates.push({
|
|
227
|
+
stepSignature: 'search:field',
|
|
228
|
+
selector,
|
|
229
|
+
source: 'agent',
|
|
230
|
+
success: action.success,
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
if ((action.action === 'click' || action.action === 'safe_expand')
|
|
234
|
+
&& (combined.includes('menu') || combined.includes('navigation') || combined.includes('hamburger') || combined.includes('drawer'))) {
|
|
235
|
+
updates.push({
|
|
236
|
+
stepSignature: 'menu:primary',
|
|
237
|
+
selector,
|
|
238
|
+
source: 'agent',
|
|
239
|
+
success: action.success,
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
if ((action.action === 'click' || action.action === 'safe_expand')
|
|
243
|
+
&& (combined.includes('account') || combined.includes('profile') || combined.includes('avatar') || combined.includes('user'))) {
|
|
244
|
+
updates.push({
|
|
245
|
+
stepSignature: 'account:menu',
|
|
246
|
+
selector,
|
|
247
|
+
source: 'agent',
|
|
248
|
+
success: action.success,
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
const languageLike = combined.includes('lang')
|
|
252
|
+
|| combined.includes('locale')
|
|
253
|
+
|| combined.includes('language')
|
|
254
|
+
|| Object.values(languageAliases).some((aliases) => aliases.some((alias) => combined.includes(alias)));
|
|
255
|
+
if (languageLike) {
|
|
256
|
+
updates.push({
|
|
257
|
+
stepSignature: 'lang:switcher',
|
|
258
|
+
selector,
|
|
259
|
+
source: 'agent',
|
|
260
|
+
success: action.success,
|
|
261
|
+
});
|
|
262
|
+
for (const [langCode, aliases] of Object.entries(languageAliases)) {
|
|
263
|
+
if (aliases.some((alias) => combined.includes(alias))) {
|
|
264
|
+
updates.push({
|
|
265
|
+
stepSignature: `lang:option:${langCode}`,
|
|
266
|
+
selector,
|
|
267
|
+
source: 'agent',
|
|
268
|
+
success: action.success,
|
|
269
|
+
});
|
|
270
|
+
break;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
if (action.action === 'click' || action.action === 'safe_expand' || action.action === 'select_option') {
|
|
275
|
+
for (const token of extractPathTokens(String(action.params.href ?? '')).slice(0, 4)) {
|
|
276
|
+
updates.push({
|
|
277
|
+
stepSignature: `nav:path:${token}`,
|
|
278
|
+
selector,
|
|
279
|
+
source: 'agent',
|
|
280
|
+
success: action.success,
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
for (const token of normalizeNavigationTokens([
|
|
284
|
+
action.params.elementLabel,
|
|
285
|
+
action.params.optionLabel,
|
|
286
|
+
]).slice(0, 4)) {
|
|
287
|
+
updates.push({
|
|
288
|
+
stepSignature: `nav:label:${token}`,
|
|
289
|
+
selector,
|
|
290
|
+
source: 'agent',
|
|
291
|
+
success: action.success,
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
const themeLike = combined.includes('theme')
|
|
296
|
+
|| combined.includes('appearance')
|
|
297
|
+
|| combined.includes('dark')
|
|
298
|
+
|| combined.includes('light')
|
|
299
|
+
|| combined.includes('mode');
|
|
300
|
+
if (themeLike) {
|
|
301
|
+
updates.push({
|
|
302
|
+
stepSignature: 'theme:toggle',
|
|
303
|
+
selector,
|
|
304
|
+
source: 'agent',
|
|
305
|
+
success: action.success,
|
|
306
|
+
});
|
|
307
|
+
if (combined.includes('dark')) {
|
|
308
|
+
updates.push({
|
|
309
|
+
stepSignature: 'theme:option:dark',
|
|
310
|
+
selector,
|
|
311
|
+
source: 'agent',
|
|
312
|
+
success: action.success,
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
if (combined.includes('light')) {
|
|
316
|
+
updates.push({
|
|
317
|
+
stepSignature: 'theme:option:light',
|
|
318
|
+
selector,
|
|
319
|
+
source: 'agent',
|
|
320
|
+
success: action.success,
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
return updates;
|
|
326
|
+
}
|
|
327
|
+
//# sourceMappingURL=capture-selector-memory.js.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { decrypt, encrypt, isEncryptedEnvelope } from './capture-encryption.js';
|
|
2
|
+
function getSecret() {
|
|
3
|
+
const secret = process.env.SESSION_PROFILE_ENCRYPTION_SECRET;
|
|
4
|
+
if (!secret) {
|
|
5
|
+
throw new Error('Missing SESSION_PROFILE_ENCRYPTION_SECRET for session profile encryption');
|
|
6
|
+
}
|
|
7
|
+
return secret;
|
|
8
|
+
}
|
|
9
|
+
export function encryptSessionField(value) {
|
|
10
|
+
if (!value)
|
|
11
|
+
return value;
|
|
12
|
+
return encrypt(JSON.stringify(value), getSecret());
|
|
13
|
+
}
|
|
14
|
+
export function decryptSessionField(value) {
|
|
15
|
+
if (!value)
|
|
16
|
+
return undefined;
|
|
17
|
+
if (isEncryptedEnvelope(value)) {
|
|
18
|
+
return JSON.parse(decrypt(value, getSecret()));
|
|
19
|
+
}
|
|
20
|
+
return value;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=capture-session-profile-encryption.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare class CaptureStepTimeoutError extends Error {
|
|
2
|
+
readonly timeoutMs: number;
|
|
3
|
+
readonly stepLabel: string;
|
|
4
|
+
constructor(stepLabel: string, timeoutMs: number);
|
|
5
|
+
}
|
|
6
|
+
export declare function isCaptureStepTimeoutError(error: unknown): error is CaptureStepTimeoutError;
|
|
7
|
+
export declare function withCaptureStepTimeout<T>(work: () => Promise<T>, params: {
|
|
8
|
+
stepLabel: string;
|
|
9
|
+
timeoutMs: number;
|
|
10
|
+
}): Promise<T>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export class CaptureStepTimeoutError extends Error {
|
|
2
|
+
timeoutMs;
|
|
3
|
+
stepLabel;
|
|
4
|
+
constructor(stepLabel, timeoutMs) {
|
|
5
|
+
super(`Timed out after ${timeoutMs}ms while ${stepLabel}.`);
|
|
6
|
+
this.name = "CaptureStepTimeoutError";
|
|
7
|
+
this.stepLabel = stepLabel;
|
|
8
|
+
this.timeoutMs = timeoutMs;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export function isCaptureStepTimeoutError(error) {
|
|
12
|
+
return error instanceof CaptureStepTimeoutError;
|
|
13
|
+
}
|
|
14
|
+
export async function withCaptureStepTimeout(work, params) {
|
|
15
|
+
return new Promise((resolve, reject) => {
|
|
16
|
+
const timer = setTimeout(() => {
|
|
17
|
+
reject(new CaptureStepTimeoutError(params.stepLabel, params.timeoutMs));
|
|
18
|
+
}, params.timeoutMs);
|
|
19
|
+
void work()
|
|
20
|
+
.then((result) => {
|
|
21
|
+
clearTimeout(timer);
|
|
22
|
+
resolve(result);
|
|
23
|
+
})
|
|
24
|
+
.catch((error) => {
|
|
25
|
+
clearTimeout(timer);
|
|
26
|
+
reject(error);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=capture-step-timeout.js.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { SupabaseClient } from '@supabase/supabase-js';
|
|
2
|
+
type StudioBrowserBarConfig = {
|
|
3
|
+
url?: string;
|
|
4
|
+
pageTitle?: string;
|
|
5
|
+
tabIconUrl?: string;
|
|
6
|
+
} | null | undefined;
|
|
7
|
+
export declare function syncStudioVariantAfterCapture(params: {
|
|
8
|
+
supabase: SupabaseClient;
|
|
9
|
+
projectId: string;
|
|
10
|
+
presetId: string;
|
|
11
|
+
targetId?: string | null;
|
|
12
|
+
lang: string;
|
|
13
|
+
theme: string;
|
|
14
|
+
elementName: string | null;
|
|
15
|
+
captureId: string;
|
|
16
|
+
screenshotUrl: string | null;
|
|
17
|
+
rawScreenshotUrl?: string | null;
|
|
18
|
+
deviceFrame?: string | null;
|
|
19
|
+
browserBar?: StudioBrowserBarConfig;
|
|
20
|
+
matchTargetId?: boolean;
|
|
21
|
+
}): Promise<void>;
|
|
22
|
+
export {};
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
function isRecord(value) {
|
|
2
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
3
|
+
}
|
|
4
|
+
function getNullableString(value) {
|
|
5
|
+
return typeof value === 'string' && value.trim().length > 0 ? value : null;
|
|
6
|
+
}
|
|
7
|
+
function mergeStudioMockupConfig(existingConfig, deviceFrame, browserBar, rawScreenshotUrl) {
|
|
8
|
+
const normalizedBrowserBar = browserBar && Object.keys(browserBar).length > 0
|
|
9
|
+
? {
|
|
10
|
+
...(browserBar.url ? { url: browserBar.url } : {}),
|
|
11
|
+
...(browserBar.pageTitle ? { pageTitle: browserBar.pageTitle } : {}),
|
|
12
|
+
...(browserBar.tabIconUrl ? { tabIconUrl: browserBar.tabIconUrl } : {}),
|
|
13
|
+
}
|
|
14
|
+
: null;
|
|
15
|
+
const normalizedFrame = deviceFrame ?? null;
|
|
16
|
+
const normalizedRawUrl = rawScreenshotUrl ?? null;
|
|
17
|
+
const base = isRecord(existingConfig) ? { ...existingConfig } : {};
|
|
18
|
+
if (normalizedFrame) {
|
|
19
|
+
base.deviceFrame = normalizedFrame;
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
delete base.deviceFrame;
|
|
23
|
+
}
|
|
24
|
+
if (normalizedRawUrl) {
|
|
25
|
+
base.rawScreenshotUrl = normalizedRawUrl;
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
delete base.rawScreenshotUrl;
|
|
29
|
+
}
|
|
30
|
+
if (normalizedBrowserBar) {
|
|
31
|
+
base.browserBar = normalizedBrowserBar;
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
delete base.browserBar;
|
|
35
|
+
}
|
|
36
|
+
return Object.keys(base).length > 0 ? base : null;
|
|
37
|
+
}
|
|
38
|
+
export async function syncStudioVariantAfterCapture(params) {
|
|
39
|
+
const { supabase, projectId, presetId, targetId = null, lang, theme, elementName, captureId, screenshotUrl, rawScreenshotUrl = null, deviceFrame = null, browserBar, matchTargetId = true, } = params;
|
|
40
|
+
if (!projectId || !presetId || !screenshotUrl) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
let linkSelect = supabase
|
|
44
|
+
.from('screenshot_links')
|
|
45
|
+
.select('id, latest_capture:captures!latest_capture_id(screenshot_url, raw_screenshot_url)')
|
|
46
|
+
.eq('preset_id', presetId)
|
|
47
|
+
.eq('lang', lang)
|
|
48
|
+
.eq('theme', theme);
|
|
49
|
+
if (matchTargetId && targetId) {
|
|
50
|
+
linkSelect = linkSelect.eq('target_id', targetId);
|
|
51
|
+
}
|
|
52
|
+
if (elementName !== null) {
|
|
53
|
+
linkSelect = linkSelect.eq('element_name', elementName);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
linkSelect = linkSelect.is('element_name', null);
|
|
57
|
+
}
|
|
58
|
+
const { data: existingLinks, error: linkSelectError } = await linkSelect;
|
|
59
|
+
if (linkSelectError) {
|
|
60
|
+
console.error('[capture] failed to load screenshot links for studio sync:', linkSelectError.message);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
let linkUpdate = supabase
|
|
64
|
+
.from('screenshot_links')
|
|
65
|
+
.update({ latest_capture_id: captureId })
|
|
66
|
+
.eq('preset_id', presetId)
|
|
67
|
+
.eq('lang', lang)
|
|
68
|
+
.eq('theme', theme);
|
|
69
|
+
if (matchTargetId && targetId) {
|
|
70
|
+
linkUpdate = linkUpdate.eq('target_id', targetId);
|
|
71
|
+
}
|
|
72
|
+
if (elementName !== null) {
|
|
73
|
+
linkUpdate = linkUpdate.eq('element_name', elementName);
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
linkUpdate = linkUpdate.is('element_name', null);
|
|
77
|
+
}
|
|
78
|
+
const { error: linkUpdateError } = await linkUpdate;
|
|
79
|
+
if (linkUpdateError) {
|
|
80
|
+
console.error('[capture] failed to update screenshot links:', linkUpdateError.message);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const previousUrls = new Set();
|
|
84
|
+
for (const link of existingLinks ?? []) {
|
|
85
|
+
const latestCapture = isRecord(link.latest_capture) ? link.latest_capture : null;
|
|
86
|
+
const previousScreenshotUrl = getNullableString(latestCapture?.screenshot_url);
|
|
87
|
+
const previousRawScreenshotUrl = getNullableString(latestCapture?.raw_screenshot_url);
|
|
88
|
+
if (previousScreenshotUrl)
|
|
89
|
+
previousUrls.add(previousScreenshotUrl);
|
|
90
|
+
if (previousRawScreenshotUrl)
|
|
91
|
+
previousUrls.add(previousRawScreenshotUrl);
|
|
92
|
+
}
|
|
93
|
+
const { data: compositions, error: compositionsError } = await supabase
|
|
94
|
+
.from('compositions')
|
|
95
|
+
.select('id, pages')
|
|
96
|
+
.eq('project_id', projectId);
|
|
97
|
+
if (compositionsError) {
|
|
98
|
+
console.error('[capture] failed to load compositions for studio sync:', compositionsError.message);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
for (const composition of compositions ?? []) {
|
|
102
|
+
const pages = Array.isArray(composition.pages) ? composition.pages : [];
|
|
103
|
+
let changed = false;
|
|
104
|
+
const nextPages = pages.map((page) => {
|
|
105
|
+
if (!isRecord(page) || !isRecord(page.screenshot)) {
|
|
106
|
+
return page;
|
|
107
|
+
}
|
|
108
|
+
const screenshot = page.screenshot;
|
|
109
|
+
if (getNullableString(screenshot.presetId) !== presetId) {
|
|
110
|
+
return page;
|
|
111
|
+
}
|
|
112
|
+
const currentTargetId = getNullableString(screenshot.targetId);
|
|
113
|
+
const currentLang = getNullableString(screenshot.lang);
|
|
114
|
+
const currentTheme = getNullableString(screenshot.theme);
|
|
115
|
+
const currentElementName = getNullableString(screenshot.elementName);
|
|
116
|
+
const currentScreenshotUrl = getNullableString(screenshot.screenshotUrl);
|
|
117
|
+
const currentRawScreenshotUrl = getNullableString(screenshot.rawScreenshotUrl);
|
|
118
|
+
const targetMatches = !matchTargetId || !targetId || currentTargetId === targetId;
|
|
119
|
+
const exactMatch = targetMatches
|
|
120
|
+
&& currentLang === lang
|
|
121
|
+
&& currentTheme === theme
|
|
122
|
+
&& currentElementName === elementName;
|
|
123
|
+
const legacyMatch = (!currentTargetId || !currentLang || !currentTheme)
|
|
124
|
+
&& (currentElementName === null || currentElementName === elementName)
|
|
125
|
+
&& ((currentScreenshotUrl !== null && previousUrls.has(currentScreenshotUrl))
|
|
126
|
+
|| (currentRawScreenshotUrl !== null && previousUrls.has(currentRawScreenshotUrl)));
|
|
127
|
+
if (!exactMatch && !legacyMatch) {
|
|
128
|
+
return page;
|
|
129
|
+
}
|
|
130
|
+
const nextMockupConfig = mergeStudioMockupConfig(screenshot.mockupConfig, deviceFrame, browserBar, rawScreenshotUrl);
|
|
131
|
+
const nextScreenshot = {
|
|
132
|
+
...screenshot,
|
|
133
|
+
presetId,
|
|
134
|
+
captureId,
|
|
135
|
+
targetId: targetId ?? currentTargetId,
|
|
136
|
+
lang,
|
|
137
|
+
theme,
|
|
138
|
+
elementName,
|
|
139
|
+
screenshotUrl,
|
|
140
|
+
rawScreenshotUrl,
|
|
141
|
+
};
|
|
142
|
+
if (nextMockupConfig) {
|
|
143
|
+
nextScreenshot.mockupConfig = nextMockupConfig;
|
|
144
|
+
}
|
|
145
|
+
if (JSON.stringify(nextScreenshot) === JSON.stringify(screenshot)) {
|
|
146
|
+
return page;
|
|
147
|
+
}
|
|
148
|
+
changed = true;
|
|
149
|
+
return {
|
|
150
|
+
...page,
|
|
151
|
+
screenshot: nextScreenshot,
|
|
152
|
+
};
|
|
153
|
+
});
|
|
154
|
+
if (!changed) {
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
const { error: compositionUpdateError } = await supabase
|
|
158
|
+
.from('compositions')
|
|
159
|
+
.update({ pages: nextPages })
|
|
160
|
+
.eq('id', composition.id);
|
|
161
|
+
if (compositionUpdateError) {
|
|
162
|
+
console.error(`[capture] failed to sync composition ${composition.id} with latest capture:`, compositionUpdateError.message);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=capture-studio-sync.js.map
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { CaptureCheckpoint, CapturePageIdentity, VariantCaptureRepairRecord, VariantCaptureManifest, VariantCaptureStatus, VariantValidatedCapture } from "./types.js";
|
|
2
|
+
import type { ScreenshotPageRunInput } from "./capture-run-optimizer.js";
|
|
3
|
+
export interface VariantCaptureState {
|
|
4
|
+
expectedPageIds: string[];
|
|
5
|
+
pageIdentities: Record<string, CapturePageIdentity>;
|
|
6
|
+
validatedCaptures: VariantValidatedCapture[];
|
|
7
|
+
captureStatuses: Record<string, VariantCaptureStatus>;
|
|
8
|
+
lastCheckpointId: string | null;
|
|
9
|
+
blockedReasons: Record<string, string | null>;
|
|
10
|
+
recoveryAttempts: Record<string, number>;
|
|
11
|
+
repairHistory: VariantCaptureRepairRecord[];
|
|
12
|
+
}
|
|
13
|
+
export declare function createVariantCaptureState(pageRuns: ScreenshotPageRunInput[], precomputedIdentities?: Record<string, CapturePageIdentity>): VariantCaptureState;
|
|
14
|
+
export declare function buildVariantManifestContext(params: {
|
|
15
|
+
state: VariantCaptureState;
|
|
16
|
+
currentPageRun: ScreenshotPageRunInput;
|
|
17
|
+
}): VariantCaptureManifest;
|
|
18
|
+
export declare function recordValidatedVariantCapture(params: {
|
|
19
|
+
state: VariantCaptureState;
|
|
20
|
+
capture: VariantValidatedCapture;
|
|
21
|
+
checkpointId?: string | null;
|
|
22
|
+
}): {
|
|
23
|
+
state: VariantCaptureState;
|
|
24
|
+
duplicateOfPageId: string | null;
|
|
25
|
+
};
|
|
26
|
+
export declare function validateVariantCaptureState(state: VariantCaptureState): {
|
|
27
|
+
ok: boolean;
|
|
28
|
+
missingPages: string[];
|
|
29
|
+
blockedPages: Array<{
|
|
30
|
+
pageId: string;
|
|
31
|
+
reason: string | null;
|
|
32
|
+
}>;
|
|
33
|
+
duplicatePageIds: Array<{
|
|
34
|
+
pageId: string;
|
|
35
|
+
duplicateOfPageId: string;
|
|
36
|
+
}>;
|
|
37
|
+
};
|
|
38
|
+
export declare function hasValidatedVariantPage(state: VariantCaptureState, pageId: string | null | undefined): boolean;
|
|
39
|
+
export declare function getVariantCaptureStatus(state: VariantCaptureState, pageId: string | null | undefined): VariantCaptureStatus;
|
|
40
|
+
export declare function markVariantCaptureInProgress(state: VariantCaptureState, pageId: string | null | undefined): VariantCaptureState;
|
|
41
|
+
export declare function markVariantCaptureBlocked(params: {
|
|
42
|
+
state: VariantCaptureState;
|
|
43
|
+
pageId: string | null | undefined;
|
|
44
|
+
reason: string;
|
|
45
|
+
}): VariantCaptureState;
|
|
46
|
+
export declare function recordVariantRecoveryAttempt(state: VariantCaptureState, pageId: string | null | undefined): VariantCaptureState;
|
|
47
|
+
export declare function recordVariantRepair(params: {
|
|
48
|
+
state: VariantCaptureState;
|
|
49
|
+
repair: VariantCaptureRepairRecord;
|
|
50
|
+
}): VariantCaptureState;
|
|
51
|
+
export declare function recordVariantCheckpoint(params: {
|
|
52
|
+
state: VariantCaptureState;
|
|
53
|
+
checkpoint: CaptureCheckpoint;
|
|
54
|
+
}): VariantCaptureState;
|