opencode-skills-collection 4.0.69 → 4.0.70

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 (185) hide show
  1. package/bundled-skills/.antigravity-install-manifest.json +39 -1
  2. package/bundled-skills/api-integration-architect/SKILL.md +241 -0
  3. package/bundled-skills/apify-generate-output-schema/SKILL.md +438 -0
  4. package/bundled-skills/apify-integration-development/SKILL.md +168 -0
  5. package/bundled-skills/apify-integration-development/references/ai-framework-package.md +158 -0
  6. package/bundled-skills/apify-integration-development/references/ai-harness-plugin.md +192 -0
  7. package/bundled-skills/apify-integration-development/references/sdk-integration.md +236 -0
  8. package/bundled-skills/apify-integration-development/references/workflow-automation.md +163 -0
  9. package/bundled-skills/architecture-review/README.md +42 -0
  10. package/bundled-skills/architecture-review/SKILL.md +77 -0
  11. package/bundled-skills/architecture-review/examples.md +11 -0
  12. package/bundled-skills/architecture-review/reference/best-practices.md +7 -0
  13. package/bundled-skills/architecture-review/reference/capabilities.md +20 -0
  14. package/bundled-skills/architecture-review/reference/fallbacks.md +11 -0
  15. package/bundled-skills/architecture-review/reference/graph.md +15 -0
  16. package/bundled-skills/architecture-review/reference/mcp.md +14 -0
  17. package/bundled-skills/architecture-review/reference/workflow.md +15 -0
  18. package/bundled-skills/architecture-review/templates/architecture-review.md +21 -0
  19. package/bundled-skills/code-review-sensei/SKILL.md +177 -0
  20. package/bundled-skills/codebase-onboarding/README.md +42 -0
  21. package/bundled-skills/codebase-onboarding/SKILL.md +77 -0
  22. package/bundled-skills/codebase-onboarding/examples.md +11 -0
  23. package/bundled-skills/codebase-onboarding/reference/best-practices.md +7 -0
  24. package/bundled-skills/codebase-onboarding/reference/capabilities.md +20 -0
  25. package/bundled-skills/codebase-onboarding/reference/fallbacks.md +11 -0
  26. package/bundled-skills/codebase-onboarding/reference/graph.md +15 -0
  27. package/bundled-skills/codebase-onboarding/reference/mcp.md +14 -0
  28. package/bundled-skills/codebase-onboarding/reference/workflow.md +15 -0
  29. package/bundled-skills/codebase-onboarding/templates/repository-onboarding.md +21 -0
  30. package/bundled-skills/connection-auth-rules/SKILL.md +199 -0
  31. package/bundled-skills/connection-auth-rules/fetch_schema.py +320 -0
  32. package/bundled-skills/dependency-analysis/README.md +42 -0
  33. package/bundled-skills/dependency-analysis/SKILL.md +76 -0
  34. package/bundled-skills/dependency-analysis/examples.md +11 -0
  35. package/bundled-skills/dependency-analysis/reference/best-practices.md +7 -0
  36. package/bundled-skills/dependency-analysis/reference/capabilities.md +20 -0
  37. package/bundled-skills/dependency-analysis/reference/fallbacks.md +11 -0
  38. package/bundled-skills/dependency-analysis/reference/graph.md +15 -0
  39. package/bundled-skills/dependency-analysis/reference/mcp.md +14 -0
  40. package/bundled-skills/dependency-analysis/reference/workflow.md +15 -0
  41. package/bundled-skills/dependency-analysis/templates/dependency-review.md +21 -0
  42. package/bundled-skills/devops-pipeline-builder/SKILL.md +200 -0
  43. package/bundled-skills/eas-app-stores/SKILL.md +197 -0
  44. package/bundled-skills/eas-app-stores/agents/openai.yaml +4 -0
  45. package/bundled-skills/eas-app-stores/references/app-store-metadata.md +497 -0
  46. package/bundled-skills/eas-app-stores/references/ios-app-store.md +376 -0
  47. package/bundled-skills/eas-app-stores/references/native-ios.md +167 -0
  48. package/bundled-skills/eas-app-stores/references/play-store.md +244 -0
  49. package/bundled-skills/eas-app-stores/references/testflight.md +62 -0
  50. package/bundled-skills/eas-app-stores/references/workflows.md +120 -0
  51. package/bundled-skills/eas-hosting/SKILL.md +448 -0
  52. package/bundled-skills/eas-hosting/agents/openai.yaml +4 -0
  53. package/bundled-skills/eas-observe/SKILL.md +75 -0
  54. package/bundled-skills/eas-observe/agents/openai.yaml +4 -0
  55. package/bundled-skills/eas-observe/references/metrics.md +98 -0
  56. package/bundled-skills/eas-observe/references/queries.md +403 -0
  57. package/bundled-skills/eas-observe/references/setup.md +476 -0
  58. package/bundled-skills/eas-observe/references/third-party.md +136 -0
  59. package/bundled-skills/eas-simulator/SKILL.md +251 -0
  60. package/bundled-skills/eas-simulator/agents/openai.yaml +4 -0
  61. package/bundled-skills/eas-simulator/references/controllers.md +135 -0
  62. package/bundled-skills/eas-simulator/references/run-your-app.md +240 -0
  63. package/bundled-skills/eas-simulator/references/troubleshooting.md +47 -0
  64. package/bundled-skills/eas-workflows/SKILL.md +119 -0
  65. package/bundled-skills/eas-workflows/agents/openai.yaml +4 -0
  66. package/bundled-skills/eas-workflows/scripts/fetch.js +109 -0
  67. package/bundled-skills/expo-animation/LICENSE +21 -0
  68. package/bundled-skills/expo-animation/RECIPES.md +385 -0
  69. package/bundled-skills/expo-animation/SKILL.md +295 -0
  70. package/bundled-skills/expo-animation/agents/openai.yaml +4 -0
  71. package/bundled-skills/fact-check-x-unified/SKILL.md +178 -0
  72. package/bundled-skills/fact-check-x-unified/agents/openai.yaml +4 -0
  73. package/bundled-skills/fact-check-x-unified/references/acceptance-criteria.md +44 -0
  74. package/bundled-skills/fact-check-x-unified/references/contracts.md +39 -0
  75. package/bundled-skills/fact-check-x-unified/scripts/common.py +31 -0
  76. package/bundled-skills/fact-check-x-unified/scripts/fact_check_x.py +1832 -0
  77. package/bundled-skills/fact-check-x-unified/scripts/trusted_search_config.py +324 -0
  78. package/bundled-skills/fact-check-x-unified/tests/anchor_downgrade_test.py +90 -0
  79. package/bundled-skills/fact-check-x-unified/tests/multi_platform_test.py +369 -0
  80. package/bundled-skills/fact-check-x-unified/tests/smoke_test.py +740 -0
  81. package/bundled-skills/fact-check-x-unified/tests/stage_checkpoint_test.py +103 -0
  82. package/bundled-skills/fact-check-x-unified/tests/trusted_search_config_test.py +156 -0
  83. package/bundled-skills/gpt-taste/SKILL.md +8 -1
  84. package/bundled-skills/hf-cli/SKILL.md +263 -0
  85. package/bundled-skills/huggingface-community-evals/SKILL.md +228 -0
  86. package/bundled-skills/huggingface-community-evals/examples/.env.example +3 -0
  87. package/bundled-skills/huggingface-community-evals/examples/USAGE_EXAMPLES.md +101 -0
  88. package/bundled-skills/huggingface-community-evals/scripts/inspect_eval_uv.py +104 -0
  89. package/bundled-skills/huggingface-community-evals/scripts/inspect_vllm_uv.py +306 -0
  90. package/bundled-skills/huggingface-community-evals/scripts/lighteval_vllm_uv.py +297 -0
  91. package/bundled-skills/huggingface-datasets/SKILL.md +130 -0
  92. package/bundled-skills/jev-social/SKILL.md +182 -0
  93. package/bundled-skills/longbridge-derivatives/SKILL.md +117 -0
  94. package/bundled-skills/longbridge-derivatives/references/option.md +36 -0
  95. package/bundled-skills/longbridge-derivatives/references/options-advanced.md +101 -0
  96. package/bundled-skills/longbridge-derivatives/references/options-pnl.md +74 -0
  97. package/bundled-skills/longbridge-derivatives/references/options-strategy.md +82 -0
  98. package/bundled-skills/longbridge-derivatives/references/options-volatility.md +70 -0
  99. package/bundled-skills/longbridge-derivatives/references/warrant.md +12 -0
  100. package/bundled-skills/longbridge-quant/SKILL.md +151 -0
  101. package/bundled-skills/longbridge-quant/references/correlation.md +51 -0
  102. package/bundled-skills/longbridge-quant/references/execution-model.md +68 -0
  103. package/bundled-skills/longbridge-quant/references/factor-research.md +95 -0
  104. package/bundled-skills/longbridge-quant/references/factor-screen.md +101 -0
  105. package/bundled-skills/longbridge-quant/references/hedging.md +136 -0
  106. package/bundled-skills/longbridge-quant/references/ml-strategy.md +77 -0
  107. package/bundled-skills/longbridge-quant/references/multifactor.md +68 -0
  108. package/bundled-skills/longbridge-quant/references/pairs-trading.md +61 -0
  109. package/bundled-skills/longbridge-quant/references/quant-cli.md +133 -0
  110. package/bundled-skills/longbridge-quant/references/quant-stats.md +150 -0
  111. package/bundled-skills/longbridge-quant/references/seasonality.md +50 -0
  112. package/bundled-skills/longbridge-quant/references/strategy-optimizer.md +68 -0
  113. package/bundled-skills/longbridge-quant/references/volatility-strategy.md +52 -0
  114. package/bundled-skills/longbridge-research/SKILL.md +187 -0
  115. package/bundled-skills/longbridge-research/references/company-profile.md +96 -0
  116. package/bundled-skills/longbridge-research/references/company-tearsheet.md +82 -0
  117. package/bundled-skills/longbridge-research/references/competitive-analysis.md +81 -0
  118. package/bundled-skills/longbridge-research/references/consensus.md +92 -0
  119. package/bundled-skills/longbridge-research/references/coverage-initiation.md +76 -0
  120. package/bundled-skills/longbridge-research/references/defi-yield.md +60 -0
  121. package/bundled-skills/longbridge-research/references/finance-calendar.md +165 -0
  122. package/bundled-skills/longbridge-research/references/financial-planning.md +77 -0
  123. package/bundled-skills/longbridge-research/references/forecast-eps.md +39 -0
  124. package/bundled-skills/longbridge-research/references/fund-holder.md +44 -0
  125. package/bundled-skills/longbridge-research/references/hkipo-analysis.md +101 -0
  126. package/bundled-skills/longbridge-research/references/industry-peers.md +46 -0
  127. package/bundled-skills/longbridge-research/references/industry-rank.md +62 -0
  128. package/bundled-skills/longbridge-research/references/insider-trades.md +48 -0
  129. package/bundled-skills/longbridge-research/references/institution-rating.md +62 -0
  130. package/bundled-skills/longbridge-research/references/investment-ideas.md +69 -0
  131. package/bundled-skills/longbridge-research/references/investment-proposal.md +95 -0
  132. package/bundled-skills/longbridge-research/references/investors.md +87 -0
  133. package/bundled-skills/longbridge-research/references/onchain.md +70 -0
  134. package/bundled-skills/longbridge-research/references/post-investment.md +76 -0
  135. package/bundled-skills/longbridge-research/references/shareholder.md +72 -0
  136. package/bundled-skills/longbridge-research/references/short-positions.md +50 -0
  137. package/bundled-skills/longbridge-research/references/short-trades.md +50 -0
  138. package/bundled-skills/longbridge-research/references/stock-research.md +61 -0
  139. package/bundled-skills/longbridge-research/references/thesis-tracker.md +64 -0
  140. package/bundled-skills/makepad-2-0-animation/SKILL.md +318 -0
  141. package/bundled-skills/makepad-2-0-animation/references/animator-reference.md +433 -0
  142. package/bundled-skills/makepad-2-0-dsl/SKILL.md +492 -0
  143. package/bundled-skills/makepad-2-0-dsl/references/dsl-syntax-reference.md +511 -0
  144. package/bundled-skills/makepad-2-0-dsl/references/extended-guide.md +56 -0
  145. package/bundled-skills/makepad-2-0-dsl/references/property-system.md +757 -0
  146. package/bundled-skills/makepad-2-0-events/SKILL.md +497 -0
  147. package/bundled-skills/makepad-2-0-events/references/event-patterns.md +802 -0
  148. package/bundled-skills/makepad-2-0-events/references/extended-guide.md +590 -0
  149. package/bundled-skills/makepad-2-0-layout/SKILL.md +499 -0
  150. package/bundled-skills/makepad-2-0-layout/references/extended-guide.md +243 -0
  151. package/bundled-skills/makepad-2-0-layout/references/layout-patterns.md +881 -0
  152. package/bundled-skills/makepad-2-0-widgets/SKILL.md +261 -0
  153. package/bundled-skills/makepad-2-0-widgets/references/widget-advanced.md +648 -0
  154. package/bundled-skills/makepad-2-0-widgets/references/widget-catalog.md +547 -0
  155. package/bundled-skills/meeting-distiller-pro/SKILL.md +120 -0
  156. package/bundled-skills/monte-carlo-analyze-root-cause/SKILL.md +12 -1
  157. package/bundled-skills/monte-carlo-asset-health/SKILL.md +12 -1
  158. package/bundled-skills/monte-carlo-context-detection/SKILL.md +170 -0
  159. package/bundled-skills/monte-carlo-context-detection/references/signal-definitions.md +46 -0
  160. package/bundled-skills/remotion-captions/SKILL.md +57 -0
  161. package/bundled-skills/remotion-captions/agents/openai.yaml +7 -0
  162. package/bundled-skills/remotion-captions/assets/remotion-icon.svg +4 -0
  163. package/bundled-skills/remotion-captions/display-captions.md +190 -0
  164. package/bundled-skills/remotion-captions/import-srt-captions.md +73 -0
  165. package/bundled-skills/remotion-captions/transcribe-captions.md +70 -0
  166. package/bundled-skills/remotion-create/SKILL.md +106 -0
  167. package/bundled-skills/remotion-create/agents/openai.yaml +7 -0
  168. package/bundled-skills/remotion-create/assets/remotion-icon.svg +4 -0
  169. package/bundled-skills/remotion-create/tailwind.md +11 -0
  170. package/bundled-skills/remotion-create/video-layout.md +9 -0
  171. package/bundled-skills/remotion-docs/SKILL.md +67 -0
  172. package/bundled-skills/remotion-docs/agents/openai.yaml +7 -0
  173. package/bundled-skills/remotion-docs/assets/remotion-icon.svg +4 -0
  174. package/bundled-skills/remotion-interactivity/SKILL.md +270 -0
  175. package/bundled-skills/remotion-interactivity/agents/openai.yaml +7 -0
  176. package/bundled-skills/remotion-interactivity/assets/remotion-icon.svg +4 -0
  177. package/bundled-skills/remotion-render/SKILL.md +48 -0
  178. package/bundled-skills/remotion-render/agents/openai.yaml +7 -0
  179. package/bundled-skills/remotion-render/assets/remotion-icon.svg +4 -0
  180. package/bundled-skills/remotion-render/transparent-videos.md +106 -0
  181. package/bundled-skills/saas-pricing-strategist/SKILL.md +169 -0
  182. package/bundled-skills/score-eval/SKILL.md +35 -0
  183. package/bundled-skills/writing-guidelines/SKILL.md +60 -0
  184. package/package.json +1 -1
  185. package/skills_index.json +980 -3
