autokap 1.0.7 → 1.0.8
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/cursors/macos.svg +4 -0
- package/assets/cursors/windows.svg +15 -0
- package/assets/skill/OPCODE-REFERENCE.md +607 -0
- package/assets/skill/README.md +39 -0
- package/assets/skill/SKILL.md +453 -468
- package/assets/skill/STUDIO-SKILL.md +476 -0
- package/assets/skill/references/examples.md +104 -0
- package/assets/skill/references/interactive-demo.md +225 -0
- package/assets/skill/references/mock-data.md +178 -0
- package/dist/action-verifier.d.ts +29 -0
- package/dist/action-verifier.js +133 -0
- package/dist/agent-action-recovery.d.ts +45 -0
- package/dist/agent-action-recovery.js +370 -0
- package/dist/agent-message-utils.d.ts +21 -0
- package/dist/agent-message-utils.js +77 -0
- package/dist/agent-url-utils.d.ts +30 -0
- package/dist/agent-url-utils.js +138 -0
- package/dist/agent.d.ts +92 -8
- package/dist/agent.js +2936 -781
- package/dist/ak-tree.d.ts +39 -0
- package/dist/ak-tree.js +368 -0
- package/dist/alt-text.d.ts +26 -0
- package/dist/alt-text.js +55 -0
- package/dist/auth-capture.d.ts +17 -0
- package/dist/auth-capture.js +164 -0
- package/dist/benchmark.d.ts +59 -0
- package/dist/benchmark.js +135 -0
- package/dist/browser-bar.d.ts +14 -6
- package/dist/browser-bar.js +145 -8
- package/dist/browser-pool.d.ts +7 -0
- package/dist/browser-pool.js +15 -5
- package/dist/browser-utils.d.ts +31 -0
- package/dist/browser-utils.js +97 -0
- package/dist/browser.d.ts +51 -1
- package/dist/browser.js +1481 -31
- package/dist/capture-alt-text.js +2 -1
- package/dist/capture-language-preflight.js +14 -0
- package/dist/capture-llm-page-identity.js +22 -10
- package/dist/capture-page-identity.d.ts +5 -7
- package/dist/capture-page-identity.js +211 -78
- package/dist/capture-preset-credentials.d.ts +50 -0
- package/dist/capture-preset-credentials.js +127 -0
- package/dist/capture-request-plan.d.ts +2 -2
- package/dist/capture-request-plan.js +64 -16
- package/dist/capture-run-optimizer.js +48 -33
- package/dist/capture-selector-memory.d.ts +5 -0
- package/dist/capture-selector-memory.js +18 -0
- package/dist/capture-strategy.d.ts +36 -0
- package/dist/capture-strategy.js +95 -0
- package/dist/capture-studio-sync.d.ts +1 -0
- package/dist/capture-studio-sync.js +9 -3
- package/dist/capture-surface-contract.d.ts +36 -0
- package/dist/capture-surface-contract.js +299 -0
- package/dist/capture-transition-engine.d.ts +28 -0
- package/dist/capture-transition-engine.js +292 -0
- package/dist/capture-variant-state.d.ts +2 -0
- package/dist/capture-variant-state.js +26 -0
- package/dist/capture-verification.d.ts +35 -0
- package/dist/capture-verification.js +95 -0
- package/dist/capture-viewport-lock.d.ts +48 -0
- package/dist/capture-viewport-lock.js +74 -0
- package/dist/circuit-breaker.d.ts +42 -0
- package/dist/circuit-breaker.js +119 -0
- package/dist/cli-config.d.ts +8 -1
- package/dist/cli-config.js +62 -6
- package/dist/cli-contract.d.ts +15 -0
- package/dist/cli-contract.js +167 -0
- package/dist/cli-runner-local.d.ts +12 -0
- package/dist/cli-runner-local.js +102 -0
- package/dist/cli-runner.d.ts +34 -0
- package/dist/cli-runner.js +433 -0
- package/dist/cli-utils.d.ts +0 -1
- package/dist/cli-utils.js +2 -5
- package/dist/cli.js +1005 -267
- package/dist/clip-orchestrator.js +9 -2
- package/dist/clip-postprocess.js +25 -16
- package/dist/cookie-dismiss.d.ts +2 -0
- package/dist/cookie-dismiss.js +48 -13
- package/dist/cost-logging.d.ts +8 -0
- package/dist/cost-logging.js +160 -46
- package/dist/cost-resolution-monitor.d.ts +16 -0
- package/dist/cost-resolution-monitor.js +34 -0
- package/dist/credential-templates.js +2 -2
- package/dist/cursor-overlay-script.d.ts +6 -0
- package/dist/cursor-overlay-script.js +169 -0
- package/dist/dom-css-purger.d.ts +65 -0
- package/dist/dom-css-purger.js +333 -0
- package/dist/dom-font-inliner.d.ts +45 -0
- package/dist/dom-font-inliner.js +148 -0
- package/dist/dom-patch-resolver.d.ts +52 -0
- package/dist/dom-patch-resolver.js +242 -0
- package/dist/dom-serializer.d.ts +82 -0
- package/dist/dom-serializer.js +378 -0
- package/dist/element-capture.d.ts +1 -41
- package/dist/element-capture.js +202 -446
- package/dist/env-validation.d.ts +5 -0
- package/dist/env-validation.js +29 -0
- package/dist/execution-schema.d.ts +4423 -0
- package/dist/execution-schema.js +507 -0
- package/dist/execution-types.d.ts +886 -0
- package/dist/execution-types.js +65 -0
- package/dist/fonts-loader.d.ts +14 -0
- package/dist/fonts-loader.js +55 -0
- package/dist/hybrid-navigator.js +12 -12
- package/dist/index.d.ts +9 -6
- package/dist/index.js +10 -4
- package/dist/legacy/agent-action-recovery.d.ts +45 -0
- package/dist/legacy/agent-action-recovery.js +370 -0
- package/dist/legacy/agent-message-utils.d.ts +21 -0
- package/dist/legacy/agent-message-utils.js +77 -0
- package/dist/legacy/agent-url-utils.d.ts +30 -0
- package/dist/legacy/agent-url-utils.js +138 -0
- package/dist/legacy/agent.d.ts +226 -0
- package/dist/legacy/agent.js +6666 -0
- package/dist/legacy/clip-orchestrator.d.ts +148 -0
- package/dist/legacy/clip-orchestrator.js +957 -0
- package/dist/legacy/credential-templates.d.ts +5 -0
- package/dist/legacy/credential-templates.js +60 -0
- package/dist/legacy/hybrid-navigator.d.ts +138 -0
- package/dist/legacy/hybrid-navigator.js +468 -0
- package/dist/legacy/llm-usage.d.ts +17 -0
- package/dist/legacy/llm-usage.js +45 -0
- package/dist/legacy/prompt-cache.d.ts +10 -0
- package/dist/legacy/prompt-cache.js +24 -0
- package/dist/legacy/prompts.d.ts +175 -0
- package/dist/legacy/prompts.js +1038 -0
- package/dist/legacy/tools.d.ts +4 -0
- package/dist/legacy/tools.js +216 -0
- package/dist/legacy/video-agent.d.ts +143 -0
- package/dist/legacy/video-agent.js +4788 -0
- package/dist/legacy/video-observation.d.ts +36 -0
- package/dist/legacy/video-observation.js +192 -0
- package/dist/legacy/video-planner.d.ts +12 -0
- package/dist/legacy/video-planner.js +501 -0
- package/dist/legacy/video-prompts.d.ts +37 -0
- package/dist/legacy/video-prompts.js +569 -0
- package/dist/legacy/video-tools.d.ts +3 -0
- package/dist/legacy/video-tools.js +59 -0
- package/dist/legacy/video-variant-state.d.ts +29 -0
- package/dist/legacy/video-variant-state.js +80 -0
- package/dist/legacy/vision-model.d.ts +17 -0
- package/dist/legacy/vision-model.js +74 -0
- package/dist/llm-healer.d.ts +63 -0
- package/dist/llm-healer.js +166 -0
- package/dist/llm-provider.d.ts +29 -0
- package/dist/llm-provider.js +80 -0
- package/dist/logger.d.ts +6 -2
- package/dist/logger.js +15 -1
- package/dist/mockup-html.js +35 -25
- package/dist/mockup.d.ts +95 -2
- package/dist/mockup.js +427 -166
- package/dist/mouse-animation.d.ts +2 -2
- package/dist/mouse-animation.js +34 -20
- package/dist/opcode-actions.d.ts +42 -0
- package/dist/opcode-actions.js +511 -0
- package/dist/opcode-runner.d.ts +51 -0
- package/dist/opcode-runner.js +770 -0
- package/dist/openrouter-client.d.ts +40 -0
- package/dist/openrouter-client.js +16 -0
- package/dist/overlay-engine.d.ts +24 -0
- package/dist/overlay-engine.js +176 -0
- package/dist/postcondition.d.ts +16 -0
- package/dist/postcondition.js +269 -0
- package/dist/program-patcher.d.ts +25 -0
- package/dist/program-patcher.js +44 -0
- package/dist/prompts.d.ts +13 -5
- package/dist/prompts.js +224 -351
- package/dist/provider-config.d.ts +12 -0
- package/dist/provider-config.js +15 -0
- package/dist/recovery-chain.d.ts +37 -0
- package/dist/recovery-chain.js +350 -0
- package/dist/remote-browser.d.ts +28 -4
- package/dist/remote-browser.js +60 -5
- package/dist/safari-browser-bar.d.ts +15 -0
- package/dist/safari-browser-bar.js +95 -0
- package/dist/safari-toolbar-asset.d.ts +15 -0
- package/dist/safari-toolbar-asset.js +12 -0
- package/dist/security.d.ts +2 -1
- package/dist/security.js +49 -10
- package/dist/selector-resolver.d.ts +34 -0
- package/dist/selector-resolver.js +181 -0
- package/dist/semantic-resolver.d.ts +35 -0
- package/dist/semantic-resolver.js +161 -0
- package/dist/server-capture-runtime.d.ts +5 -3
- package/dist/server-capture-runtime.js +42 -95
- package/dist/server-credit-usage.d.ts +2 -2
- package/dist/server-project-webhooks.d.ts +15 -1
- package/dist/server-project-webhooks.js +34 -8
- package/dist/server-screenshot-watermark.js +27 -5
- package/dist/session-profile.js +164 -1
- package/dist/sf-pro-symbols.d.ts +1 -0
- package/dist/sf-pro-symbols.js +55 -0
- package/dist/skill-packaging.d.ts +28 -0
- package/dist/skill-packaging.js +169 -0
- package/dist/smart-wait.d.ts +27 -0
- package/dist/smart-wait.js +81 -0
- package/dist/status-bar-render.d.ts +20 -0
- package/dist/status-bar-render.js +410 -0
- package/dist/status-bar.d.ts +9 -0
- package/dist/status-bar.js +298 -14
- package/dist/svg-browser-bar.d.ts +33 -0
- package/dist/svg-browser-bar.js +206 -0
- package/dist/svg-status-bar.d.ts +36 -0
- package/dist/svg-status-bar.js +597 -0
- package/dist/svg-text.d.ts +61 -0
- package/dist/svg-text.js +118 -0
- package/dist/tools.js +89 -451
- package/dist/types.d.ts +240 -5
- package/dist/types.js +23 -1
- package/dist/v2/action-verifier.d.ts +29 -0
- package/dist/v2/action-verifier.js +133 -0
- package/dist/v2/alt-text.d.ts +26 -0
- package/dist/v2/alt-text.js +55 -0
- package/dist/v2/benchmark.d.ts +59 -0
- package/dist/v2/benchmark.js +135 -0
- package/dist/v2/capture-strategy.d.ts +30 -0
- package/dist/v2/capture-strategy.js +67 -0
- package/dist/v2/capture-verification.d.ts +35 -0
- package/dist/v2/capture-verification.js +95 -0
- package/dist/v2/circuit-breaker.d.ts +42 -0
- package/dist/v2/circuit-breaker.js +119 -0
- package/dist/v2/cli-runner-local.d.ts +11 -0
- package/dist/v2/cli-runner-local.js +91 -0
- package/dist/v2/cli-runner.d.ts +34 -0
- package/dist/v2/cli-runner.js +300 -0
- package/dist/v2/compiler-prompts.d.ts +27 -0
- package/dist/v2/compiler-prompts.js +123 -0
- package/dist/v2/compiler.d.ts +37 -0
- package/dist/v2/compiler.js +147 -0
- package/dist/v2/explorer.d.ts +41 -0
- package/dist/v2/explorer.js +56 -0
- package/dist/v2/index.d.ts +37 -0
- package/dist/v2/index.js +31 -0
- package/dist/v2/llm-healer.d.ts +62 -0
- package/dist/v2/llm-healer.js +166 -0
- package/dist/v2/llm-provider.d.ts +29 -0
- package/dist/v2/llm-provider.js +80 -0
- package/dist/v2/opcode-runner.d.ts +47 -0
- package/dist/v2/opcode-runner.js +634 -0
- package/dist/v2/overlay-engine.d.ts +24 -0
- package/dist/v2/overlay-engine.js +150 -0
- package/dist/v2/postcondition.d.ts +16 -0
- package/dist/v2/postcondition.js +249 -0
- package/dist/v2/program-patcher.d.ts +25 -0
- package/dist/v2/program-patcher.js +44 -0
- package/dist/v2/recovery-chain.d.ts +30 -0
- package/dist/v2/recovery-chain.js +368 -0
- package/dist/v2/schema.d.ts +2580 -0
- package/dist/v2/schema.js +295 -0
- package/dist/v2/selector-resolver.d.ts +34 -0
- package/dist/v2/selector-resolver.js +181 -0
- package/dist/v2/semantic-resolver.d.ts +35 -0
- package/dist/v2/semantic-resolver.js +161 -0
- package/dist/v2/smart-wait.d.ts +27 -0
- package/dist/v2/smart-wait.js +81 -0
- package/dist/v2/types.d.ts +444 -0
- package/dist/v2/types.js +19 -0
- package/dist/v2/web-playwright-local.d.ts +69 -0
- package/dist/v2/web-playwright-local.js +392 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +5 -0
- package/dist/video-agent.js +18 -13
- package/dist/video-planner.js +2 -1
- package/dist/video-prompts.js +3 -3
- package/dist/web-playwright-local.d.ts +126 -0
- package/dist/web-playwright-local.js +819 -0
- package/dist/ws-auth.js +4 -1
- package/dist/ws-broadcast.d.ts +34 -0
- package/dist/ws-broadcast.js +85 -0
- package/dist/ws-connection-limits.d.ts +12 -0
- package/dist/ws-connection-limits.js +44 -0
- package/dist/ws-handler-utils.d.ts +32 -0
- package/dist/ws-handler-utils.js +139 -0
- package/dist/ws-handler.js +294 -164
- package/dist/ws-metrics-server.d.ts +9 -0
- package/dist/ws-metrics-server.js +31 -0
- package/dist/ws-server.js +41 -1
- package/package.json +51 -34
|
@@ -37,21 +37,148 @@ export function decryptPresetCredentials(value) {
|
|
|
37
37
|
}
|
|
38
38
|
return normalizePresetCredentials(value);
|
|
39
39
|
}
|
|
40
|
+
export function normalizePresetAuthCookies(value) {
|
|
41
|
+
if (!Array.isArray(value))
|
|
42
|
+
return undefined;
|
|
43
|
+
const cleaned = [];
|
|
44
|
+
for (const entry of value) {
|
|
45
|
+
if (!entry || typeof entry !== 'object')
|
|
46
|
+
continue;
|
|
47
|
+
const candidate = entry;
|
|
48
|
+
const name = typeof candidate.name === 'string' ? candidate.name.trim() : '';
|
|
49
|
+
const cookieValue = typeof candidate.value === 'string' ? candidate.value : '';
|
|
50
|
+
const domain = typeof candidate.domain === 'string' ? candidate.domain.trim().toLowerCase() : '';
|
|
51
|
+
if (!name || !cookieValue || !domain)
|
|
52
|
+
continue;
|
|
53
|
+
const path = typeof candidate.path === 'string' && candidate.path.trim()
|
|
54
|
+
? candidate.path.trim()
|
|
55
|
+
: undefined;
|
|
56
|
+
cleaned.push({
|
|
57
|
+
name,
|
|
58
|
+
value: cookieValue,
|
|
59
|
+
domain,
|
|
60
|
+
...(path ? { path } : {}),
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
return cleaned.length > 0 ? cleaned : undefined;
|
|
64
|
+
}
|
|
65
|
+
export function encryptPresetAuthCookies(cookies) {
|
|
66
|
+
return encrypt(JSON.stringify(cookies), getSecret());
|
|
67
|
+
}
|
|
68
|
+
export function decryptPresetAuthCookies(value) {
|
|
69
|
+
if (!value)
|
|
70
|
+
return undefined;
|
|
71
|
+
if (isEncryptedEnvelope(value)) {
|
|
72
|
+
const decrypted = decrypt(value, getSecret());
|
|
73
|
+
return normalizePresetAuthCookies(JSON.parse(decrypted));
|
|
74
|
+
}
|
|
75
|
+
return normalizePresetAuthCookies(value);
|
|
76
|
+
}
|
|
77
|
+
export function hasStoredAuthCookies(raw) {
|
|
78
|
+
if (!raw || typeof raw !== 'object')
|
|
79
|
+
return false;
|
|
80
|
+
return raw.authCookies != null;
|
|
81
|
+
}
|
|
82
|
+
export function buildAuthCookiesMeta(cookies) {
|
|
83
|
+
const domainSet = new Set();
|
|
84
|
+
for (const cookie of cookies) {
|
|
85
|
+
if (cookie.domain)
|
|
86
|
+
domainSet.add(cookie.domain);
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
count: cookies.length,
|
|
90
|
+
domains: Array.from(domainSet),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Normalize a Playwright storageState payload, dropping anything that doesn't
|
|
95
|
+
* match the expected shape. Returns undefined if the payload is empty (no
|
|
96
|
+
* cookies and no origin data).
|
|
97
|
+
*/
|
|
98
|
+
export function normalizeStorageState(value) {
|
|
99
|
+
if (!value || typeof value !== 'object')
|
|
100
|
+
return undefined;
|
|
101
|
+
const candidate = value;
|
|
102
|
+
const cookies = Array.isArray(candidate.cookies)
|
|
103
|
+
? candidate.cookies
|
|
104
|
+
.filter((c) => !!c && typeof c === 'object' && typeof c.name === 'string' && typeof c.value === 'string' && typeof c.domain === 'string')
|
|
105
|
+
.map((c) => ({
|
|
106
|
+
name: c.name,
|
|
107
|
+
value: c.value,
|
|
108
|
+
domain: c.domain,
|
|
109
|
+
path: typeof c.path === 'string' ? c.path : '/',
|
|
110
|
+
expires: typeof c.expires === 'number' ? c.expires : -1,
|
|
111
|
+
httpOnly: !!c.httpOnly,
|
|
112
|
+
secure: !!c.secure,
|
|
113
|
+
sameSite: c.sameSite === 'Strict' || c.sameSite === 'Lax' || c.sameSite === 'None'
|
|
114
|
+
? c.sameSite
|
|
115
|
+
: 'Lax',
|
|
116
|
+
}))
|
|
117
|
+
: [];
|
|
118
|
+
const origins = Array.isArray(candidate.origins)
|
|
119
|
+
? candidate.origins
|
|
120
|
+
.filter((o) => !!o && typeof o === 'object' && typeof o.origin === 'string')
|
|
121
|
+
.map((o) => ({
|
|
122
|
+
origin: o.origin,
|
|
123
|
+
localStorage: Array.isArray(o.localStorage)
|
|
124
|
+
? o.localStorage
|
|
125
|
+
.filter((entry) => entry && typeof entry === 'object' && typeof entry.name === 'string')
|
|
126
|
+
.map((entry) => ({ name: entry.name, value: typeof entry.value === 'string' ? entry.value : '' }))
|
|
127
|
+
: [],
|
|
128
|
+
}))
|
|
129
|
+
: [];
|
|
130
|
+
if (cookies.length === 0 && origins.length === 0)
|
|
131
|
+
return undefined;
|
|
132
|
+
return { cookies, origins };
|
|
133
|
+
}
|
|
134
|
+
export function encryptStorageState(state) {
|
|
135
|
+
return encrypt(JSON.stringify(state), getSecret());
|
|
136
|
+
}
|
|
137
|
+
export function decryptStorageState(value) {
|
|
138
|
+
if (!value)
|
|
139
|
+
return undefined;
|
|
140
|
+
if (isEncryptedEnvelope(value)) {
|
|
141
|
+
const decrypted = decrypt(value, getSecret());
|
|
142
|
+
return normalizeStorageState(JSON.parse(decrypted));
|
|
143
|
+
}
|
|
144
|
+
return normalizeStorageState(value);
|
|
145
|
+
}
|
|
146
|
+
export function summarizeStorageState(state) {
|
|
147
|
+
const domainSet = new Set();
|
|
148
|
+
for (const cookie of state.cookies) {
|
|
149
|
+
if (cookie.domain)
|
|
150
|
+
domainSet.add(cookie.domain);
|
|
151
|
+
}
|
|
152
|
+
return {
|
|
153
|
+
cookieCount: state.cookies.length,
|
|
154
|
+
originCount: state.origins.length,
|
|
155
|
+
domains: Array.from(domainSet),
|
|
156
|
+
};
|
|
157
|
+
}
|
|
40
158
|
export function preparePresetConfigForStorage(config) {
|
|
41
159
|
const credentials = normalizePresetCredentials(config.credentials);
|
|
160
|
+
const authCookies = normalizePresetAuthCookies(config.authCookies);
|
|
42
161
|
return {
|
|
43
162
|
...config,
|
|
44
163
|
...(credentials
|
|
45
164
|
? { credentials: encryptPresetCredentials(credentials) }
|
|
46
165
|
: { credentials: undefined }),
|
|
166
|
+
...(authCookies
|
|
167
|
+
? {
|
|
168
|
+
authCookies: encryptPresetAuthCookies(authCookies),
|
|
169
|
+
authCookiesMeta: buildAuthCookiesMeta(authCookies),
|
|
170
|
+
}
|
|
171
|
+
: { authCookies: undefined, authCookiesMeta: undefined }),
|
|
47
172
|
};
|
|
48
173
|
}
|
|
49
174
|
export function hydratePresetConfigFromStorage(raw) {
|
|
50
175
|
const config = (raw && typeof raw === 'object' ? raw : {});
|
|
51
176
|
const credentials = decryptPresetCredentials(config.credentials);
|
|
177
|
+
const authCookies = decryptPresetAuthCookies(config.authCookies);
|
|
52
178
|
return {
|
|
53
179
|
...config,
|
|
54
180
|
...(credentials ? { credentials } : { credentials: undefined }),
|
|
181
|
+
...(authCookies ? { authCookies } : { authCookies: undefined }),
|
|
55
182
|
};
|
|
56
183
|
}
|
|
57
184
|
//# sourceMappingURL=capture-preset-credentials.js.map
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { type ScreenshotPageRunInput, type ScreenshotVariantExecution } from "./capture-run-optimizer.js";
|
|
2
|
-
import { findVariantPageDefinitionIssues } from "./capture-page-identity.js";
|
|
3
2
|
export type CaptureTheme = "light" | "dark";
|
|
4
3
|
export interface CaptureViewport {
|
|
5
4
|
width: number;
|
|
@@ -40,9 +39,10 @@ export interface CaptureRequestPlan<TTarget extends CaptureTargetLike = CaptureT
|
|
|
40
39
|
elements: TElement[];
|
|
41
40
|
pageRuns: ScreenshotPageRunInput[];
|
|
42
41
|
variantPlan: ScreenshotVariantExecution[];
|
|
43
|
-
pageDefinitionIssues: ReturnType<typeof findVariantPageDefinitionIssues>;
|
|
44
42
|
requestedCaptureCount: number;
|
|
45
43
|
totalCaptures: number;
|
|
44
|
+
/** Warnings about non-fatal issues (e.g. unknown device frames). */
|
|
45
|
+
warnings: string[];
|
|
46
46
|
}
|
|
47
47
|
export declare function hasNonEmptyCapturePrompt(prompt: string | null | undefined): boolean;
|
|
48
48
|
export declare function getCapturePromptValidationError(input: Pick<CaptureRequestPlanInput, "url" | "prompt" | "pages">): string | null;
|
|
@@ -1,11 +1,34 @@
|
|
|
1
1
|
import { resolveDeviceFrameDescriptor, } from "./mockup.js";
|
|
2
2
|
import { buildScreenshotVariantPlan, } from "./capture-run-optimizer.js";
|
|
3
|
-
import { findVariantPageDefinitionIssues } from "./capture-page-identity.js";
|
|
4
3
|
function normalizeViewportDimension(value) {
|
|
5
4
|
if (!Number.isFinite(value))
|
|
6
5
|
return 1;
|
|
7
6
|
return Math.max(1, Math.round(value));
|
|
8
7
|
}
|
|
8
|
+
function viewportAspectRatio(viewport) {
|
|
9
|
+
return viewport.width / Math.max(1, viewport.height);
|
|
10
|
+
}
|
|
11
|
+
function shouldPreserveStoredDeviceViewport(storedViewport, resolvedViewport) {
|
|
12
|
+
const sameOrientation = (storedViewport.width >= storedViewport.height)
|
|
13
|
+
=== (resolvedViewport.width >= resolvedViewport.height);
|
|
14
|
+
if (!sameOrientation) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
if (storedViewport.width > resolvedViewport.width
|
|
18
|
+
|| storedViewport.height > resolvedViewport.height) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
const widthDelta = Math.abs(storedViewport.width - resolvedViewport.width);
|
|
22
|
+
const heightDelta = Math.abs(storedViewport.height - resolvedViewport.height);
|
|
23
|
+
const differsMeaningfully = widthDelta > 24 || heightDelta > 24;
|
|
24
|
+
if (!differsMeaningfully) {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
const resolvedAspect = viewportAspectRatio(resolvedViewport);
|
|
28
|
+
const storedAspect = viewportAspectRatio(storedViewport);
|
|
29
|
+
const aspectDelta = Math.abs(storedAspect - resolvedAspect) / resolvedAspect;
|
|
30
|
+
return aspectDelta <= 0.015;
|
|
31
|
+
}
|
|
9
32
|
function getRequestedOrientation(target) {
|
|
10
33
|
const orientation = target.mockupOptions?.orientation;
|
|
11
34
|
return orientation === "portrait" || orientation === "landscape"
|
|
@@ -18,17 +41,18 @@ async function resolveDeviceViewport(deviceId, orientation) {
|
|
|
18
41
|
});
|
|
19
42
|
if (!descriptor)
|
|
20
43
|
return null;
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
44
|
+
// Compute viewport from screen dimensions minus safe areas.
|
|
45
|
+
// The device config's `viewport` field is NOT reliable (Supabase configs may
|
|
46
|
+
// store stale/wrong values like 400x740). Always compute from logicalWidth/Height.
|
|
47
|
+
const viewport = {
|
|
48
|
+
width: normalizeViewportDimension(descriptor.screen.logicalWidth
|
|
49
|
+
- (descriptor.safeArea.left ?? 0)
|
|
50
|
+
- (descriptor.safeArea.right ?? 0)),
|
|
51
|
+
height: normalizeViewportDimension(descriptor.screen.logicalHeight
|
|
52
|
+
- descriptor.safeArea.top
|
|
53
|
+
- descriptor.safeArea.bottom),
|
|
31
54
|
};
|
|
55
|
+
return { label: descriptor.name, viewport };
|
|
32
56
|
}
|
|
33
57
|
export function hasNonEmptyCapturePrompt(prompt) {
|
|
34
58
|
return typeof prompt === "string" && prompt.trim().length > 0;
|
|
@@ -122,9 +146,19 @@ export async function normalizeCaptureTargets(input) {
|
|
|
122
146
|
}
|
|
123
147
|
const deviceViewportCache = new Map();
|
|
124
148
|
const resolvedTargets = await Promise.all(targets.map(async (target) => {
|
|
125
|
-
if (!target.deviceFrame)
|
|
149
|
+
if (!target.deviceFrame) {
|
|
150
|
+
console.log(`[capture-plan] target ${target.id}: no deviceFrame, keeping stored viewport ${target.viewport.width}x${target.viewport.height}`);
|
|
126
151
|
return target;
|
|
127
|
-
|
|
152
|
+
}
|
|
153
|
+
let orientation = getRequestedOrientation(target);
|
|
154
|
+
// Fallback: infer orientation from stored viewport aspect ratio when
|
|
155
|
+
// mockupOptions.orientation is missing (e.g. legacy presets).
|
|
156
|
+
if (!orientation && target.viewport) {
|
|
157
|
+
orientation = target.viewport.height > target.viewport.width
|
|
158
|
+
? "portrait"
|
|
159
|
+
: "landscape";
|
|
160
|
+
console.log(`[capture-plan] target ${target.id}: mockupOptions.orientation missing (mockupOptions=${JSON.stringify(target.mockupOptions)}), inferred "${orientation}" from stored viewport ${target.viewport.width}x${target.viewport.height}`);
|
|
161
|
+
}
|
|
128
162
|
const cacheKey = `${target.deviceFrame}:${orientation ?? "default"}`;
|
|
129
163
|
let pending = deviceViewportCache.get(cacheKey);
|
|
130
164
|
if (!pending) {
|
|
@@ -132,8 +166,15 @@ export async function normalizeCaptureTargets(input) {
|
|
|
132
166
|
deviceViewportCache.set(cacheKey, pending);
|
|
133
167
|
}
|
|
134
168
|
const resolved = await pending;
|
|
135
|
-
if (!resolved)
|
|
169
|
+
if (!resolved) {
|
|
170
|
+
console.log(`[capture-plan] target ${target.id} (${target.deviceFrame}): device NOT FOUND, keeping stored viewport ${target.viewport.width}x${target.viewport.height}`);
|
|
171
|
+
return { ...target, _deviceNotFound: true };
|
|
172
|
+
}
|
|
173
|
+
if (shouldPreserveStoredDeviceViewport(target.viewport, resolved.viewport)) {
|
|
174
|
+
console.log(`[capture-plan] target ${target.id} (${target.deviceFrame}): keeping custom viewport ${target.viewport.width}x${target.viewport.height} instead of native ${resolved.viewport.width}x${resolved.viewport.height}`);
|
|
136
175
|
return target;
|
|
176
|
+
}
|
|
177
|
+
console.log(`[capture-plan] target ${target.id} (${target.deviceFrame}): resolved viewport ${resolved.viewport.width}x${resolved.viewport.height} (was ${target.viewport.width}x${target.viewport.height})`);
|
|
137
178
|
return {
|
|
138
179
|
...target,
|
|
139
180
|
viewport: resolved.viewport,
|
|
@@ -197,9 +238,16 @@ export async function resolveCaptureRequestPlan(input) {
|
|
|
197
238
|
themes,
|
|
198
239
|
pages: pageRuns,
|
|
199
240
|
});
|
|
200
|
-
const pageDefinitionIssues = findVariantPageDefinitionIssues(pageRuns);
|
|
201
241
|
const requestedCaptureCount = pageRuns.length * targets.length * langs.length * themes.length
|
|
202
242
|
+ elements.length * langs.length * themes.length;
|
|
243
|
+
// Collect warnings for device frames that could not be resolved
|
|
244
|
+
const warnings = [];
|
|
245
|
+
for (const target of targets) {
|
|
246
|
+
if (target._deviceNotFound) {
|
|
247
|
+
warnings.push(`Device frame "${target.deviceFrame}" not found — using stored viewport ${target.viewport.width}x${target.viewport.height} as fallback.`);
|
|
248
|
+
delete target._deviceNotFound;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
203
251
|
return {
|
|
204
252
|
targets,
|
|
205
253
|
outputScale,
|
|
@@ -208,9 +256,9 @@ export async function resolveCaptureRequestPlan(input) {
|
|
|
208
256
|
elements,
|
|
209
257
|
pageRuns,
|
|
210
258
|
variantPlan,
|
|
211
|
-
pageDefinitionIssues,
|
|
212
259
|
requestedCaptureCount,
|
|
213
260
|
totalCaptures: requestedCaptureCount,
|
|
261
|
+
warnings,
|
|
214
262
|
};
|
|
215
263
|
}
|
|
216
264
|
//# sourceMappingURL=capture-request-plan.js.map
|
|
@@ -50,27 +50,27 @@ const ELEMENT_ASSIGNMENT_STOP_WORDS = new Set([
|
|
|
50
50
|
]);
|
|
51
51
|
const ELEMENT_DOMAIN_HINTS = [
|
|
52
52
|
{
|
|
53
|
-
element: /\b(gallery|
|
|
54
|
-
page: /\b(gallery|
|
|
53
|
+
element: /\b(gallery|grid|list|overview|results?|catalog|library|feed|table|items?|cards?|thumbnail|thumbnails)\b/i,
|
|
54
|
+
page: /\b(gallery|grid|list|overview|results?|catalog|library|feed|table|items?|cards?)\b/i,
|
|
55
55
|
score: 5,
|
|
56
56
|
},
|
|
57
57
|
{
|
|
58
|
-
element: /\b(
|
|
59
|
-
page: /\b(
|
|
58
|
+
element: /\b(edit|editor|editing|configuration|configure|settings?|preferences|details?|detail|instructions?|form|compose|customi[sz]e)\b/i,
|
|
59
|
+
page: /\b(edit|editor|editing|configuration|configure|settings?|preferences|details?|detail|instructions?|form|compose|customi[sz]e)\b/i,
|
|
60
60
|
score: 5,
|
|
61
61
|
},
|
|
62
62
|
{
|
|
63
|
-
element: /\b(assistant|chat|conversation|
|
|
64
|
-
page: /\b(assistant|chat|conversation)\b/i,
|
|
63
|
+
element: /\b(assistant|chat|conversation|message|messages|inbox|thread)\b/i,
|
|
64
|
+
page: /\b(assistant|chat|conversation|message|messages|inbox|thread)\b/i,
|
|
65
65
|
score: 5,
|
|
66
66
|
},
|
|
67
67
|
{
|
|
68
|
-
element: /\b(home|homepage|dashboard|accueil)\b/i,
|
|
69
|
-
page: /\b(home|homepage|dashboard|accueil)\b/i,
|
|
68
|
+
element: /\b(home|homepage|landing|dashboard|welcome|overview|accueil)\b/i,
|
|
69
|
+
page: /\b(home|homepage|landing|dashboard|welcome|overview|accueil)\b/i,
|
|
70
70
|
score: 4,
|
|
71
71
|
},
|
|
72
72
|
];
|
|
73
|
-
const MODAL_CAPTURE_RE = /\b(modal|dialog|popup|drawer|
|
|
73
|
+
const MODAL_CAPTURE_RE = /\b(modal|dialog|popup|drawer|overlay|selection|select|choose|pick|picker)\b/i;
|
|
74
74
|
const CONFIG_CAPTURE_RE = /\b(edit|editor|config|configuration|configure|settings|instruction|instructions|textarea|form|detail|details|modify|modifier|edition)\b/i;
|
|
75
75
|
const MODAL_OPENER_RE = /\b(new|nouveau|create|creer|cr[eé]er|add|ajouter|open|ouvrir|plus)\b/i;
|
|
76
76
|
function uniqueInOrder(values) {
|
|
@@ -140,24 +140,24 @@ function inferCaptureIntent(values) {
|
|
|
140
140
|
tokens: normalizeNavigationTokens(values),
|
|
141
141
|
modalSelection: MODAL_CAPTURE_RE.test(joined),
|
|
142
142
|
configuration: CONFIG_CAPTURE_RE.test(joined),
|
|
143
|
-
|
|
143
|
+
structure: inferCaptureStructure(joined),
|
|
144
144
|
};
|
|
145
145
|
}
|
|
146
|
-
function
|
|
146
|
+
function inferCaptureStructure(value) {
|
|
147
147
|
if (/\b(assistant|chat|conversation|copilot|prompt ia|ai assistant)\b/i.test(value)) {
|
|
148
|
-
return "
|
|
148
|
+
return "conversation";
|
|
149
149
|
}
|
|
150
|
-
if (/\b(gallery|screenshots?|captures?|thumbnail
|
|
151
|
-
return "
|
|
150
|
+
if (/\b(gallery|grid|list|overview|results?|catalog|library|feed|table|items?|cards?|screenshots?|captures?|thumbnail)\b/i.test(value)) {
|
|
151
|
+
return "overview";
|
|
152
152
|
}
|
|
153
|
-
if (/\b(
|
|
154
|
-
return "
|
|
153
|
+
if (/\b(edit|editor|detail|details|instructions?|configuration|configure|form|compose|customi[sz]e)\b/i.test(value)) {
|
|
154
|
+
return "detail";
|
|
155
155
|
}
|
|
156
156
|
if (/\b(settings|preferences|appearance|language|theme|param[eè]tres)\b/i.test(value)) {
|
|
157
|
-
return "
|
|
157
|
+
return "preferences";
|
|
158
158
|
}
|
|
159
|
-
if (/\b(home|homepage|dashboard|accueil)\b/i.test(value)) {
|
|
160
|
-
return "
|
|
159
|
+
if (/\b(home|homepage|landing|dashboard|welcome|accueil)\b/i.test(value)) {
|
|
160
|
+
return "landing";
|
|
161
161
|
}
|
|
162
162
|
return "unknown";
|
|
163
163
|
}
|
|
@@ -212,9 +212,14 @@ export function urlsShareOrigin(left, right) {
|
|
|
212
212
|
}
|
|
213
213
|
export function resolveCaptureResumeUrl(params) {
|
|
214
214
|
const normalizedPageId = params.pageId ?? "main";
|
|
215
|
-
const
|
|
216
|
-
|
|
217
|
-
|
|
215
|
+
const checkpoints = [...params.checkpoints].reverse();
|
|
216
|
+
const checkpoint = checkpoints.find((entry) => entry.pageId === normalizedPageId);
|
|
217
|
+
const sameOriginCheckpoint = checkpoints.find((entry) => entry.pageId !== normalizedPageId
|
|
218
|
+
&& [
|
|
219
|
+
entry.resumeUrl,
|
|
220
|
+
entry.url,
|
|
221
|
+
entry.canonicalUrl,
|
|
222
|
+
].some((candidate) => urlsShareOrigin(candidate, params.pageUrl)));
|
|
218
223
|
const candidates = [
|
|
219
224
|
params.cursorResumeUrl,
|
|
220
225
|
checkpoint?.resumeUrl,
|
|
@@ -223,7 +228,11 @@ export function resolveCaptureResumeUrl(params) {
|
|
|
223
228
|
params.currentUrl,
|
|
224
229
|
params.sessionProfile?.lastKnownUrl,
|
|
225
230
|
params.sessionProfile?.validatedStartUrl,
|
|
226
|
-
|
|
231
|
+
];
|
|
232
|
+
const sameOriginCheckpointCandidates = [
|
|
233
|
+
sameOriginCheckpoint?.resumeUrl,
|
|
234
|
+
sameOriginCheckpoint?.url,
|
|
235
|
+
sameOriginCheckpoint?.canonicalUrl,
|
|
227
236
|
];
|
|
228
237
|
for (const candidate of candidates) {
|
|
229
238
|
if (!candidate)
|
|
@@ -232,6 +241,12 @@ export function resolveCaptureResumeUrl(params) {
|
|
|
232
241
|
return candidate;
|
|
233
242
|
}
|
|
234
243
|
if (params.pageIndex > 0) {
|
|
244
|
+
for (const candidate of sameOriginCheckpointCandidates) {
|
|
245
|
+
if (!candidate)
|
|
246
|
+
continue;
|
|
247
|
+
if (urlsShareOrigin(candidate, params.pageUrl))
|
|
248
|
+
return candidate;
|
|
249
|
+
}
|
|
235
250
|
for (const candidate of candidates) {
|
|
236
251
|
if (!candidate)
|
|
237
252
|
continue;
|
|
@@ -344,7 +359,7 @@ export function shouldReuseLivePageState(params) {
|
|
|
344
359
|
if (!currentOrigin || !pageOrigin || currentOrigin !== pageOrigin) {
|
|
345
360
|
return false;
|
|
346
361
|
}
|
|
347
|
-
// Only skip the reset when the page uses the
|
|
362
|
+
// Only skip the reset when the page uses the shared root URL for the flow.
|
|
348
363
|
// Explicit per-page URLs still navigate directly for determinism.
|
|
349
364
|
return params.pageUrl === params.presetRootUrl;
|
|
350
365
|
}
|
|
@@ -507,10 +522,10 @@ export function buildVisibleControlNavigationPlan(params) {
|
|
|
507
522
|
if (targetIntent.modalSelection && MODAL_OPENER_RE.test(normalizedLabel)) {
|
|
508
523
|
score += 5;
|
|
509
524
|
}
|
|
510
|
-
if (/\
|
|
525
|
+
if (/\b(edit|editor|detail|details|configuration|configure|settings?|preferences|form)\b/i.test(`${params.pageId ?? ""} ${params.prompt}`) && /\b(edit|editor|detail|details|configuration|configure|settings?|preferences|form)\b/i.test(normalizedLabel)) {
|
|
511
526
|
score += 4;
|
|
512
527
|
}
|
|
513
|
-
if (/\
|
|
528
|
+
if (/\b(gallery|grid|list|overview|results?|catalog|library|feed|table|items?|cards?|capture|captures|screenshot|screenshots)\b/i.test(`${params.pageId ?? ""} ${params.prompt}`) && /\b(gallery|grid|list|overview|results?|catalog|library|feed|table|items?|cards?|capture|captures|screenshot|screenshots)\b/i.test(normalizedLabel)) {
|
|
514
529
|
score += 4;
|
|
515
530
|
}
|
|
516
531
|
if (element.visibilityState === "full")
|
|
@@ -562,13 +577,13 @@ export function shouldAttemptSequentialReadyCheck(params) {
|
|
|
562
577
|
}
|
|
563
578
|
const intentOverlap = countOverlap(previousIntent.tokens, targetIntent.tokens);
|
|
564
579
|
if (countOverlap(currentPathTokens, targetPathTokens) > 0) {
|
|
565
|
-
// Many
|
|
566
|
-
//
|
|
567
|
-
//
|
|
568
|
-
//
|
|
569
|
-
if (previousIntent.
|
|
570
|
-
&& targetIntent.
|
|
571
|
-
&& previousIntent.
|
|
580
|
+
// Many apps reuse the same base route while representing materially
|
|
581
|
+
// different UI states. Do not use the fast ready check on same-path
|
|
582
|
+
// transitions unless the target is clearly a close continuation of the
|
|
583
|
+
// previous page.
|
|
584
|
+
if (previousIntent.structure !== "unknown"
|
|
585
|
+
&& targetIntent.structure !== "unknown"
|
|
586
|
+
&& previousIntent.structure !== targetIntent.structure) {
|
|
572
587
|
return false;
|
|
573
588
|
}
|
|
574
589
|
return intentOverlap >= 2;
|
|
@@ -5,12 +5,16 @@ export interface ScreenshotSelectorMemoryUpdate {
|
|
|
5
5
|
source: 'agent' | 'deterministic' | 'manual';
|
|
6
6
|
success: boolean;
|
|
7
7
|
}
|
|
8
|
+
/** Map a viewport width to a device category for selector memory scoping. */
|
|
9
|
+
export declare function getViewportCategory(width: number): 'mobile' | 'tablet' | 'desktop';
|
|
8
10
|
export declare function loadScreenshotSelectorMemory(supabase: SupabaseClient, params: {
|
|
9
11
|
projectId: string | null;
|
|
10
12
|
presetId?: string | null;
|
|
11
13
|
domain: string;
|
|
12
14
|
lang: string;
|
|
13
15
|
theme: 'light' | 'dark';
|
|
16
|
+
/** Optional viewport width — when provided, filters selectors by viewport category. */
|
|
17
|
+
viewportWidth?: number | null;
|
|
14
18
|
}): Promise<Record<string, string[]>>;
|
|
15
19
|
export declare function persistScreenshotSelectorMemoryUpdates(supabase: SupabaseClient, params: {
|
|
16
20
|
projectId: string | null;
|
|
@@ -18,6 +22,7 @@ export declare function persistScreenshotSelectorMemoryUpdates(supabase: Supabas
|
|
|
18
22
|
domain: string;
|
|
19
23
|
lang: string;
|
|
20
24
|
theme: 'light' | 'dark';
|
|
25
|
+
viewportWidth?: number | null;
|
|
21
26
|
}, updates: ScreenshotSelectorMemoryUpdate[]): Promise<void>;
|
|
22
27
|
export declare function extractSelectorUpdates(actions: Array<{
|
|
23
28
|
action: string;
|
|
@@ -59,6 +59,14 @@ function extractPathTokens(rawUrl) {
|
|
|
59
59
|
return normalizeNavigationTokens(rawUrl.split('/'));
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
|
+
/** Map a viewport width to a device category for selector memory scoping. */
|
|
63
|
+
export function getViewportCategory(width) {
|
|
64
|
+
if (width < 768)
|
|
65
|
+
return 'mobile';
|
|
66
|
+
if (width < 1024)
|
|
67
|
+
return 'tablet';
|
|
68
|
+
return 'desktop';
|
|
69
|
+
}
|
|
62
70
|
export async function loadScreenshotSelectorMemory(supabase, params) {
|
|
63
71
|
const selectFields = 'step_signature, selector, confidence, last_success_at';
|
|
64
72
|
const grouped = {};
|
|
@@ -75,6 +83,9 @@ export async function loadScreenshotSelectorMemory(supabase, params) {
|
|
|
75
83
|
}
|
|
76
84
|
}
|
|
77
85
|
};
|
|
86
|
+
const viewportCategory = params.viewportWidth
|
|
87
|
+
? getViewportCategory(params.viewportWidth)
|
|
88
|
+
: null;
|
|
78
89
|
if (params.presetId) {
|
|
79
90
|
let query = supabase
|
|
80
91
|
.from('screenshot_step_memory')
|
|
@@ -89,6 +100,9 @@ export async function loadScreenshotSelectorMemory(supabase, params) {
|
|
|
89
100
|
if (params.projectId) {
|
|
90
101
|
query = query.eq('project_id', params.projectId);
|
|
91
102
|
}
|
|
103
|
+
if (viewportCategory) {
|
|
104
|
+
query = query.or(`viewport_category.eq.${viewportCategory},viewport_category.is.null`);
|
|
105
|
+
}
|
|
92
106
|
const { data } = await query;
|
|
93
107
|
if (data && data.length > 0) {
|
|
94
108
|
appendRows(data);
|
|
@@ -105,6 +119,9 @@ export async function loadScreenshotSelectorMemory(supabase, params) {
|
|
|
105
119
|
if (params.projectId) {
|
|
106
120
|
fallbackQuery = fallbackQuery.eq('project_id', params.projectId);
|
|
107
121
|
}
|
|
122
|
+
if (viewportCategory) {
|
|
123
|
+
fallbackQuery = fallbackQuery.or(`viewport_category.eq.${viewportCategory},viewport_category.is.null`);
|
|
124
|
+
}
|
|
108
125
|
const { data: fallbackData } = await fallbackQuery;
|
|
109
126
|
if (fallbackData) {
|
|
110
127
|
appendRows(fallbackData);
|
|
@@ -145,6 +162,7 @@ export async function persistScreenshotSelectorMemoryUpdates(supabase, params, u
|
|
|
145
162
|
domain: params.domain,
|
|
146
163
|
lang: params.lang,
|
|
147
164
|
theme: params.theme,
|
|
165
|
+
viewport_category: params.viewportWidth ? getViewportCategory(params.viewportWidth) : null,
|
|
148
166
|
step_signature: update.stepSignature,
|
|
149
167
|
selector: update.selector,
|
|
150
168
|
source: update.source,
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capture Agent — Capture Strategy
|
|
3
|
+
*
|
|
4
|
+
* Abstraction over the supported media modes: screenshot and clip.
|
|
5
|
+
* The opcode runner is identical for all three — only the capture
|
|
6
|
+
* opcodes dispatch to different strategies.
|
|
7
|
+
*/
|
|
8
|
+
import type { ArtifactSpec, ArtifactResult, RuntimeAdapter, MediaMode } from './execution-types.js';
|
|
9
|
+
export interface CaptureStrategy {
|
|
10
|
+
readonly mediaMode: MediaMode;
|
|
11
|
+
/** Prepare the adapter for capture (e.g. lock viewport, inject cursor overlay) */
|
|
12
|
+
prepare(adapter: RuntimeAdapter, spec: ArtifactSpec): Promise<void>;
|
|
13
|
+
/** Perform the capture and return the raw artifact */
|
|
14
|
+
capture(adapter: RuntimeAdapter, spec: ArtifactSpec): Promise<ArtifactResult>;
|
|
15
|
+
/** Post-process the artifact (mockup frame, status bar, format conversion) */
|
|
16
|
+
postProcess(artifact: ArtifactResult, spec: ArtifactSpec): Promise<ArtifactResult>;
|
|
17
|
+
}
|
|
18
|
+
export declare class ScreenshotStrategy implements CaptureStrategy {
|
|
19
|
+
readonly mediaMode: "screenshot";
|
|
20
|
+
prepare(_adapter: RuntimeAdapter, _spec: ArtifactSpec): Promise<void>;
|
|
21
|
+
capture(adapter: RuntimeAdapter, _spec: ArtifactSpec): Promise<ArtifactResult>;
|
|
22
|
+
postProcess(artifact: ArtifactResult, spec: ArtifactSpec): Promise<ArtifactResult>;
|
|
23
|
+
}
|
|
24
|
+
export declare class ClipStrategy implements CaptureStrategy {
|
|
25
|
+
readonly mediaMode: "clip";
|
|
26
|
+
prepare(adapter: RuntimeAdapter, _spec: ArtifactSpec): Promise<void>;
|
|
27
|
+
capture(adapter: RuntimeAdapter, _spec: ArtifactSpec): Promise<ArtifactResult>;
|
|
28
|
+
postProcess(artifact: ArtifactResult, spec: ArtifactSpec): Promise<ArtifactResult>;
|
|
29
|
+
}
|
|
30
|
+
export declare class DomStrategy implements CaptureStrategy {
|
|
31
|
+
readonly mediaMode: "dom";
|
|
32
|
+
prepare(_adapter: RuntimeAdapter, _spec: ArtifactSpec): Promise<void>;
|
|
33
|
+
capture(adapter: RuntimeAdapter, _spec: ArtifactSpec): Promise<ArtifactResult>;
|
|
34
|
+
postProcess(artifact: ArtifactResult, _spec: ArtifactSpec): Promise<ArtifactResult>;
|
|
35
|
+
}
|
|
36
|
+
export declare function createCaptureStrategy(mediaMode: MediaMode): CaptureStrategy;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capture Agent — Capture Strategy
|
|
3
|
+
*
|
|
4
|
+
* Abstraction over the supported media modes: screenshot and clip.
|
|
5
|
+
* The opcode runner is identical for all three — only the capture
|
|
6
|
+
* opcodes dispatch to different strategies.
|
|
7
|
+
*/
|
|
8
|
+
// ── Screenshot strategy ─────────────────────────────────────────────
|
|
9
|
+
export class ScreenshotStrategy {
|
|
10
|
+
mediaMode = 'screenshot';
|
|
11
|
+
async prepare(_adapter, _spec) {
|
|
12
|
+
// Viewport locking is handled externally by the CLI runner
|
|
13
|
+
// when creating the browser context per variant.
|
|
14
|
+
}
|
|
15
|
+
async capture(adapter, _spec) {
|
|
16
|
+
const buffer = await adapter.takeScreenshot();
|
|
17
|
+
return {
|
|
18
|
+
mediaMode: 'screenshot',
|
|
19
|
+
buffer,
|
|
20
|
+
mimeType: 'image/png',
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
async postProcess(artifact, spec) {
|
|
24
|
+
// Post-processing (mockup frame, status bar) will be applied
|
|
25
|
+
// by the CLI runner using existing utilities:
|
|
26
|
+
// - applyDeviceFrame() from src/mockup.ts
|
|
27
|
+
// - generateStatusBarHtml() from src/status-bar.ts
|
|
28
|
+
// For now, return as-is. Phase 4 CLI runner handles the orchestration.
|
|
29
|
+
return artifact;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
// ── Clip strategy ───────────────────────────────────────────────────
|
|
33
|
+
export class ClipStrategy {
|
|
34
|
+
mediaMode = 'clip';
|
|
35
|
+
async prepare(adapter, _spec) {
|
|
36
|
+
// Clip recording preparation:
|
|
37
|
+
// - The browser context is created with video recording enabled
|
|
38
|
+
// - Cursor overlay is injected by Browser.forVideoRecording()
|
|
39
|
+
}
|
|
40
|
+
async capture(adapter, _spec) {
|
|
41
|
+
// Clip capture is bounded by BEGIN_CLIP / END_CLIP opcodes.
|
|
42
|
+
// The opcode runner calls adapter.beginRecording() and adapter.endRecording().
|
|
43
|
+
// This method is called for the final artifact assembly.
|
|
44
|
+
const recording = await adapter.endRecording();
|
|
45
|
+
return {
|
|
46
|
+
mediaMode: 'clip',
|
|
47
|
+
buffer: recording.buffer,
|
|
48
|
+
mimeType: recording.mimeType,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
async postProcess(artifact, spec) {
|
|
52
|
+
// Post-processing for clips:
|
|
53
|
+
// - Convert WebM to GIF/MP4 via src/clip-postprocess.ts (if it exists)
|
|
54
|
+
// - Trim start/end
|
|
55
|
+
// - Apply cursor theme
|
|
56
|
+
// Handled by CLI runner using existing video processing utilities.
|
|
57
|
+
return artifact;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
// ── DOM strategy (Interactive Demos — AUT-121) ──────────────────────
|
|
61
|
+
export class DomStrategy {
|
|
62
|
+
mediaMode = 'dom';
|
|
63
|
+
async prepare(_adapter, _spec) {
|
|
64
|
+
// Nothing to prepare — DOM serialization happens inline in the
|
|
65
|
+
// CAPTURE_DOM opcode handler in opcode-runner.ts.
|
|
66
|
+
}
|
|
67
|
+
async capture(adapter, _spec) {
|
|
68
|
+
if (!adapter.serializeDom) {
|
|
69
|
+
throw new Error('DOM capture requires an adapter that implements serializeDom()');
|
|
70
|
+
}
|
|
71
|
+
const result = await adapter.serializeDom();
|
|
72
|
+
return {
|
|
73
|
+
mediaMode: 'dom',
|
|
74
|
+
buffer: Buffer.from(result.html, 'utf8'),
|
|
75
|
+
mimeType: 'text/html; charset=utf-8',
|
|
76
|
+
domHtml: result.html,
|
|
77
|
+
domAssetUrls: result.assetUrls,
|
|
78
|
+
domHtmlBytes: result.html.length,
|
|
79
|
+
dimensions: result.viewport,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
async postProcess(artifact, _spec) {
|
|
83
|
+
// Phase 3 will add asset extraction (CAS), PurgeCSS and Brotli here.
|
|
84
|
+
return artifact;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// ── Factory ─────────────────────────────────────────────────────────
|
|
88
|
+
export function createCaptureStrategy(mediaMode) {
|
|
89
|
+
switch (mediaMode) {
|
|
90
|
+
case 'screenshot': return new ScreenshotStrategy();
|
|
91
|
+
case 'clip': return new ClipStrategy();
|
|
92
|
+
case 'dom': return new DomStrategy();
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=capture-strategy.js.map
|