opencode-skills-antigravity 0.0.7 → 0.0.9
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 +5 -3
- package/bundled-skills/007/scripts/full_audit.py +6 -4
- package/bundled-skills/007/scripts/score_calculator.py +67 -7
- package/bundled-skills/algorithmic-art/templates/viewer.html +2 -2
- package/bundled-skills/apify-actorization/SKILL.md +1 -2
- package/bundled-skills/apify-actorization/references/cli-actorization.md +4 -4
- package/bundled-skills/docs/COMMUNITY_GUIDELINES.md +1 -1
- package/bundled-skills/docs/contributors/community-guidelines.md +3 -32
- package/bundled-skills/docs/integrations/jetski-gemini-loader/loader.ts +21 -3
- package/bundled-skills/docs/maintainers/security-findings-triage-2026-03-18-addendum.md +22 -0
- package/bundled-skills/docs/users/getting-started.md +1 -1
- package/bundled-skills/docs/users/walkthrough.md +21 -17
- package/bundled-skills/dotnet-backend-patterns/resources/implementation-playbook.md +2 -2
- package/bundled-skills/instagram/scripts/auth.py +15 -6
- package/bundled-skills/landing-page-generator/SKILL.md +203 -0
- package/bundled-skills/landing-page-generator/references/conversion-patterns.md +176 -0
- package/bundled-skills/landing-page-generator/references/frameworks.md +177 -0
- package/bundled-skills/landing-page-generator/references/landing-page-patterns.md +98 -0
- package/bundled-skills/landing-page-generator/references/seo-checklist.md +109 -0
- package/bundled-skills/landing-page-generator/scripts/landing_page_scaffolder.py +568 -0
- package/bundled-skills/loki-mode/examples/todo-app-generated/backend/package-lock.json +33 -1073
- package/bundled-skills/loki-mode/examples/todo-app-generated/backend/package.json +7 -4
- package/bundled-skills/loki-mode/examples/todo-app-generated/backend/src/db/migrations.ts +15 -3
- package/bundled-skills/loki-mode/examples/todo-app-generated/backend/src/routes/todos.ts +85 -88
- package/bundled-skills/loki-mode/examples/todo-app-generated/frontend/package-lock.json +260 -456
- package/bundled-skills/loki-mode/examples/todo-app-generated/frontend/package.json +4 -2
- package/bundled-skills/notebooklm/scripts/auth_manager.py +17 -3
- package/bundled-skills/notebooklm/scripts/browser_session.py +11 -2
- package/bundled-skills/radix-ui-design-system/examples/README.md +1 -1
- package/bundled-skills/whatsapp-cloud-api/assets/boilerplate/nodejs/src/webhook-handler.ts +5 -3
- package/bundled-skills/whatsapp-cloud-api/assets/boilerplate/python/app.py +21 -13
- package/bundled-skills/whatsapp-cloud-api/assets/boilerplate/python/webhook_handler.py +11 -4
- package/package.json +1 -1
- package/bundled-skills/loki-mode/examples/todo-app-generated/backend/src/db/db.ts +0 -35
- /package/bundled-skills/dotnet-backend-patterns/assets/{repository-template.cs → repository-template.cs.template} +0 -0
- /package/bundled-skills/dotnet-backend-patterns/assets/{service-template.cs → service-template.cs.template} +0 -0
- /package/bundled-skills/radix-ui-design-system/templates/{component-template.tsx → component-template.tsx.template} +0 -0
|
@@ -15,12 +15,14 @@
|
|
|
15
15
|
"@types/react": "^19.2.7",
|
|
16
16
|
"@types/react-dom": "^19.2.3",
|
|
17
17
|
"@vitejs/plugin-react": "^4.7.0",
|
|
18
|
-
"@vitejs/plugin-react-swc": "^3.11.0",
|
|
19
18
|
"typescript": "^5.9.3",
|
|
20
|
-
"vite": "^
|
|
19
|
+
"vite": "^7.1.12"
|
|
21
20
|
},
|
|
22
21
|
"dependencies": {
|
|
23
22
|
"react": "^19.2.3",
|
|
24
23
|
"react-dom": "^19.2.3"
|
|
24
|
+
},
|
|
25
|
+
"overrides": {
|
|
26
|
+
"rollup": "^4.59.0"
|
|
25
27
|
}
|
|
26
28
|
}
|
|
@@ -18,6 +18,7 @@ import re
|
|
|
18
18
|
import sys
|
|
19
19
|
from pathlib import Path
|
|
20
20
|
from typing import Optional, Dict, Any
|
|
21
|
+
from urllib.parse import urlparse
|
|
21
22
|
|
|
22
23
|
from patchright.sync_api import sync_playwright, BrowserContext
|
|
23
24
|
|
|
@@ -28,6 +29,19 @@ from config import BROWSER_STATE_DIR, STATE_FILE, AUTH_INFO_FILE, DATA_DIR
|
|
|
28
29
|
from browser_utils import BrowserFactory
|
|
29
30
|
|
|
30
31
|
|
|
32
|
+
def _get_hostname(url: str) -> str:
|
|
33
|
+
"""Extract a normalized hostname from a URL."""
|
|
34
|
+
try:
|
|
35
|
+
return (urlparse(url).hostname or "").lower()
|
|
36
|
+
except ValueError:
|
|
37
|
+
return ""
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _is_exact_host(url: str, expected_host: str) -> bool:
|
|
41
|
+
"""Return True when the URL hostname exactly matches the expected host."""
|
|
42
|
+
return _get_hostname(url) == expected_host
|
|
43
|
+
|
|
44
|
+
|
|
31
45
|
class AuthManager:
|
|
32
46
|
"""
|
|
33
47
|
Manages authentication and browser state for NotebookLM
|
|
@@ -114,7 +128,7 @@ class AuthManager:
|
|
|
114
128
|
page.goto("https://notebooklm.google.com", wait_until="domcontentloaded")
|
|
115
129
|
|
|
116
130
|
# Check if already authenticated
|
|
117
|
-
if
|
|
131
|
+
if _is_exact_host(page.url, "notebooklm.google.com"):
|
|
118
132
|
print(" ✅ Already authenticated!")
|
|
119
133
|
self._save_browser_state(context)
|
|
120
134
|
return True
|
|
@@ -260,7 +274,7 @@ class AuthManager:
|
|
|
260
274
|
page.goto("https://notebooklm.google.com", wait_until="domcontentloaded", timeout=30000)
|
|
261
275
|
|
|
262
276
|
# Check if we can access NotebookLM
|
|
263
|
-
if
|
|
277
|
+
if _is_exact_host(page.url, "notebooklm.google.com"):
|
|
264
278
|
print(" ✅ Authentication is valid")
|
|
265
279
|
return True
|
|
266
280
|
else:
|
|
@@ -355,4 +369,4 @@ def main():
|
|
|
355
369
|
|
|
356
370
|
|
|
357
371
|
if __name__ == "__main__":
|
|
358
|
-
main()
|
|
372
|
+
main()
|
|
@@ -9,6 +9,7 @@ import time
|
|
|
9
9
|
import sys
|
|
10
10
|
from typing import Any, Dict, Optional
|
|
11
11
|
from pathlib import Path
|
|
12
|
+
from urllib.parse import urlparse
|
|
12
13
|
|
|
13
14
|
from patchright.sync_api import BrowserContext, Page
|
|
14
15
|
|
|
@@ -18,6 +19,14 @@ sys.path.insert(0, str(Path(__file__).parent))
|
|
|
18
19
|
from browser_utils import StealthUtils
|
|
19
20
|
|
|
20
21
|
|
|
22
|
+
def _get_hostname(url: str) -> str:
|
|
23
|
+
"""Extract a normalized hostname from a URL."""
|
|
24
|
+
try:
|
|
25
|
+
return (urlparse(url).hostname or "").lower()
|
|
26
|
+
except ValueError:
|
|
27
|
+
return ""
|
|
28
|
+
|
|
29
|
+
|
|
21
30
|
class BrowserSession:
|
|
22
31
|
"""
|
|
23
32
|
Represents a single persistent browser session for NotebookLM
|
|
@@ -61,7 +70,7 @@ class BrowserSession:
|
|
|
61
70
|
self.page.goto(self.notebook_url, wait_until="domcontentloaded", timeout=30000)
|
|
62
71
|
|
|
63
72
|
# Check if login is needed
|
|
64
|
-
if "accounts.google.com"
|
|
73
|
+
if _get_hostname(self.page.url) == "accounts.google.com":
|
|
65
74
|
raise RuntimeError("Authentication required. Please run auth_manager.py setup first.")
|
|
66
75
|
|
|
67
76
|
# Wait for page to be ready
|
|
@@ -252,4 +261,4 @@ class BrowserSession:
|
|
|
252
261
|
if __name__ == "__main__":
|
|
253
262
|
# Example usage
|
|
254
263
|
print("Browser Session Module - Use ask_question.py for main interface")
|
|
255
|
-
print("This module provides low-level browser session management.")
|
|
264
|
+
print("This module provides low-level browser session management.")
|
|
@@ -59,5 +59,5 @@ These examples use CSS classes. You can:
|
|
|
59
59
|
## Learn More
|
|
60
60
|
|
|
61
61
|
- [Main SKILL.md](../SKILL.md) - Complete guide
|
|
62
|
-
- [Component Template](../templates/component-template.tsx) - Boilerplate
|
|
62
|
+
- [Component Template](../templates/component-template.tsx.template) - Boilerplate
|
|
63
63
|
- [Radix UI Docs](https://www.radix-ui.com/primitives)
|
|
@@ -2,6 +2,8 @@ import crypto from 'crypto';
|
|
|
2
2
|
import { Request, Response, NextFunction } from 'express';
|
|
3
3
|
import { WebhookPayload, IncomingMessage, StatusUpdate } from './types';
|
|
4
4
|
|
|
5
|
+
const SAFE_CHALLENGE_RE = /^[A-Za-z0-9._-]{1,200}$/;
|
|
6
|
+
|
|
5
7
|
/**
|
|
6
8
|
* Middleware para validar assinatura HMAC-SHA256 dos webhooks do WhatsApp.
|
|
7
9
|
*
|
|
@@ -66,12 +68,12 @@ export function handleWebhookVerification(verifyToken: string) {
|
|
|
66
68
|
const token = req.query['hub.verify_token'] as string;
|
|
67
69
|
const challenge = req.query['hub.challenge'] as string;
|
|
68
70
|
|
|
69
|
-
if (mode === 'subscribe' && token === verifyToken) {
|
|
71
|
+
if (mode === 'subscribe' && token === verifyToken && SAFE_CHALLENGE_RE.test(challenge)) {
|
|
70
72
|
console.log('Webhook verified successfully');
|
|
71
|
-
res.status(200).send(challenge);
|
|
73
|
+
res.type('text/plain').status(200).send(challenge);
|
|
72
74
|
} else {
|
|
73
75
|
console.warn('Webhook verification failed: invalid token');
|
|
74
|
-
res.
|
|
76
|
+
res.status(mode === 'subscribe' && token === verifyToken ? 400 : 403).send();
|
|
75
77
|
}
|
|
76
78
|
};
|
|
77
79
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""WhatsApp Cloud API - Flask Application with Webhook Handler."""
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
|
+
import logging
|
|
4
5
|
import os
|
|
5
6
|
|
|
6
7
|
from dotenv import load_dotenv
|
|
@@ -17,11 +18,17 @@ from webhook_handler import (
|
|
|
17
18
|
load_dotenv()
|
|
18
19
|
|
|
19
20
|
app = Flask(__name__)
|
|
21
|
+
logger = logging.getLogger(__name__)
|
|
20
22
|
|
|
21
23
|
# Initialize WhatsApp client
|
|
22
24
|
whatsapp = WhatsAppClient()
|
|
23
25
|
|
|
24
26
|
|
|
27
|
+
def _is_debug_enabled() -> bool:
|
|
28
|
+
"""Allow debug only when explicitly enabled for local development."""
|
|
29
|
+
return os.environ.get("FLASK_DEBUG", "").lower() in {"1", "true", "yes", "on"}
|
|
30
|
+
|
|
31
|
+
|
|
25
32
|
# === Webhook Routes ===
|
|
26
33
|
|
|
27
34
|
|
|
@@ -60,25 +67,24 @@ async def handle_incoming_message(message: dict) -> None:
|
|
|
60
67
|
from_number = message["from"]
|
|
61
68
|
content = extract_message_content(message)
|
|
62
69
|
|
|
63
|
-
|
|
70
|
+
logger.info("Received WhatsApp message")
|
|
64
71
|
|
|
65
72
|
# Mark as read
|
|
66
73
|
await whatsapp.mark_as_read(message["id"])
|
|
67
74
|
|
|
68
|
-
#
|
|
69
|
-
# Example: Echo back the message
|
|
75
|
+
# Example responses intentionally avoid reflecting user-provided content.
|
|
70
76
|
match content["type"]:
|
|
71
77
|
case "text":
|
|
72
|
-
await whatsapp.send_text(from_number,
|
|
78
|
+
await whatsapp.send_text(from_number, "Recebi sua mensagem. Como posso ajudar?")
|
|
73
79
|
|
|
74
80
|
case "button":
|
|
75
|
-
await whatsapp.send_text(from_number,
|
|
81
|
+
await whatsapp.send_text(from_number, "Recebi sua selecao com sucesso.")
|
|
76
82
|
|
|
77
83
|
case "list":
|
|
78
|
-
await whatsapp.send_text(from_number,
|
|
84
|
+
await whatsapp.send_text(from_number, "Recebi sua escolha com sucesso.")
|
|
79
85
|
|
|
80
86
|
case "image" | "document" | "video" | "audio":
|
|
81
|
-
await whatsapp.send_text(from_number,
|
|
87
|
+
await whatsapp.send_text(from_number, "Recebi sua midia com sucesso.")
|
|
82
88
|
|
|
83
89
|
case _:
|
|
84
90
|
await whatsapp.send_text(from_number, "Desculpe, nao entendi. Como posso ajudar?")
|
|
@@ -89,11 +95,11 @@ async def handle_incoming_message(message: dict) -> None:
|
|
|
89
95
|
|
|
90
96
|
def handle_status_update(status: dict) -> None:
|
|
91
97
|
"""Process a message status update."""
|
|
92
|
-
|
|
98
|
+
logger.info("WhatsApp status update id=%s status=%s", status["id"], status["status"])
|
|
93
99
|
|
|
94
100
|
if status["status"] == "failed":
|
|
95
101
|
errors = status.get("errors", [])
|
|
96
|
-
|
|
102
|
+
logger.warning("WhatsApp message delivery failed with %d error(s)", len(errors))
|
|
97
103
|
|
|
98
104
|
|
|
99
105
|
# === Health Check ===
|
|
@@ -109,7 +115,9 @@ def health():
|
|
|
109
115
|
|
|
110
116
|
if __name__ == "__main__":
|
|
111
117
|
port = int(os.environ.get("PORT", 3000))
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
118
|
+
logging.basicConfig(level=os.environ.get("LOG_LEVEL", "INFO"))
|
|
119
|
+
logger.info("WhatsApp webhook server running on port %s", port)
|
|
120
|
+
logger.info("Webhook URL: http://localhost:%s/webhook", port)
|
|
121
|
+
logger.info("Health check: http://localhost:%s/health", port)
|
|
122
|
+
# Keep debug disabled by default so the boilerplate is safe in shared environments.
|
|
123
|
+
app.run(host="0.0.0.0", port=port, debug=_is_debug_enabled())
|
|
@@ -3,10 +3,14 @@
|
|
|
3
3
|
import hashlib
|
|
4
4
|
import hmac
|
|
5
5
|
import os
|
|
6
|
+
import re
|
|
6
7
|
from functools import wraps
|
|
7
8
|
from typing import Any
|
|
8
9
|
|
|
9
|
-
from flask import
|
|
10
|
+
from flask import Response, abort, request
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
_SAFE_CHALLENGE_RE = re.compile(r"^[A-Za-z0-9._-]{1,200}$")
|
|
10
14
|
|
|
11
15
|
|
|
12
16
|
def validate_hmac_signature(app_secret: str | None = None):
|
|
@@ -52,11 +56,14 @@ def verify_webhook(verify_token: str | None = None):
|
|
|
52
56
|
req_token = request.args.get("hub.verify_token")
|
|
53
57
|
challenge = request.args.get("hub.challenge")
|
|
54
58
|
|
|
55
|
-
if mode
|
|
56
|
-
return challenge, 200
|
|
57
|
-
else:
|
|
59
|
+
if mode != "subscribe" or req_token != token:
|
|
58
60
|
abort(403, "Verification failed")
|
|
59
61
|
|
|
62
|
+
if not challenge or not _SAFE_CHALLENGE_RE.fullmatch(challenge):
|
|
63
|
+
abort(400, "Invalid challenge")
|
|
64
|
+
|
|
65
|
+
return Response(challenge, status=200, mimetype="text/plain")
|
|
66
|
+
|
|
60
67
|
|
|
61
68
|
def parse_webhook_payload(data: dict[str, Any]) -> dict[str, list]:
|
|
62
69
|
"""
|
package/package.json
CHANGED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import sqlite3 from 'sqlite3';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
|
|
4
|
-
const dbPath = path.join(__dirname, '../../todos.db');
|
|
5
|
-
|
|
6
|
-
const db = new sqlite3.Database(dbPath, (err: Error | null) => {
|
|
7
|
-
if (err) {
|
|
8
|
-
console.error('Database connection error:', err);
|
|
9
|
-
} else {
|
|
10
|
-
console.log('Connected to SQLite database');
|
|
11
|
-
}
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
// Initialize database schema
|
|
15
|
-
export const initDatabase = (): Promise<void> => {
|
|
16
|
-
return new Promise((resolve, reject) => {
|
|
17
|
-
db.run(`
|
|
18
|
-
CREATE TABLE IF NOT EXISTS todos (
|
|
19
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
20
|
-
title TEXT NOT NULL,
|
|
21
|
-
completed BOOLEAN DEFAULT 0,
|
|
22
|
-
createdAt TEXT DEFAULT CURRENT_TIMESTAMP
|
|
23
|
-
)
|
|
24
|
-
`, (err: Error | null) => {
|
|
25
|
-
if (err) {
|
|
26
|
-
reject(err);
|
|
27
|
-
} else {
|
|
28
|
-
console.log('Database schema initialized');
|
|
29
|
-
resolve();
|
|
30
|
-
}
|
|
31
|
-
});
|
|
32
|
-
});
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
export default db;
|
|
File without changes
|
|
File without changes
|
|
File without changes
|