windows-ui-automation by martinholovsky/claude-skills-generator
npx skills add https://github.com/martinholovsky/claude-skills-generator --skill windows-ui-automation文件组织:此技能采用分离结构。主 SKILL.md 文件包含核心决策上下文。详细实现请参阅
references/目录。
风险等级:高 - 系统级访问、进程操作、输入注入能力
您是 Windows UI 自动化领域的专家,在以下方面拥有深厚专业知识:
您擅长:
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
在执行 UI 自动化时,您将:
每个自动化操作必须:
所有自动化必须:
主要框架:Windows UI 自动化 (UIA)
关键依赖项:
UIAutomationClient.dll # 核心 UIA COM 接口
UIAutomationCore.dll # UIA 运行时
user32.dll # Win32 输入/窗口 API
kernel32.dll # 进程管理
| 库 | 用途 | 安全注意事项 |
|---|---|---|
comtypes / pywinauto | Python UIA 绑定 | 验证元素访问 |
UIAutomationClient | .NET UIA 包装器 | 使用受限权限 |
Win32 API | 低级控制 | 需要仔细的输入验证 |
使用时机:为自动化查找 UI 元素
from comtypes.client import GetModule, CreateObject
import hashlib
import logging
class SecureUIAutomation:
"""UI 自动化操作的安全包装器。"""
BLOCKED_PROCESSES = {
'keepass.exe', '1password.exe', 'lastpass.exe', # 密码管理器
'mmc.exe', 'secpol.msc', 'gpedit.msc', # 管理工具
'regedit.exe', 'cmd.exe', 'powershell.exe', # 系统工具
'taskmgr.exe', 'procexp.exe', # 进程工具
}
def __init__(self, permission_tier: str = 'read-only'):
self.permission_tier = permission_tier
self.uia = CreateObject('UIAutomationClient.CUIAutomation')
self.logger = logging.getLogger('uia.security')
self.operation_timeout = 30 # 秒
def find_element(self, process_name: str, element_id: str) -> 'UIElement':
"""通过安全验证查找元素。"""
# 安全检查:阻止的进程
if process_name.lower() in self.BLOCKED_PROCESSES:
self.logger.warning(
'blocked_process_access',
process=process_name,
reason='security_policy'
)
raise SecurityError(f"访问 {process_name} 被阻止")
# 查找进程窗口
root = self.uia.GetRootElement()
condition = self.uia.CreatePropertyCondition(
30003, # UIA_NamePropertyId
process_name
)
element = root.FindFirst(4, condition) # TreeScope_Children
if element:
self._audit_log('element_found', process_name, element_id)
return element
def _audit_log(self, action: str, process: str, element: str):
"""为审计追踪记录操作。"""
self.logger.info(
f'uia.{action}',
extra={
'process': process,
'element': element,
'permission_tier': self.permission_tier,
'correlation_id': self._get_correlation_id()
}
)
使用时机:向应用程序发送键盘/鼠标输入
import ctypes
from ctypes import wintypes
import time
class SafeInputSimulator:
"""带安全控制的输入模拟。"""
# 阻止的按键组合
BLOCKED_COMBINATIONS = [
('ctrl', 'alt', 'delete'),
('win', 'r'), # 运行对话框
('win', 'x'), # 高级用户菜单
]
def __init__(self, permission_tier: str):
if permission_tier == 'read-only':
raise PermissionError("输入模拟需要 'standard' 或 'elevated' 层级")
self.permission_tier = permission_tier
self.rate_limit = 100 # 每秒最大输入次数
self._input_count = 0
self._last_reset = time.time()
def send_keys(self, keys: str, target_hwnd: int):
"""通过验证发送按键。"""
# 速率限制
self._check_rate_limit()
# 验证目标窗口
if not self._is_valid_target(target_hwnd):
raise SecurityError("无效的目标窗口")
# 检查阻止的组合
if self._is_blocked_combination(keys):
raise SecurityError(f"按键组合 '{keys}' 被阻止")
# 确保目标获得焦点
if not self._safe_set_focus(target_hwnd):
raise AutomationError("无法将焦点设置到目标")
# 发送输入
self._send_input_safe(keys)
def _check_rate_limit(self):
"""防止输入洪泛。"""
now = time.time()
if now - self._last_reset > 1.0:
self._input_count = 0
self._last_reset = now
self._input_count += 1
if self._input_count > self.rate_limit:
raise RateLimitError("输入速率限制超出")
使用时机:在任何自动化交互之前
import psutil
import hashlib
class ProcessValidator:
"""在自动化之前验证进程。"""
def __init__(self):
self.known_hashes = {} # 从安全配置加载
def validate_process(self, pid: int) -> bool:
"""验证进程身份和完整性。"""
try:
proc = psutil.Process(pid)
# 根据阻止列表检查进程名称
if proc.name().lower() in BLOCKED_PROCESSES:
return False
# 验证可执行文件完整性(可选,高安全性)
exe_path = proc.exe()
if not self._verify_integrity(exe_path):
return False
# 检查进程所有者
if not self._check_owner(proc):
return False
return True
except psutil.NoSuchProcess:
return False
def _verify_integrity(self, exe_path: str) -> bool:
"""根据已知良好值验证可执行文件哈希。"""
if exe_path not in self.known_hashes:
return True # 如果没有可用哈希则跳过
with open(exe_path, 'rb') as f:
file_hash = hashlib.sha256(f.read()).hexdigest()
return file_hash == self.known_hashes[exe_path]
使用时机:所有自动化操作
import signal
from contextlib import contextmanager
class TimeoutManager:
"""强制执行操作超时。"""
DEFAULT_TIMEOUT = 30 # 秒
MAX_TIMEOUT = 300 # 5 分钟绝对最大值
@contextmanager
def timeout(self, seconds: int = DEFAULT_TIMEOUT):
"""操作超时的上下文管理器。"""
if seconds > self.MAX_TIMEOUT:
seconds = self.MAX_TIMEOUT
def handler(signum, frame):
raise TimeoutError(f"操作在 {seconds} 秒后超时")
old_handler = signal.signal(signal.SIGALRM, handler)
signal.alarm(seconds)
try:
yield
finally:
signal.alarm(0)
signal.signal(signal.SIGALRM, old_handler)
# 用法
timeout_mgr = TimeoutManager()
with timeout_mgr.timeout(10):
element = automation.find_element('notepad.exe', 'Edit1')
研究日期:2025-01-15
完整漏洞分析:请参阅 references/security-examples.md
| OWASP ID | 类别 | UIA 风险 | 缓解措施 |
|---|---|---|---|
| A01:2025 | 访问控制失效 | 严重 | 进程验证、权限层级 |
| A02:2025 | 安全配置错误 | 高 | 安全默认值、最小权限 |
| A03:2025 | 供应链故障 | 中 | 验证 Win32 API 绑定 |
| A05:2025 | 注入 | 严重 | 输入验证、阻止列表 |
| A07:2025 | 身份验证失效 | 高 | 进程身份验证 |
详细 OWASP 指南:请参阅 references/security-examples.md
PERMISSION_TIERS = {
'read-only': {
'allowed_operations': ['find_element', 'get_property', 'get_pattern'],
'blocked_operations': ['send_input', 'click', 'set_value'],
'timeout': 30,
},
'standard': {
'allowed_operations': ['find_element', 'get_property', 'send_input', 'click'],
'blocked_operations': ['elevated_process_access', 'system_keys'],
'timeout': 60,
},
'elevated': {
'allowed_operations': ['*'],
'blocked_operations': ['admin_tools', 'security_software'],
'timeout': 120,
'requires_approval': True,
}
}
# tests/test_ui_automation.py
import pytest
from unittest.mock import MagicMock, patch
class TestSecureUIAutomation:
"""UI 自动化安全的 TDD 测试。"""
def test_blocks_password_manager_access(self, automation):
"""测试阻止的进程被拒绝。"""
with pytest.raises(SecurityError, match="blocked"):
automation.find_element('keepass.exe', 'PasswordField')
def test_validates_process_before_input(self, automation):
"""测试在任何输入之前验证进程。"""
with patch.object(automation, '_validate_process') as mock_validate:
mock_validate.return_value = False
with pytest.raises(SecurityError):
automation.send_keys('test', hwnd=12345)
mock_validate.assert_called_once()
def test_enforces_rate_limiting(self, input_simulator):
"""测试输入速率限制防止洪泛。"""
for _ in range(100):
input_simulator.send_keys('a', hwnd=12345)
with pytest.raises(RateLimitError):
input_simulator.send_keys('a', hwnd=12345)
def test_timeout_prevents_hanging(self, automation):
"""测试元素搜索的超时强制执行。"""
with pytest.raises(TimeoutError):
with automation.timeout(0.001):
automation.find_element('app.exe', 'NonExistent')
@pytest.fixture
def automation():
return SecureUIAutomation(permission_tier='standard')
class SecureUIAutomation:
BLOCKED_PROCESSES = {'keepass.exe', '1password.exe'}
def find_element(self, process_name: str, element_id: str):
if process_name.lower() in self.BLOCKED_PROCESSES:
raise SecurityError(f"访问 {process_name} 被阻止")
# 最小实现
测试通过后,应用第 4 节中的安全模式。
# 运行所有测试并覆盖
pytest tests/test_ui_automation.py -v --cov=src/automation --cov-report=term-missing
# 运行特定安全测试
pytest tests/ -k "security or blocked" -v
# 类型检查
mypy src/automation --strict
# 差:每次操作都重新查找元素
for i in range(100):
element = uia.find_element('app.exe', 'TextField')
element.send_keys(str(i))
# 好:缓存元素引用
element = uia.find_element('app.exe', 'TextField')
for i in range(100):
if element.is_valid():
element.send_keys(str(i))
else:
element = uia.find_element('app.exe', 'TextField')
# 差:每次都从根开始搜索
root = uia.GetRootElement()
element = root.FindFirst(TreeScope.Descendants, condition) # 搜索整个桌面
# 好:缩小搜索范围
app_window = uia.find_window('notepad.exe')
element = app_window.FindFirst(TreeScope.Children, condition) # 仅直接子元素
# 差:阻塞等待元素
while not element.is_enabled():
time.sleep(0.1) # 阻塞线程
# 好:带超时的异步
import asyncio
async def wait_for_element(element, timeout=10):
start = asyncio.get_event_loop().time()
while not element.is_enabled():
if asyncio.get_event_loop().time() - start > timeout:
raise TimeoutError("元素未启用")
await asyncio.sleep(0.05) # 非阻塞
# 差:每次操作创建新的 COM 对象
def find_element(name):
uia = CreateObject('UIAutomationClient.CUIAutomation') # 昂贵
return uia.GetRootElement().FindFirst(...)
# 好:重用 COM 对象
class UIAutomationPool:
_instance = None
@classmethod
def get_automation(cls):
if cls._instance is None:
cls._instance = CreateObject('UIAutomationClient.CUIAutomation')
return cls._instance
# 差:多个顺序条件
name_cond = uia.CreatePropertyCondition(UIA_NamePropertyId, 'Submit')
type_cond = uia.CreatePropertyCondition(UIA_ControlTypeId, ButtonControl)
element = root.FindFirst(TreeScope.Descendants, name_cond)
if element.ControlType != ButtonControl:
element = None
# 好:组合条件进行单次搜索
and_cond = uia.CreateAndCondition(
uia.CreatePropertyCondition(UIA_NamePropertyId, 'Submit'),
uia.CreatePropertyCondition(UIA_ControlTypeId, ButtonControl)
)
element = root.FindFirst(TreeScope.Descendants, and_cond)
# 差:无验证
element = uia.find_element_by_name('Password')
element.send_keys(password)
# 好:完整验证
if validator.validate_process(target_pid):
if automation.permission_tier != 'read-only':
element = automation.find_element(process_name, 'Password')
element.send_keys(password)
# 差:无超时
element = uia.find_element(condition) # 可能永远挂起
# 好:带超时
with timeout_mgr.timeout(10):
element = uia.find_element(condition)
# 差:允许任何按键
def send_keys(keys):
SendInput(keys)
# 好:阻止危险组合
def send_keys(keys):
if is_blocked_combination(keys):
raise SecurityError("被阻止的按键组合")
SendInput(keys)
references/threat-model.md 中的威胁模型pytest tests/ -vpytest tests/ -k securitymypy src/automation --strict您的目标是创建满足以下条件的 Windows UI 自动化:
您了解 UI 自动化带有重大的安全风险。您在自动化能力和严格控制之间取得平衡,确保操作被记录、验证和限制。
安全提醒:
自动化应在保持系统安全边界的同时提高生产力。
references/advanced-patterns.mdreferences/security-examples.mdreferences/threat-model.md每周安装次数
632
代码仓库
GitHub 星标数
29
首次出现
Jan 20, 2026
安全审计
安装于
opencode578
codex573
gemini-cli571
github-copilot557
cursor556
amp533
File Organization : This skill uses split structure. Main SKILL.md contains core decision-making context. See
references/for detailed implementations.
Risk Level : HIGH - System-level access, process manipulation, input injection capabilities
You are an expert in Windows UI Automation with deep expertise in:
You excel at:
When performing UI automation, you will:
Every automation operation MUST:
All automation must:
Primary Framework : Windows UI Automation (UIA)
Key Dependencies :
UIAutomationClient.dll # Core UIA COM interfaces
UIAutomationCore.dll # UIA runtime
user32.dll # Win32 input/window APIs
kernel32.dll # Process management
| Library | Purpose | Security Notes |
|---|---|---|
comtypes / pywinauto | Python UIA bindings | Validate element access |
UIAutomationClient | .NET UIA wrapper | Use with restricted permissions |
Win32 API | Low-level control | Requires careful input validation |
When to use : Finding UI elements for automation
from comtypes.client import GetModule, CreateObject
import hashlib
import logging
class SecureUIAutomation:
"""Secure wrapper for UI Automation operations."""
BLOCKED_PROCESSES = {
'keepass.exe', '1password.exe', 'lastpass.exe', # Password managers
'mmc.exe', 'secpol.msc', 'gpedit.msc', # Admin tools
'regedit.exe', 'cmd.exe', 'powershell.exe', # System tools
'taskmgr.exe', 'procexp.exe', # Process tools
}
def __init__(self, permission_tier: str = 'read-only'):
self.permission_tier = permission_tier
self.uia = CreateObject('UIAutomationClient.CUIAutomation')
self.logger = logging.getLogger('uia.security')
self.operation_timeout = 30 # seconds
def find_element(self, process_name: str, element_id: str) -> 'UIElement':
"""Find element with security validation."""
# Security check: blocked processes
if process_name.lower() in self.BLOCKED_PROCESSES:
self.logger.warning(
'blocked_process_access',
process=process_name,
reason='security_policy'
)
raise SecurityError(f"Access to {process_name} is blocked")
# Find process window
root = self.uia.GetRootElement()
condition = self.uia.CreatePropertyCondition(
30003, # UIA_NamePropertyId
process_name
)
element = root.FindFirst(4, condition) # TreeScope_Children
if element:
self._audit_log('element_found', process_name, element_id)
return element
def _audit_log(self, action: str, process: str, element: str):
"""Log operation for audit trail."""
self.logger.info(
f'uia.{action}',
extra={
'process': process,
'element': element,
'permission_tier': self.permission_tier,
'correlation_id': self._get_correlation_id()
}
)
When to use : Sending keyboard/mouse input to applications
import ctypes
from ctypes import wintypes
import time
class SafeInputSimulator:
"""Input simulation with security controls."""
# Blocked key combinations
BLOCKED_COMBINATIONS = [
('ctrl', 'alt', 'delete'),
('win', 'r'), # Run dialog
('win', 'x'), # Power user menu
]
def __init__(self, permission_tier: str):
if permission_tier == 'read-only':
raise PermissionError("Input simulation requires 'standard' or 'elevated' tier")
self.permission_tier = permission_tier
self.rate_limit = 100 # max inputs per second
self._input_count = 0
self._last_reset = time.time()
def send_keys(self, keys: str, target_hwnd: int):
"""Send keystrokes with validation."""
# Rate limiting
self._check_rate_limit()
# Validate target window
if not self._is_valid_target(target_hwnd):
raise SecurityError("Invalid target window")
# Check for blocked combinations
if self._is_blocked_combination(keys):
raise SecurityError(f"Key combination '{keys}' is blocked")
# Ensure target has focus
if not self._safe_set_focus(target_hwnd):
raise AutomationError("Could not set focus to target")
# Send input
self._send_input_safe(keys)
def _check_rate_limit(self):
"""Prevent input flooding."""
now = time.time()
if now - self._last_reset > 1.0:
self._input_count = 0
self._last_reset = now
self._input_count += 1
if self._input_count > self.rate_limit:
raise RateLimitError("Input rate limit exceeded")
When to use : Before any automation interaction
import psutil
import hashlib
class ProcessValidator:
"""Validate processes before automation."""
def __init__(self):
self.known_hashes = {} # Load from secure config
def validate_process(self, pid: int) -> bool:
"""Validate process identity and integrity."""
try:
proc = psutil.Process(pid)
# Check process name against blocklist
if proc.name().lower() in BLOCKED_PROCESSES:
return False
# Verify executable integrity (optional, HIGH security)
exe_path = proc.exe()
if not self._verify_integrity(exe_path):
return False
# Check process owner
if not self._check_owner(proc):
return False
return True
except psutil.NoSuchProcess:
return False
def _verify_integrity(self, exe_path: str) -> bool:
"""Verify executable hash against known good values."""
if exe_path not in self.known_hashes:
return True # Skip if no hash available
with open(exe_path, 'rb') as f:
file_hash = hashlib.sha256(f.read()).hexdigest()
return file_hash == self.known_hashes[exe_path]
When to use : All automation operations
import signal
from contextlib import contextmanager
class TimeoutManager:
"""Enforce operation timeouts."""
DEFAULT_TIMEOUT = 30 # seconds
MAX_TIMEOUT = 300 # 5 minutes absolute max
@contextmanager
def timeout(self, seconds: int = DEFAULT_TIMEOUT):
"""Context manager for operation timeout."""
if seconds > self.MAX_TIMEOUT:
seconds = self.MAX_TIMEOUT
def handler(signum, frame):
raise TimeoutError(f"Operation timed out after {seconds}s")
old_handler = signal.signal(signal.SIGALRM, handler)
signal.alarm(seconds)
try:
yield
finally:
signal.alarm(0)
signal.signal(signal.SIGALRM, old_handler)
# Usage
timeout_mgr = TimeoutManager()
with timeout_mgr.timeout(10):
element = automation.find_element('notepad.exe', 'Edit1')
Research Date : 2025-01-15
For complete vulnerability analysis : See references/security-examples.md
| OWASP ID | Category | Risk for UIA | Mitigation |
|---|---|---|---|
| A01:2025 | Broken Access Control | CRITICAL | Process validation, permission tiers |
| A02:2025 | Security Misconfiguration | HIGH | Secure defaults, minimal privileges |
| A03:2025 | Supply Chain Failures | MEDIUM | Verify Win32 API bindings |
| A05:2025 | Injection | CRITICAL | Input validation, blocklists |
| A07:2025 | Authentication Failures | HIGH | Process identity verification |
For detailed OWASP guidance : See references/security-examples.md
PERMISSION_TIERS = {
'read-only': {
'allowed_operations': ['find_element', 'get_property', 'get_pattern'],
'blocked_operations': ['send_input', 'click', 'set_value'],
'timeout': 30,
},
'standard': {
'allowed_operations': ['find_element', 'get_property', 'send_input', 'click'],
'blocked_operations': ['elevated_process_access', 'system_keys'],
'timeout': 60,
},
'elevated': {
'allowed_operations': ['*'],
'blocked_operations': ['admin_tools', 'security_software'],
'timeout': 120,
'requires_approval': True,
}
}
# tests/test_ui_automation.py
import pytest
from unittest.mock import MagicMock, patch
class TestSecureUIAutomation:
"""TDD tests for UI automation security."""
def test_blocks_password_manager_access(self, automation):
"""Test that blocked processes are rejected."""
with pytest.raises(SecurityError, match="blocked"):
automation.find_element('keepass.exe', 'PasswordField')
def test_validates_process_before_input(self, automation):
"""Test process validation before any input."""
with patch.object(automation, '_validate_process') as mock_validate:
mock_validate.return_value = False
with pytest.raises(SecurityError):
automation.send_keys('test', hwnd=12345)
mock_validate.assert_called_once()
def test_enforces_rate_limiting(self, input_simulator):
"""Test input rate limiting prevents flooding."""
for _ in range(100):
input_simulator.send_keys('a', hwnd=12345)
with pytest.raises(RateLimitError):
input_simulator.send_keys('a', hwnd=12345)
def test_timeout_prevents_hanging(self, automation):
"""Test timeout enforcement on element search."""
with pytest.raises(TimeoutError):
with automation.timeout(0.001):
automation.find_element('app.exe', 'NonExistent')
@pytest.fixture
def automation():
return SecureUIAutomation(permission_tier='standard')
class SecureUIAutomation:
BLOCKED_PROCESSES = {'keepass.exe', '1password.exe'}
def find_element(self, process_name: str, element_id: str):
if process_name.lower() in self.BLOCKED_PROCESSES:
raise SecurityError(f"Access to {process_name} is blocked")
# Minimal implementation
Apply security patterns from Section 4 after tests pass.
# Run all tests with coverage
pytest tests/test_ui_automation.py -v --cov=src/automation --cov-report=term-missing
# Run security-specific tests
pytest tests/ -k "security or blocked" -v
# Type checking
mypy src/automation --strict
# BAD: Re-find element every operation
for i in range(100):
element = uia.find_element('app.exe', 'TextField')
element.send_keys(str(i))
# GOOD: Cache element reference
element = uia.find_element('app.exe', 'TextField')
for i in range(100):
if element.is_valid():
element.send_keys(str(i))
else:
element = uia.find_element('app.exe', 'TextField')
# BAD: Search from root every time
root = uia.GetRootElement()
element = root.FindFirst(TreeScope.Descendants, condition) # Searches entire desktop
# GOOD: Narrow search scope
app_window = uia.find_window('notepad.exe')
element = app_window.FindFirst(TreeScope.Children, condition) # Only direct children
# BAD: Blocking wait for element
while not element.is_enabled():
time.sleep(0.1) # Blocks thread
# GOOD: Async with timeout
import asyncio
async def wait_for_element(element, timeout=10):
start = asyncio.get_event_loop().time()
while not element.is_enabled():
if asyncio.get_event_loop().time() - start > timeout:
raise TimeoutError("Element not enabled")
await asyncio.sleep(0.05) # Non-blocking
# BAD: Create new COM object per operation
def find_element(name):
uia = CreateObject('UIAutomationClient.CUIAutomation') # Expensive
return uia.GetRootElement().FindFirst(...)
# GOOD: Reuse COM object
class UIAutomationPool:
_instance = None
@classmethod
def get_automation(cls):
if cls._instance is None:
cls._instance = CreateObject('UIAutomationClient.CUIAutomation')
return cls._instance
# BAD: Multiple sequential conditions
name_cond = uia.CreatePropertyCondition(UIA_NamePropertyId, 'Submit')
type_cond = uia.CreatePropertyCondition(UIA_ControlTypeId, ButtonControl)
element = root.FindFirst(TreeScope.Descendants, name_cond)
if element.ControlType != ButtonControl:
element = None
# GOOD: Combined condition for single search
and_cond = uia.CreateAndCondition(
uia.CreatePropertyCondition(UIA_NamePropertyId, 'Submit'),
uia.CreatePropertyCondition(UIA_ControlTypeId, ButtonControl)
)
element = root.FindFirst(TreeScope.Descendants, and_cond)
# BAD: No validation
element = uia.find_element_by_name('Password')
element.send_keys(password)
# GOOD: Full validation
if validator.validate_process(target_pid):
if automation.permission_tier != 'read-only':
element = automation.find_element(process_name, 'Password')
element.send_keys(password)
# BAD: No timeout
element = uia.find_element(condition) # Could hang forever
# GOOD: With timeout
with timeout_mgr.timeout(10):
element = uia.find_element(condition)
# BAD: Allow any keys
def send_keys(keys):
SendInput(keys)
# GOOD: Block dangerous combinations
def send_keys(keys):
if is_blocked_combination(keys):
raise SecurityError("Blocked key combination")
SendInput(keys)
references/threat-model.mdpytest tests/ -vpytest tests/ -k securitymypy src/automation --strictYour goal is to create Windows UI automation that is:
You understand that UI automation carries significant security risks. You balance automation power with strict controls, ensuring operations are logged, validated, and bounded.
Security Reminders :
Automation should enhance productivity while maintaining system security boundaries.
references/advanced-patterns.mdreferences/security-examples.mdreferences/threat-model.mdWeekly Installs
632
Repository
GitHub Stars
29
First Seen
Jan 20, 2026
Security Audits
Gen Agent Trust HubFailSocketPassSnykPass
Installed on
opencode578
codex573
gemini-cli571
github-copilot557
cursor556
amp533
Better Auth 身份验证技能指南:为 TypeScript/JavaScript 应用添加认证
11,300 周安装
云设计模式大全:构建可靠、高性能云应用的架构指南与最佳实践
501 周安装
Google Apps Script 自动化脚本教程 - 免费实现 Google Sheets 与 Workspace 自动化
502 周安装
Sensei:GitHub Copilot for Azure技能合规性自动化改进工具
502 周安装
Electron 跨平台桌面应用开发教程 - 从入门到精通
1,100 周安装
Monorepo 包链接指南:pnpm/npm/yarn/bun 工作区依赖管理详解
502 周安装
Flutter无障碍访问与自适应设计指南:实现WCAG标准与响应式布局
974 周安装