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.
Files changed (37) hide show
  1. package/README.md +5 -3
  2. package/bundled-skills/007/scripts/full_audit.py +6 -4
  3. package/bundled-skills/007/scripts/score_calculator.py +67 -7
  4. package/bundled-skills/algorithmic-art/templates/viewer.html +2 -2
  5. package/bundled-skills/apify-actorization/SKILL.md +1 -2
  6. package/bundled-skills/apify-actorization/references/cli-actorization.md +4 -4
  7. package/bundled-skills/docs/COMMUNITY_GUIDELINES.md +1 -1
  8. package/bundled-skills/docs/contributors/community-guidelines.md +3 -32
  9. package/bundled-skills/docs/integrations/jetski-gemini-loader/loader.ts +21 -3
  10. package/bundled-skills/docs/maintainers/security-findings-triage-2026-03-18-addendum.md +22 -0
  11. package/bundled-skills/docs/users/getting-started.md +1 -1
  12. package/bundled-skills/docs/users/walkthrough.md +21 -17
  13. package/bundled-skills/dotnet-backend-patterns/resources/implementation-playbook.md +2 -2
  14. package/bundled-skills/instagram/scripts/auth.py +15 -6
  15. package/bundled-skills/landing-page-generator/SKILL.md +203 -0
  16. package/bundled-skills/landing-page-generator/references/conversion-patterns.md +176 -0
  17. package/bundled-skills/landing-page-generator/references/frameworks.md +177 -0
  18. package/bundled-skills/landing-page-generator/references/landing-page-patterns.md +98 -0
  19. package/bundled-skills/landing-page-generator/references/seo-checklist.md +109 -0
  20. package/bundled-skills/landing-page-generator/scripts/landing_page_scaffolder.py +568 -0
  21. package/bundled-skills/loki-mode/examples/todo-app-generated/backend/package-lock.json +33 -1073
  22. package/bundled-skills/loki-mode/examples/todo-app-generated/backend/package.json +7 -4
  23. package/bundled-skills/loki-mode/examples/todo-app-generated/backend/src/db/migrations.ts +15 -3
  24. package/bundled-skills/loki-mode/examples/todo-app-generated/backend/src/routes/todos.ts +85 -88
  25. package/bundled-skills/loki-mode/examples/todo-app-generated/frontend/package-lock.json +260 -456
  26. package/bundled-skills/loki-mode/examples/todo-app-generated/frontend/package.json +4 -2
  27. package/bundled-skills/notebooklm/scripts/auth_manager.py +17 -3
  28. package/bundled-skills/notebooklm/scripts/browser_session.py +11 -2
  29. package/bundled-skills/radix-ui-design-system/examples/README.md +1 -1
  30. package/bundled-skills/whatsapp-cloud-api/assets/boilerplate/nodejs/src/webhook-handler.ts +5 -3
  31. package/bundled-skills/whatsapp-cloud-api/assets/boilerplate/python/app.py +21 -13
  32. package/bundled-skills/whatsapp-cloud-api/assets/boilerplate/python/webhook_handler.py +11 -4
  33. package/package.json +1 -1
  34. package/bundled-skills/loki-mode/examples/todo-app-generated/backend/src/db/db.ts +0 -35
  35. /package/bundled-skills/dotnet-backend-patterns/assets/{repository-template.cs → repository-template.cs.template} +0 -0
  36. /package/bundled-skills/dotnet-backend-patterns/assets/{service-template.cs → service-template.cs.template} +0 -0
  37. /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": "^6.4.1"
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 "notebooklm.google.com" in page.url and "accounts.google.com" not in page.url:
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 "notebooklm.google.com" in page.url and "accounts.google.com" not in page.url:
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" in self.page.url:
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.sendStatus(403);
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
- print(f"Message from {from_number}: [{content['type']}] {content.get('text', '')}")
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
- # TODO: Implement your message handling logic here
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, f"Recebi sua mensagem: \"{content['text']}\"")
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, f"Voce selecionou: {content['text']}")
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, f"Voce escolheu: {content['text']}")
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, f"Recebi sua midia ({content['type']}).")
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
- print(f"Status update: {status['id']} -> {status['status']}")
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
- print(f"Message delivery failed: {errors}")
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
- print(f"WhatsApp webhook server running on port {port}")
113
- print(f"Webhook URL: http://localhost:{port}/webhook")
114
- print(f"Health check: http://localhost:{port}/health")
115
- app.run(host="0.0.0.0", port=port, debug=True)
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 Request, abort, request
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 == "subscribe" and req_token == token:
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,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-skills-antigravity",
3
- "version": "0.0.7",
3
+ "version": "0.0.9",
4
4
  "description": "OpenCode CLI plugin that automatically downloads and keeps Antigravity Awesome Skills up to date.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -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;