whale-code 6.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +95 -0
- package/bin/swag-agent.js +9 -0
- package/bin/swagmanager-mcp.js +321 -0
- package/dist/cli/app.d.ts +26 -0
- package/dist/cli/app.js +64 -0
- package/dist/cli/chat/AgentSelector.d.ts +14 -0
- package/dist/cli/chat/AgentSelector.js +14 -0
- package/dist/cli/chat/ChatApp.d.ts +9 -0
- package/dist/cli/chat/ChatApp.js +267 -0
- package/dist/cli/chat/ChatInput.d.ts +39 -0
- package/dist/cli/chat/ChatInput.js +509 -0
- package/dist/cli/chat/MarkdownText.d.ts +10 -0
- package/dist/cli/chat/MarkdownText.js +20 -0
- package/dist/cli/chat/MessageList.d.ts +37 -0
- package/dist/cli/chat/MessageList.js +80 -0
- package/dist/cli/chat/ModelSelector.d.ts +20 -0
- package/dist/cli/chat/ModelSelector.js +73 -0
- package/dist/cli/chat/RewindViewer.d.ts +26 -0
- package/dist/cli/chat/RewindViewer.js +185 -0
- package/dist/cli/chat/StoreSelector.d.ts +14 -0
- package/dist/cli/chat/StoreSelector.js +24 -0
- package/dist/cli/chat/StreamingText.d.ts +12 -0
- package/dist/cli/chat/StreamingText.js +12 -0
- package/dist/cli/chat/SubagentPanel.d.ts +45 -0
- package/dist/cli/chat/SubagentPanel.js +110 -0
- package/dist/cli/chat/TeamPanel.d.ts +21 -0
- package/dist/cli/chat/TeamPanel.js +42 -0
- package/dist/cli/chat/ToolIndicator.d.ts +25 -0
- package/dist/cli/chat/ToolIndicator.js +436 -0
- package/dist/cli/chat/hooks/useAgentLoop.d.ts +39 -0
- package/dist/cli/chat/hooks/useAgentLoop.js +382 -0
- package/dist/cli/chat/hooks/useSlashCommands.d.ts +37 -0
- package/dist/cli/chat/hooks/useSlashCommands.js +387 -0
- package/dist/cli/commands/config-cmd.d.ts +10 -0
- package/dist/cli/commands/config-cmd.js +99 -0
- package/dist/cli/commands/doctor.d.ts +14 -0
- package/dist/cli/commands/doctor.js +172 -0
- package/dist/cli/commands/init.d.ts +16 -0
- package/dist/cli/commands/init.js +278 -0
- package/dist/cli/commands/mcp.d.ts +12 -0
- package/dist/cli/commands/mcp.js +162 -0
- package/dist/cli/login/LoginApp.d.ts +7 -0
- package/dist/cli/login/LoginApp.js +157 -0
- package/dist/cli/print-mode.d.ts +31 -0
- package/dist/cli/print-mode.js +202 -0
- package/dist/cli/serve-mode.d.ts +37 -0
- package/dist/cli/serve-mode.js +636 -0
- package/dist/cli/services/agent-definitions.d.ts +25 -0
- package/dist/cli/services/agent-definitions.js +91 -0
- package/dist/cli/services/agent-events.d.ts +178 -0
- package/dist/cli/services/agent-events.js +175 -0
- package/dist/cli/services/agent-loop.d.ts +90 -0
- package/dist/cli/services/agent-loop.js +762 -0
- package/dist/cli/services/agent-worker-base.d.ts +97 -0
- package/dist/cli/services/agent-worker-base.js +220 -0
- package/dist/cli/services/auth-service.d.ts +30 -0
- package/dist/cli/services/auth-service.js +160 -0
- package/dist/cli/services/background-processes.d.ts +126 -0
- package/dist/cli/services/background-processes.js +318 -0
- package/dist/cli/services/browser-auth.d.ts +24 -0
- package/dist/cli/services/browser-auth.js +180 -0
- package/dist/cli/services/claude-md-loader.d.ts +16 -0
- package/dist/cli/services/claude-md-loader.js +58 -0
- package/dist/cli/services/config-store.d.ts +47 -0
- package/dist/cli/services/config-store.js +79 -0
- package/dist/cli/services/debug-log.d.ts +10 -0
- package/dist/cli/services/debug-log.js +52 -0
- package/dist/cli/services/error-logger.d.ts +58 -0
- package/dist/cli/services/error-logger.js +269 -0
- package/dist/cli/services/file-history.d.ts +21 -0
- package/dist/cli/services/file-history.js +83 -0
- package/dist/cli/services/format-server-response.d.ts +16 -0
- package/dist/cli/services/format-server-response.js +440 -0
- package/dist/cli/services/git-context.d.ts +11 -0
- package/dist/cli/services/git-context.js +66 -0
- package/dist/cli/services/hooks.d.ts +85 -0
- package/dist/cli/services/hooks.js +258 -0
- package/dist/cli/services/interactive-tools.d.ts +125 -0
- package/dist/cli/services/interactive-tools.js +260 -0
- package/dist/cli/services/keybinding-manager.d.ts +52 -0
- package/dist/cli/services/keybinding-manager.js +115 -0
- package/dist/cli/services/local-tools.d.ts +22 -0
- package/dist/cli/services/local-tools.js +697 -0
- package/dist/cli/services/lsp-manager.d.ts +18 -0
- package/dist/cli/services/lsp-manager.js +717 -0
- package/dist/cli/services/mcp-client.d.ts +48 -0
- package/dist/cli/services/mcp-client.js +157 -0
- package/dist/cli/services/memory-manager.d.ts +16 -0
- package/dist/cli/services/memory-manager.js +57 -0
- package/dist/cli/services/model-manager.d.ts +18 -0
- package/dist/cli/services/model-manager.js +71 -0
- package/dist/cli/services/model-router.d.ts +26 -0
- package/dist/cli/services/model-router.js +149 -0
- package/dist/cli/services/permission-modes.d.ts +13 -0
- package/dist/cli/services/permission-modes.js +43 -0
- package/dist/cli/services/rewind.d.ts +84 -0
- package/dist/cli/services/rewind.js +194 -0
- package/dist/cli/services/ripgrep.d.ts +28 -0
- package/dist/cli/services/ripgrep.js +138 -0
- package/dist/cli/services/sandbox.d.ts +29 -0
- package/dist/cli/services/sandbox.js +97 -0
- package/dist/cli/services/server-tools.d.ts +61 -0
- package/dist/cli/services/server-tools.js +543 -0
- package/dist/cli/services/session-persistence.d.ts +23 -0
- package/dist/cli/services/session-persistence.js +99 -0
- package/dist/cli/services/subagent-worker.d.ts +19 -0
- package/dist/cli/services/subagent-worker.js +41 -0
- package/dist/cli/services/subagent.d.ts +47 -0
- package/dist/cli/services/subagent.js +647 -0
- package/dist/cli/services/system-prompt.d.ts +7 -0
- package/dist/cli/services/system-prompt.js +198 -0
- package/dist/cli/services/team-lead.d.ts +73 -0
- package/dist/cli/services/team-lead.js +512 -0
- package/dist/cli/services/team-state.d.ts +77 -0
- package/dist/cli/services/team-state.js +398 -0
- package/dist/cli/services/teammate.d.ts +31 -0
- package/dist/cli/services/teammate.js +689 -0
- package/dist/cli/services/telemetry.d.ts +61 -0
- package/dist/cli/services/telemetry.js +209 -0
- package/dist/cli/services/tools/agent-tools.d.ts +14 -0
- package/dist/cli/services/tools/agent-tools.js +347 -0
- package/dist/cli/services/tools/file-ops.d.ts +15 -0
- package/dist/cli/services/tools/file-ops.js +487 -0
- package/dist/cli/services/tools/search-tools.d.ts +8 -0
- package/dist/cli/services/tools/search-tools.js +186 -0
- package/dist/cli/services/tools/shell-exec.d.ts +10 -0
- package/dist/cli/services/tools/shell-exec.js +168 -0
- package/dist/cli/services/tools/task-manager.d.ts +28 -0
- package/dist/cli/services/tools/task-manager.js +209 -0
- package/dist/cli/services/tools/web-tools.d.ts +11 -0
- package/dist/cli/services/tools/web-tools.js +395 -0
- package/dist/cli/setup/SetupApp.d.ts +9 -0
- package/dist/cli/setup/SetupApp.js +191 -0
- package/dist/cli/shared/MatrixIntro.d.ts +4 -0
- package/dist/cli/shared/MatrixIntro.js +83 -0
- package/dist/cli/shared/Theme.d.ts +74 -0
- package/dist/cli/shared/Theme.js +127 -0
- package/dist/cli/shared/WhaleBanner.d.ts +10 -0
- package/dist/cli/shared/WhaleBanner.js +12 -0
- package/dist/cli/shared/markdown.d.ts +21 -0
- package/dist/cli/shared/markdown.js +756 -0
- package/dist/cli/status/StatusApp.d.ts +4 -0
- package/dist/cli/status/StatusApp.js +105 -0
- package/dist/cli/stores/StoreApp.d.ts +7 -0
- package/dist/cli/stores/StoreApp.js +81 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +538 -0
- package/dist/local-agent/connection.d.ts +48 -0
- package/dist/local-agent/connection.js +332 -0
- package/dist/local-agent/discovery.d.ts +18 -0
- package/dist/local-agent/discovery.js +146 -0
- package/dist/local-agent/executor.d.ts +34 -0
- package/dist/local-agent/executor.js +241 -0
- package/dist/local-agent/index.d.ts +14 -0
- package/dist/local-agent/index.js +198 -0
- package/dist/node/adapters/base.d.ts +35 -0
- package/dist/node/adapters/base.js +10 -0
- package/dist/node/adapters/discord.d.ts +29 -0
- package/dist/node/adapters/discord.js +299 -0
- package/dist/node/adapters/email.d.ts +23 -0
- package/dist/node/adapters/email.js +218 -0
- package/dist/node/adapters/imessage.d.ts +17 -0
- package/dist/node/adapters/imessage.js +118 -0
- package/dist/node/adapters/slack.d.ts +26 -0
- package/dist/node/adapters/slack.js +259 -0
- package/dist/node/adapters/sms.d.ts +23 -0
- package/dist/node/adapters/sms.js +161 -0
- package/dist/node/adapters/telegram.d.ts +17 -0
- package/dist/node/adapters/telegram.js +101 -0
- package/dist/node/adapters/webchat.d.ts +27 -0
- package/dist/node/adapters/webchat.js +160 -0
- package/dist/node/adapters/whatsapp.d.ts +28 -0
- package/dist/node/adapters/whatsapp.js +230 -0
- package/dist/node/cli.d.ts +2 -0
- package/dist/node/cli.js +325 -0
- package/dist/node/config.d.ts +17 -0
- package/dist/node/config.js +31 -0
- package/dist/node/runtime.d.ts +50 -0
- package/dist/node/runtime.js +351 -0
- package/dist/server/handlers/__test-utils__/mock-supabase.d.ts +11 -0
- package/dist/server/handlers/__test-utils__/mock-supabase.js +393 -0
- package/dist/server/handlers/analytics.d.ts +17 -0
- package/dist/server/handlers/analytics.js +266 -0
- package/dist/server/handlers/api-keys.d.ts +6 -0
- package/dist/server/handlers/api-keys.js +221 -0
- package/dist/server/handlers/billing.d.ts +33 -0
- package/dist/server/handlers/billing.js +272 -0
- package/dist/server/handlers/browser.d.ts +10 -0
- package/dist/server/handlers/browser.js +517 -0
- package/dist/server/handlers/catalog.d.ts +99 -0
- package/dist/server/handlers/catalog.js +976 -0
- package/dist/server/handlers/comms.d.ts +254 -0
- package/dist/server/handlers/comms.js +588 -0
- package/dist/server/handlers/creations.d.ts +6 -0
- package/dist/server/handlers/creations.js +479 -0
- package/dist/server/handlers/crm.d.ts +89 -0
- package/dist/server/handlers/crm.js +538 -0
- package/dist/server/handlers/discovery.d.ts +6 -0
- package/dist/server/handlers/discovery.js +288 -0
- package/dist/server/handlers/embeddings.d.ts +92 -0
- package/dist/server/handlers/embeddings.js +197 -0
- package/dist/server/handlers/enrichment.d.ts +8 -0
- package/dist/server/handlers/enrichment.js +768 -0
- package/dist/server/handlers/image-gen.d.ts +6 -0
- package/dist/server/handlers/image-gen.js +409 -0
- package/dist/server/handlers/inventory.d.ts +319 -0
- package/dist/server/handlers/inventory.js +447 -0
- package/dist/server/handlers/kali.d.ts +10 -0
- package/dist/server/handlers/kali.js +210 -0
- package/dist/server/handlers/llm-providers.d.ts +6 -0
- package/dist/server/handlers/llm-providers.js +673 -0
- package/dist/server/handlers/local-agent.d.ts +6 -0
- package/dist/server/handlers/local-agent.js +118 -0
- package/dist/server/handlers/meta-ads.d.ts +111 -0
- package/dist/server/handlers/meta-ads.js +2279 -0
- package/dist/server/handlers/nodes.d.ts +33 -0
- package/dist/server/handlers/nodes.js +699 -0
- package/dist/server/handlers/operations.d.ts +138 -0
- package/dist/server/handlers/operations.js +131 -0
- package/dist/server/handlers/platform.d.ts +23 -0
- package/dist/server/handlers/platform.js +227 -0
- package/dist/server/handlers/supply-chain.d.ts +19 -0
- package/dist/server/handlers/supply-chain.js +327 -0
- package/dist/server/handlers/transcription.d.ts +17 -0
- package/dist/server/handlers/transcription.js +121 -0
- package/dist/server/handlers/video-gen.d.ts +6 -0
- package/dist/server/handlers/video-gen.js +466 -0
- package/dist/server/handlers/voice.d.ts +8 -0
- package/dist/server/handlers/voice.js +1146 -0
- package/dist/server/handlers/workflow-steps.d.ts +86 -0
- package/dist/server/handlers/workflow-steps.js +2349 -0
- package/dist/server/handlers/workflows.d.ts +7 -0
- package/dist/server/handlers/workflows.js +989 -0
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.js +2427 -0
- package/dist/server/lib/batch-client.d.ts +80 -0
- package/dist/server/lib/batch-client.js +467 -0
- package/dist/server/lib/code-worker-pool.d.ts +31 -0
- package/dist/server/lib/code-worker-pool.js +224 -0
- package/dist/server/lib/code-worker.d.ts +1 -0
- package/dist/server/lib/code-worker.js +188 -0
- package/dist/server/lib/compaction-service.d.ts +32 -0
- package/dist/server/lib/compaction-service.js +162 -0
- package/dist/server/lib/logger.d.ts +19 -0
- package/dist/server/lib/logger.js +46 -0
- package/dist/server/lib/otel.d.ts +38 -0
- package/dist/server/lib/otel.js +126 -0
- package/dist/server/lib/pg-rate-limiter.d.ts +21 -0
- package/dist/server/lib/pg-rate-limiter.js +86 -0
- package/dist/server/lib/prompt-sanitizer.d.ts +37 -0
- package/dist/server/lib/prompt-sanitizer.js +177 -0
- package/dist/server/lib/provider-capabilities.d.ts +85 -0
- package/dist/server/lib/provider-capabilities.js +190 -0
- package/dist/server/lib/provider-failover.d.ts +74 -0
- package/dist/server/lib/provider-failover.js +210 -0
- package/dist/server/lib/rate-limiter.d.ts +39 -0
- package/dist/server/lib/rate-limiter.js +147 -0
- package/dist/server/lib/server-agent-loop.d.ts +107 -0
- package/dist/server/lib/server-agent-loop.js +667 -0
- package/dist/server/lib/server-subagent.d.ts +78 -0
- package/dist/server/lib/server-subagent.js +203 -0
- package/dist/server/lib/session-checkpoint.d.ts +51 -0
- package/dist/server/lib/session-checkpoint.js +145 -0
- package/dist/server/lib/ssrf-guard.d.ts +13 -0
- package/dist/server/lib/ssrf-guard.js +240 -0
- package/dist/server/lib/supabase-client.d.ts +7 -0
- package/dist/server/lib/supabase-client.js +78 -0
- package/dist/server/lib/template-resolver.d.ts +31 -0
- package/dist/server/lib/template-resolver.js +215 -0
- package/dist/server/lib/utils.d.ts +16 -0
- package/dist/server/lib/utils.js +147 -0
- package/dist/server/local-agent-gateway.d.ts +82 -0
- package/dist/server/local-agent-gateway.js +426 -0
- package/dist/server/providers/anthropic.d.ts +20 -0
- package/dist/server/providers/anthropic.js +199 -0
- package/dist/server/providers/bedrock.d.ts +20 -0
- package/dist/server/providers/bedrock.js +194 -0
- package/dist/server/providers/gemini.d.ts +24 -0
- package/dist/server/providers/gemini.js +486 -0
- package/dist/server/providers/openai.d.ts +24 -0
- package/dist/server/providers/openai.js +522 -0
- package/dist/server/providers/registry.d.ts +32 -0
- package/dist/server/providers/registry.js +58 -0
- package/dist/server/providers/shared.d.ts +32 -0
- package/dist/server/providers/shared.js +124 -0
- package/dist/server/providers/types.d.ts +92 -0
- package/dist/server/providers/types.js +12 -0
- package/dist/server/proxy-handlers.d.ts +6 -0
- package/dist/server/proxy-handlers.js +89 -0
- package/dist/server/tool-router.d.ts +149 -0
- package/dist/server/tool-router.js +803 -0
- package/dist/server/validation.d.ts +24 -0
- package/dist/server/validation.js +301 -0
- package/dist/server/worker.d.ts +19 -0
- package/dist/server/worker.js +201 -0
- package/dist/setup.d.ts +8 -0
- package/dist/setup.js +181 -0
- package/dist/shared/agent-core.d.ts +157 -0
- package/dist/shared/agent-core.js +534 -0
- package/dist/shared/anthropic-types.d.ts +105 -0
- package/dist/shared/anthropic-types.js +7 -0
- package/dist/shared/api-client.d.ts +90 -0
- package/dist/shared/api-client.js +379 -0
- package/dist/shared/constants.d.ts +33 -0
- package/dist/shared/constants.js +80 -0
- package/dist/shared/sse-parser.d.ts +26 -0
- package/dist/shared/sse-parser.js +259 -0
- package/dist/shared/tool-dispatch.d.ts +52 -0
- package/dist/shared/tool-dispatch.js +191 -0
- package/dist/shared/types.d.ts +72 -0
- package/dist/shared/types.js +7 -0
- package/dist/updater.d.ts +25 -0
- package/dist/updater.js +140 -0
- package/dist/webchat/widget.d.ts +0 -0
- package/dist/webchat/widget.js +397 -0
- package/package.json +95 -0
- package/src/cli/services/builtin-skills/commit.md +19 -0
- package/src/cli/services/builtin-skills/review-pr.md +21 -0
- package/src/cli/services/builtin-skills/review.md +18 -0
|
@@ -0,0 +1,517 @@
|
|
|
1
|
+
// server/handlers/browser.ts — Browser automation via Playwright
|
|
2
|
+
// Supports: navigate, screenshot, extract, click, fill, evaluate, pdf
|
|
3
|
+
import { chromium } from "playwright-core";
|
|
4
|
+
// ============================================================================
|
|
5
|
+
// BROWSER INSTANCE LIFECYCLE
|
|
6
|
+
// ============================================================================
|
|
7
|
+
let browserInstance = null;
|
|
8
|
+
let idleTimer = null;
|
|
9
|
+
const IDLE_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes
|
|
10
|
+
const PAGE_TIMEOUT_MS = 60_000; // 60 seconds per page operation (increased for CAPTCHA solving)
|
|
11
|
+
const MAX_RESULT_BYTES = 500 * 1024; // 500KB safety cap — context_management handles limits
|
|
12
|
+
const MAX_TEXT_CHARS = 200_000; // 200K chars for text content
|
|
13
|
+
function resetIdleTimer() {
|
|
14
|
+
if (idleTimer)
|
|
15
|
+
clearTimeout(idleTimer);
|
|
16
|
+
idleTimer = setTimeout(async () => {
|
|
17
|
+
if (browserInstance) {
|
|
18
|
+
try {
|
|
19
|
+
await browserInstance.close();
|
|
20
|
+
}
|
|
21
|
+
catch { /* browser may already be closed */ }
|
|
22
|
+
browserInstance = null;
|
|
23
|
+
console.log("[browser] Closed after idle timeout");
|
|
24
|
+
}
|
|
25
|
+
}, IDLE_TIMEOUT_MS);
|
|
26
|
+
}
|
|
27
|
+
async function getBrowser() {
|
|
28
|
+
if (browserInstance && browserInstance.isConnected()) {
|
|
29
|
+
resetIdleTimer();
|
|
30
|
+
return browserInstance;
|
|
31
|
+
}
|
|
32
|
+
browserInstance = await chromium.launch({
|
|
33
|
+
headless: true,
|
|
34
|
+
executablePath: process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH || undefined,
|
|
35
|
+
args: [
|
|
36
|
+
"--no-sandbox",
|
|
37
|
+
"--disable-setuid-sandbox",
|
|
38
|
+
"--disable-dev-shm-usage",
|
|
39
|
+
"--disable-gpu",
|
|
40
|
+
],
|
|
41
|
+
});
|
|
42
|
+
browserInstance.on("disconnected", () => {
|
|
43
|
+
browserInstance = null;
|
|
44
|
+
if (idleTimer) {
|
|
45
|
+
clearTimeout(idleTimer);
|
|
46
|
+
idleTimer = null;
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
resetIdleTimer();
|
|
50
|
+
console.log("[browser] Launched new instance");
|
|
51
|
+
return browserInstance;
|
|
52
|
+
}
|
|
53
|
+
async function withPage(url, waitFor, fn) {
|
|
54
|
+
const browser = await getBrowser();
|
|
55
|
+
const context = await browser.newContext({
|
|
56
|
+
userAgent: "WhaleBot/1.0 (https://whale.app)",
|
|
57
|
+
viewport: { width: 1280, height: 720 },
|
|
58
|
+
ignoreHTTPSErrors: true,
|
|
59
|
+
});
|
|
60
|
+
context.setDefaultTimeout(PAGE_TIMEOUT_MS);
|
|
61
|
+
const page = await context.newPage();
|
|
62
|
+
try {
|
|
63
|
+
await page.goto(url, { waitUntil: "domcontentloaded", timeout: PAGE_TIMEOUT_MS });
|
|
64
|
+
// Auto-detect and solve CAPTCHAs after page load
|
|
65
|
+
const captchaResult = await detectAndSolveCaptcha(page);
|
|
66
|
+
if (captchaResult.solved) {
|
|
67
|
+
console.log(`[browser] Solved ${captchaResult.type} CAPTCHA on ${url}`);
|
|
68
|
+
// Wait for page to update after CAPTCHA solution
|
|
69
|
+
await page.waitForTimeout(2000);
|
|
70
|
+
}
|
|
71
|
+
if (waitFor) {
|
|
72
|
+
await page.waitForSelector(waitFor, { timeout: PAGE_TIMEOUT_MS });
|
|
73
|
+
}
|
|
74
|
+
return await fn(page);
|
|
75
|
+
}
|
|
76
|
+
finally {
|
|
77
|
+
await page.close().catch(() => { });
|
|
78
|
+
await context.close().catch(() => { });
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
// ============================================================================
|
|
82
|
+
// SSRF PROTECTION
|
|
83
|
+
// ============================================================================
|
|
84
|
+
function isBlockedUrl(url) {
|
|
85
|
+
let parsed;
|
|
86
|
+
try {
|
|
87
|
+
parsed = new URL(url);
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return "Invalid URL";
|
|
91
|
+
}
|
|
92
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
93
|
+
return `Blocked protocol: ${parsed.protocol}`;
|
|
94
|
+
}
|
|
95
|
+
const host = parsed.hostname.toLowerCase();
|
|
96
|
+
// Block localhost variants
|
|
97
|
+
if (host === "localhost" || host === "127.0.0.1" || host === "0.0.0.0" ||
|
|
98
|
+
host === "::1" || host === "[::1]") {
|
|
99
|
+
return "Blocked: localhost";
|
|
100
|
+
}
|
|
101
|
+
// Block private/internal domains
|
|
102
|
+
if (host.endsWith(".internal") || host.endsWith(".local") ||
|
|
103
|
+
host === "metadata.google.internal") {
|
|
104
|
+
return "Blocked: internal/local domain";
|
|
105
|
+
}
|
|
106
|
+
// Block private IP ranges
|
|
107
|
+
if (/^10\./.test(host) ||
|
|
108
|
+
/^172\.(1[6-9]|2\d|3[01])\./.test(host) ||
|
|
109
|
+
/^192\.168\./.test(host) ||
|
|
110
|
+
/^169\.254\./.test(host)) {
|
|
111
|
+
return "Blocked: private IP range";
|
|
112
|
+
}
|
|
113
|
+
// Block IPv6 private
|
|
114
|
+
if (host.startsWith("fd") || host.startsWith("fc00:") ||
|
|
115
|
+
host.startsWith("fe80:") || host.includes("::ffff:")) {
|
|
116
|
+
return "Blocked: IPv6 private range";
|
|
117
|
+
}
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
// ============================================================================
|
|
121
|
+
// TEXT CLEANUP
|
|
122
|
+
// ============================================================================
|
|
123
|
+
function stripScriptsAndStyles(html) {
|
|
124
|
+
return html
|
|
125
|
+
.replace(/<script[\s\S]*?<\/script>/gi, "")
|
|
126
|
+
.replace(/<style[\s\S]*?<\/style>/gi, "")
|
|
127
|
+
.replace(/<[^>]+>/g, " ")
|
|
128
|
+
.replace(/\s+/g, " ")
|
|
129
|
+
.trim();
|
|
130
|
+
}
|
|
131
|
+
function truncate(text, maxChars) {
|
|
132
|
+
if (text.length <= maxChars)
|
|
133
|
+
return text;
|
|
134
|
+
return text.substring(0, maxChars) + "\n...[truncated]";
|
|
135
|
+
}
|
|
136
|
+
function truncateResult(data) {
|
|
137
|
+
const json = JSON.stringify(data);
|
|
138
|
+
if (json.length <= MAX_RESULT_BYTES)
|
|
139
|
+
return data;
|
|
140
|
+
// If too large, stringify and truncate
|
|
141
|
+
return JSON.parse(truncate(json, MAX_RESULT_BYTES));
|
|
142
|
+
}
|
|
143
|
+
// ============================================================================
|
|
144
|
+
// ACTION HANDLERS
|
|
145
|
+
// ============================================================================
|
|
146
|
+
async function actionNavigate(url, waitFor) {
|
|
147
|
+
return withPage(url, waitFor, async (page) => {
|
|
148
|
+
const title = await page.title();
|
|
149
|
+
const html = await page.content();
|
|
150
|
+
const textContent = stripScriptsAndStyles(html);
|
|
151
|
+
return {
|
|
152
|
+
success: true,
|
|
153
|
+
data: {
|
|
154
|
+
url: page.url(),
|
|
155
|
+
title,
|
|
156
|
+
text: truncate(textContent, MAX_TEXT_CHARS),
|
|
157
|
+
text_length: textContent.length,
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
async function actionScreenshot(url, waitFor) {
|
|
163
|
+
return withPage(url, waitFor, async (page) => {
|
|
164
|
+
const buffer = await page.screenshot({ type: "png", fullPage: false });
|
|
165
|
+
const base64 = buffer.toString("base64");
|
|
166
|
+
const title = await page.title();
|
|
167
|
+
return {
|
|
168
|
+
success: true,
|
|
169
|
+
data: {
|
|
170
|
+
url: page.url(),
|
|
171
|
+
title,
|
|
172
|
+
screenshot_base64: base64,
|
|
173
|
+
format: "png",
|
|
174
|
+
size_bytes: buffer.length,
|
|
175
|
+
},
|
|
176
|
+
};
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
async function actionExtract(url, selector, waitFor) {
|
|
180
|
+
if (!selector) {
|
|
181
|
+
return { success: false, error: "selector is required for extract action" };
|
|
182
|
+
}
|
|
183
|
+
return withPage(url, waitFor, async (page) => {
|
|
184
|
+
const elements = await page.$$eval(selector, (els) => els.map((el) => ({
|
|
185
|
+
tag: el.tagName.toLowerCase(),
|
|
186
|
+
text: el.textContent?.trim() || "",
|
|
187
|
+
html: el.innerHTML.substring(0, 2000),
|
|
188
|
+
attributes: Object.fromEntries(Array.from(el.attributes).map((a) => [a.name, a.value])),
|
|
189
|
+
})));
|
|
190
|
+
return {
|
|
191
|
+
success: true,
|
|
192
|
+
data: {
|
|
193
|
+
url: page.url(),
|
|
194
|
+
selector,
|
|
195
|
+
count: elements.length,
|
|
196
|
+
elements: elements.slice(0, 50), // Cap at 50 elements
|
|
197
|
+
},
|
|
198
|
+
};
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
async function actionClick(url, selector, waitFor) {
|
|
202
|
+
if (!selector) {
|
|
203
|
+
return { success: false, error: "selector is required for click action" };
|
|
204
|
+
}
|
|
205
|
+
return withPage(url, waitFor, async (page) => {
|
|
206
|
+
await page.click(selector, { timeout: PAGE_TIMEOUT_MS });
|
|
207
|
+
// Wait a moment for any navigation or dynamic content
|
|
208
|
+
await page.waitForTimeout(1000);
|
|
209
|
+
const title = await page.title();
|
|
210
|
+
const html = await page.content();
|
|
211
|
+
const textContent = stripScriptsAndStyles(html);
|
|
212
|
+
return {
|
|
213
|
+
success: true,
|
|
214
|
+
data: {
|
|
215
|
+
url: page.url(),
|
|
216
|
+
title,
|
|
217
|
+
clicked: selector,
|
|
218
|
+
text: truncate(textContent, MAX_TEXT_CHARS),
|
|
219
|
+
},
|
|
220
|
+
};
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
async function actionFill(url, selector, value, waitFor) {
|
|
224
|
+
if (!selector) {
|
|
225
|
+
return { success: false, error: "selector is required for fill action" };
|
|
226
|
+
}
|
|
227
|
+
if (value === undefined || value === null) {
|
|
228
|
+
return { success: false, error: "value is required for fill action" };
|
|
229
|
+
}
|
|
230
|
+
return withPage(url, waitFor, async (page) => {
|
|
231
|
+
await page.fill(selector, value, { timeout: PAGE_TIMEOUT_MS });
|
|
232
|
+
return {
|
|
233
|
+
success: true,
|
|
234
|
+
data: {
|
|
235
|
+
url: page.url(),
|
|
236
|
+
filled: { selector, value },
|
|
237
|
+
},
|
|
238
|
+
};
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
async function actionEvaluate(url, script, waitFor) {
|
|
242
|
+
if (!script) {
|
|
243
|
+
return { success: false, error: "script is required for evaluate action" };
|
|
244
|
+
}
|
|
245
|
+
return withPage(url, waitFor, async (page) => {
|
|
246
|
+
const result = await page.evaluate(script);
|
|
247
|
+
return {
|
|
248
|
+
success: true,
|
|
249
|
+
data: {
|
|
250
|
+
url: page.url(),
|
|
251
|
+
result: truncateResult(result),
|
|
252
|
+
},
|
|
253
|
+
};
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
async function actionPdf(url, waitFor) {
|
|
257
|
+
return withPage(url, waitFor, async (page) => {
|
|
258
|
+
const buffer = await page.pdf({
|
|
259
|
+
format: "A4",
|
|
260
|
+
printBackground: true,
|
|
261
|
+
margin: { top: "1cm", bottom: "1cm", left: "1cm", right: "1cm" },
|
|
262
|
+
});
|
|
263
|
+
const base64 = buffer.toString("base64");
|
|
264
|
+
const title = await page.title();
|
|
265
|
+
return {
|
|
266
|
+
success: true,
|
|
267
|
+
data: {
|
|
268
|
+
url: page.url(),
|
|
269
|
+
title,
|
|
270
|
+
pdf_base64: base64,
|
|
271
|
+
format: "A4",
|
|
272
|
+
size_bytes: buffer.length,
|
|
273
|
+
},
|
|
274
|
+
};
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
// ============================================================================
|
|
278
|
+
// PDF FROM HTML (reusable by documents handler)
|
|
279
|
+
// ============================================================================
|
|
280
|
+
export async function generatePdfFromHtml(html, options) {
|
|
281
|
+
// P0 FIX: Strip dangerous tags from user HTML before rendering to prevent
|
|
282
|
+
// script execution, SSRF via iframes, and local file reads
|
|
283
|
+
const sanitizedHtml = html
|
|
284
|
+
.replace(/<script[\s\S]*?<\/script>/gi, "")
|
|
285
|
+
.replace(/<script[^>]*\/?>/gi, "")
|
|
286
|
+
.replace(/<iframe[\s\S]*?<\/iframe>/gi, "")
|
|
287
|
+
.replace(/<iframe[^>]*\/?>/gi, "")
|
|
288
|
+
.replace(/<object[\s\S]*?<\/object>/gi, "")
|
|
289
|
+
.replace(/<object[^>]*\/?>/gi, "")
|
|
290
|
+
.replace(/<embed[^>]*\/?>/gi, "")
|
|
291
|
+
.replace(/<link[^>]*\/?>/gi, "");
|
|
292
|
+
const browser = await getBrowser();
|
|
293
|
+
const context = await browser.newContext({ viewport: { width: 1280, height: 720 } });
|
|
294
|
+
const page = await context.newPage();
|
|
295
|
+
try {
|
|
296
|
+
// P0 FIX: Block all network requests to prevent SSRF from headless Chromium.
|
|
297
|
+
// Only allow data: URIs (inline images/fonts) and about:blank.
|
|
298
|
+
await page.route('**/*', (route) => {
|
|
299
|
+
const url = route.request().url();
|
|
300
|
+
if (url.startsWith('data:') || url === 'about:blank') {
|
|
301
|
+
route.continue();
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
route.abort();
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
// P0 FIX: Use domcontentloaded instead of networkidle to avoid waiting for
|
|
308
|
+
// (now-blocked) network requests and reduce attack surface
|
|
309
|
+
await page.setContent(sanitizedHtml, { waitUntil: "domcontentloaded", timeout: PAGE_TIMEOUT_MS });
|
|
310
|
+
const buffer = await page.pdf({
|
|
311
|
+
format: options?.format || "A4",
|
|
312
|
+
printBackground: true,
|
|
313
|
+
landscape: options?.landscape || false,
|
|
314
|
+
margin: { top: "1cm", bottom: "1cm", left: "1cm", right: "1cm" },
|
|
315
|
+
});
|
|
316
|
+
return Buffer.from(buffer);
|
|
317
|
+
}
|
|
318
|
+
finally {
|
|
319
|
+
await page.close().catch(() => { });
|
|
320
|
+
await context.close().catch(() => { });
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
async function callCapMonster(taskType, params) {
|
|
324
|
+
const apiKey = process.env.CAPMONSTER_API_KEY;
|
|
325
|
+
if (!apiKey)
|
|
326
|
+
throw new Error("CAPMONSTER_API_KEY not configured");
|
|
327
|
+
const createResp = await fetch("https://api.capmonster.cloud/createTask", {
|
|
328
|
+
method: "POST",
|
|
329
|
+
headers: { "Content-Type": "application/json" },
|
|
330
|
+
body: JSON.stringify({ clientKey: apiKey, task: { type: taskType, ...params } }),
|
|
331
|
+
});
|
|
332
|
+
const createData = await createResp.json();
|
|
333
|
+
if (createData.errorId)
|
|
334
|
+
throw new Error(`CapMonster create: ${createData.errorDescription}`);
|
|
335
|
+
const taskId = createData.taskId;
|
|
336
|
+
// Poll for result (max 120s, 2s intervals)
|
|
337
|
+
for (let i = 0; i < 60; i++) {
|
|
338
|
+
await new Promise(r => setTimeout(r, 2000));
|
|
339
|
+
const resultResp = await fetch("https://api.capmonster.cloud/getTaskResult", {
|
|
340
|
+
method: "POST",
|
|
341
|
+
headers: { "Content-Type": "application/json" },
|
|
342
|
+
body: JSON.stringify({ clientKey: apiKey, taskId }),
|
|
343
|
+
});
|
|
344
|
+
const resultData = await resultResp.json();
|
|
345
|
+
if (resultData.status === "ready") {
|
|
346
|
+
return resultData.solution?.gRecaptchaResponse || resultData.solution?.token || resultData.solution?.cf_clearance || "";
|
|
347
|
+
}
|
|
348
|
+
if (resultData.errorId)
|
|
349
|
+
throw new Error(`CapMonster result: ${resultData.errorDescription}`);
|
|
350
|
+
}
|
|
351
|
+
throw new Error("CapMonster: timeout waiting for solution (120s)");
|
|
352
|
+
}
|
|
353
|
+
async function detectAndSolveCaptcha(page) {
|
|
354
|
+
try {
|
|
355
|
+
// Skip if no API key configured
|
|
356
|
+
if (!process.env.CAPMONSTER_API_KEY)
|
|
357
|
+
return { solved: false };
|
|
358
|
+
const pageUrl = page.url();
|
|
359
|
+
// Detect reCAPTCHA v2
|
|
360
|
+
const recaptchaKey = await page.evaluate(() => {
|
|
361
|
+
const el = document.querySelector('.g-recaptcha[data-sitekey], [data-sitekey]');
|
|
362
|
+
if (el && el.classList.contains('g-recaptcha'))
|
|
363
|
+
return el.getAttribute('data-sitekey');
|
|
364
|
+
// Also check for reCAPTCHA iframe
|
|
365
|
+
const iframe = document.querySelector('iframe[src*="recaptcha"]');
|
|
366
|
+
if (iframe) {
|
|
367
|
+
const src = iframe.getAttribute('src') || '';
|
|
368
|
+
const match = src.match(/[?&]k=([^&]+)/);
|
|
369
|
+
return match ? match[1] : null;
|
|
370
|
+
}
|
|
371
|
+
return null;
|
|
372
|
+
}).catch(() => null);
|
|
373
|
+
if (recaptchaKey) {
|
|
374
|
+
console.log(`[browser] Detected reCAPTCHA v2 on ${pageUrl}`);
|
|
375
|
+
const token = await callCapMonster("RecaptchaV2TaskProxyless", {
|
|
376
|
+
websiteURL: pageUrl,
|
|
377
|
+
websiteKey: recaptchaKey,
|
|
378
|
+
});
|
|
379
|
+
await page.evaluate((t) => {
|
|
380
|
+
const textarea = document.getElementById('g-recaptcha-response');
|
|
381
|
+
if (textarea) {
|
|
382
|
+
textarea.style.display = 'block';
|
|
383
|
+
textarea.value = t;
|
|
384
|
+
}
|
|
385
|
+
// Try triggering the callback
|
|
386
|
+
const win = window;
|
|
387
|
+
try {
|
|
388
|
+
const cfg = win.___grecaptcha_cfg;
|
|
389
|
+
if (cfg?.clients) {
|
|
390
|
+
const clients = cfg.clients;
|
|
391
|
+
const firstKey = Object.keys(clients)[0];
|
|
392
|
+
const first = firstKey ? clients[firstKey] : null;
|
|
393
|
+
if (first) {
|
|
394
|
+
const findCallback = (obj, depth = 0) => {
|
|
395
|
+
if (depth > 4 || !obj || typeof obj !== 'object')
|
|
396
|
+
return null;
|
|
397
|
+
for (const val of Object.values(obj)) {
|
|
398
|
+
if (typeof val === 'function')
|
|
399
|
+
return val;
|
|
400
|
+
const found = findCallback(val, depth + 1);
|
|
401
|
+
if (found)
|
|
402
|
+
return found;
|
|
403
|
+
}
|
|
404
|
+
return null;
|
|
405
|
+
};
|
|
406
|
+
const cb = findCallback(first);
|
|
407
|
+
if (cb)
|
|
408
|
+
cb(t);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
catch { /* callback search failed, form submit will work */ }
|
|
413
|
+
}, token);
|
|
414
|
+
return { solved: true, type: "reCAPTCHA v2" };
|
|
415
|
+
}
|
|
416
|
+
// Detect hCaptcha
|
|
417
|
+
const hcaptchaKey = await page.evaluate(() => {
|
|
418
|
+
const el = document.querySelector('.h-captcha[data-sitekey]');
|
|
419
|
+
return el?.getAttribute('data-sitekey') || null;
|
|
420
|
+
}).catch(() => null);
|
|
421
|
+
if (hcaptchaKey) {
|
|
422
|
+
console.log(`[browser] Detected hCaptcha on ${pageUrl}`);
|
|
423
|
+
const token = await callCapMonster("HCaptchaTaskProxyless", {
|
|
424
|
+
websiteURL: pageUrl,
|
|
425
|
+
websiteKey: hcaptchaKey,
|
|
426
|
+
});
|
|
427
|
+
await page.evaluate((t) => {
|
|
428
|
+
const textarea = document.querySelector('[name="h-captcha-response"], [name="g-recaptcha-response"]');
|
|
429
|
+
if (textarea) {
|
|
430
|
+
textarea.value = t;
|
|
431
|
+
}
|
|
432
|
+
const win = window;
|
|
433
|
+
if (typeof win.hcaptchaCallback === 'function') {
|
|
434
|
+
win.hcaptchaCallback(t);
|
|
435
|
+
}
|
|
436
|
+
}, token);
|
|
437
|
+
return { solved: true, type: "hCaptcha" };
|
|
438
|
+
}
|
|
439
|
+
// Detect Cloudflare Turnstile
|
|
440
|
+
const turnstileKey = await page.evaluate(() => {
|
|
441
|
+
const el = document.querySelector('.cf-turnstile[data-sitekey]');
|
|
442
|
+
return el?.getAttribute('data-sitekey') || null;
|
|
443
|
+
}).catch(() => null);
|
|
444
|
+
if (turnstileKey) {
|
|
445
|
+
console.log(`[browser] Detected Turnstile on ${pageUrl}`);
|
|
446
|
+
const token = await callCapMonster("TurnstileTaskProxyless", {
|
|
447
|
+
websiteURL: pageUrl,
|
|
448
|
+
websiteKey: turnstileKey,
|
|
449
|
+
});
|
|
450
|
+
await page.evaluate((t) => {
|
|
451
|
+
const input = document.querySelector('[name="cf-turnstile-response"]');
|
|
452
|
+
if (input)
|
|
453
|
+
input.value = t;
|
|
454
|
+
const win = window;
|
|
455
|
+
if (typeof win.turnstileCallback === 'function') {
|
|
456
|
+
win.turnstileCallback(t);
|
|
457
|
+
}
|
|
458
|
+
}, token);
|
|
459
|
+
return { solved: true, type: "Turnstile" };
|
|
460
|
+
}
|
|
461
|
+
return { solved: false };
|
|
462
|
+
}
|
|
463
|
+
catch (err) {
|
|
464
|
+
console.log(`[browser] CAPTCHA detection/solving failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
465
|
+
return { solved: false };
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
// ============================================================================
|
|
469
|
+
// MAIN HANDLER
|
|
470
|
+
// ============================================================================
|
|
471
|
+
export async function handleBrowser(_sb, args, _storeId) {
|
|
472
|
+
const action = args.action;
|
|
473
|
+
const url = args.url;
|
|
474
|
+
const selector = args.selector;
|
|
475
|
+
const value = args.value;
|
|
476
|
+
const script = args.script;
|
|
477
|
+
const waitFor = args.wait_for;
|
|
478
|
+
if (!action) {
|
|
479
|
+
return { success: false, error: "action is required" };
|
|
480
|
+
}
|
|
481
|
+
if (!url) {
|
|
482
|
+
return { success: false, error: "url is required" };
|
|
483
|
+
}
|
|
484
|
+
// SSRF check
|
|
485
|
+
const blocked = isBlockedUrl(url);
|
|
486
|
+
if (blocked) {
|
|
487
|
+
return { success: false, error: blocked };
|
|
488
|
+
}
|
|
489
|
+
try {
|
|
490
|
+
switch (action) {
|
|
491
|
+
case "navigate":
|
|
492
|
+
return await actionNavigate(url, waitFor);
|
|
493
|
+
case "screenshot":
|
|
494
|
+
return await actionScreenshot(url, waitFor);
|
|
495
|
+
case "extract":
|
|
496
|
+
return await actionExtract(url, selector, waitFor);
|
|
497
|
+
case "click":
|
|
498
|
+
return await actionClick(url, selector, waitFor);
|
|
499
|
+
case "fill":
|
|
500
|
+
return await actionFill(url, selector, value, waitFor);
|
|
501
|
+
case "evaluate":
|
|
502
|
+
return await actionEvaluate(url, script, waitFor);
|
|
503
|
+
case "pdf":
|
|
504
|
+
return await actionPdf(url, waitFor);
|
|
505
|
+
default:
|
|
506
|
+
return { success: false, error: `Unknown browser action: ${action}. Valid: navigate, screenshot, extract, click, fill, evaluate, pdf` };
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
catch (err) {
|
|
510
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
511
|
+
// Clean up common Playwright error noise
|
|
512
|
+
const cleanMessage = message
|
|
513
|
+
.replace(/=========================== logs ===========================[^]*?============================================================/gs, "")
|
|
514
|
+
.trim();
|
|
515
|
+
return { success: false, error: `Browser error: ${cleanMessage.substring(0, 500)}` };
|
|
516
|
+
}
|
|
517
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { SupabaseClient } from "@supabase/supabase-js";
|
|
2
|
+
export declare function handleProducts(sb: SupabaseClient, args: Record<string, unknown>, storeId?: string): Promise<{
|
|
3
|
+
success: boolean;
|
|
4
|
+
error: string;
|
|
5
|
+
data?: undefined;
|
|
6
|
+
count?: undefined;
|
|
7
|
+
} | {
|
|
8
|
+
data: any[];
|
|
9
|
+
limit?: {} | undefined;
|
|
10
|
+
success: boolean;
|
|
11
|
+
total: number | null;
|
|
12
|
+
count: number;
|
|
13
|
+
offset: number;
|
|
14
|
+
error?: undefined;
|
|
15
|
+
} | {
|
|
16
|
+
success: boolean;
|
|
17
|
+
data: any;
|
|
18
|
+
error?: undefined;
|
|
19
|
+
count?: undefined;
|
|
20
|
+
} | {
|
|
21
|
+
success: boolean;
|
|
22
|
+
count: number;
|
|
23
|
+
data: {
|
|
24
|
+
id: any;
|
|
25
|
+
name: any;
|
|
26
|
+
slug: any;
|
|
27
|
+
description: any;
|
|
28
|
+
icon: any;
|
|
29
|
+
parent_id: any;
|
|
30
|
+
display_order: any;
|
|
31
|
+
is_active: any;
|
|
32
|
+
featured: any;
|
|
33
|
+
product_count: any;
|
|
34
|
+
catalog_id: any;
|
|
35
|
+
field_schema_id: any;
|
|
36
|
+
created_at: any;
|
|
37
|
+
}[];
|
|
38
|
+
error?: undefined;
|
|
39
|
+
} | {
|
|
40
|
+
success: boolean;
|
|
41
|
+
count: number;
|
|
42
|
+
data: {
|
|
43
|
+
id: any;
|
|
44
|
+
name: any;
|
|
45
|
+
slug: any;
|
|
46
|
+
description: any;
|
|
47
|
+
icon: any;
|
|
48
|
+
fields: any;
|
|
49
|
+
is_public: any;
|
|
50
|
+
is_active: any;
|
|
51
|
+
catalog_id: any;
|
|
52
|
+
store_id: any;
|
|
53
|
+
install_count: any;
|
|
54
|
+
created_at: any;
|
|
55
|
+
}[];
|
|
56
|
+
error?: undefined;
|
|
57
|
+
} | {
|
|
58
|
+
success: boolean;
|
|
59
|
+
count: number;
|
|
60
|
+
data: {
|
|
61
|
+
id: any;
|
|
62
|
+
name: any;
|
|
63
|
+
slug: any;
|
|
64
|
+
description: any;
|
|
65
|
+
tiers: any;
|
|
66
|
+
quality_tier: any;
|
|
67
|
+
is_public: any;
|
|
68
|
+
is_active: any;
|
|
69
|
+
catalog_id: any;
|
|
70
|
+
store_id: any;
|
|
71
|
+
install_count: any;
|
|
72
|
+
created_at: any;
|
|
73
|
+
}[];
|
|
74
|
+
error?: undefined;
|
|
75
|
+
} | {
|
|
76
|
+
success: boolean;
|
|
77
|
+
count: number;
|
|
78
|
+
data: {
|
|
79
|
+
id: any;
|
|
80
|
+
name: any;
|
|
81
|
+
slug: any;
|
|
82
|
+
description: any;
|
|
83
|
+
vertical: any;
|
|
84
|
+
is_active: any;
|
|
85
|
+
is_default: any;
|
|
86
|
+
display_order: any;
|
|
87
|
+
created_at: any;
|
|
88
|
+
}[];
|
|
89
|
+
error?: undefined;
|
|
90
|
+
}>;
|
|
91
|
+
export declare function handleCollections(sb: SupabaseClient, args: Record<string, unknown>, storeId?: string): Promise<{
|
|
92
|
+
success: boolean;
|
|
93
|
+
error: string;
|
|
94
|
+
data?: undefined;
|
|
95
|
+
} | {
|
|
96
|
+
success: boolean;
|
|
97
|
+
data: any;
|
|
98
|
+
error?: undefined;
|
|
99
|
+
}>;
|