create-dwy 0.25.3 → 0.26.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.
Files changed (31) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/package.json +1 -1
  3. package/templates/ai-tools/CHANGELOG.md +10 -0
  4. package/templates/ai-tools/VERSION +1 -1
  5. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-commercial-sdk/SKILL.md +20 -0
  6. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-github-action-publish/SKILL.md +1 -1
  7. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/SKILL.md +6 -2
  8. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/android.md +2 -2
  9. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/backend-deploy.md +1 -1
  10. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/frontend-deploy.md +1 -1
  11. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/generic-release.md +2 -2
  12. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/harmony.md +2 -2
  13. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/ios.md +2 -2
  14. package/templates/ai-tools/skills//351/200/232/347/224/250/dwy-dependency-choice/SKILL.md +2 -2
  15. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-sdk-spec/SKILL.md +0 -244
  16. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-sdk-spec/assets/files-whitelist-snippet.json +0 -22
  17. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-sdk-spec/assets/js-commercial.npmignore +0 -76
  18. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-sdk-spec/assets/python-commercial-MANIFEST.in +0 -37
  19. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-sdk-spec/assets/python-commercial.gitignore +0 -62
  20. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-sdk-spec/references/js-commercial-guide.md +0 -133
  21. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-sdk-spec/references/publish-blacklist.md +0 -106
  22. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-sdk-spec/references/python-commercial-guide.md +0 -147
  23. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-sdk-spec/references/sensitive-patterns.md +0 -112
  24. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-sdk-spec/scripts/check_comments.py +0 -260
  25. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-sdk-spec/scripts/check_js_publish.sh +0 -110
  26. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-sdk-spec/scripts/check_python_publish.sh +0 -171
  27. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-sdk-spec/scripts/detect_sdk.sh +0 -71
  28. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-sdk-spec/scripts/init_config.sh +0 -91
  29. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-semver/SKILL.md +0 -136
  30. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-semver/references/semver-spec.md +0 -70
  31. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-semver/scripts/bump.py +0 -137