@@ -0,0 +1,324 @@
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+
4
+ import argparse
5
+ import json
6
+ import os
7
+ import ssl
8
+ import subprocess
9
+ import sys
10
+ import urllib.error
11
+ import urllib.request
12
+ from pathlib import Path
13
+
14
+
15
+ PROVIDER_URL = "https://platform.dknowc.cn/auth/#/login"
16
+ DEFAULT_ENDPOINT = "https://open.dknowc.cn/dependable/search"
17
+
18
+
19
+ class ConfigurationError(RuntimeError):
20
+ pass
21
+
22
+
23
+ class InvalidCredentialError(ConfigurationError):
24
+ pass
25
+
26
+
27
+ class ServiceUnavailableError(ConfigurationError):
28
+ pass
29
+
30
+
31
+ def trusted_search_ssl_context() -> ssl.SSLContext:
32
+ candidates: list[str] = []
33
+ try:
34
+ import certifi
35
+
36
+ candidates.append(certifi.where())
37
+ except ImportError:
38
+ pass
39
+ defaults = ssl.get_default_verify_paths()
40
+ candidates.extend(
41
+ path
42
+ for path in (
43
+ defaults.cafile,
44
+ "/etc/ssl/cert.pem",
45
+ "/opt/homebrew/etc/openssl@3/cert.pem",
46
+ "/usr/local/etc/openssl@3/cert.pem",
47
+ )
48
+ if path
49
+ )
50
+ for candidate in dict.fromkeys(candidates):
51
+ if Path(candidate).is_file():
52
+ return ssl.create_default_context(cafile=candidate)
53
+ return ssl.create_default_context()
54
+
55
+
56
+ def credential_path() -> Path:
57
+ override = os.getenv("FACT_CHECK_X_TRUSTED_SEARCH_KEY_FILE", "").strip()
58
+ if override:
59
+ return Path(override).expanduser()
60
+ return Path.home() / ".fact-check-x" / "credentials" / "trusted-search-key"
61
+
62
+
63
+ def _validate_key_shape(key: str) -> str:
64
+ normalized = key.strip()
65
+ if (
66
+ len(normalized) < 16
67
+ or any(character.isspace() for character in normalized)
68
+ or "*" in normalized
69
+ ):
70
+ raise ConfigurationError("可信搜索 Key 格式不正确。")
71
+ return normalized
72
+
73
+
74
+ def _shared_trusted_search_key() -> str:
75
+ path = credential_path()
76
+ if not path.is_file():
77
+ return ""
78
+ if os.name != "nt" and path.stat().st_mode & 0o077:
79
+ raise ConfigurationError("本机可信搜索配置权限不安全,请重新执行自动配置。")
80
+ return _validate_key_shape(path.read_text(encoding="utf-8"))
81
+
82
+
83
+ def trusted_search_key_candidates() -> list[tuple[str, str]]:
84
+ candidates = []
85
+ shared_key = _shared_trusted_search_key()
86
+ if shared_key:
87
+ candidates.append(("shared_local_credential", shared_key))
88
+ environment_key = os.getenv("TRUSTED_SEARCH_KEY", "").strip()
89
+ if environment_key:
90
+ normalized = _validate_key_shape(environment_key)
91
+ if all(normalized != key for _, key in candidates):
92
+ candidates.append(("environment", normalized))
93
+ return candidates
94
+
95
+
96
+ def load_trusted_search_key() -> str:
97
+ candidates = trusted_search_key_candidates()
98
+ return candidates[0][1] if candidates else ""
99
+
100
+
101
+ def key_source() -> str:
102
+ candidates = trusted_search_key_candidates()
103
+ return candidates[0][0] if candidates else "missing"
104
+
105
+
106
+ def clear_trusted_search_key() -> bool:
107
+ path = credential_path()
108
+ existed = path.exists()
109
+ path.unlink(missing_ok=True)
110
+ return existed
111
+
112
+
113
+ def validate_trusted_search_key(key: str) -> None:
114
+ normalized = _validate_key_shape(key)
115
+ endpoint = os.getenv("FACTCHECK_TRUSTED_SEARCH_URL", DEFAULT_ENDPOINT).strip()
116
+ payload = {
117
+ "query": "住房公积金管理条例",
118
+ "segmentCount": 1,
119
+ "simplified": True,
120
+ "return_full_content": False,
121
+ }
122
+ request = urllib.request.Request(
123
+ endpoint,
124
+ data=json.dumps(payload, ensure_ascii=False).encode("utf-8"),
125
+ method="POST",
126
+ headers={"Content-Type": "application/json", "api-key": normalized},
127
+ )
128
+ try:
129
+ with urllib.request.urlopen(
130
+ request,
131
+ timeout=20,
132
+ context=trusted_search_ssl_context(),
133
+ ) as response:
134
+ raw = response.read().decode("utf-8")
135
+ status = response.status
136
+ except urllib.error.HTTPError as exc:
137
+ if exc.code in {401, 403}:
138
+ raise InvalidCredentialError("MaaS 返回的 Key 无效或无可信搜索权限。") from None
139
+ raise ServiceUnavailableError(f"可信搜索服务暂时不可用(HTTP {exc.code})。") from None
140
+ except (urllib.error.URLError, TimeoutError, OSError):
141
+ raise ServiceUnavailableError("暂时无法连接可信搜索服务,请检查网络后重试。") from None
142
+ try:
143
+ data = json.loads(raw)
144
+ except json.JSONDecodeError:
145
+ raise ServiceUnavailableError("可信搜索服务返回异常,请稍后重试。") from None
146
+ code = data.get("code") if isinstance(data, dict) else None
147
+ if status != 200 or not isinstance(data, dict):
148
+ raise ServiceUnavailableError("可信搜索服务返回异常,请稍后重试。")
149
+ if code not in (None, 0, 200):
150
+ if code in (401, 403):
151
+ raise InvalidCredentialError("MaaS 返回的 Key 无效或无可信搜索权限。")
152
+ raise ServiceUnavailableError("可信搜索服务返回异常,请稍后重试。")
153
+
154
+
155
+ def validated_trusted_search_key() -> tuple[str, str]:
156
+ for source, key in trusted_search_key_candidates():
157
+ try:
158
+ validate_trusted_search_key(key)
159
+ except ConfigurationError:
160
+ continue
161
+ return key, source
162
+ return "", "missing"
163
+
164
+
165
+ def trusted_search_key_for_execution() -> tuple[str, str, str]:
166
+ transient_candidate: tuple[str, str] | None = None
167
+ for source, key in trusted_search_key_candidates():
168
+ try:
169
+ validate_trusted_search_key(key)
170
+ except ServiceUnavailableError:
171
+ if transient_candidate is None:
172
+ transient_candidate = (source, key)
173
+ continue
174
+ except ConfigurationError:
175
+ continue
176
+ return key, source, "valid"
177
+ if transient_candidate is not None:
178
+ source, key = transient_candidate
179
+ return key, source, "service_unavailable"
180
+ return "", "missing", "missing"
181
+
182
+
183
+ def onboarding_script_candidates() -> list[Path]:
184
+ script_root = Path(__file__).resolve().parent
185
+ skill_root = script_root.parent
186
+ candidates = [
187
+ skill_root
188
+ / "modules"
189
+ / "llm-answer-reference-compare"
190
+ / "assets"
191
+ / "tool"
192
+ / "dist"
193
+ / "trusted-search-onboarding.js",
194
+ skill_root.parent
195
+ / "llm-answer-reference-compare"
196
+ / "assets"
197
+ / "tool"
198
+ / "dist"
199
+ / "trusted-search-onboarding.js",
200
+ ]
201
+ output = []
202
+ for candidate in candidates:
203
+ resolved = candidate.resolve()
204
+ if resolved not in output:
205
+ output.append(resolved)
206
+ return output
207
+
208
+
209
+ def locate_onboarding_script() -> Path:
210
+ for candidate in onboarding_script_candidates():
211
+ if candidate.is_file():
212
+ return candidate
213
+ searched = "、".join(str(path) for path in onboarding_script_candidates())
214
+ raise ConfigurationError(f"未找到可信搜索自动配置组件;已检查:{searched}")
215
+
216
+
217
+ def run_onboarding(*, force: bool = False) -> dict:
218
+ script = locate_onboarding_script()
219
+ command = [
220
+ "node",
221
+ str(script),
222
+ "--credential-file",
223
+ str(credential_path()),
224
+ "--provider-url",
225
+ PROVIDER_URL,
226
+ ]
227
+ if force:
228
+ command.append("--force")
229
+ process = subprocess.run(
230
+ command,
231
+ text=True,
232
+ capture_output=True,
233
+ check=False,
234
+ env=os.environ.copy(),
235
+ )
236
+ lines = [line for line in process.stdout.splitlines() if line.strip()]
237
+ result = {}
238
+ if lines:
239
+ try:
240
+ result = json.loads(lines[-1])
241
+ except json.JSONDecodeError:
242
+ result = {}
243
+ if process.returncode or result.get("status") not in {
244
+ "configured",
245
+ "already_configured",
246
+ }:
247
+ message = str(result.get("error") or "").strip()
248
+ if not message:
249
+ message = "可信搜索自动配置未完成,请保持浏览器打开并完成登录。"
250
+ raise ConfigurationError(message)
251
+ load_trusted_search_key()
252
+ return {
253
+ "status": result["status"],
254
+ "providerUrl": PROVIDER_URL,
255
+ "validated": True,
256
+ "source": str(result.get("source") or "maas_console"),
257
+ "created": bool(result.get("created")),
258
+ "message": (
259
+ "检测到已有有效配置,已跳过 MaaS 登录。"
260
+ if result["status"] == "already_configured"
261
+ else "MaaS 登录与可信搜索配置已完成,流水线可以自动继续。"
262
+ ),
263
+ }
264
+
265
+
266
+ def configure() -> dict:
267
+ existing, source, validation = trusted_search_key_for_execution()
268
+ if existing:
269
+ return {
270
+ "status": "already_configured",
271
+ "providerUrl": PROVIDER_URL,
272
+ "validated": validation == "valid",
273
+ "validation": validation,
274
+ "source": source,
275
+ "created": False,
276
+ "message": (
277
+ "检测到已有有效可信搜索配置,已跳过 MaaS 登录。"
278
+ if validation == "valid"
279
+ else "检测到已有可信搜索配置;服务暂时不可达,已保留配置且不会要求重复登录。"
280
+ ),
281
+ }
282
+ return run_onboarding(force=True)
283
+
284
+
285
+ def parser() -> argparse.ArgumentParser:
286
+ root = argparse.ArgumentParser(description="Fact-Check-X 可信搜索跨载体自动配置。")
287
+ commands = root.add_subparsers(dest="command", required=True)
288
+ commands.add_parser("configure", help="登录 MaaS 后自动获取或创建 Key 并完成配置")
289
+ commands.add_parser("status", help="检查共享配置,不显示 Key")
290
+ commands.add_parser("clear", help="清除本机共享的可信搜索 Key")
291
+ return root
292
+
293
+
294
+ def main() -> int:
295
+ args = parser().parse_args()
296
+ try:
297
+ if args.command == "configure":
298
+ print(json.dumps(configure(), ensure_ascii=False))
299
+ return 0
300
+ if args.command == "clear":
301
+ print(json.dumps({
302
+ "status": "cleared",
303
+ "removed": clear_trusted_search_key(),
304
+ }, ensure_ascii=False))
305
+ return 0
306
+ configured = bool(load_trusted_search_key())
307
+ print(json.dumps({
308
+ "status": "configured" if configured else "configuration_required",
309
+ "configured": configured,
310
+ "source": key_source(),
311
+ "providerUrl": PROVIDER_URL,
312
+ }, ensure_ascii=False))
313
+ return 0 if configured else 3
314
+ except (ConfigurationError, OSError) as exc:
315
+ print(json.dumps({
316
+ "status": "failed",
317
+ "error": str(exc),
318
+ "providerUrl": PROVIDER_URL,
319
+ }, ensure_ascii=False))
320
+ return 1
321
+
322
+
323
+ if __name__ == "__main__":
324
+ raise SystemExit(main())
@@ -0,0 +1,90 @@
1
+ #!/usr/bin/env python3
2
+ """锚点模式门禁的非对称性回归。
3
+
4
+ 比较阶段依据引用池认定「免搜索」,核验阶段可能独立判定该锚点撑不住主张而改走
5
+ 可信搜索。多搜一次是保守方向,必须放行(否则整条流水线死锁);反过来把本应搜索
6
+ 的知识点降级成免搜索属于洗白,必须继续拦住。
7
+ """
8
+ from __future__ import annotations
9
+
10
+ import json
11
+ import sys
12
+ import tempfile
13
+ from pathlib import Path
14
+
15
+ ROOT = Path(__file__).resolve().parents[1]
16
+ sys.path.insert(0, str(ROOT / "scripts"))
17
+ from common import PipelineError
18
+ from fact_check_x import merge_verification
19
+
20
+
21
+ def comparison_for(anchor: dict) -> dict:
22
+ return {
23
+ "knowledgePoints": [
24
+ {
25
+ "id": "K1",
26
+ "role": "direct",
27
+ "statement": "示例主张",
28
+ "trustedAnchor": anchor,
29
+ "claims": {"alpha": {"covered": True}},
30
+ }
31
+ ]
32
+ }
33
+
34
+
35
+ def authority(search_mode: str, request_count: int) -> dict:
36
+ return {
37
+ "schemaVersion": "fact-check-x/authority-result@1",
38
+ "requestId": "K1",
39
+ "searchMode": search_mode,
40
+ "requestCount": request_count,
41
+ "authoritativeFinding": "示例权威结论",
42
+ "verdicts": {"alpha": {"verdict": "supported", "category": "direct_accurate"}},
43
+ "evidenceGaps": [],
44
+ }
45
+
46
+
47
+ GOV_ANCHOR = {"eligible": True, "sourcePolicy": "gov_cn_reference"}
48
+ NO_ANCHOR: dict = {}
49
+
50
+
51
+ def run(anchor: dict, search_mode: str, request_count: int) -> dict:
52
+ with tempfile.TemporaryDirectory(prefix="fcx-anchor-") as raw:
53
+ results_dir = Path(raw)
54
+ (results_dir / "K1.json").write_text(
55
+ json.dumps(authority(search_mode, request_count), ensure_ascii=False),
56
+ encoding="utf-8",
57
+ )
58
+ return merge_verification(comparison_for(anchor), results_dir)
59
+
60
+
61
+ # 1. 期望免搜 + 核验阶段判锚点无效改走可信搜索:放行并留痕
62
+ # 修复前这里直接 PipelineError,整条流水线停在合并阶段,第四步出不来
63
+ downgraded = run(GOV_ANCHOR, "trusted_search", 1)
64
+ assert downgraded["govExemptCount"] == 0, downgraded["govExemptCount"]
65
+ assert downgraded["trustedSearchRequestCount"] == 1
66
+ assert downgraded["anchorDowngrades"] == ["K1"], downgraded["anchorDowngrades"]
67
+
68
+ # 2. 期望免搜 + 实际免搜:正常,不留降级记录
69
+ ok = run(GOV_ANCHOR, "gov_exempt", 0)
70
+ assert ok["govExemptCount"] == 1, ok["govExemptCount"]
71
+ assert ok["anchorDowngrades"] == [], ok["anchorDowngrades"]
72
+
73
+ # 3. 期望搜索 + 实际降级成免搜索:洗白,必须继续拒绝
74
+ for forged_mode in ("gov_exempt", "dknow_exempt"):
75
+ try:
76
+ run(NO_ANCHOR, forged_mode, 0)
77
+ except PipelineError:
78
+ pass
79
+ else:
80
+ raise AssertionError(f"{forged_mode} 降级未被拦截")
81
+
82
+ # 4. 模式相符但请求数对不上:仍然拒绝
83
+ try:
84
+ run(GOV_ANCHOR, "gov_exempt", 3)
85
+ except PipelineError:
86
+ pass
87
+ else:
88
+ raise AssertionError("免搜模式下的异常请求数未被拦截")
89
+
90
+ print("PASS 锚点门禁非对称:多搜放行、洗白拦截")