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
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capture Agent — Semantic Target Resolver
|
|
3
|
+
*
|
|
4
|
+
* Resolves a SemanticTarget to a concrete Playwright locator.
|
|
5
|
+
* This is the core mechanism that allows opcodes to work WITHOUT CSS selectors.
|
|
6
|
+
*
|
|
7
|
+
* Resolution cascade:
|
|
8
|
+
* 1. CSS selector (if provided, fast path)
|
|
9
|
+
* 2. Playwright semantic locators (getByRole, getByText, getByLabel, getByPlaceholder)
|
|
10
|
+
* 3. Selector alternates
|
|
11
|
+
* 4. Composite: role + name + near (for disambiguation)
|
|
12
|
+
*
|
|
13
|
+
* All resolution happens via Playwright's built-in locator API — no AKTree needed.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Resolves an element on the page using the best available method.
|
|
17
|
+
* Returns the first visible locator found, or null if nothing matches.
|
|
18
|
+
*/
|
|
19
|
+
export async function resolveTarget(page, options) {
|
|
20
|
+
const timeout = options.timeoutMs ?? 3000;
|
|
21
|
+
// 1. Primary CSS selector
|
|
22
|
+
if (options.selector) {
|
|
23
|
+
const locator = page.locator(options.selector).first();
|
|
24
|
+
if (await isVisible(locator, timeout)) {
|
|
25
|
+
return { locator, method: 'selector' };
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
// 2. Semantic locators from target
|
|
29
|
+
if (options.target) {
|
|
30
|
+
const resolved = await resolveSemanticTarget(page, options.target, timeout);
|
|
31
|
+
if (resolved)
|
|
32
|
+
return resolved;
|
|
33
|
+
}
|
|
34
|
+
// 3. Alternate selectors
|
|
35
|
+
if (options.selectorAlternates) {
|
|
36
|
+
for (const alt of options.selectorAlternates) {
|
|
37
|
+
const locator = page.locator(alt).first();
|
|
38
|
+
if (await isVisible(locator, timeout)) {
|
|
39
|
+
return { locator, method: 'alternate' };
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Resolves a SemanticTarget using Playwright's semantic locator API.
|
|
47
|
+
*/
|
|
48
|
+
async function resolveSemanticTarget(page, target, timeout) {
|
|
49
|
+
const exact = target.exact ?? false;
|
|
50
|
+
// Strategy A: getByRole with name (most precise)
|
|
51
|
+
if (target.role && (target.text || target.label)) {
|
|
52
|
+
const name = target.label || target.text;
|
|
53
|
+
try {
|
|
54
|
+
let locator = page.getByRole(target.role, {
|
|
55
|
+
name: name,
|
|
56
|
+
exact,
|
|
57
|
+
});
|
|
58
|
+
if (target.near) {
|
|
59
|
+
locator = narrowByNear(page, locator, target.near);
|
|
60
|
+
}
|
|
61
|
+
locator = locator.first();
|
|
62
|
+
if (await isVisible(locator, timeout)) {
|
|
63
|
+
return { locator, method: 'role' };
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// Invalid role or no match
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// Strategy B: getByRole without name
|
|
71
|
+
if (target.role && !target.text && !target.label) {
|
|
72
|
+
try {
|
|
73
|
+
let locator = page.getByRole(target.role);
|
|
74
|
+
if (target.near) {
|
|
75
|
+
locator = narrowByNear(page, locator, target.near);
|
|
76
|
+
}
|
|
77
|
+
locator = locator.first();
|
|
78
|
+
if (await isVisible(locator, timeout)) {
|
|
79
|
+
return { locator, method: 'role' };
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
// Invalid role
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// Strategy C: getByText
|
|
87
|
+
if (target.text) {
|
|
88
|
+
let locator = page.getByText(target.text, { exact });
|
|
89
|
+
if (target.near) {
|
|
90
|
+
locator = narrowByNear(page, locator, target.near);
|
|
91
|
+
}
|
|
92
|
+
locator = locator.first();
|
|
93
|
+
if (await isVisible(locator, timeout)) {
|
|
94
|
+
return { locator, method: 'text' };
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// Strategy D: getByLabel
|
|
98
|
+
if (target.label) {
|
|
99
|
+
let locator = page.getByLabel(target.label, { exact });
|
|
100
|
+
if (target.near) {
|
|
101
|
+
locator = narrowByNear(page, locator, target.near);
|
|
102
|
+
}
|
|
103
|
+
locator = locator.first();
|
|
104
|
+
if (await isVisible(locator, timeout)) {
|
|
105
|
+
return { locator, method: 'label' };
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// Strategy E: getByPlaceholder (for inputs)
|
|
109
|
+
if (target.placeholder) {
|
|
110
|
+
let locator = page.getByPlaceholder(target.placeholder, { exact });
|
|
111
|
+
if (target.near) {
|
|
112
|
+
locator = narrowByNear(page, locator, target.near);
|
|
113
|
+
}
|
|
114
|
+
locator = locator.first();
|
|
115
|
+
if (await isVisible(locator, timeout)) {
|
|
116
|
+
return { locator, method: 'placeholder' };
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
// Strategy F: Composite — try role-based with looser matching
|
|
120
|
+
if (target.role) {
|
|
121
|
+
try {
|
|
122
|
+
const allByRole = page.getByRole(target.role);
|
|
123
|
+
const count = await allByRole.count();
|
|
124
|
+
for (let i = 0; i < Math.min(count, 10); i++) {
|
|
125
|
+
const candidate = allByRole.nth(i);
|
|
126
|
+
if (await isVisible(candidate, 500)) {
|
|
127
|
+
const text = await candidate.textContent().catch(() => null);
|
|
128
|
+
if (text && target.text && text.toLowerCase().includes(target.text.toLowerCase())) {
|
|
129
|
+
return { locator: candidate, method: 'composite' };
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
// No match
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Narrows a locator to one near a specific text/heading on the page.
|
|
142
|
+
* Uses Playwright's `.filter()` with a parent locator containing the near text.
|
|
143
|
+
*/
|
|
144
|
+
function narrowByNear(page, locator, nearText) {
|
|
145
|
+
// Find a section/container that contains the near text
|
|
146
|
+
const container = page.locator(`section:has-text("${escapeForSelector(nearText)}"), div:has-text("${escapeForSelector(nearText)}"), [role="region"]:has-text("${escapeForSelector(nearText)}")`).first();
|
|
147
|
+
return container.locator(locator);
|
|
148
|
+
}
|
|
149
|
+
async function isVisible(locator, timeout) {
|
|
150
|
+
try {
|
|
151
|
+
await locator.waitFor({ state: 'visible', timeout });
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
function escapeForSelector(text) {
|
|
159
|
+
return text.replace(/"/g, '\\"').replace(/\n/g, ' ');
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=semantic-resolver.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { SupabaseClient } from '@supabase/supabase-js';
|
|
2
|
-
export type BillingPlanId = 'free' | '
|
|
2
|
+
export type BillingPlanId = 'free' | 'maker' | 'team';
|
|
3
3
|
export interface BillingPlanEntitlements {
|
|
4
4
|
creditsPerMonth: number;
|
|
5
5
|
maxProjects: number | null;
|
|
@@ -16,13 +16,15 @@ export interface BillingPlanEntitlements {
|
|
|
16
16
|
maxParallelCaptures: number;
|
|
17
17
|
teamMembers: boolean;
|
|
18
18
|
aiDailyCostLimitUsd: number;
|
|
19
|
-
maxApiKeys: number;
|
|
20
19
|
apiRateLimitRpm: number;
|
|
20
|
+
maxDevLinks: number | null;
|
|
21
|
+
maxCompositions: number | null;
|
|
22
|
+
maxTeamMembersPerProject: number | null;
|
|
21
23
|
}
|
|
22
24
|
export interface BillingPlan {
|
|
23
25
|
id: BillingPlanId;
|
|
24
26
|
nameKey: BillingPlanId;
|
|
25
|
-
descriptionKey: 'forEvaluation' | '
|
|
27
|
+
descriptionKey: 'forEvaluation' | 'forMakers' | 'forTeams';
|
|
26
28
|
monthlyPriceEur: number;
|
|
27
29
|
yearlyPriceEur: number;
|
|
28
30
|
highlighted?: boolean;
|
|
@@ -11,9 +11,8 @@ export class PlanLimitError extends Error {
|
|
|
11
11
|
export const SCREENSHOT_CREDIT_COST = 1;
|
|
12
12
|
const CREDIT_OVERAGE_CENTS = {
|
|
13
13
|
free: 0,
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
business: 4,
|
|
14
|
+
maker: 6,
|
|
15
|
+
team: 5,
|
|
17
16
|
};
|
|
18
17
|
const STRIPE_ACTIVE_STATUSES = new Set(['active', 'trialing', 'past_due']);
|
|
19
18
|
const BILLING_PLANS = [
|
|
@@ -24,11 +23,11 @@ const BILLING_PLANS = [
|
|
|
24
23
|
monthlyPriceEur: 0,
|
|
25
24
|
yearlyPriceEur: 0,
|
|
26
25
|
previewCurrent: true,
|
|
27
|
-
highlights: ['credits', 'projects', '
|
|
26
|
+
highlights: ['credits', 'projects', 'devLinks', 'retention', 'support'],
|
|
28
27
|
entitlements: {
|
|
29
|
-
creditsPerMonth:
|
|
28
|
+
creditsPerMonth: 25,
|
|
30
29
|
maxProjects: 1,
|
|
31
|
-
maxPresetsPerProject:
|
|
30
|
+
maxPresetsPerProject: 2,
|
|
32
31
|
allowFullPageCapture: true,
|
|
33
32
|
allowElementCapture: true,
|
|
34
33
|
retentionDays: 7,
|
|
@@ -41,22 +40,24 @@ const BILLING_PLANS = [
|
|
|
41
40
|
maxParallelCaptures: 1,
|
|
42
41
|
teamMembers: false,
|
|
43
42
|
aiDailyCostLimitUsd: 0.05,
|
|
44
|
-
maxApiKeys: 2,
|
|
45
43
|
apiRateLimitRpm: 10,
|
|
44
|
+
maxDevLinks: 1,
|
|
45
|
+
maxCompositions: 2,
|
|
46
|
+
maxTeamMembersPerProject: null,
|
|
46
47
|
},
|
|
47
48
|
},
|
|
48
49
|
{
|
|
49
|
-
id: '
|
|
50
|
-
nameKey: '
|
|
51
|
-
descriptionKey: '
|
|
52
|
-
monthlyPriceEur:
|
|
53
|
-
yearlyPriceEur:
|
|
50
|
+
id: 'maker',
|
|
51
|
+
nameKey: 'maker',
|
|
52
|
+
descriptionKey: 'forMakers',
|
|
53
|
+
monthlyPriceEur: 15,
|
|
54
|
+
yearlyPriceEur: 144,
|
|
54
55
|
highlighted: true,
|
|
55
|
-
highlights: ['credits', 'projects', '
|
|
56
|
+
highlights: ['credits', 'projects', 'devLinks', 'retention', 'parallelCaptures', 'captureCompleteWebhook', 'support'],
|
|
56
57
|
entitlements: {
|
|
57
|
-
creditsPerMonth:
|
|
58
|
+
creditsPerMonth: 100,
|
|
58
59
|
maxProjects: 3,
|
|
59
|
-
maxPresetsPerProject:
|
|
60
|
+
maxPresetsPerProject: 5,
|
|
60
61
|
allowFullPageCapture: true,
|
|
61
62
|
allowElementCapture: true,
|
|
62
63
|
retentionDays: 30,
|
|
@@ -64,24 +65,26 @@ const BILLING_PLANS = [
|
|
|
64
65
|
maxLanguages: null,
|
|
65
66
|
maxThemes: null,
|
|
66
67
|
apiAccess: true,
|
|
67
|
-
captureCompleteWebhook:
|
|
68
|
+
captureCompleteWebhook: true,
|
|
68
69
|
priorityQueue: false,
|
|
69
|
-
maxParallelCaptures:
|
|
70
|
+
maxParallelCaptures: 2,
|
|
70
71
|
teamMembers: false,
|
|
71
|
-
aiDailyCostLimitUsd: 0.
|
|
72
|
-
maxApiKeys: 5,
|
|
72
|
+
aiDailyCostLimitUsd: 0.10,
|
|
73
73
|
apiRateLimitRpm: 60,
|
|
74
|
+
maxDevLinks: 10,
|
|
75
|
+
maxCompositions: 10,
|
|
76
|
+
maxTeamMembersPerProject: null,
|
|
74
77
|
},
|
|
75
78
|
},
|
|
76
79
|
{
|
|
77
|
-
id: '
|
|
78
|
-
nameKey: '
|
|
79
|
-
descriptionKey: '
|
|
80
|
-
monthlyPriceEur:
|
|
81
|
-
yearlyPriceEur:
|
|
82
|
-
highlights: ['credits', 'projects', '
|
|
80
|
+
id: 'team',
|
|
81
|
+
nameKey: 'team',
|
|
82
|
+
descriptionKey: 'forTeams',
|
|
83
|
+
monthlyPriceEur: 49,
|
|
84
|
+
yearlyPriceEur: 468,
|
|
85
|
+
highlights: ['credits', 'projects', 'devLinks', 'retention', 'parallelCaptures', 'teamMembers', 'priorityQueue', 'support'],
|
|
83
86
|
entitlements: {
|
|
84
|
-
creditsPerMonth:
|
|
87
|
+
creditsPerMonth: 500,
|
|
85
88
|
maxProjects: null,
|
|
86
89
|
maxPresetsPerProject: null,
|
|
87
90
|
allowFullPageCapture: true,
|
|
@@ -92,48 +95,14 @@ const BILLING_PLANS = [
|
|
|
92
95
|
maxThemes: null,
|
|
93
96
|
apiAccess: true,
|
|
94
97
|
captureCompleteWebhook: true,
|
|
95
|
-
priorityQueue: false,
|
|
96
|
-
maxParallelCaptures: 1,
|
|
97
|
-
teamMembers: false,
|
|
98
|
-
aiDailyCostLimitUsd: 0.15,
|
|
99
|
-
maxApiKeys: 10,
|
|
100
|
-
apiRateLimitRpm: 120,
|
|
101
|
-
},
|
|
102
|
-
},
|
|
103
|
-
{
|
|
104
|
-
id: 'business',
|
|
105
|
-
nameKey: 'business',
|
|
106
|
-
descriptionKey: 'forOperationsHeavyTeams',
|
|
107
|
-
monthlyPriceEur: 149,
|
|
108
|
-
yearlyPriceEur: 1490,
|
|
109
|
-
highlights: [
|
|
110
|
-
'credits',
|
|
111
|
-
'projects',
|
|
112
|
-
'presets',
|
|
113
|
-
'retention',
|
|
114
|
-
'captureCompleteWebhook',
|
|
115
|
-
'priorityQueue',
|
|
116
|
-
'parallelCaptures',
|
|
117
|
-
'teamMembers',
|
|
118
|
-
],
|
|
119
|
-
entitlements: {
|
|
120
|
-
creditsPerMonth: 2500,
|
|
121
|
-
maxProjects: null,
|
|
122
|
-
maxPresetsPerProject: null,
|
|
123
|
-
allowFullPageCapture: true,
|
|
124
|
-
allowElementCapture: true,
|
|
125
|
-
retentionDays: 365,
|
|
126
|
-
watermarkScreenshots: false,
|
|
127
|
-
maxLanguages: null,
|
|
128
|
-
maxThemes: null,
|
|
129
|
-
apiAccess: true,
|
|
130
|
-
captureCompleteWebhook: true,
|
|
131
98
|
priorityQueue: true,
|
|
132
|
-
maxParallelCaptures:
|
|
99
|
+
maxParallelCaptures: 5,
|
|
133
100
|
teamMembers: true,
|
|
134
101
|
aiDailyCostLimitUsd: 0.15,
|
|
135
|
-
|
|
136
|
-
|
|
102
|
+
apiRateLimitRpm: 200,
|
|
103
|
+
maxDevLinks: null,
|
|
104
|
+
maxCompositions: null,
|
|
105
|
+
maxTeamMembersPerProject: 5,
|
|
137
106
|
},
|
|
138
107
|
},
|
|
139
108
|
];
|
|
@@ -145,31 +114,19 @@ export function coerceBillingPlanId(value) {
|
|
|
145
114
|
return null;
|
|
146
115
|
switch (value) {
|
|
147
116
|
case 'free':
|
|
148
|
-
case '
|
|
149
|
-
case '
|
|
150
|
-
case 'business':
|
|
117
|
+
case 'maker':
|
|
118
|
+
case 'team':
|
|
151
119
|
return value;
|
|
152
120
|
default:
|
|
153
121
|
return null;
|
|
154
122
|
}
|
|
155
123
|
}
|
|
156
|
-
function getMetadataBillingPlanId(user) {
|
|
157
|
-
const metadata = user?.user_metadata;
|
|
158
|
-
if (!metadata)
|
|
159
|
-
return null;
|
|
160
|
-
for (const key of ['billingPlan', 'billing_plan', 'plan']) {
|
|
161
|
-
const planId = coerceBillingPlanId(metadata[key]);
|
|
162
|
-
if (planId)
|
|
163
|
-
return planId;
|
|
164
|
-
}
|
|
165
|
-
return null;
|
|
166
|
-
}
|
|
167
124
|
function getServerDefaultBillingPlanId() {
|
|
168
125
|
return (coerceBillingPlanId(process.env.DEFAULT_BILLING_PLAN)
|
|
169
126
|
?? coerceBillingPlanId(process.env.NEXT_PUBLIC_DEFAULT_BILLING_PLAN)
|
|
170
127
|
?? 'free');
|
|
171
128
|
}
|
|
172
|
-
function resolvePlanFromBillingAccount(account,
|
|
129
|
+
function resolvePlanFromBillingAccount(account, defaultPlanId) {
|
|
173
130
|
const adminPlanId = coerceBillingPlanId(account?.admin_override_plan_id);
|
|
174
131
|
if (adminPlanId) {
|
|
175
132
|
return { planId: adminPlanId, source: 'admin_override' };
|
|
@@ -178,9 +135,6 @@ function resolvePlanFromBillingAccount(account, metadataPlanId, defaultPlanId) {
|
|
|
178
135
|
if (stripePlanId && shouldUseStripePlan(account?.stripe_subscription_status)) {
|
|
179
136
|
return { planId: stripePlanId, source: 'stripe' };
|
|
180
137
|
}
|
|
181
|
-
if (metadataPlanId) {
|
|
182
|
-
return { planId: metadataPlanId, source: 'metadata' };
|
|
183
|
-
}
|
|
184
138
|
return { planId: defaultPlanId, source: 'default' };
|
|
185
139
|
}
|
|
186
140
|
export async function getBillingAccountForUser(supabase, userId) {
|
|
@@ -198,13 +152,8 @@ export async function getBillingAccountForUser(supabase, userId) {
|
|
|
198
152
|
return data ?? null;
|
|
199
153
|
}
|
|
200
154
|
async function getResolvedBillingPlanForUserId(supabase, userId) {
|
|
201
|
-
const { data, error } = await supabase.auth.admin.getUserById(userId);
|
|
202
|
-
if (error) {
|
|
203
|
-
throw new Error(`Failed to load user billing plan: ${error.message}`);
|
|
204
|
-
}
|
|
205
|
-
const metadataPlanId = getMetadataBillingPlanId(data.user);
|
|
206
155
|
const account = await getBillingAccountForUser(supabase, userId);
|
|
207
|
-
const resolved = resolvePlanFromBillingAccount(account,
|
|
156
|
+
const resolved = resolvePlanFromBillingAccount(account, getServerDefaultBillingPlanId());
|
|
208
157
|
return getBillingPlan(resolved.planId);
|
|
209
158
|
}
|
|
210
159
|
export async function getProjectOwnerBillingContext(supabase, projectId) {
|
|
@@ -234,12 +183,10 @@ export function isYearlySubscription(account) {
|
|
|
234
183
|
}
|
|
235
184
|
export function getStripeOveragePriceIdForPlan(planId) {
|
|
236
185
|
switch (planId) {
|
|
237
|
-
case '
|
|
238
|
-
return process.env.
|
|
239
|
-
case '
|
|
240
|
-
return process.env.
|
|
241
|
-
case 'business':
|
|
242
|
-
return process.env.STRIPE_PRICE_ID_BUSINESS_OVERAGE ?? null;
|
|
186
|
+
case 'maker':
|
|
187
|
+
return process.env.STRIPE_PRICE_ID_MAKER_OVERAGE ?? null;
|
|
188
|
+
case 'team':
|
|
189
|
+
return process.env.STRIPE_PRICE_ID_TEAM_OVERAGE ?? null;
|
|
243
190
|
case 'free':
|
|
244
191
|
return null;
|
|
245
192
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { SupabaseClient } from '@supabase/supabase-js';
|
|
2
|
-
export type CreditUsageType = 'screenshot' | 'clip' | 'video' | 'preset_analysis' | 'ai_chat';
|
|
2
|
+
export type CreditUsageType = 'screenshot' | 'clip' | 'video' | 'preset_analysis' | 'ai_chat' | 'interactive_demo_state';
|
|
3
3
|
export declare function recordCreditUsage(supabase: SupabaseClient, params: {
|
|
4
4
|
userId: string;
|
|
5
|
-
projectId: string;
|
|
5
|
+
projectId: string | null;
|
|
6
6
|
presetId?: string | null;
|
|
7
7
|
type: CreditUsageType;
|
|
8
8
|
credits: number;
|
|
@@ -29,7 +29,20 @@ export interface CaptureWebhookPayload {
|
|
|
29
29
|
total_captures: number;
|
|
30
30
|
success_count: number;
|
|
31
31
|
results: CaptureWebhookPayloadResult[];
|
|
32
|
+
/** IDs of screenshot_endpoints affected by this capture run (for proxy cache invalidation). */
|
|
33
|
+
affected_endpoint_ids: string[];
|
|
32
34
|
}
|
|
35
|
+
export interface EndpointChangedWebhookPayload {
|
|
36
|
+
event: 'endpoint.changed';
|
|
37
|
+
project_id: string;
|
|
38
|
+
preset_id: string | null;
|
|
39
|
+
timestamp: string;
|
|
40
|
+
created_endpoint_ids: string[];
|
|
41
|
+
deleted_endpoint_ids: string[];
|
|
42
|
+
/** Full list of currently active endpoint IDs for the project. */
|
|
43
|
+
current_endpoint_ids: string[];
|
|
44
|
+
}
|
|
45
|
+
export type WebhookPayload = CaptureWebhookPayload | EndpointChangedWebhookPayload;
|
|
33
46
|
export declare function getProjectWebhookConfig(supabase: SupabaseClient, projectId: string): Promise<ProjectWebhookConfig | null>;
|
|
34
47
|
export declare function buildCaptureWebhookPayload(params: {
|
|
35
48
|
runId: string;
|
|
@@ -37,9 +50,10 @@ export declare function buildCaptureWebhookPayload(params: {
|
|
|
37
50
|
presetId?: string | null;
|
|
38
51
|
completedAt?: string;
|
|
39
52
|
results: CaptureWebhookPayloadResult[];
|
|
53
|
+
affectedEndpointIds?: string[];
|
|
40
54
|
}): CaptureWebhookPayload;
|
|
41
55
|
export declare function dispatchProjectCaptureWebhook(params: {
|
|
42
56
|
config: ProjectWebhookConfig;
|
|
43
|
-
payload:
|
|
57
|
+
payload: WebhookPayload;
|
|
44
58
|
}): Promise<void>;
|
|
45
59
|
export declare function encryptProjectWebhookSigningSecret(plaintext: string): string;
|
|
@@ -64,8 +64,11 @@ export function buildCaptureWebhookPayload(params) {
|
|
|
64
64
|
iterations: result.iterations,
|
|
65
65
|
finalScreenshotUrl: result.finalScreenshotUrl,
|
|
66
66
|
})),
|
|
67
|
+
affected_endpoint_ids: params.affectedEndpointIds ?? [],
|
|
67
68
|
};
|
|
68
69
|
}
|
|
70
|
+
const WEBHOOK_RETRY_DELAYS = [0, 1_000, 3_000, 9_000];
|
|
71
|
+
const WEBHOOK_TIMEOUT_MS = 10_000;
|
|
69
72
|
export async function dispatchProjectCaptureWebhook(params) {
|
|
70
73
|
const body = JSON.stringify(params.payload);
|
|
71
74
|
const timestamp = Math.floor(Date.now() / 1000).toString();
|
|
@@ -81,15 +84,38 @@ export async function dispatchProjectCaptureWebhook(params) {
|
|
|
81
84
|
.digest('hex');
|
|
82
85
|
headers['X-AutoKap-Signature'] = signature;
|
|
83
86
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
87
|
+
let lastError = null;
|
|
88
|
+
for (let attempt = 0; attempt < WEBHOOK_RETRY_DELAYS.length; attempt++) {
|
|
89
|
+
if (attempt > 0) {
|
|
90
|
+
await new Promise((resolve) => setTimeout(resolve, WEBHOOK_RETRY_DELAYS[attempt]));
|
|
91
|
+
}
|
|
92
|
+
try {
|
|
93
|
+
const response = await fetch(params.config.url, {
|
|
94
|
+
method: 'POST',
|
|
95
|
+
headers,
|
|
96
|
+
body,
|
|
97
|
+
signal: AbortSignal.timeout(WEBHOOK_TIMEOUT_MS),
|
|
98
|
+
});
|
|
99
|
+
if (response.ok)
|
|
100
|
+
return;
|
|
101
|
+
// Don't retry on 4xx client errors (except 429 rate limiting)
|
|
102
|
+
if (response.status >= 400 && response.status < 500 && response.status !== 429) {
|
|
103
|
+
const responseText = await response.text().catch(() => '');
|
|
104
|
+
throw new Error(`Webhook responded with ${response.status}${responseText ? `: ${responseText.slice(0, 300)}` : ''}`);
|
|
105
|
+
}
|
|
106
|
+
// 5xx or 429 — retry
|
|
107
|
+
const responseText = await response.text().catch(() => '');
|
|
108
|
+
lastError = new Error(`Webhook responded with ${response.status}${responseText ? `: ${responseText.slice(0, 300)}` : ''}`);
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
lastError = error;
|
|
112
|
+
// Network errors and timeouts are retryable
|
|
113
|
+
if (error.name === 'AbortError') {
|
|
114
|
+
lastError = new Error(`Webhook timed out after ${WEBHOOK_TIMEOUT_MS}ms`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
92
117
|
}
|
|
118
|
+
throw lastError ?? new Error('Webhook delivery failed after retries');
|
|
93
119
|
}
|
|
94
120
|
export function encryptProjectWebhookSigningSecret(plaintext) {
|
|
95
121
|
return JSON.stringify(encrypt(plaintext, getWebhookEncryptionSecret()));
|
|
@@ -7,11 +7,18 @@ function escapeXml(value) {
|
|
|
7
7
|
.replaceAll('"', '"')
|
|
8
8
|
.replaceAll('\'', ''');
|
|
9
9
|
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const
|
|
10
|
+
/** LRU cache for watermark SVG overlays — avoids regenerating tiles for repeated dimensions. */
|
|
11
|
+
const SVG_CACHE_MAX = 20;
|
|
12
|
+
const svgOverlayCache = new Map();
|
|
13
|
+
function getOrBuildSvgOverlay(width, height, label) {
|
|
14
|
+
const cacheKey = `${width}:${height}:${label}`;
|
|
15
|
+
const cached = svgOverlayCache.get(cacheKey);
|
|
16
|
+
if (cached) {
|
|
17
|
+
// Move to end (LRU refresh)
|
|
18
|
+
svgOverlayCache.delete(cacheKey);
|
|
19
|
+
svgOverlayCache.set(cacheKey, cached);
|
|
20
|
+
return cached;
|
|
21
|
+
}
|
|
15
22
|
const fontSize = Math.max(18, Math.round(Math.min(width, height) * 0.035));
|
|
16
23
|
const tileGap = fontSize * 4;
|
|
17
24
|
const safeLabel = escapeXml(label);
|
|
@@ -27,6 +34,21 @@ export async function applyScreenshotWatermark(buffer, label = 'AutoKap Free') {
|
|
|
27
34
|
${tiles.join('')}
|
|
28
35
|
</g>
|
|
29
36
|
</svg>`);
|
|
37
|
+
// Evict oldest entry if cache is full
|
|
38
|
+
if (svgOverlayCache.size >= SVG_CACHE_MAX) {
|
|
39
|
+
const firstKey = svgOverlayCache.keys().next().value;
|
|
40
|
+
if (firstKey !== undefined)
|
|
41
|
+
svgOverlayCache.delete(firstKey);
|
|
42
|
+
}
|
|
43
|
+
svgOverlayCache.set(cacheKey, overlay);
|
|
44
|
+
return overlay;
|
|
45
|
+
}
|
|
46
|
+
export async function applyScreenshotWatermark(buffer, label = 'AutoKap Free') {
|
|
47
|
+
const image = sharp(buffer);
|
|
48
|
+
const metadata = await image.metadata();
|
|
49
|
+
const width = metadata.width ?? 1440;
|
|
50
|
+
const height = metadata.height ?? 900;
|
|
51
|
+
const overlay = getOrBuildSvgOverlay(width, height, label);
|
|
30
52
|
return image.composite([{ input: overlay }]).png().toBuffer();
|
|
31
53
|
}
|
|
32
54
|
export async function applyPlanScreenshotWatermark(plan, buffer) {
|