@@ -1,260 +0,0 @@
1
- #!/usr/bin/env python3
2
- """扫 Python docstring 与 JS/TS JSDoc 中的敏感模式。
3
-
4
- 用法:
5
- check_comments.py <root> [--sdk-paths <json>] [--format text|json]
6
-
7
- 退出码:
8
- 0 = 无违规
9
- 1 = 有违规
10
- 2 = 用法错误
11
- """
12
- from __future__ import annotations
13
-
14
- import argparse
15
- import ast
16
- import json
17
- import re
18
- import sys
19
- from dataclasses import dataclass, asdict
20
- from pathlib import Path
21
- from typing import Iterable
22
-
23
-
24
- # 敏感模式表,与 references/sensitive-patterns.md 对齐
25
- PATTERNS: list[tuple[str, re.Pattern[str]]] = [
26
- ("internal-domain", re.compile(r"\b[\w-]+\.(internal|corp|local|intranet)\b", re.I)),
27
- ("private-ip", re.compile(
28
- r"\b(?:10\.\d{1,3}\.\d{1,3}\.\d{1,3}"
29
- r"|192\.168\.\d{1,3}\.\d{1,3}"
30
- r"|172\.(?:1[6-9]|2\d|3[01])\.\d{1,3}\.\d{1,3})\b"
31
- )),
32
- ("sql-statement", re.compile(
33
- r"\b(SELECT|UPDATE|DELETE|INSERT)\b[^.\n]{0,80}\b(FROM|WHERE|SET|INTO)\b", re.I
34
- )),
35
- ("redis-key-pattern", re.compile(r"\b(cache|redis|fmt|lock|queue):[a-z_]+:")),
36
- ("jira-ticket", re.compile(r"\b[A-Z]{2,6}-\d{2,6}\b")),
37
- ("internal-service", re.compile(
38
- r"\b[a-z_]+_(?:service|server|grpc|rpc)_(?:v\d|prod|stg|stage|dev)\b"
39
- )),
40
- ("aws-arn", re.compile(r"arn:aws:[a-z0-9-]+:[a-z0-9-]*:\d+:[a-zA-Z0-9-_/:]+")),
41
- ("aws-key", re.compile(r"\bAKIA[0-9A-Z]{16}\b")),
42
- ("cred-keyword", re.compile(
43
- r"\b(API_KEY|TOKEN|SECRET|PASSWORD|PRIVATE_KEY)\s*[=:]\s*['\"][\w\-./+]{6,}['\"]"
44
- )),
45
- ]
46
-
47
- # 弱模式(仅在行内有上下文关键字时命中,降误报)
48
- PORT_PATTERN = re.compile(r":(\d{4,5})\b")
49
- PORT_CONTEXT = re.compile(r"\b(connect|server|host|endpoint|address|listen|bind)\b", re.I)
50
-
51
- EMAIL_PATTERN = re.compile(r"[\w.+-]+@[\w.-]+\.[a-z]{2,}")
52
- PUBLIC_EMAIL_DOMAINS = {
53
- "example.com", "example.org", "example.net",
54
- "gmail.com", "outlook.com", "hotmail.com", "qq.com", "163.com",
55
- "yourdomain.com", "your-domain.com", "domain.com",
56
- }
57
-
58
- # 行内示例 / 占位关键字 → 整行视为示例不告警
59
- EXAMPLE_HINTS = re.compile(r"\b(example|示例|e\.g\.|placeholder|占位|your[- ]?domain|<.*>)\b", re.I)
60
-
61
- # 行内豁免
62
- IGNORE_MARKER = re.compile(r"#\s*sdk-spec:\s*ignore|//\s*sdk-spec:\s*ignore")
63
-
64
-
65
- @dataclass
66
- class Violation:
67
- file: str
68
- line: int
69
- pattern: str
70
- match: str
71
- context: str
72
-
73
-
74
- def scan_text(file_rel: str, text: str, start_line_offset: int = 0) -> list[Violation]:
75
- """扫一段连续文本(docstring 或注释块),返回违规列表。
76
-
77
- `text` 内部带换行,`start_line_offset` 是该文本第一行在文件中的实际行号。
78
- """
79
- out: list[Violation] = []
80
- for i, line in enumerate(text.splitlines(), start=start_line_offset):
81
- if IGNORE_MARKER.search(line):
82
- continue
83
- line_stripped = line.strip()
84
- if not line_stripped:
85
- continue
86
-
87
- is_example = bool(EXAMPLE_HINTS.search(line))
88
-
89
- # 主模式
90
- for pat_name, pat in PATTERNS:
91
- for m in pat.finditer(line):
92
- if is_example and pat_name in {"sql-statement", "internal-domain", "jira-ticket"}:
93
- continue
94
- out.append(Violation(
95
- file=file_rel, line=i,
96
- pattern=pat_name, match=m.group(0),
97
- context=line_stripped[:160],
98
- ))
99
-
100
- # 端口(弱模式)
101
- if not is_example and PORT_CONTEXT.search(line):
102
- for m in PORT_PATTERN.finditer(line):
103
- port = int(m.group(1))
104
- if port in {8080, 8000, 3000, 5000, 5173, 5432, 6379, 9000}:
105
- # 常见示例端口,跳过
106
- continue
107
- out.append(Violation(
108
- file=file_rel, line=i,
109
- pattern="service-port", match=m.group(0),
110
- context=line_stripped[:160],
111
- ))
112
-
113
- # 邮箱(弱模式)
114
- if not is_example:
115
- for m in EMAIL_PATTERN.finditer(line):
116
- email = m.group(0)
117
- domain = email.split("@", 1)[1].lower()
118
- if domain in PUBLIC_EMAIL_DOMAINS:
119
- continue
120
- out.append(Violation(
121
- file=file_rel, line=i,
122
- pattern="internal-email", match=email,
123
- context=line_stripped[:160],
124
- ))
125
- return out
126
-
127
-
128
- def scan_python_file(path: Path, root: Path) -> list[Violation]:
129
- rel = str(path.relative_to(root))
130
- try:
131
- source = path.read_text(encoding="utf-8")
132
- except (UnicodeDecodeError, OSError):
133
- return []
134
- try:
135
- tree = ast.parse(source)
136
- except SyntaxError:
137
- return []
138
-
139
- out: list[Violation] = []
140
- # 模块、函数、类、异步函数的 docstring
141
- for node in ast.walk(tree):
142
- if not isinstance(node, (ast.Module, ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)):
143
- continue
144
- doc = ast.get_docstring(node, clean=False)
145
- if not doc:
146
- continue
147
- # docstring 首行在 node.body[0].lineno(第一条 Expr 是字符串)
148
- first_stmt = node.body[0] if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)) else node.body[0]
149
- start_line = getattr(first_stmt, "lineno", 1)
150
- out.extend(scan_text(rel, doc, start_line_offset=start_line))
151
- return out
152
-
153
-
154
- # 匹配 JS/TS 的 /** ... */ JSDoc 块(不含其他 /* */ 注释)
155
- JSDOC_BLOCK = re.compile(r"/\*\*(.*?)\*/", re.DOTALL)
156
- # 行注释 //
157
- LINE_COMMENT = re.compile(r"^\s*//\s?(.*)$")
158
-
159
-
160
- def scan_js_file(path: Path, root: Path) -> list[Violation]:
161
- rel = str(path.relative_to(root))
162
- try:
163
- source = path.read_text(encoding="utf-8")
164
- except (UnicodeDecodeError, OSError):
165
- return []
166
-
167
- out: list[Violation] = []
168
-
169
- # JSDoc 块
170
- for m in JSDOC_BLOCK.finditer(source):
171
- block_text = m.group(1)
172
- # 起始行号
173
- start_line = source.count("\n", 0, m.start()) + 1
174
- # 去掉每行的 leading * 和空白
175
- cleaned_lines = []
176
- for line in block_text.splitlines():
177
- cleaned_lines.append(re.sub(r"^\s*\*\s?", "", line))
178
- out.extend(scan_text(rel, "\n".join(cleaned_lines), start_line_offset=start_line))
179
-
180
- # 行内注释(只扫描,因为商业 SDK 内部实现的行注释虽然不打包但开发期能看到)
181
- # 注:源文件其实是否打包取决于配置,行注释泄露主要看是否打包 src/
182
- # 跨边界谨慎起见也扫
183
- for i, line in enumerate(source.splitlines(), start=1):
184
- lm = LINE_COMMENT.match(line)
185
- if not lm:
186
- continue
187
- comment = lm.group(1)
188
- out.extend(scan_text(rel, comment, start_line_offset=i))
189
-
190
- return out
191
-
192
-
193
- def iter_target_files(root: Path, sdk_paths: list[str] | None) -> Iterable[Path]:
194
- """枚举要扫的文件。sdk_paths 为 None 时全扫;否则只扫指定路径。"""
195
- excludes = {
196
- "node_modules", ".venv", "venv", "dist", "build",
197
- ".git", "__pycache__", ".mypy_cache", ".pytest_cache",
198
- ".ruff_cache", ".tox", "egg-info", "site-packages",
199
- }
200
- bases = [root / p for p in sdk_paths] if sdk_paths else [root]
201
- for base in bases:
202
- if not base.exists():
203
- continue
204
- for p in base.rglob("*"):
205
- if not p.is_file():
206
- continue
207
- if any(part in excludes or part.endswith(".egg-info") for part in p.parts):
208
- continue
209
- suf = p.suffix.lower()
210
- if suf in {".py", ".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"}:
211
- yield p
212
-
213
-
214
- def main() -> int:
215
- parser = argparse.ArgumentParser(description="Scan SDK comments for sensitive patterns.")
216
- parser.add_argument("root", help="Project root directory (absolute path)")
217
- parser.add_argument("--sdk-paths", default=None,
218
- help='JSON {"python":[...], "js":[...]} relative to root. Omit to scan all.')
219
- parser.add_argument("--format", choices=["text", "json"], default="text")
220
- args = parser.parse_args()
221
-
222
- root = Path(args.root).resolve()
223
- if not root.is_dir():
224
- print(f"ERROR: not a directory: {root}", file=sys.stderr)
225
- return 2
226
-
227
- target_paths: list[str] | None = None
228
- if args.sdk_paths:
229
- try:
230
- sdk_spec = json.loads(args.sdk_paths)
231
- except json.JSONDecodeError as e:
232
- print(f"ERROR: invalid --sdk-paths JSON: {e}", file=sys.stderr)
233
- return 2
234
- target_paths = []
235
- for key in ("python", "js"):
236
- target_paths.extend(sdk_spec.get(key, []))
237
-
238
- violations: list[Violation] = []
239
- for fp in iter_target_files(root, target_paths):
240
- if fp.suffix == ".py":
241
- violations.extend(scan_python_file(fp, root))
242
- else:
243
- violations.extend(scan_js_file(fp, root))
244
-
245
- if args.format == "json":
246
- print(json.dumps([asdict(v) for v in violations], ensure_ascii=False, indent=2))
247
- else:
248
- if not violations:
249
- print("OK: no sensitive patterns found")
250
- else:
251
- print(f"FAIL: {len(violations)} violation(s) found:\n")
252
- for v in violations:
253
- print(f" {v.file}:{v.line} [{v.pattern}] match={v.match!r}")
254
- print(f" > {v.context}")
255
-
256
- return 1 if violations else 0
257
-
258
-
259
- if __name__ == "__main__":
260
- sys.exit(main())
@@ -1,110 +0,0 @@
1
- #!/usr/bin/env bash
2
- # JS/TS SDK 发布前文件清单审计(npm pack --dry-run)
3
- # 用法: check_js_publish.sh <js_sdk_root> --edition commercial|opensource
4
-
5
- set -euo pipefail
6
-
7
- if [[ $# -lt 3 ]]; then
8
- echo "Usage: $0 <js_sdk_root> --edition commercial|opensource" >&2
9
- exit 2
10
- fi
11
-
12
- PKG_ROOT="$1"
13
- shift
14
-
15
- EDITION=""
16
- while [[ $# -gt 0 ]]; do
17
- case "$1" in
18
- --edition) EDITION="$2"; shift 2 ;;
19
- *) echo "ERROR: unknown arg: $1" >&2; exit 2 ;;
20
- esac
21
- done
22
-
23
- if [[ "$EDITION" != "commercial" && "$EDITION" != "opensource" ]]; then
24
- echo "ERROR: --edition must be 'commercial' or 'opensource'" >&2
25
- exit 2
26
- fi
27
-
28
- if [[ ! -d "$PKG_ROOT" ]]; then
29
- echo "ERROR: not a directory: $PKG_ROOT" >&2
30
- exit 2
31
- fi
32
-
33
- if ! command -v npm >/dev/null 2>&1; then
34
- echo "ERROR: npm not found in PATH" >&2
35
- exit 2
36
- fi
37
-
38
- echo "[1/2] Running npm pack --dry-run in $PKG_ROOT ..."
39
-
40
- # npm pack --dry-run --json 输出包内文件列表
41
- # 注:某些 npm 版本 --json 输出到 stderr,--silent 抑制非 JSON 输出
42
- PACK_OUTPUT=$(cd "$PKG_ROOT" && npm pack --dry-run --json 2>/dev/null) || {
43
- echo "ERROR: npm pack --dry-run failed; run 'npm install' first?" >&2
44
- exit 2
45
- }
46
-
47
- # 提取文件路径列表
48
- FILES=$(echo "$PACK_OUTPUT" | jq -r '.[0].files[].path' 2>/dev/null) || {
49
- echo "ERROR: failed to parse npm pack output:" >&2
50
- echo "$PACK_OUTPUT" | head -20 >&2
51
- exit 2
52
- }
53
-
54
- echo "[2/2] Audit result (edition=$EDITION):"
55
- echo "----------------------------------------"
56
-
57
- # 致命级(任何版本都禁)
58
- CRITICAL_REGEX='(^|/)(\.env(\..+)?|\.npmrc|\.pypirc|\.git-credentials|id_rsa|id_ed25519|secrets\.json|credentials\.json)$|\.(pem|key|p12)$|(^|/)\.aws/credentials$'
59
-
60
- # 开源版黑名单
61
- OPENSOURCE_DENY_REGEX='(^|/)(src|tests|__tests__|\.git|\.github|\.gitlab-ci\.yml|\.travis\.yml|\.circleci|\.vscode|\.idea|node_modules|\.husky)/|(^|/)(tsconfig.*\.json|vite\.config\.|vitest\.config\.|tsup\.config\.|rollup\.config\.|webpack\.config\.|jest\.config\.|\.eslintrc.*|\.prettierrc.*|eslint\.config\.|prettier\.config\.|commitlint\.config\.|\.gitignore|\.gitattributes|\.DS_Store|Thumbs\.db|pnpm-lock\.yaml|package-lock\.json|yarn\.lock)|\.(map|tsbuildinfo|swp|swo)$|\.(test|spec)\.(ts|tsx|js|jsx)$'
62
-
63
- # 商业版白名单(其他视为违规)
64
- COMMERCIAL_ALLOW_REGEX='^(dist/|bin/|package\.json$|README(\..+)?$|LICEN[SC]E(\..+)?$|CHANGELOG(\..+)?$)'
65
-
66
- violations=()
67
- critical=()
68
-
69
- while IFS= read -r f; do
70
- [[ -z "$f" ]] && continue
71
-
72
- if [[ "$f" =~ $CRITICAL_REGEX ]]; then
73
- critical+=("$f")
74
- continue
75
- fi
76
-
77
- if [[ "$EDITION" == "opensource" ]]; then
78
- if [[ "$f" =~ $OPENSOURCE_DENY_REGEX ]]; then
79
- violations+=("$f [denied by opensource rules]")
80
- fi
81
- else
82
- if [[ ! "$f" =~ $COMMERCIAL_ALLOW_REGEX ]]; then
83
- violations+=("$f [not in commercial whitelist]")
84
- fi
85
- fi
86
- done <<< "$FILES"
87
-
88
- if [[ ${#critical[@]} -gt 0 ]]; then
89
- echo "[CRITICAL] secret-like files in package — DO NOT PUBLISH:"
90
- for f in "${critical[@]}"; do
91
- echo " $f"
92
- done
93
- echo ""
94
- fi
95
-
96
- if [[ ${#violations[@]} -gt 0 ]]; then
97
- echo "[VIOLATION] ${#violations[@]} file(s) violate $EDITION rules:"
98
- for v in "${violations[@]}"; do
99
- echo " $v"
100
- done
101
- echo ""
102
- exit 1
103
- fi
104
-
105
- if [[ ${#critical[@]} -gt 0 ]]; then
106
- exit 1
107
- fi
108
-
109
- count=$(echo "$FILES" | grep -c .)
110
- echo "OK: package passes $EDITION audit ($count file(s))"
@@ -1,171 +0,0 @@
1
- #!/usr/bin/env bash
2
- # Python SDK 发布前文件清单审计(不实际发布)
3
- # 用法: check_python_publish.sh <python_sdk_root> --edition commercial|opensource
4
-
5
- set -euo pipefail
6
-
7
- if [[ $# -lt 3 ]]; then
8
- echo "Usage: $0 <python_sdk_root> --edition commercial|opensource" >&2
9
- exit 2
10
- fi
11
-
12
- PKG_ROOT="$1"
13
- shift
14
-
15
- EDITION=""
16
- while [[ $# -gt 0 ]]; do
17
- case "$1" in
18
- --edition) EDITION="$2"; shift 2 ;;
19
- *) echo "ERROR: unknown arg: $1" >&2; exit 2 ;;
20
- esac
21
- done
22
-
23
- if [[ "$EDITION" != "commercial" && "$EDITION" != "opensource" ]]; then
24
- echo "ERROR: --edition must be 'commercial' or 'opensource'" >&2
25
- exit 2
26
- fi
27
-
28
- if [[ ! -d "$PKG_ROOT" ]]; then
29
- echo "ERROR: not a directory: $PKG_ROOT" >&2
30
- exit 2
31
- fi
32
-
33
- if ! command -v python >/dev/null 2>&1 && ! command -v python3 >/dev/null 2>&1; then
34
- echo "ERROR: python not found in PATH" >&2
35
- exit 2
36
- fi
37
- PY=$(command -v python || command -v python3)
38
-
39
- # 临时构建目录
40
- TMPDIR=$(mktemp -d -t dwy-sdk-spec-py-XXXX)
41
- trap 'rm -rf "$TMPDIR"' EXIT
42
-
43
- echo "[1/3] Building wheel to $TMPDIR (--wheel: skip sdist 中转,商业 Cython 项目 MANIFEST 通常排除 _*.py 源码,默认 sdist→wheel 会失败) ..."
44
- (cd "$PKG_ROOT" && "$PY" -m build --wheel --outdir "$TMPDIR") >&2
45
-
46
- WHEEL=$(find "$TMPDIR" -name '*.whl' -type f | head -1)
47
- if [[ -z "$WHEEL" ]]; then
48
- echo "ERROR: no wheel produced" >&2
49
- exit 2
50
- fi
51
- echo "[2/3] Listing wheel contents: $(basename "$WHEEL")"
52
-
53
- # 列文件(剥掉 .dist-info 路径前缀,便于审计)
54
- FILES=$(unzip -Z1 "$WHEEL")
55
-
56
- # 致命级(任何版本都禁)
57
- declare -a CRITICAL_PATTERNS=(
58
- '(^|/)\.env(\..+)?$'
59
- '(^|/)\.npmrc$'
60
- '(^|/)\.pypirc$'
61
- '(^|/)\.git-credentials$'
62
- '(^|/)id_rsa$'
63
- '(^|/)id_ed25519$'
64
- '\.pem$'
65
- '\.key$'
66
- '\.p12$'
67
- '(^|/)secrets\.json$'
68
- '(^|/)credentials\.json$'
69
- )
70
-
71
- # 开源版黑名单
72
- declare -a OPENSOURCE_DENY=(
73
- '__pycache__/'
74
- '\.pyc$'
75
- '\.pyo$'
76
- '\.egg-info/'
77
- '\.mypy_cache/'
78
- '\.pytest_cache/'
79
- '\.ruff_cache/'
80
- '\.pdb$'
81
- '(^|/)\.git/'
82
- '(^|/)\.gitignore$'
83
- '(^|/)\.DS_Store$'
84
- '(^|/)\.vscode/'
85
- '(^|/)\.idea/'
86
- '\.bak$'
87
- '\.swp$'
88
- )
89
-
90
- # 商业版白名单(其他视为违规)
91
- declare -a COMMERCIAL_ALLOW=(
92
- '\.so$'
93
- '\.pyi$'
94
- '(^|/)py\.typed$'
95
- '\.dist-info/'
96
- '(^|/)LICEN[SC]E(\..+)?$'
97
- '(^|/)README(\..+)?$'
98
- '\.json$'
99
- '\.txt$'
100
- )
101
-
102
- violations=()
103
- critical=()
104
-
105
- while IFS= read -r f; do
106
- [[ -z "$f" ]] && continue
107
-
108
- # 致命级
109
- for pat in "${CRITICAL_PATTERNS[@]}"; do
110
- if [[ "$f" =~ $pat ]]; then
111
- critical+=("$f")
112
- continue 2
113
- fi
114
- done
115
-
116
- if [[ "$EDITION" == "opensource" ]]; then
117
- for pat in "${OPENSOURCE_DENY[@]}"; do
118
- if [[ "$f" =~ $pat ]]; then
119
- violations+=("$f [denied: $pat]")
120
- break
121
- fi
122
- done
123
- else
124
- # commercial: 白名单检查
125
- # 排除目录条目(以 / 结尾)
126
- [[ "$f" == */ ]] && continue
127
- matched=0
128
- for pat in "${COMMERCIAL_ALLOW[@]}"; do
129
- if [[ "$f" =~ $pat ]]; then
130
- matched=1
131
- break
132
- fi
133
- done
134
- # 单独允许:包根的 __init__.py(公开入口)/ 不以 _ 开头的 .py
135
- if [[ $matched -eq 0 ]] && [[ "$f" =~ \.py$ ]]; then
136
- base=$(basename "$f")
137
- if [[ "$base" == "__init__.py" ]] || [[ ! "$base" =~ ^_ ]]; then
138
- matched=1
139
- fi
140
- fi
141
- if [[ $matched -eq 0 ]]; then
142
- violations+=("$f [not in commercial whitelist]")
143
- fi
144
- fi
145
- done <<< "$FILES"
146
-
147
- echo "[3/3] Audit result (edition=$EDITION):"
148
- echo "----------------------------------------"
149
-
150
- if [[ ${#critical[@]} -gt 0 ]]; then
151
- echo "[CRITICAL] secret-like files in wheel — DO NOT PUBLISH:"
152
- for f in "${critical[@]}"; do
153
- echo " $f"
154
- done
155
- echo ""
156
- fi
157
-
158
- if [[ ${#violations[@]} -gt 0 ]]; then
159
- echo "[VIOLATION] ${#violations[@]} file(s) violate $EDITION rules:"
160
- for v in "${violations[@]}"; do
161
- echo " $v"
162
- done
163
- echo ""
164
- exit 1
165
- fi
166
-
167
- if [[ ${#critical[@]} -gt 0 ]]; then
168
- exit 1
169
- fi
170
-
171
- echo "OK: wheel passes $EDITION audit ($(echo "$FILES" | wc -l | tr -d ' ') file(s))"
@@ -1,71 +0,0 @@
1
- #!/usr/bin/env bash
2
- # 探测项目内的 Python / JS SDK 子包
3
- # 用法: detect_sdk.sh <project_root>
4
- # 输出: JSON {"python": [...], "js": [...]} 路径相对项目根
5
-
6
- set -euo pipefail
7
-
8
- PROJECT_ROOT="${1:-$(pwd)}"
9
-
10
- if [[ ! -d "$PROJECT_ROOT" ]]; then
11
- echo "ERROR: not a directory: $PROJECT_ROOT" >&2
12
- exit 2
13
- fi
14
-
15
- # 找 Python SDK:pyproject.toml 含 [project] 段,或 setup.py
16
- find_python_sdks() {
17
- local results=()
18
- # 项目根
19
- if [[ -f "$PROJECT_ROOT/pyproject.toml" ]] && grep -q '^\[project\]' "$PROJECT_ROOT/pyproject.toml" 2>/dev/null; then
20
- results+=(".")
21
- elif [[ -f "$PROJECT_ROOT/setup.py" ]]; then
22
- results+=(".")
23
- fi
24
- # 子目录(限 2 层深度,排除 node_modules / .venv / venv / dist / build / .git)
25
- while IFS= read -r f; do
26
- local d
27
- d=$(dirname "$f")
28
- local rel="${d#"$PROJECT_ROOT"/}"
29
- # 验证含 [project] 段
30
- if grep -q '^\[project\]' "$f" 2>/dev/null; then
31
- results+=("$rel")
32
- fi
33
- done < <(find "$PROJECT_ROOT" -mindepth 2 -maxdepth 3 -name pyproject.toml -type f \
34
- -not -path "*/node_modules/*" \
35
- -not -path "*/.venv/*" \
36
- -not -path "*/venv/*" \
37
- -not -path "*/dist/*" \
38
- -not -path "*/build/*" \
39
- -not -path "*/.git/*" 2>/dev/null)
40
-
41
- # 去重(空数组下 ${arr[@]} 在 set -u 会 unbound,用 ${arr[@]+...} 安全展开)
42
- printf '%s\n' "${results[@]+"${results[@]}"}" | awk 'NF && !seen[$0]++'
43
- }
44
-
45
- # 找 JS SDK:package.json 含 main / exports / module 字段
46
- find_js_sdks() {
47
- local results=()
48
- if [[ -f "$PROJECT_ROOT/package.json" ]] && jq -e '.main // .exports // .module' "$PROJECT_ROOT/package.json" >/dev/null 2>&1; then
49
- results+=(".")
50
- fi
51
- while IFS= read -r f; do
52
- local d
53
- d=$(dirname "$f")
54
- local rel="${d#"$PROJECT_ROOT"/}"
55
- if jq -e '.main // .exports // .module' "$f" >/dev/null 2>&1; then
56
- results+=("$rel")
57
- fi
58
- done < <(find "$PROJECT_ROOT" -mindepth 2 -maxdepth 3 -name package.json -type f \
59
- -not -path "*/node_modules/*" \
60
- -not -path "*/.venv/*" \
61
- -not -path "*/dist/*" \
62
- -not -path "*/build/*" \
63
- -not -path "*/.git/*" 2>/dev/null)
64
-
65
- printf '%s\n' "${results[@]+"${results[@]}"}" | awk 'NF && !seen[$0]++'
66
- }
67
-
68
- python_list=$(find_python_sdks | jq -R . | jq -s .)
69
- js_list=$(find_js_sdks | jq -R . | jq -s .)
70
-
71
- jq -n --argjson py "$python_list" --argjson js "$js_list" '{python: $py, js: $js}'