pdd-skills 3.0.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 +1478 -0
- package/bin/pdd.js +354 -0
- package/config/bpmn-rules.yaml +166 -0
- package/config/checkstyle.xml +105 -0
- package/config/eslint.config.js +48 -0
- package/config/pmd.xml +91 -0
- package/config/prd-rules.yaml +113 -0
- package/config/ruff.toml +45 -0
- package/config/sqlfluff.cfg +82 -0
- package/hooks/hook-executor.js +332 -0
- package/index.js +43 -0
- package/lib/api-routes.js +750 -0
- package/lib/api-server.js +408 -0
- package/lib/cache/cache-config.js +209 -0
- package/lib/cache/system-cache.js +852 -0
- package/lib/config-manager.js +373 -0
- package/lib/generate.js +528 -0
- package/lib/grpc/grpc-routes.js +1134 -0
- package/lib/grpc/grpc-server.js +912 -0
- package/lib/grpc/proto-definitions.js +1033 -0
- package/lib/init.js +172 -0
- package/lib/iteration/auto-fixer.js +1025 -0
- package/lib/iteration/auto-reviewer.js +923 -0
- package/lib/iteration/controller.js +577 -0
- package/lib/list.js +130 -0
- package/lib/mcp-server.js +548 -0
- package/lib/openclaw/api-integration.js +535 -0
- package/lib/openclaw/cli-integration.js +567 -0
- package/lib/openclaw/data-sync.js +845 -0
- package/lib/openclaw/openclaw-adapter.js +783 -0
- package/lib/plugin/example-plugins/code-stats/index.js +332 -0
- package/lib/plugin/example-plugins/code-stats/plugin.json +1 -0
- package/lib/plugin/example-plugins/custom-linter/index.js +472 -0
- package/lib/plugin/example-plugins/custom-linter/plugin.json +1 -0
- package/lib/plugin/example-plugins/hello-world/index.js +86 -0
- package/lib/plugin/example-plugins/hello-world/plugin.json +1 -0
- package/lib/plugin/plugin-manager.js +655 -0
- package/lib/plugin/plugin-sdk.js +565 -0
- package/lib/plugin/sandbox.js +627 -0
- package/lib/quality/rules/maintainability.js +418 -0
- package/lib/quality/rules/performance.js +498 -0
- package/lib/quality/rules/readability.js +441 -0
- package/lib/quality/rules/robustness.js +504 -0
- package/lib/quality/rules/security.js +444 -0
- package/lib/quality/scorer.js +576 -0
- package/lib/report.js +669 -0
- package/lib/sdk-base.js +301 -0
- package/lib/sdk-js.js +446 -0
- package/lib/sdk-python/README.md +546 -0
- package/lib/sdk-python/examples/basic_usage.py +450 -0
- package/lib/sdk-python/pdd_sdk/__init__.py +180 -0
- package/lib/sdk-python/pdd_sdk/client.py +1170 -0
- package/lib/sdk-python/pdd_sdk/events.py +423 -0
- package/lib/sdk-python/pdd_sdk/exceptions.py +158 -0
- package/lib/sdk-python/pdd_sdk/models.py +518 -0
- package/lib/sdk-python/pdd_sdk/utils.py +759 -0
- package/lib/token/budget-alert.js +367 -0
- package/lib/token/budget-manager.js +485 -0
- package/lib/update.js +54 -0
- package/lib/utils/logger.js +88 -0
- package/lib/verify.js +741 -0
- package/lib/version.js +52 -0
- package/lib/vm/README.md +102 -0
- package/lib/vm/dashboard/api-routes.js +669 -0
- package/lib/vm/dashboard/server.js +391 -0
- package/lib/vm/dashboard/sse.js +358 -0
- package/lib/vm/dashboard/static/css/dashboard.css +1378 -0
- package/lib/vm/dashboard/static/index.html +118 -0
- package/lib/vm/dashboard/static/js/app.js +949 -0
- package/lib/vm/dashboard/static/js/charts.js +913 -0
- package/lib/vm/dashboard/static/js/kanban-view.js +1053 -0
- package/lib/vm/dashboard/static/js/pipeline-view.js +463 -0
- package/lib/vm/dashboard/static/js/quality-view.js +598 -0
- package/lib/vm/dashboard/static/js/system-view.js +1021 -0
- package/lib/vm/data-provider.js +1191 -0
- package/lib/vm/event-bus.js +402 -0
- package/lib/vm/hooks/extract-hook.js +307 -0
- package/lib/vm/hooks/generate-hook.js +374 -0
- package/lib/vm/hooks/hook-interface.js +458 -0
- package/lib/vm/hooks/report-hook.js +331 -0
- package/lib/vm/hooks/verify-hook.js +454 -0
- package/lib/vm/models.js +1003 -0
- package/lib/vm/reconciler.js +855 -0
- package/lib/vm/scanner.js +988 -0
- package/lib/vm/state-schema.js +955 -0
- package/lib/vm/state-store.js +733 -0
- package/lib/vm/tui/components/card.js +339 -0
- package/lib/vm/tui/components/progress-bar.js +368 -0
- package/lib/vm/tui/components/sparkline.js +327 -0
- package/lib/vm/tui/components/status-light.js +294 -0
- package/lib/vm/tui/components/table.js +370 -0
- package/lib/vm/tui/input.js +335 -0
- package/lib/vm/tui/renderer.js +548 -0
- package/lib/vm/tui/screens/kanban-screen.js +397 -0
- package/lib/vm/tui/screens/overview-screen.js +357 -0
- package/lib/vm/tui/screens/quality-screen.js +336 -0
- package/lib/vm/tui/screens/system-screen.js +379 -0
- package/lib/vm/tui/tui.js +805 -0
- package/package.json +1 -0
- package/scripts/cso-analyzer.js +198 -0
- package/scripts/eval-runner.js +359 -0
- package/scripts/i18n-checker.js +109 -0
- package/scripts/linter/activiti-linter.js +272 -0
- package/scripts/linter/prd-linter.js +162 -0
- package/scripts/linter/report-generator.js +207 -0
- package/scripts/linter/run-linters.js +285 -0
- package/scripts/linter/sql-linter.js +166 -0
- package/scripts/token-analyzer.js +162 -0
- package/scripts/vm-test.js +180 -0
- package/skills/core/official-doc-writer/LICENSE +21 -0
- package/skills/core/official-doc-writer/README.md +232 -0
- package/skills/core/official-doc-writer/SKILL.md +475 -0
- package/skills/core/official-doc-writer/_meta.json +1 -0
- package/skills/core/official-doc-writer/document_generator.py +580 -0
- package/skills/core/official-doc-writer/evals/default-evals.json +1 -0
- package/skills/core/official-doc-writer/examples.md +150 -0
- package/skills/core/official-doc-writer/fonts/FONTS_LIST.md +45 -0
- package/skills/core/official-doc-writer/fonts/README.md +141 -0
- package/skills/core/official-doc-writer/fonts/SIMFANG.TTF +0 -0
- package/skills/core/official-doc-writer/fonts/SIMHEI.TTF +0 -0
- package/skills/core/official-doc-writer/fonts/SIMKAI.TTF +0 -0
- package/skills/core/official-doc-writer/fonts/SIMSUN.TTC +0 -0
- package/skills/core/official-doc-writer/fonts//346/226/271/346/255/243/345/260/217/346/240/207/345/256/213GBK.TTF +0 -0
- package/skills/core/official-doc-writer/references/GBT_9704-2012_/345/205/232/346/224/277/346/234/272/345/205/263/345/205/254/346/226/207/346/240/274/345/274/217.md +422 -0
- package/skills/core/official-doc-writer/scripts/__pycache__/generate_official_doc.cpython-313.pyc +0 -0
- package/skills/core/official-doc-writer/scripts/dialog_manager.py +564 -0
- package/skills/core/official-doc-writer/scripts/generate_official_doc.py +252 -0
- package/skills/core/official-doc-writer/scripts/install_fonts.py +390 -0
- package/skills/core/official-doc-writer/scripts/smart_prompts.py +363 -0
- package/skills/core/pdd-ba/SKILL.md +305 -0
- package/skills/core/pdd-ba/_meta.json +1 -0
- package/skills/core/pdd-ba/evals/default-evals.json +1 -0
- package/skills/core/pdd-code-reviewer/SKILL.md +378 -0
- package/skills/core/pdd-code-reviewer/_meta.json +1 -0
- package/skills/core/pdd-code-reviewer/evals/default-evals.json +1 -0
- package/skills/core/pdd-doc-change/SKILL.md +350 -0
- package/skills/core/pdd-doc-change/_meta.json +1 -0
- package/skills/core/pdd-doc-change/evals/default-evals.json +1 -0
- package/skills/core/pdd-doc-gardener/SKILL.md +248 -0
- package/skills/core/pdd-doc-gardener/_meta.json +1 -0
- package/skills/core/pdd-doc-gardener/evals/default-evals.json +1 -0
- package/skills/core/pdd-entropy-reduction/SKILL.md +360 -0
- package/skills/core/pdd-entropy-reduction/_meta.json +1 -0
- package/skills/core/pdd-entropy-reduction/evals/default-evals.json +1 -0
- package/skills/core/pdd-entropy-reduction/references/entropy-report-template.md +287 -0
- package/skills/core/pdd-entropy-reduction/references/golden-principles.md +573 -0
- package/skills/core/pdd-entropy-reduction/scripts/entropy_scan.py +712 -0
- package/skills/core/pdd-extract-features/SKILL.md +320 -0
- package/skills/core/pdd-extract-features/_meta.json +1 -0
- package/skills/core/pdd-extract-features/evals/default-evals.json +1 -0
- package/skills/core/pdd-generate-spec/SKILL.md +418 -0
- package/skills/core/pdd-generate-spec/_meta.json +1 -0
- package/skills/core/pdd-generate-spec/evals/default-evals.json +1 -0
- package/skills/core/pdd-implement-feature/SKILL.md +332 -0
- package/skills/core/pdd-implement-feature/_meta.json +1 -0
- package/skills/core/pdd-implement-feature/evals/default-evals.json +1 -0
- package/skills/core/pdd-main/SKILL.md +540 -0
- package/skills/core/pdd-main/_meta.json +1 -0
- package/skills/core/pdd-main/evals/default-evals.json +1 -0
- package/skills/core/pdd-main/evals/evals.json +215 -0
- package/skills/core/pdd-verify-feature/SKILL.md +474 -0
- package/skills/core/pdd-verify-feature/_meta.json +1 -0
- package/skills/core/pdd-verify-feature/evals/default-evals.json +1 -0
- package/skills/core/pdd-vm/evals/default-evals.json +1 -0
- package/skills/core/traffic-accident-assessor/LICENSE +29 -0
- package/skills/core/traffic-accident-assessor/SKILL.md +439 -0
- package/skills/core/traffic-accident-assessor/evals/evals.json +1 -0
- package/skills/core/traffic-accident-assessor/references/accident-types.md +369 -0
- package/skills/core/traffic-accident-assessor/references/liability-rules.md +287 -0
- package/skills/core/traffic-accident-assessor/references/traffic-laws.md +226 -0
- package/skills/core/traffic-accident-assessor/references//351/253/230/345/260/224/345/244/253/350/257/264/346/230/216/344/271/246.pdf +32576 -106
- package/skills/core/traffic-accident-assessor/scripts/generate_official_statement.py +588 -0
- package/skills/core/traffic-accident-assessor/scripts/generate_report.py +495 -0
- package/skills/core/traffic-accident-assessor/scripts/generate_statement.py +528 -0
- package/skills/core/traffic-accident-assessor.zip +0 -0
- package/skills/entropy/expert-arch-enforcer/SKILL.md +292 -0
- package/skills/entropy/expert-arch-enforcer/_meta.json +1 -0
- package/skills/entropy/expert-arch-enforcer/evals/default-evals.json +1 -0
- package/skills/entropy/expert-auto-refactor/SKILL.md +327 -0
- package/skills/entropy/expert-auto-refactor/_meta.json +1 -0
- package/skills/entropy/expert-auto-refactor/evals/default-evals.json +1 -0
- package/skills/entropy/expert-code-quality/SKILL.md +468 -0
- package/skills/entropy/expert-code-quality/_meta.json +1 -0
- package/skills/entropy/expert-code-quality/evals/default-evals.json +1 -0
- package/skills/entropy/expert-code-quality/evals/evals.json +109 -0
- package/skills/entropy/expert-code-quality/references/code-smells.md +605 -0
- package/skills/entropy/expert-code-quality/references/design-patterns.md +1111 -0
- package/skills/entropy/expert-code-quality/references/refactoring-catalog.md +1281 -0
- package/skills/entropy/expert-code-quality/references/solid-principles.md +524 -0
- package/skills/entropy/expert-entropy-auditor/SKILL.md +276 -0
- package/skills/entropy/expert-entropy-auditor/_meta.json +1 -0
- package/skills/entropy/expert-entropy-auditor/evals/default-evals.json +1 -0
- package/skills/expert/expert-activiti/SKILL.md +497 -0
- package/skills/expert/expert-activiti/_meta.json +1 -0
- package/skills/expert/expert-mysql/SKILL.md +832 -0
- package/skills/expert/expert-mysql/_meta.json +1 -0
- package/skills/expert/expert-performance/SKILL.md +379 -0
- package/skills/expert/expert-performance/_meta.json +1 -0
- package/skills/expert/expert-performance/evals/default-evals.json +1 -0
- package/skills/expert/expert-ruoyi/SKILL.md +472 -0
- package/skills/expert/expert-ruoyi/_meta.json +1 -0
- package/skills/expert/expert-security/SKILL.md +1341 -0
- package/skills/expert/expert-security/_meta.json +1 -0
- package/skills/expert/expert-security/evals/default-evals.json +1 -0
- package/skills/expert/software-architect/SKILL.md +350 -0
- package/skills/expert/software-architect/_meta.json +1 -0
- package/skills/expert/software-engineer/SKILL.md +437 -0
- package/skills/expert/software-engineer/_meta.json +1 -0
- package/skills/expert/software-engineer/architecture.md +130 -0
- package/skills/expert/software-engineer/patterns.md +151 -0
- package/skills/expert/software-engineer/testing.md +135 -0
- package/skills/expert/system-architect/SKILL.md +628 -0
- package/skills/expert/system-architect/_meta.json +1 -0
- package/skills/expert/system-architect/assets/templates/ARCHITECTURE.md +25 -0
- package/skills/expert/system-architect/assets/templates/README.md +44 -0
- package/skills/expert/system-architect/references/js-ts-standards.md +18 -0
- package/skills/expert/system-architect/references/python-standards.md +19 -0
- package/skills/expert/system-architect/references/scaffolding.md +61 -0
- package/skills/expert/system-architect/references/security-checklist.md +21 -0
- package/skills/openspec/openspec-apply-change/SKILL.md +156 -0
- package/skills/openspec/openspec-apply-change/_meta.json +1 -0
- package/skills/openspec/openspec-archive-change/SKILL.md +114 -0
- package/skills/openspec/openspec-archive-change/_meta.json +1 -0
- package/skills/openspec/openspec-bulk-archive-change/SKILL.md +246 -0
- package/skills/openspec/openspec-bulk-archive-change/_meta.json +1 -0
- package/skills/openspec/openspec-continue-change/SKILL.md +118 -0
- package/skills/openspec/openspec-continue-change/_meta.json +1 -0
- package/skills/openspec/openspec-explore/SKILL.md +288 -0
- package/skills/openspec/openspec-explore/_meta.json +1 -0
- package/skills/openspec/openspec-ff-change/SKILL.md +101 -0
- package/skills/openspec/openspec-ff-change/_meta.json +1 -0
- package/skills/openspec/openspec-new-change/SKILL.md +74 -0
- package/skills/openspec/openspec-new-change/_meta.json +1 -0
- package/skills/openspec/openspec-onboard/SKILL.md +554 -0
- package/skills/openspec/openspec-onboard/_meta.json +1 -0
- package/skills/openspec/openspec-sync-specs/SKILL.md +138 -0
- package/skills/openspec/openspec-sync-specs/_meta.json +1 -0
- package/skills/openspec/openspec-verify-change/SKILL.md +168 -0
- package/skills/openspec/openspec-verify-change/_meta.json +1 -0
- package/skills/pr/pdd-multi-review/SKILL.md +534 -0
- package/skills/pr/pdd-multi-review/_meta.json +1 -0
- package/skills/pr/pdd-pr-batch/SKILL.md +303 -0
- package/skills/pr/pdd-pr-batch/_meta.json +1 -0
- package/skills/pr/pdd-pr-create/SKILL.md +344 -0
- package/skills/pr/pdd-pr-create/_meta.json +1 -0
- package/skills/pr/pdd-pr-merge/SKILL.md +286 -0
- package/skills/pr/pdd-pr-merge/_meta.json +1 -0
- package/skills/pr/pdd-pr-review/SKILL.md +217 -0
- package/skills/pr/pdd-pr-review/_meta.json +1 -0
- package/skills/pr/pdd-task-manager/SKILL.md +636 -0
- package/skills/pr/pdd-task-manager/_meta.json +1 -0
- package/skills/pr/pdd-template-engine/SKILL.md +306 -0
- package/skills/pr/pdd-template-engine/_meta.json +1 -0
- package/templates/behavior-shaping/iron-law-template.md +87 -0
- package/templates/behavior-shaping/rationalization-template.md +62 -0
- package/templates/behavior-shaping/red-flags-template.md +70 -0
- package/templates/bilingual-template.md +139 -0
- package/templates/config/default.yaml +47 -0
- package/templates/project/default/README.md +31 -0
- package/templates/project/frontend/README.md +46 -0
- package/templates/project/java/README.md +48 -0
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
"""
|
|
2
|
+
PDD SDK 事件系统
|
|
3
|
+
|
|
4
|
+
提供发布-订阅模式的事件发射器,用于 SDK 内部状态通知和用户自定义监听。
|
|
5
|
+
支持同步/异步回调、一次性事件监听和错误处理。
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import asyncio
|
|
9
|
+
from typing import Callable, Any, Dict, List, Optional, Set
|
|
10
|
+
from datetime import datetime
|
|
11
|
+
from functools import wraps
|
|
12
|
+
import threading
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# ==================== 预定义事件常量 ====================
|
|
16
|
+
|
|
17
|
+
class Events:
|
|
18
|
+
"""
|
|
19
|
+
预定义事件类型常量
|
|
20
|
+
|
|
21
|
+
所有 SDK 内部事件的统一命名空间,避免字符串硬编码导致的拼写错误。
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
# 请求生命周期
|
|
25
|
+
REQUEST_START = "request:start"
|
|
26
|
+
"""请求开始发送"""
|
|
27
|
+
REQUEST_END = "request:end"
|
|
28
|
+
"""请求成功完成"""
|
|
29
|
+
REQUEST_ERROR = "request:error"
|
|
30
|
+
"""请求发生错误"""
|
|
31
|
+
|
|
32
|
+
# 重试机制
|
|
33
|
+
RETRY = "retry"
|
|
34
|
+
"""正在重试请求"""
|
|
35
|
+
RETRY_EXHAUSTED = "retry:exhausted"
|
|
36
|
+
"""重试次数耗尽"""
|
|
37
|
+
|
|
38
|
+
# 缓存
|
|
39
|
+
CACHE_HIT = "cache:hit"
|
|
40
|
+
"""缓存命中"""
|
|
41
|
+
CACHE_MISS = "cache:miss"
|
|
42
|
+
"""缓存未命中"""
|
|
43
|
+
CACHE_EXPIRED = "cache:expired"
|
|
44
|
+
"""缓存过期"""
|
|
45
|
+
|
|
46
|
+
# 批量操作
|
|
47
|
+
BATCH_START = "batch:start"
|
|
48
|
+
"""批量操作开始"""
|
|
49
|
+
BATCH_PROGRESS = "batch:progress"
|
|
50
|
+
"""批量操作进度更新"""
|
|
51
|
+
BATCH_COMPLETE = "batch:complete"
|
|
52
|
+
"""批量操作完成"""
|
|
53
|
+
|
|
54
|
+
# 会话管理
|
|
55
|
+
SESSION_CREATED = "session:created"
|
|
56
|
+
"""会话创建成功"""
|
|
57
|
+
SESSION_UPDATED = "session:updated"
|
|
58
|
+
"""会话信息更新"""
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
# 类型别名,便于使用
|
|
62
|
+
EventHandler = Callable[..., Any]
|
|
63
|
+
"""事件处理器函数类型"""
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class EventEmitter:
|
|
67
|
+
"""
|
|
68
|
+
事件发射器
|
|
69
|
+
|
|
70
|
+
实现发布-订阅模式,支持同步和异步回调函数注册。
|
|
71
|
+
|
|
72
|
+
Attributes:
|
|
73
|
+
_listeners: 事件名到处理器列表的映射
|
|
74
|
+
_once_listeners: 一次性事件处理器映射
|
|
75
|
+
_max_listeners: 单个事件最大监听器数量(防止内存泄漏)
|
|
76
|
+
_lock: 线程安全锁
|
|
77
|
+
|
|
78
|
+
Example:
|
|
79
|
+
>>> emitter = EventEmitter()
|
|
80
|
+
>>> def on_request(data):
|
|
81
|
+
... print(f"请求完成: {data}")
|
|
82
|
+
>>> emitter.on("request:end", on_request)
|
|
83
|
+
>>> emitter.emit("request:end", {"method": "GET", "path": "/api/status"})
|
|
84
|
+
请求完成: {'method': 'GET', 'path': '/api/status'}
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
DEFAULT_MAX_LISTENERS = 10
|
|
88
|
+
"""默认最大监听器数量"""
|
|
89
|
+
|
|
90
|
+
def __init__(self, max_listeners: int = DEFAULT_MAX_LISTENERS):
|
|
91
|
+
"""
|
|
92
|
+
初始化事件发射器
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
max_listeners: 每个事件允许的最大监听器数量,
|
|
96
|
+
超过此数量会发出警告(默认 10)
|
|
97
|
+
"""
|
|
98
|
+
self._listeners: Dict[str, List[EventHandler]] = {}
|
|
99
|
+
self._once_listeners: Dict[str, List[EventHandler]] = {}
|
|
100
|
+
self._max_listeners = max_listeners
|
|
101
|
+
self._lock = threading.Lock()
|
|
102
|
+
self._event_history: List[Dict[str, Any]] = []
|
|
103
|
+
self._history_max_size = 100
|
|
104
|
+
|
|
105
|
+
def on(self, event: str, callback: EventHandler) -> "EventEmitter":
|
|
106
|
+
"""
|
|
107
|
+
注册事件监听器
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
event: 事件名称
|
|
111
|
+
callback: 回调函数,接收事件数据作为参数
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
自身,支持链式调用
|
|
115
|
+
|
|
116
|
+
Raises:
|
|
117
|
+
TypeError: 如果回调不是可调用对象
|
|
118
|
+
ValueError: 如果监听器数量超过限制
|
|
119
|
+
|
|
120
|
+
Example:
|
|
121
|
+
>>> emitter.on("request:end", lambda e: print(e))
|
|
122
|
+
"""
|
|
123
|
+
if not callable(callback):
|
|
124
|
+
raise TypeError(f"事件处理器必须是可调用对象,收到: {type(callback)}")
|
|
125
|
+
|
|
126
|
+
with self._lock:
|
|
127
|
+
if event not in self._listeners:
|
|
128
|
+
self._listeners[event] = []
|
|
129
|
+
|
|
130
|
+
current_count = len(self._listeners[event]) + len(
|
|
131
|
+
self._once_listeners.get(event, [])
|
|
132
|
+
)
|
|
133
|
+
if current_count >= self._max_listeners:
|
|
134
|
+
import warnings
|
|
135
|
+
warnings.warn(
|
|
136
|
+
f"事件 '{event}' 的监听器数量 ({current_count}) "
|
|
137
|
+
f"可能超过限制 ({self._max_listeners})",
|
|
138
|
+
RuntimeWarning
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
self._listeners[event].append(callback)
|
|
142
|
+
|
|
143
|
+
return self
|
|
144
|
+
|
|
145
|
+
def off(self, event: str, callback: Optional[EventHandler] = None) -> "EventEmitter":
|
|
146
|
+
"""
|
|
147
|
+
移除事件监听器
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
event: 事件名称
|
|
151
|
+
callback: 要移除的回调函数。如果为 None,则移除该事件的所有监听器。
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
自身,支持链式调用
|
|
155
|
+
|
|
156
|
+
Example:
|
|
157
|
+
>>> emitter.off("request:end", my_handler)
|
|
158
|
+
>>> emitter.off("request:end") # 移除所有监听器
|
|
159
|
+
"""
|
|
160
|
+
with self._lock:
|
|
161
|
+
if callback is None:
|
|
162
|
+
# 移除该事件的所有监听器
|
|
163
|
+
self._listeners.pop(event, None)
|
|
164
|
+
self._once_listeners.pop(event, None)
|
|
165
|
+
else:
|
|
166
|
+
if event in self._listeners:
|
|
167
|
+
self._listeners[event] = [
|
|
168
|
+
cb for cb in self._listeners[event] if cb != callback
|
|
169
|
+
]
|
|
170
|
+
# 清理空列表
|
|
171
|
+
if not self._listeners[event]:
|
|
172
|
+
del self._listeners[event]
|
|
173
|
+
|
|
174
|
+
if event in self._once_listeners:
|
|
175
|
+
self._once_listeners[event] = [
|
|
176
|
+
cb for cb in self._once_listeners[event] if cb != callback
|
|
177
|
+
]
|
|
178
|
+
if not self._once_listeners[event]:
|
|
179
|
+
del self._once_listeners[event]
|
|
180
|
+
|
|
181
|
+
return self
|
|
182
|
+
|
|
183
|
+
def once(self, event: str, callback: EventHandler) -> "EventEmitter":
|
|
184
|
+
"""
|
|
185
|
+
注册一次性事件监听器
|
|
186
|
+
|
|
187
|
+
该监听器在首次触发后自动移除。
|
|
188
|
+
|
|
189
|
+
Args:
|
|
190
|
+
event: 事件名称
|
|
191
|
+
callback: 回调函数
|
|
192
|
+
|
|
193
|
+
Returns:
|
|
194
|
+
自身,支持链式调用
|
|
195
|
+
|
|
196
|
+
Example:
|
|
197
|
+
>>> emitter.once("session:created", lambda e: print(f"新会话: {e}"))
|
|
198
|
+
"""
|
|
199
|
+
if not callable(callback):
|
|
200
|
+
raise TypeError(f"事件处理器必须是可调用对象,收到: {type(callback)}")
|
|
201
|
+
|
|
202
|
+
with self._lock:
|
|
203
|
+
if event not in self._once_listeners:
|
|
204
|
+
self._once_listeners[event] = []
|
|
205
|
+
self._once_listeners[event].append(callback)
|
|
206
|
+
|
|
207
|
+
return self
|
|
208
|
+
|
|
209
|
+
def emit(self, event: str, *args: Any, **kwargs: Any) -> None:
|
|
210
|
+
"""
|
|
211
|
+
触发事件
|
|
212
|
+
|
|
213
|
+
同步调用所有已注册的监听器。
|
|
214
|
+
|
|
215
|
+
Args:
|
|
216
|
+
event: 事件名称
|
|
217
|
+
*args: 位置参数传递给监听器
|
|
218
|
+
**kwargs: 关键字参数传递给监听器
|
|
219
|
+
|
|
220
|
+
Note:
|
|
221
|
+
- 监听器执行异常不会影响其他监听器的执行
|
|
222
|
+
- 一次性监听器在触发后自动移除
|
|
223
|
+
"""
|
|
224
|
+
# 记录事件历史
|
|
225
|
+
self._record_event(event, args, kwargs)
|
|
226
|
+
|
|
227
|
+
# 收集要执行的监听器
|
|
228
|
+
listeners_to_call: List[EventHandler] = []
|
|
229
|
+
once_listeners_to_remove: List[EventHandler] = []
|
|
230
|
+
|
|
231
|
+
with self._lock:
|
|
232
|
+
# 常规监听器
|
|
233
|
+
if event in self._listeners:
|
|
234
|
+
listeners_to_call.extend(self._listeners[event])
|
|
235
|
+
|
|
236
|
+
# 一次性监听器
|
|
237
|
+
if event in self._once_listeners:
|
|
238
|
+
listeners_to_call.extend(self._once_listeners[event])
|
|
239
|
+
once_listeners_to_remove.extend(self._once_listeners[event])
|
|
240
|
+
|
|
241
|
+
# 执行常规监听器
|
|
242
|
+
for listener in listeners_to_call:
|
|
243
|
+
try:
|
|
244
|
+
listener(*args, **kwargs)
|
|
245
|
+
except Exception as e:
|
|
246
|
+
# 监听器异常不应影响其他监听器和主流程
|
|
247
|
+
self._handle_listener_error(event, listener, e)
|
|
248
|
+
|
|
249
|
+
# 移除已触发的一次性监听器
|
|
250
|
+
if once_listeners_to_remove:
|
|
251
|
+
with self._lock:
|
|
252
|
+
for cb in once_listeners_to_remove:
|
|
253
|
+
if event in self._once_listeners and cb in self._once_listeners[event]:
|
|
254
|
+
self._once_listeners[event].remove(cb)
|
|
255
|
+
if event in self._once_listeners and not self._once_listeners[event]:
|
|
256
|
+
del self._once_listeners[event]
|
|
257
|
+
|
|
258
|
+
async def async_emit(self, event: str, *args: Any, **kwargs: Any) -> None:
|
|
259
|
+
"""
|
|
260
|
+
异步触发事件
|
|
261
|
+
|
|
262
|
+
支持异步回调函数的并发执行。
|
|
263
|
+
|
|
264
|
+
Args:
|
|
265
|
+
event: 事件名称
|
|
266
|
+
*args: 位置参数
|
|
267
|
+
**kwargs: 关键字参数
|
|
268
|
+
"""
|
|
269
|
+
# 记录事件历史
|
|
270
|
+
self._record_event(event, args, kwargs)
|
|
271
|
+
|
|
272
|
+
# 收集要执行的监听器
|
|
273
|
+
listeners_to_call: List[EventHandler] = []
|
|
274
|
+
once_listeners_to_remove: List[EventHandler] = []
|
|
275
|
+
|
|
276
|
+
with self._lock:
|
|
277
|
+
if event in self._listeners:
|
|
278
|
+
listeners_to_call.extend(self._listeners[event])
|
|
279
|
+
if event in self._once_listeners:
|
|
280
|
+
listeners_to_call.extend(self._once_listeners[event])
|
|
281
|
+
once_listeners_to_remove.extend(self._once_listeners[event])
|
|
282
|
+
|
|
283
|
+
# 并发执行所有监听器
|
|
284
|
+
tasks = []
|
|
285
|
+
for listener in listeners_to_call:
|
|
286
|
+
try:
|
|
287
|
+
result = listener(*args, **kwargs)
|
|
288
|
+
if asyncio.iscoroutine(result):
|
|
289
|
+
tasks.append(result)
|
|
290
|
+
else:
|
|
291
|
+
# 同步函数直接等待完成
|
|
292
|
+
pass
|
|
293
|
+
except Exception as e:
|
|
294
|
+
self._handle_listener_error(event, listener, e)
|
|
295
|
+
|
|
296
|
+
# 并发执行异步任务
|
|
297
|
+
if tasks:
|
|
298
|
+
await asyncio.gather(*tasks, return_exceptions=True)
|
|
299
|
+
|
|
300
|
+
# 清理一次性监听器
|
|
301
|
+
if once_listeners_to_remove:
|
|
302
|
+
with self._lock:
|
|
303
|
+
for cb in once_listeners_to_remove:
|
|
304
|
+
if event in self._once_listeners and cb in self._once_listeners[event]:
|
|
305
|
+
self._once_listeners[event].remove(cb)
|
|
306
|
+
if event in self._once_listeners and not self._once_listeners[event]:
|
|
307
|
+
del self._once_listeners[event]
|
|
308
|
+
|
|
309
|
+
def listener_count(self, event: str) -> int:
|
|
310
|
+
"""
|
|
311
|
+
获取指定事件的监听器数量
|
|
312
|
+
|
|
313
|
+
Args:
|
|
314
|
+
event: 事件名称
|
|
315
|
+
|
|
316
|
+
Returns:
|
|
317
|
+
监听器总数(包括一次性监听器)
|
|
318
|
+
"""
|
|
319
|
+
with self._lock:
|
|
320
|
+
count = len(self._listeners.get(event, []))
|
|
321
|
+
count += len(self._once_listeners.get(event, []))
|
|
322
|
+
return count
|
|
323
|
+
|
|
324
|
+
def event_names(self) -> List[str]:
|
|
325
|
+
"""
|
|
326
|
+
获取所有已注册的事件名称
|
|
327
|
+
|
|
328
|
+
Returns:
|
|
329
|
+
事件名称列表
|
|
330
|
+
"""
|
|
331
|
+
with self._lock:
|
|
332
|
+
names = set(self._listeners.keys()) | set(self._once_listeners.keys())
|
|
333
|
+
return list(names)
|
|
334
|
+
|
|
335
|
+
def remove_all_listeners(self, event: Optional[str] = None) -> "EventEmitter":
|
|
336
|
+
"""
|
|
337
|
+
移除所有监听器
|
|
338
|
+
|
|
339
|
+
Args:
|
|
340
|
+
event: 如果指定,只移除该事件的监听器;
|
|
341
|
+
如果为 None,移除所有事件的所有监听器。
|
|
342
|
+
|
|
343
|
+
Returns:
|
|
344
|
+
自身,支持链式调用
|
|
345
|
+
"""
|
|
346
|
+
with self._lock:
|
|
347
|
+
if event is None:
|
|
348
|
+
self._listeners.clear()
|
|
349
|
+
self._once_listeners.clear()
|
|
350
|
+
else:
|
|
351
|
+
self._listeners.pop(event, None)
|
|
352
|
+
self._once_listeners.pop(event, None)
|
|
353
|
+
return self
|
|
354
|
+
|
|
355
|
+
def get_event_history(self, limit: int = 20) -> List[Dict[str, Any]]:
|
|
356
|
+
"""
|
|
357
|
+
获取最近的事件历史记录
|
|
358
|
+
|
|
359
|
+
Args:
|
|
360
|
+
limit: 返回的最大记录数
|
|
361
|
+
|
|
362
|
+
Returns:
|
|
363
|
+
事件记录列表
|
|
364
|
+
"""
|
|
365
|
+
with self._lock:
|
|
366
|
+
return list(reversed(self._event_history[-limit:]))
|
|
367
|
+
|
|
368
|
+
def clear_history(self) -> None:
|
|
369
|
+
"""清空事件历史记录"""
|
|
370
|
+
with self._lock:
|
|
371
|
+
self._event_history.clear()
|
|
372
|
+
|
|
373
|
+
def _record_event(self, event: str, args: tuple, kwargs: dict) -> None:
|
|
374
|
+
"""内部方法:记录事件到历史"""
|
|
375
|
+
record = {
|
|
376
|
+
"event": event,
|
|
377
|
+
"timestamp": datetime.now().isoformat(),
|
|
378
|
+
"args_count": len(args),
|
|
379
|
+
"kwargs_keys": list(kwargs.keys())
|
|
380
|
+
}
|
|
381
|
+
with self._lock:
|
|
382
|
+
self._event_history.append(record)
|
|
383
|
+
# 限制历史大小
|
|
384
|
+
if len(self._event_history) > self._history_max_size:
|
|
385
|
+
self._event_history = self._event_history[-self._history_max_size:]
|
|
386
|
+
|
|
387
|
+
@staticmethod
|
|
388
|
+
def _handle_listener_error(event: str, listener: EventHandler, error: Exception) -> None:
|
|
389
|
+
"""内部方法:处理监听器执行错误"""
|
|
390
|
+
# 使用简单的 stderr 输出,避免循环依赖
|
|
391
|
+
import sys
|
|
392
|
+
print(
|
|
393
|
+
f"[PDD SDK Event] 监听器在事件 '{event}' 中抛出异常: {error}",
|
|
394
|
+
file=sys.stderr
|
|
395
|
+
)
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
# ==================== 便捷装饰器 ====================
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
def event_handler(emitter: EventEmitter, event: str):
|
|
402
|
+
"""
|
|
403
|
+
事件处理器装饰器
|
|
404
|
+
|
|
405
|
+
将普通函数注册为指定事件的处理器。
|
|
406
|
+
|
|
407
|
+
Args:
|
|
408
|
+
emitter: 事件发射器实例
|
|
409
|
+
event: 要监听的事件名称
|
|
410
|
+
|
|
411
|
+
Example:
|
|
412
|
+
>>> emitter = EventEmitter()
|
|
413
|
+
>>> @event_handler(emitter, "request:end")
|
|
414
|
+
... def log_request(data):
|
|
415
|
+
... print(f"请求完成: {data['path']}")
|
|
416
|
+
"""
|
|
417
|
+
def decorator(func: EventHandler) -> EventHandler:
|
|
418
|
+
emitter.on(event, func)
|
|
419
|
+
@wraps(func)
|
|
420
|
+
def wrapper(*args, **kwargs):
|
|
421
|
+
return func(*args, **kwargs)
|
|
422
|
+
return wrapper
|
|
423
|
+
return decorator
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"""
|
|
2
|
+
PDD SDK 自定义异常体系
|
|
3
|
+
|
|
4
|
+
提供层次化的异常类,用于精确捕获和处理不同类型的错误。
|
|
5
|
+
所有异常都继承自 PDDError 基类,便于统一处理。
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Optional, Any, Dict
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class PDDError(Exception):
|
|
12
|
+
"""
|
|
13
|
+
PDD SDK 基础异常类
|
|
14
|
+
|
|
15
|
+
所有 PDD SDK 异常的基类,包含通用的错误信息和元数据。
|
|
16
|
+
|
|
17
|
+
Attributes:
|
|
18
|
+
message: 错误描述信息
|
|
19
|
+
code: 错误代码(可选)
|
|
20
|
+
details: 额外的错误详情(可选)
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def __init__(
|
|
24
|
+
self,
|
|
25
|
+
message: str = "PDD SDK 发生未知错误",
|
|
26
|
+
code: Optional[str] = None,
|
|
27
|
+
details: Optional[Dict[str, Any]] = None
|
|
28
|
+
):
|
|
29
|
+
super().__init__(message)
|
|
30
|
+
self.message = message
|
|
31
|
+
self.code = code
|
|
32
|
+
self.details = details or {}
|
|
33
|
+
|
|
34
|
+
def __str__(self) -> str:
|
|
35
|
+
"""返回格式化的错误信息"""
|
|
36
|
+
parts = [self.message]
|
|
37
|
+
if self.code:
|
|
38
|
+
parts.append(f"[{self.code}]")
|
|
39
|
+
if self.details:
|
|
40
|
+
detail_str = ", ".join(f"{k}={v}" for k, v in self.details.items())
|
|
41
|
+
parts.append(f"({detail_str})")
|
|
42
|
+
return " ".join(parts)
|
|
43
|
+
|
|
44
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
45
|
+
"""
|
|
46
|
+
将异常转换为字典格式
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
包含异常信息的字典
|
|
50
|
+
"""
|
|
51
|
+
return {
|
|
52
|
+
"type": self.__class__.__name__,
|
|
53
|
+
"message": self.message,
|
|
54
|
+
"code": self.code,
|
|
55
|
+
"details": self.details
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class ConnectionError(PDDError):
|
|
60
|
+
"""
|
|
61
|
+
连接失败异常
|
|
62
|
+
|
|
63
|
+
当无法连接到 PDD 服务端时抛出。
|
|
64
|
+
|
|
65
|
+
Example:
|
|
66
|
+
>>> raise ConnectionError(
|
|
67
|
+
... message="无法连接到服务端",
|
|
68
|
+
... details={"endpoint": "http://localhost:3000", "reason": "Connection refused"}
|
|
69
|
+
... )
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
def __init__(self, message: str = "无法连接到 PDD 服务端", **kwargs):
|
|
73
|
+
super().__init__(message=message, code="CONNECTION_ERROR", **kwargs)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class AuthError(PDDError):
|
|
77
|
+
"""
|
|
78
|
+
认证失败异常
|
|
79
|
+
|
|
80
|
+
当 API Key 无效或缺失时抛出。
|
|
81
|
+
|
|
82
|
+
Example:
|
|
83
|
+
>>> raise AuthError(
|
|
84
|
+
... message="API Key 无效",
|
|
85
|
+
... details={"status_code": 401}
|
|
86
|
+
... )
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
def __init__(self, message: str = "认证失败,请检查 API Key", **kwargs):
|
|
90
|
+
super().__init__(message=message, code="AUTH_ERROR", **kwargs)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class ValidationError(PDDError):
|
|
94
|
+
"""
|
|
95
|
+
参数验证失败异常
|
|
96
|
+
|
|
97
|
+
当请求参数不符合要求时抛出。
|
|
98
|
+
|
|
99
|
+
Example:
|
|
100
|
+
>>> raise ValidationError(
|
|
101
|
+
... message="参数验证失败",
|
|
102
|
+
... details={"field": "prd_path", "reason": "文件不存在"}
|
|
103
|
+
... )
|
|
104
|
+
"""
|
|
105
|
+
|
|
106
|
+
def __init__(self, message: str = "请求参数验证失败", **kwargs):
|
|
107
|
+
super().__init__(message=message, code="VALIDATION_ERROR", **kwargs)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class ServerError(PDDError):
|
|
111
|
+
"""
|
|
112
|
+
服务端错误异常
|
|
113
|
+
|
|
114
|
+
当服务端返回 5xx 错误时抛出。
|
|
115
|
+
|
|
116
|
+
Example:
|
|
117
|
+
>>> raise ServerError(
|
|
118
|
+
... message="服务器内部错误",
|
|
119
|
+
... details={"status_code": 500, "request_id": "abc123"}
|
|
120
|
+
... )
|
|
121
|
+
"""
|
|
122
|
+
|
|
123
|
+
def __init__(self, message: str = "PDD 服务端发生内部错误", **kwargs):
|
|
124
|
+
super().__init__(message=message, code="SERVER_ERROR", **kwargs)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class TimeoutError(PDDError):
|
|
128
|
+
"""
|
|
129
|
+
超时异常
|
|
130
|
+
|
|
131
|
+
当请求超过设定的时间限制时抛出。
|
|
132
|
+
|
|
133
|
+
Example:
|
|
134
|
+
>>> raise TimeoutError(
|
|
135
|
+
... message="请求超时",
|
|
136
|
+
... details={"timeout": 30, "elapsed": 35}
|
|
137
|
+
... )
|
|
138
|
+
"""
|
|
139
|
+
|
|
140
|
+
def __init__(self, message: str = "请求超时,请稍后重试", **kwargs):
|
|
141
|
+
super().__init__(message=message, code="TIMEOUT_ERROR", **kwargs)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
class RateLimitError(PDDError):
|
|
145
|
+
"""
|
|
146
|
+
限流异常
|
|
147
|
+
|
|
148
|
+
当请求频率超过限制时抛出。
|
|
149
|
+
|
|
150
|
+
Example:
|
|
151
|
+
>>> raise RateLimitError(
|
|
152
|
+
... message="请求过于频繁",
|
|
153
|
+
... details={"retry_after": 60, "limit": 100}
|
|
154
|
+
... )
|
|
155
|
+
"""
|
|
156
|
+
|
|
157
|
+
def __init__(self, message: str = "请求频率超限,请稍后重试", **kwargs):
|
|
158
|
+
super().__init__(message=message, code="RATE_LIMIT_ERROR", **kwargs)
|