foxclaw 0.1.2 → 0.1.4

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 (368) hide show
  1. package/index.js +1 -8
  2. package/package.json +1 -1
  3. package/python/.env.example +8 -0
  4. package/python/.github/workflows/ci.yml +47 -2
  5. package/python/.github/workflows/release.yml +5 -0
  6. package/python/CHANGELOG.md +100 -0
  7. package/python/CHANGELOG_CN.md +100 -0
  8. package/python/README.md +17 -2
  9. package/python/README_CN.md +38 -20
  10. package/python/bench/bm25_audit.py +1 -14
  11. package/python/bench/bm25_regression.py +0 -1
  12. package/python/bench/bm25_stress.py +0 -4
  13. package/python/bench/cd_audit.py +0 -23
  14. package/python/bench/chaos.py +6 -8
  15. package/python/bench/e2e_real_models.py +0 -1
  16. package/python/bench/llm_retry_test.py +2 -9
  17. package/python/bench/load_test.py +5 -8
  18. package/python/bench/vector_ivf_upgrade.py +1 -7
  19. package/python/docs/CONTENT_GUARD.md +189 -0
  20. package/python/docs/CONTENT_GUARD_CN.md +189 -0
  21. package/python/docs/RELEASE.md +78 -37
  22. package/python/docs/RUN_CN.md +2 -2
  23. package/python/evals/governance/cases.py +0 -37
  24. package/python/evals/governance/run_evals.py +5 -37
  25. package/python/examples/governance_middleware_demo.py +1 -6
  26. package/python/examples/langgraph_governance_demo.py +7 -11
  27. package/python/foxclaw/__init__.py +74 -152
  28. package/python/foxclaw/agents/base.py +82 -136
  29. package/python/foxclaw/agents/impl/_helpers.py +2 -10
  30. package/python/foxclaw/agents/impl/finance.py +4 -21
  31. package/python/foxclaw/agents/impl/office.py +7 -24
  32. package/python/foxclaw/agents/impl/supervisor.py +12 -79
  33. package/python/foxclaw/agents/mixins/_context.py +0 -23
  34. package/python/foxclaw/agents/mixins/augment.py +10 -16
  35. package/python/foxclaw/agents/mixins/llm.py +7 -6
  36. package/python/foxclaw/agents/mixins/memory.py +23 -3
  37. package/python/foxclaw/agents/mixins/preference.py +58 -8
  38. package/python/foxclaw/agents/mixins/prompt.py +59 -12
  39. package/python/foxclaw/agents/mixins/skillbox.py +6 -6
  40. package/python/foxclaw/agents/mixins/tool_exec.py +16 -42
  41. package/python/foxclaw/agents/rules/finance/RULES.md +131 -0
  42. package/python/foxclaw/api/__init__.py +18 -77
  43. package/python/foxclaw/api/_state.py +43 -0
  44. package/python/foxclaw/api/bootstrap.py +114 -0
  45. package/python/foxclaw/api/middleware.py +5 -28
  46. package/python/foxclaw/api/routes/ahs.py +60 -73
  47. package/python/foxclaw/api/routes/attachments.py +5 -6
  48. package/python/foxclaw/api/routes/browser.py +7 -11
  49. package/python/foxclaw/api/routes/domain.py +18 -26
  50. package/python/foxclaw/api/routes/knowledge.py +3 -21
  51. package/python/foxclaw/api/routes/llm_admin.py +21 -33
  52. package/python/foxclaw/api/routes/memory.py +50 -12
  53. package/python/foxclaw/api/routes/orchestration.py +3 -5
  54. package/python/foxclaw/api/routes/recovery.py +4 -3
  55. package/python/foxclaw/api/routes/review.py +2 -11
  56. package/python/foxclaw/api/routes/search.py +3 -7
  57. package/python/foxclaw/api/routes/system.py +33 -49
  58. package/python/foxclaw/api/routes/task.py +31 -49
  59. package/python/foxclaw/api/routes/ws.py +2 -6
  60. package/python/foxclaw/api/spine.py +1 -4
  61. package/python/foxclaw/behavior/__init__.py +20 -0
  62. package/python/foxclaw/behavior/behavior.yaml +62 -0
  63. package/python/foxclaw/behavior/spec.py +287 -0
  64. package/python/foxclaw/browser/action/action_executor.py +1 -5
  65. package/python/foxclaw/browser/action/action_types.py +0 -1
  66. package/python/foxclaw/browser/action/url_policy.py +1 -5
  67. package/python/foxclaw/browser/adapter/web_adapter.py +1 -1
  68. package/python/foxclaw/browser/driver/playwright_driver.py +20 -16
  69. package/python/foxclaw/browser/perception/dom_extractor.py +0 -1
  70. package/python/foxclaw/browser/perception/element_indexer.py +1 -2
  71. package/python/foxclaw/browser/service.py +7 -11
  72. package/python/foxclaw/capability/embedding_service.py +4 -6
  73. package/python/foxclaw/channels/__init__.py +9 -11
  74. package/python/foxclaw/channels/discord.py +4 -13
  75. package/python/foxclaw/channels/dispatcher.py +2 -27
  76. package/python/foxclaw/channels/feishu.py +10 -20
  77. package/python/foxclaw/channels/openclaw_bridge.py +4 -9
  78. package/python/foxclaw/channels/signing.py +9 -9
  79. package/python/foxclaw/channels/telegram.py +3 -4
  80. package/python/foxclaw/channels/weixin.py +7 -17
  81. package/python/foxclaw/cli.py +42 -13
  82. package/python/foxclaw/cognitive/reasoner.py +9 -10
  83. package/python/foxclaw/cognitive/state.py +18 -24
  84. package/python/foxclaw/core/audit_chain.py +1 -3
  85. package/python/foxclaw/core/audit_sink.py +3 -3
  86. package/python/foxclaw/core/compaction.py +8 -26
  87. package/python/foxclaw/core/config.py +2 -2
  88. package/python/foxclaw/core/confirm_nonce.py +6 -12
  89. package/python/foxclaw/core/content_guard.py +341 -0
  90. package/python/foxclaw/core/envfile.py +2 -2
  91. package/python/foxclaw/core/failure_policy.py +17 -26
  92. package/python/foxclaw/core/gateway.py +6 -10
  93. package/python/foxclaw/core/governance_audit.py +3 -7
  94. package/python/foxclaw/core/idempotency.py +15 -22
  95. package/python/foxclaw/core/injection_guard.py +10 -29
  96. package/python/foxclaw/core/memory_adapter.py +53 -60
  97. package/python/foxclaw/core/metrics.py +4 -5
  98. package/python/foxclaw/core/model_router.py +74 -150
  99. package/python/foxclaw/core/paths.py +1 -1
  100. package/python/foxclaw/core/recovery.py +21 -34
  101. package/python/foxclaw/core/scheduler.py +23 -26
  102. package/python/foxclaw/core/secrets.py +0 -3
  103. package/python/foxclaw/core/sensitive_write.py +283 -0
  104. package/python/foxclaw/core/sessions.py +4 -6
  105. package/python/foxclaw/core/settings.py +16 -32
  106. package/python/foxclaw/core/shared_write.py +11 -23
  107. package/python/foxclaw/core/steering_queue.py +12 -11
  108. package/python/foxclaw/core/system_bus.py +1 -1
  109. package/python/foxclaw/core/thought_chain.py +30 -32
  110. package/python/foxclaw/core/token_meter.py +9 -13
  111. package/python/foxclaw/core/tools.py +4 -12
  112. package/python/foxclaw/core/tracing.py +25 -40
  113. package/python/foxclaw/core/truth_pipeline.py +5 -9
  114. package/python/foxclaw/core/update_check.py +149 -0
  115. package/python/foxclaw/core/vec_recall_adapter.py +11 -37
  116. package/python/foxclaw/core/verify.py +6 -14
  117. package/python/foxclaw/core/web_search.py +4 -28
  118. package/python/foxclaw/finance/evidence.py +14 -18
  119. package/python/foxclaw/finance/fx.py +1 -3
  120. package/python/foxclaw/finance/graph.py +9 -18
  121. package/python/foxclaw/finance/models.py +20 -24
  122. package/python/foxclaw/finance/nlparse.py +12 -72
  123. package/python/foxclaw/finance/policy.py +8 -36
  124. package/python/foxclaw/finance/reasoner.py +3 -11
  125. package/python/foxclaw/finance/reasoning.py +13 -34
  126. package/python/foxclaw/finance/reconcile.py +5 -10
  127. package/python/foxclaw/finance/runtime.py +19 -46
  128. package/python/foxclaw/governance/middleware.py +13 -21
  129. package/python/foxclaw/harness/_base.py +2 -8
  130. package/python/foxclaw/harness/_policy.py +19 -38
  131. package/python/foxclaw/harness/drift.py +1 -13
  132. package/python/foxclaw/harness/event_bus.py +2 -5
  133. package/python/foxclaw/harness/harness.py +9 -97
  134. package/python/foxclaw/harness/loop_detection.py +11 -18
  135. package/python/foxclaw/itinerary/amap_client.py +2 -5
  136. package/python/foxclaw/itinerary/models.py +7 -7
  137. package/python/foxclaw/itinerary/reasoner.py +1 -6
  138. package/python/foxclaw/itinerary/reasoning.py +15 -21
  139. package/python/foxclaw/itinerary/runtime.py +8 -12
  140. package/python/foxclaw/kernel/capabilities.py +5 -6
  141. package/python/foxclaw/kernel/policy_gate.py +4 -10
  142. package/python/foxclaw/kernel/process_manager.py +17 -36
  143. package/python/foxclaw/kernel/scheduler.py +3 -9
  144. package/python/foxclaw/kernel/sentinel.py +13 -23
  145. package/python/foxclaw/kernel/sentinel_client.py +12 -15
  146. package/python/foxclaw/kernel/spine.py +9 -9
  147. package/python/foxclaw/knowledge/__init__.py +4 -20
  148. package/python/foxclaw/knowledge/truth_crystallizer.py +5 -8
  149. package/python/foxclaw/learning/knowledge_graph.py +4 -9
  150. package/python/foxclaw/learning/models.py +13 -13
  151. package/python/foxclaw/learning/reasoner.py +1 -4
  152. package/python/foxclaw/learning/runtime.py +19 -31
  153. package/python/foxclaw/learning/spaced_repetition.py +2 -7
  154. package/python/foxclaw/mcp_server.py +2 -3
  155. package/python/foxclaw/orchestration/__init__.py +16 -33
  156. package/python/foxclaw/pipeline/cognitive_pipeline.py +45 -97
  157. package/python/foxclaw/pipeline/stage_meta.py +21 -0
  158. package/python/foxclaw/pipeline/supervisor_stages.py +13 -42
  159. package/python/foxclaw/planning/llm_planner.py +3 -3
  160. package/python/foxclaw/planning/planner.py +6 -11
  161. package/python/foxclaw/search/agent_port.py +7 -11
  162. package/python/foxclaw/search/evidence.py +0 -4
  163. package/python/foxclaw/search/internal_search.py +3 -5
  164. package/python/foxclaw/search/models.py +11 -13
  165. package/python/foxclaw/search/planner.py +3 -3
  166. package/python/foxclaw/search/retriever.py +1 -1
  167. package/python/foxclaw/search/runtime.py +1 -8
  168. package/python/foxclaw/skills/__init__.py +46 -100
  169. package/python/foxclaw/skills/archive.py +111 -0
  170. package/python/foxclaw/skills/fetch.py +226 -0
  171. package/python/foxclaw/skills/hooks_spec.py +21 -25
  172. package/python/foxclaw/skills/install_cmd.py +216 -0
  173. package/python/foxclaw/skills/lifecycle.py +16 -42
  174. package/python/foxclaw/skills/markdown_skill.py +10 -9
  175. package/python/foxclaw/skills/mcp_client.py +5 -11
  176. package/python/foxclaw/skills/mcp_store.py +0 -3
  177. package/python/foxclaw/skills/permission.py +13 -48
  178. package/python/foxclaw/skills/pipeline.py +21 -53
  179. package/python/foxclaw/skills/policy.py +4 -10
  180. package/python/foxclaw/skills/review.py +3 -3
  181. package/python/frontend/deck/auth.js +0 -7
  182. package/python/frontend/deck/chat_model_picker.js +0 -54
  183. package/python/frontend/deck/domain.js +0 -20
  184. package/python/frontend/deck/main.js +0 -144
  185. package/python/frontend/deck/markdown.js +0 -4
  186. package/python/frontend/deck/settings.js +1 -18
  187. package/python/frontend/deck/state.js +0 -1
  188. package/python/frontend/deck/util.js +0 -2
  189. package/python/frontend/deck/visuals.js +0 -22
  190. package/python/frontend/planet/planet.js +18 -201
  191. package/python/frontend/v3/assets/index-JKfHlb-a.css +1 -0
  192. package/python/frontend/v3/assets/index-tN90dpUH.js +34 -0
  193. package/python/frontend/v3/index.html +3 -3
  194. package/python/frontend/widget_runtime.js +0 -43
  195. package/python/frontend/widgets.js +0 -57
  196. package/python/frontend_src/index.html +1 -1
  197. package/python/frontend_src/package-lock.json +2 -2
  198. package/python/frontend_src/package.json +1 -1
  199. package/python/frontend_src/src/App.vue +3 -7
  200. package/python/frontend_src/src/api.js +0 -7
  201. package/python/frontend_src/src/components/ChatDock.vue +27 -39
  202. package/python/frontend_src/src/components/DocViewer.vue +0 -9
  203. package/python/frontend_src/src/components/Inspector.vue +0 -6
  204. package/python/frontend_src/src/components/LeftRail.vue +60 -39
  205. package/python/frontend_src/src/components/MenuPopover.vue +9 -11
  206. package/python/frontend_src/src/components/MessageStream.vue +18 -30
  207. package/python/frontend_src/src/components/ModelPicker.vue +0 -5
  208. package/python/frontend_src/src/components/QuickIngest.vue +0 -6
  209. package/python/frontend_src/src/components/Stage.vue +71 -25
  210. package/python/frontend_src/src/components/ToolMenu.vue +0 -3
  211. package/python/frontend_src/src/components/TopBar.vue +38 -10
  212. package/python/frontend_src/src/components/panels/ActivityPanel.vue +0 -2
  213. package/python/frontend_src/src/components/panels/AgentsPanel.vue +2 -5
  214. package/python/frontend_src/src/components/panels/KnowledgePanel.vue +0 -16
  215. package/python/frontend_src/src/components/panels/MemoryPanel.vue +42 -3
  216. package/python/frontend_src/src/components/panels/PanelBox.vue +6 -7
  217. package/python/frontend_src/src/components/panels/ReviewPanel.vue +0 -16
  218. package/python/frontend_src/src/components/panels/SettingsPanel.vue +142 -38
  219. package/python/frontend_src/src/components/panels/TasksPanel.vue +0 -1
  220. package/python/frontend_src/src/components/panels/ToolsPanel.vue +125 -19
  221. package/python/frontend_src/src/lib/fmt.js +0 -4
  222. package/python/frontend_src/src/lib/i18n.js +53 -0
  223. package/python/frontend_src/src/lib/locales/en.js +173 -0
  224. package/python/frontend_src/src/lib/locales/zh.js +174 -0
  225. package/python/frontend_src/src/lib/md.js +0 -14
  226. package/python/frontend_src/src/main.js +2 -0
  227. package/python/frontend_src/src/stores/app.js +17 -32
  228. package/python/frontend_src/src/stores/chat.js +0 -34
  229. package/python/frontend_src/src/stores/model.js +0 -8
  230. package/python/frontend_src/src/stores/panels.js +16 -44
  231. package/python/frontend_src/src/stores/profile.js +31 -0
  232. package/python/frontend_src/src/stores/system.js +25 -0
  233. package/python/frontend_src/vite.config.js +0 -7
  234. package/python/pyproject.toml +17 -3
  235. package/python/requirements.txt +311 -0
  236. package/python/tests/conftest.py +0 -22
  237. package/python/tests/run_all.py +4 -66
  238. package/python/tests/test_agent_lifecycle.py +4 -38
  239. package/python/tests/test_agent_rules.py +81 -0
  240. package/python/tests/test_ahs_p2.py +4 -25
  241. package/python/tests/test_anthropic_tools.py +1 -15
  242. package/python/tests/test_api.py +19 -9
  243. package/python/tests/test_api_auth.py +1 -5
  244. package/python/tests/test_attachments.py +9 -25
  245. package/python/tests/test_audit_chain.py +2 -7
  246. package/python/tests/test_audit_sink.py +0 -6
  247. package/python/tests/test_behavior_spec.py +198 -0
  248. package/python/tests/test_browser.py +2 -6
  249. package/python/tests/test_browser_request_guard.py +0 -4
  250. package/python/tests/test_capabilities.py +0 -6
  251. package/python/tests/test_channel_signing.py +2 -9
  252. package/python/tests/test_channels.py +1 -11
  253. package/python/tests/test_config_layering.py +0 -4
  254. package/python/tests/test_confirm_nonce.py +1 -12
  255. package/python/tests/test_conflict_dismiss.py +143 -0
  256. package/python/tests/test_content_guard.py +177 -0
  257. package/python/tests/test_context_compaction_wiring.py +1 -7
  258. package/python/tests/test_dispatch.py +0 -6
  259. package/python/tests/test_domain_evals.py +11 -80
  260. package/python/tests/test_domain_import.py +0 -6
  261. package/python/tests/test_domain_reasoners.py +0 -14
  262. package/python/tests/test_e2e.py +0 -17
  263. package/python/tests/test_emb_cache.py +7 -17
  264. package/python/tests/test_embedding_singleton.py +1 -3
  265. package/python/tests/test_evals_smoke.py +0 -24
  266. package/python/tests/test_event_spine.py +10 -18
  267. package/python/tests/test_evidence_semantic.py +1 -14
  268. package/python/tests/test_evidence_sse.py +3 -6
  269. package/python/tests/test_file_ingest.py +0 -11
  270. package/python/tests/test_finance.py +0 -9
  271. package/python/tests/test_finance_ledger.py +5 -12
  272. package/python/tests/test_finance_persist.py +8 -12
  273. package/python/tests/test_gateway.py +3 -22
  274. package/python/tests/test_governance.py +5 -52
  275. package/python/tests/test_governance_audit.py +4 -39
  276. package/python/tests/test_governance_middleware.py +1 -7
  277. package/python/tests/test_hkc.py +6 -24
  278. package/python/tests/test_hkc_index_build_manager.py +0 -33
  279. package/python/tests/test_hkc_manifest_reuse.py +4 -27
  280. package/python/tests/test_hkc_search_event_sync.py +3 -54
  281. package/python/tests/test_import_path_guard.py +0 -6
  282. package/python/tests/test_injection_guard.py +18 -36
  283. package/python/tests/test_integration.py +5 -29
  284. package/python/tests/test_itinerary.py +0 -3
  285. package/python/tests/test_kee_semantic.py +0 -5
  286. package/python/tests/test_kernel.py +1 -11
  287. package/python/tests/test_learning.py +3 -12
  288. package/python/tests/test_llm_admin.py +4 -19
  289. package/python/tests/test_llm_planner.py +1 -23
  290. package/python/tests/test_markdown_skills.py +3 -14
  291. package/python/tests/test_markdown_skills_openclaw.py +4 -9
  292. package/python/tests/test_mcp_connectors.py +3 -15
  293. package/python/tests/test_mcp_guardian_discovery.py +2 -6
  294. package/python/tests/test_mcp_no_memory.py +3 -3
  295. package/python/tests/test_mcp_policy_gate.py +0 -1
  296. package/python/tests/test_mcp_server.py +6 -9
  297. package/python/tests/test_memory_recall.py +1 -4
  298. package/python/tests/test_metrics.py +0 -8
  299. package/python/tests/test_multi_chain.py +1 -13
  300. package/python/tests/test_multimodal.py +5 -18
  301. package/python/tests/test_negotiation.py +3 -13
  302. package/python/tests/test_orchestration.py +1 -11
  303. package/python/tests/test_orchestration_decoupled.py +0 -6
  304. package/python/tests/test_p3_p5_hardening.py +3 -17
  305. package/python/tests/test_parallel_dispatch.py +4 -12
  306. package/python/tests/test_pipeline.py +17 -58
  307. package/python/tests/test_plan_visible.py +4 -4
  308. package/python/tests/test_planner.py +0 -7
  309. package/python/tests/test_policy_gate.py +2 -3
  310. package/python/tests/test_profile.py +67 -0
  311. package/python/tests/test_readme_claims.py +163 -0
  312. package/python/tests/test_reasoner.py +2 -17
  313. package/python/tests/test_release_check.py +2 -13
  314. package/python/tests/test_resume_checkpoint.py +7 -15
  315. package/python/tests/test_routing.py +2 -12
  316. package/python/tests/test_scheduler.py +7 -10
  317. package/python/tests/test_search.py +1 -11
  318. package/python/tests/test_secrets.py +4 -10
  319. package/python/tests/test_semantic.py +1 -10
  320. package/python/tests/test_sensitive_write.py +181 -0
  321. package/python/tests/test_sessions.py +3 -4
  322. package/python/tests/test_settings.py +2 -18
  323. package/python/tests/test_shadow_learning.py +3 -43
  324. package/python/tests/test_shared_write.py +0 -7
  325. package/python/tests/test_skill_taint_gate.py +1 -10
  326. package/python/tests/test_skills.py +2 -18
  327. package/python/tests/test_skills_fetch.py +282 -0
  328. package/python/tests/test_stats_and_events.py +2 -5
  329. package/python/tests/test_streaming.py +2 -7
  330. package/python/tests/test_supervisor_pipeline.py +4 -5
  331. package/python/tests/test_supervisor_triage.py +4 -10
  332. package/python/tests/test_taint.py +2 -7
  333. package/python/tests/test_task_stream.py +1 -12
  334. package/python/tests/test_thinking_sse.py +0 -1
  335. package/python/tests/test_tool_center.py +1 -17
  336. package/python/tests/test_tool_parallel.py +2 -2
  337. package/python/tests/test_tool_params.py +1 -5
  338. package/python/tests/test_tool_use.py +0 -13
  339. package/python/tests/test_toplevel_concurrency.py +3 -4
  340. package/python/tests/test_tracing.py +7 -41
  341. package/python/tests/test_truth_crystallizer.py +4 -6
  342. package/python/tests/test_truth_flow.py +2 -7
  343. package/python/tests/test_update_check.py +95 -0
  344. package/python/tests/test_verify_evidence.py +8 -21
  345. package/python/tests/test_version_sync.py +35 -6
  346. package/python/tests/test_web_trigger.py +0 -17
  347. package/python/tools/build_content_lexicon.py +218 -0
  348. package/python/tools/bundle_web.py +98 -0
  349. package/python/tools/check_faiss_type.py +0 -1
  350. package/python/tools/check_log.ps1 +0 -8
  351. package/python/tools/mypy_baseline.txt +12 -4
  352. package/python/tools/mypy_ratchet.py +52 -6
  353. package/python/tools/pack.py +0 -5
  354. package/python/tools/recheck_conflicts.py +156 -0
  355. package/python/tools/release_check.py +24 -15
  356. package/python/tools/restart_foxclaw.ps1 +5 -8
  357. package/python/tools/shared_write_audit.py +1 -15
  358. package/python/tools/size_check.py +1 -1
  359. package/python/tools/strip_comments.py +228 -0
  360. package/python/vendor/hkc/hkc_api/routes/ability.py +31 -0
  361. package/python/vendor/hkc/hkc_api/schemas.py +11 -0
  362. package/python/vendor/hkc/hkc_kee/kee.py +124 -3
  363. package/python/vendor/hkc/hkc_sdk/base.py +22 -0
  364. package/python/vendor/hkc/hkc_sdk/direct_client.py +10 -0
  365. package/python/vendor/hkc/hkc_sdk/http_client.py +19 -0
  366. package/python/foxclaw/api/app.py +0 -123
  367. package/python/frontend/v3/assets/index-BD7VPbMV.js +0 -33
  368. package/python/frontend/v3/assets/index-CAvnYWGH.css +0 -1
@@ -3,11 +3,11 @@
3
3
  <head>
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <title>Foxclaw Deck · v3</title>
6
+ <title>Foxclaw</title>
7
7
  <!-- 内联 SVG 图标:不新增静态文件,也避免浏览器探 /favicon.ico 报 404 -->
8
8
  <link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><rect width='32' height='32' rx='9' fill='%23179d5f'/><circle cx='16' cy='16' r='6.5' fill='%23f4fbf0'/></svg>" />
9
- <script type="module" crossorigin src="/v3/assets/index-BD7VPbMV.js"></script>
10
- <link rel="stylesheet" crossorigin href="/v3/assets/index-CAvnYWGH.css">
9
+ <script type="module" crossorigin src="/v3/assets/index-tN90dpUH.js"></script>
10
+ <link rel="stylesheet" crossorigin href="/v3/assets/index-JKfHlb-a.css">
11
11
  </head>
12
12
  <body>
13
13
  <div id="app"></div>
@@ -1,39 +1,16 @@
1
- /* ════════════════════════════════════════════════════════════
2
- FOXCLAW DECK — Widget Runtime
3
- 可插拔 widget 系统。新模块只需 registerWidget() 一次。
4
- ════════════════════════════════════════════════════════════ */
5
1
 
6
2
  const WidgetRuntime = (() => {
7
3
 
8
- // ── 注册表:定义有哪些 widget 类型 ──
9
4
  const registry = new Map();
10
5
 
11
- // ── 实例:当前布局中存在的 widget ──
12
- // { instanceId, widgetId, column, order }
13
6
  let instances = [];
14
7
 
15
- // ── 事件订阅:widgetId -> Set<eventType> ──
16
8
  const eventSubs = new Map();
17
9
 
18
- // ── 共享上下文,所有 widget 都能访问 ──
19
10
  let ctx = {};
20
11
 
21
- // ── DOM 列引用 ──
22
12
  const columns = {};
23
13
 
24
- /* ───────────────────────────────────────────
25
- 注册一个 widget 类型
26
- spec = {
27
- id, title, icon,
28
- defaultColumn: 'left'|'center'|'right',
29
- size: {minH, maxH, flex},
30
- events: ['harness_block', ...], // 关心的事件类型
31
- render(el, ctx, instance), // 渲染主体
32
- onEvent(evt, el, ctx), // 收到订阅事件
33
- onAgentChange(agentId, el, ctx), // 激活 Agent 变化
34
- refresh(el, ctx, agentId), // 数据刷新
35
- }
36
- ─────────────────────────────────────────── */
37
14
  function register(spec) {
38
15
  if (!spec.id) throw new Error('widget 必须有 id');
39
16
  registry.set(spec.id, {
@@ -46,14 +23,12 @@ const WidgetRuntime = (() => {
46
23
  refresh: null,
47
24
  ...spec,
48
25
  });
49
- // 建立事件订阅索引
50
26
  (spec.events || []).forEach(ev => {
51
27
  if (!eventSubs.has(ev)) eventSubs.set(ev, new Set());
52
28
  eventSubs.get(ev).add(spec.id);
53
29
  });
54
30
  }
55
31
 
56
- /* 初始化:绑定列容器 + 共享上下文 */
57
32
  function init(columnEls, sharedCtx) {
58
33
  columns.left = columnEls.left;
59
34
  columns.center = columnEls.center;
@@ -61,7 +36,6 @@ const WidgetRuntime = (() => {
61
36
  ctx = sharedCtx || {};
62
37
  }
63
38
 
64
- /* 设置布局(从默认或 localStorage 恢复) */
65
39
  function setLayout(layout) {
66
40
  instances = layout.map((item, i) => ({
67
41
  instanceId: item.instanceId || `inst_${item.widgetId}_${i}`,
@@ -72,7 +46,6 @@ const WidgetRuntime = (() => {
72
46
  renderAll();
73
47
  }
74
48
 
75
- /* 默认布局:每个注册的 widget 放进它的 defaultColumn */
76
49
  function defaultLayout() {
77
50
  return [...registry.values()].map((spec, i) => ({
78
51
  instanceId: `inst_${spec.id}`,
@@ -82,7 +55,6 @@ const WidgetRuntime = (() => {
82
55
  }));
83
56
  }
84
57
 
85
- /* 渲染全部实例 */
86
58
  function renderAll() {
87
59
  Object.values(columns).forEach(col => { if (col) col.innerHTML = ''; });
88
60
  const byCol = { left: [], center: [], right: [] };
@@ -96,7 +68,6 @@ const WidgetRuntime = (() => {
96
68
  });
97
69
  }
98
70
 
99
- /* 挂载单个 widget 到 DOM */
100
71
  function mountWidget(inst, colName) {
101
72
  const spec = registry.get(inst.widgetId);
102
73
  if (!spec) return;
@@ -127,26 +98,22 @@ const WidgetRuntime = (() => {
127
98
  inst._el = panel;
128
99
  inst._body = body;
129
100
 
130
- // 移除按钮
131
101
  panel.querySelector('.ph-remove').onclick = (e) => {
132
102
  e.stopPropagation();
133
103
  removeInstance(inst.instanceId);
134
104
  };
135
105
 
136
- // 调用 widget 自己的渲染
137
106
  try {
138
107
  spec.render(body, ctx, inst);
139
108
  } catch (err) {
140
109
  body.innerHTML = `<div class="empty">渲染错误: ${err.message}</div>`;
141
110
  }
142
111
 
143
- // 首次同步当前 Agent
144
112
  if (spec.onAgentChange && ctx.activeAgent) {
145
113
  try { spec.onAgentChange(ctx.activeAgent, body, ctx); } catch {}
146
114
  }
147
115
  }
148
116
 
149
- /* 事件分发:把事件发给所有订阅的 widget 实例 */
150
117
  function dispatch(eventType, payload) {
151
118
  const subscribers = eventSubs.get(eventType);
152
119
  if (!subscribers) return;
@@ -159,7 +126,6 @@ const WidgetRuntime = (() => {
159
126
  });
160
127
  }
161
128
 
162
- /* 通知所有 widget:激活 Agent 变了 */
163
129
  function notifyAgentChange(agentId) {
164
130
  ctx.activeAgent = agentId;
165
131
  instances.forEach(inst => {
@@ -170,7 +136,6 @@ const WidgetRuntime = (() => {
170
136
  });
171
137
  }
172
138
 
173
- /* 刷新所有 widget 的数据 */
174
139
  function refreshAll(agentId) {
175
140
  instances.forEach(inst => {
176
141
  const spec = registry.get(inst.widgetId);
@@ -180,7 +145,6 @@ const WidgetRuntime = (() => {
180
145
  });
181
146
  }
182
147
 
183
- /* 设置某个实例的 head status 文本 */
184
148
  function setStatus(instanceId, text) {
185
149
  const inst = instances.find(i => i.instanceId === instanceId);
186
150
  if (inst && inst._el) {
@@ -189,7 +153,6 @@ const WidgetRuntime = (() => {
189
153
  }
190
154
  }
191
155
 
192
- /* ── 增删实例 ── */
193
156
  function addInstance(widgetId, column) {
194
157
  const spec = registry.get(widgetId);
195
158
  if (!spec) return;
@@ -214,7 +177,6 @@ const WidgetRuntime = (() => {
214
177
  saveLayout();
215
178
  }
216
179
 
217
- /* ── 拖拽:重排实例 ── */
218
180
  function captureLayoutFromDOM() {
219
181
  const newInstances = [];
220
182
  ['left', 'center', 'right'].forEach(colName => {
@@ -239,7 +201,6 @@ const WidgetRuntime = (() => {
239
201
  saveLayout();
240
202
  }
241
203
 
242
- /* ── 持久化 ── */
243
204
  function saveLayout() {
244
205
  try {
245
206
  const layout = instances.map(i => ({
@@ -256,7 +217,6 @@ const WidgetRuntime = (() => {
256
217
  try {
257
218
  const saved = JSON.parse(localStorage.getItem('foxclaw_widget_layout'));
258
219
  if (saved && saved.length) {
259
- // 过滤掉已不存在的 widget 类型
260
220
  const valid = saved.filter(i => registry.has(i.widgetId));
261
221
  if (valid.length) { setLayout(valid); return true; }
262
222
  }
@@ -264,7 +224,6 @@ const WidgetRuntime = (() => {
264
224
  return false;
265
225
  }
266
226
 
267
- /* ── 编辑模式:显示/隐藏移除按钮 ── */
268
227
  function setEditMode(on) {
269
228
  instances.forEach(inst => {
270
229
  if (!inst._el) return;
@@ -273,7 +232,6 @@ const WidgetRuntime = (() => {
273
232
  });
274
233
  }
275
234
 
276
- /* ── 面板库:列出未激活的 widget 供添加 ── */
277
235
  function getLibrary() {
278
236
  return [...registry.values()].map(spec => ({
279
237
  id: spec.id,
@@ -283,7 +241,6 @@ const WidgetRuntime = (() => {
283
241
  }));
284
242
  }
285
243
 
286
- /* 公开 API */
287
244
  return {
288
245
  register, init, setLayout, defaultLayout, renderAll,
289
246
  dispatch, notifyAgentChange, refreshAll, setStatus,
@@ -1,16 +1,7 @@
1
- /* ════════════════════════════════════════════════════════════
2
- FOXCLAW DECK — Widget Definitions
3
- 每个 widget 是一个 registerWidget() 注册。
4
- 新增模块只需在这里加一个 register block。
5
- ════════════════════════════════════════════════════════════ */
6
1
 
7
- /* 共享工具(由主文件注入到 ctx) */
8
2
  function _esc(s){return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/\n/g,'<br>');}
9
3
  function _statusColor(s){return{PASS:'#1dffa6',WARN:'#ffb347',BLOCK:'#ff4060',BASELINE:'#b066ff',PENDING:'#3a4d68'}[s]||'#1de9ff';}
10
4
 
11
- /* ─────────────────────────────────────────────
12
- WIDGET 1: AGENT 编队 (roster)
13
- ───────────────────────────────────────────── */
14
5
  WidgetRuntime.register({
15
6
  id: 'roster',
16
7
  title: 'AGENT 编队',
@@ -44,9 +35,6 @@ WidgetRuntime.register({
44
35
  },
45
36
  });
46
37
 
47
- /* ─────────────────────────────────────────────
48
- WIDGET 2: 任务流 (tasks)
49
- ───────────────────────────────────────────── */
50
38
  WidgetRuntime.register({
51
39
  id: 'tasks',
52
40
  title: '任务流',
@@ -59,9 +47,6 @@ WidgetRuntime.register({
59
47
  },
60
48
  });
61
49
 
62
- /* ─────────────────────────────────────────────
63
- WIDGET 3: 中央核心 (core) — 激活 Agent
64
- ───────────────────────────────────────────── */
65
50
  WidgetRuntime.register({
66
51
  id: 'core',
67
52
  title: '中央核心 · 激活单元',
@@ -94,7 +79,6 @@ WidgetRuntime.register({
94
79
  </div>
95
80
  <div class="chat-log" data-role="chat-log"></div>
96
81
  </div>`;
97
- // orbit ticks
98
82
  const tick = el.querySelector('[data-role="orbit-tick"]');
99
83
  let h=''; for(let i=0;i<24;i++)h+=`<span style="transform:translateX(-50%) rotate(${i*15}deg)"></span>`;
100
84
  tick.innerHTML=h;
@@ -111,9 +95,6 @@ WidgetRuntime.register({
111
95
  },
112
96
  });
113
97
 
114
- /* ─────────────────────────────────────────────
115
- WIDGET 4: HARNESS 遥测 (harness)
116
- ───────────────────────────────────────────── */
117
98
  WidgetRuntime.register({
118
99
  id: 'harness',
119
100
  title: 'HARNESS 遥测',
@@ -150,14 +131,10 @@ WidgetRuntime.register({
150
131
  ctx.loadHarnessInto(el, agentId);
151
132
  },
152
133
  onEvent(evt, el, ctx) {
153
- // policy 更新或校准完成时立即刷新
154
134
  ctx.loadHarnessInto(el, ctx.activeAgent);
155
135
  },
156
136
  });
157
137
 
158
- /* ─────────────────────────────────────────────
159
- WIDGET 5: 推理链 STREAM (thoughtchain)
160
- ───────────────────────────────────────────── */
161
138
  WidgetRuntime.register({
162
139
  id: 'thoughtchain',
163
140
  title: '推理链 STREAM',
@@ -175,9 +152,6 @@ WidgetRuntime.register({
175
152
  },
176
153
  });
177
154
 
178
- /* ─────────────────────────────────────────────
179
- WIDGET 6: 事件流 LIVE (events)
180
- ───────────────────────────────────────────── */
181
155
  WidgetRuntime.register({
182
156
  id: 'events',
183
157
  title: '事件流 · LIVE',
@@ -206,10 +180,6 @@ WidgetRuntime.register({
206
180
  },
207
181
  });
208
182
 
209
- /* ─────────────────────────────────────────────
210
- WIDGET 7: 协同 DAG (dag) — 多 Agent 星舰控制流
211
- 命令 → 主管分发 → 子 Agent → Harness 关卡 → 记忆汇聚
212
- ───────────────────────────────────────────── */
213
183
  WidgetRuntime.register({
214
184
  id: 'dag',
215
185
  title: '协同 DAG · 控制流',
@@ -243,7 +213,6 @@ WidgetRuntime.register({
243
213
  },
244
214
  });
245
215
 
246
- /* DAG 拓扑定义 */
247
216
  const _DAG_NODES = {
248
217
  command: { x: 60, y: 115, label: '指令', icon: '❯', kind: 'entry', color: '#1de9ff' },
249
218
  supervisor: { x: 180, y: 115, label: '主管', icon: '🏢', kind: 'hub', color: '#1de9ff' },
@@ -265,7 +234,6 @@ function _dagBuild(el, ctx) {
265
234
  const edgesG = el.querySelector('[data-role="dag-edges"]');
266
235
  const nodesG = el.querySelector('[data-role="dag-nodes"]');
267
236
 
268
- // edges
269
237
  edgesG.innerHTML = _DAG_EDGES.map(([from, to], i) => {
270
238
  const a = _DAG_NODES[from], b = _DAG_NODES[to];
271
239
  const curve = (from === 'memory' && to === 'supervisor')
@@ -275,7 +243,6 @@ function _dagBuild(el, ctx) {
275
243
  fill="none" stroke="#1a2942" stroke-width="1.5" marker-end="url(#dagArrow)"/>`;
276
244
  }).join('');
277
245
 
278
- // nodes
279
246
  nodesG.innerHTML = Object.entries(_DAG_NODES).map(([id, n]) => {
280
247
  const r = n.kind === 'hub' ? 17 : n.kind === 'gate' ? 16 : 14;
281
248
  const shape = n.kind === 'gate'
@@ -301,7 +268,6 @@ function _dagSetActive(el, agentId) {
301
268
  el.querySelectorAll('.dag-node').forEach(g => {
302
269
  g.classList.toggle('dag-dim', false);
303
270
  });
304
- // highlight active agent path: command→supervisor→agent→harness→memory
305
271
  const path = ['command','supervisor', agentId, 'harness','memory'];
306
272
  el.querySelectorAll('.dag-node').forEach(g => {
307
273
  const id = g.dataset.nodeG;
@@ -353,7 +319,6 @@ function _dagFlashGate(el, color) {
353
319
  const gate = el.querySelector('[data-node="harness"]');
354
320
  if (!gate) return;
355
321
  const g = gate.closest('.dag-node') || gate;
356
- // 注入一次性 keyframes(如未注入)
357
322
  if (!document.getElementById('dag-flash-style')) {
358
323
  const st = document.createElement('style');
359
324
  st.id = 'dag-flash-style';
@@ -371,7 +336,6 @@ function _dagFlashGate(el, color) {
371
336
  gate.classList.remove('dag-gate-flash');
372
337
  void gate.offsetWidth; // 强制重排,允许动画重放
373
338
  gate.classList.add('dag-gate-flash');
374
- // 同时让 HARNESS 文字标签也闪一下
375
339
  const label = el.querySelector('.dag-node[data-node-g="harness"] .dag-label');
376
340
  if (label) { label.style.fill = color; label.style.fontWeight = '700'; }
377
341
  setTimeout(() => {
@@ -381,11 +345,6 @@ function _dagFlashGate(el, color) {
381
345
  }, 1200);
382
346
  }
383
347
 
384
- /* ─────────────────────────────────────────────
385
- WIDGET 8: 执行终端 (terminal) — Shell 执行流
386
- 命令回显 · 记忆检索 · 推理 · 工具调用 · Harness 校验
387
- 带打字机流式效果,营造 "AI 正在工作" 的真实感
388
- ───────────────────────────────────────────── */
389
348
  WidgetRuntime.register({
390
349
  id: 'terminal',
391
350
  title: '执行终端 · SHELL',
@@ -464,14 +423,9 @@ function _termWrite(el, text, type = 'out', stream = false) {
464
423
  txtEl.textContent = text;
465
424
  }
466
425
  out.scrollTop = out.scrollHeight;
467
- // 限制行数
468
426
  while (out.children.length > 80) out.firstChild?.remove();
469
427
  }
470
428
 
471
- /* ─────────────────────────────────────────────
472
- WIDGET 9: AI 浏览器 (browser) — 网页操作可视化
473
- 模拟 Agent 浏览网页:地址栏 · 加载流 · 操作高亮(点击/输入/滚动)
474
- ───────────────────────────────────────────── */
475
429
  WidgetRuntime.register({
476
430
  id: 'browser',
477
431
  title: 'AI 浏览器 · BROWSE',
@@ -507,14 +461,12 @@ function _brzNavigate(el, url, actions) {
507
461
  statusEl.textContent = 'LOADING';
508
462
  statusEl.className = 'brz-status loading';
509
463
 
510
- // 进度条动画
511
464
  progEl.style.width = '0%';
512
465
  progEl.style.opacity = '1';
513
466
  setTimeout(() => progEl.style.width = '70%', 80);
514
467
  setTimeout(() => { progEl.style.width = '100%'; }, 700);
515
468
  setTimeout(() => { progEl.style.opacity = '0'; statusEl.textContent = 'LOADED'; statusEl.className = 'brz-status loaded'; }, 950);
516
469
 
517
- // 渲染一个抽象的"网页骨架" + 操作高亮
518
470
  vp.innerHTML = `
519
471
  <div class="brz-page">
520
472
  <div class="brz-skel brz-skel-title"></div>
@@ -531,7 +483,6 @@ function _brzNavigate(el, url, actions) {
531
483
  <div class="brz-cursor" data-role="brz-cursor">➤</div>
532
484
  </div>`;
533
485
 
534
- // AI 操作序列高亮
535
486
  const acts = actions || [
536
487
  { type: 'scan', label: '解析页面结构', x: 50, y: 20 },
537
488
  { type: 'click', label: '点击主内容区', x: 30, y: 45 },
@@ -548,7 +499,6 @@ function _brzNavigate(el, url, actions) {
548
499
  cursor.style.top = a.y + '%';
549
500
  cursor.setAttribute('data-label', a.label);
550
501
  cursor.className = `brz-cursor act-${a.type}`;
551
- // 涟漪
552
502
  const ripple = document.createElement('div');
553
503
  ripple.className = 'brz-ripple';
554
504
  ripple.style.left = a.x + '%';
@@ -561,11 +511,6 @@ function _brzNavigate(el, url, actions) {
561
511
  setTimeout(runAct, 1000);
562
512
  }
563
513
 
564
- /* ─────────────────────────────────────────────
565
- WIDGET 10: 语音输入 (voice) — 麦克风波形 + 语音识别
566
- Web Audio API 真实波形 + 浏览器原生 SpeechRecognition
567
- 识别结果可直接作为指令传输给激活 Agent
568
- ───────────────────────────────────────────── */
569
514
  WidgetRuntime.register({
570
515
  id: 'voice',
571
516
  title: '语音输入 · VOICE',
@@ -600,7 +545,6 @@ function _voiceInit(el, ctx) {
600
545
  let audioCtx = null, analyser = null, dataArr = null, rafId = null, stream = null;
601
546
  let recognition = null, recognizing = false, finalText = '';
602
547
 
603
- // 语音识别(浏览器原生)
604
548
  const SR = window.SpeechRecognition || window.webkitSpeechRecognition;
605
549
  if (SR) {
606
550
  recognition = new SR();
@@ -685,7 +629,6 @@ function _voiceInit(el, ctx) {
685
629
  const text = transcriptEl.textContent.trim();
686
630
  if (!text || text === '聆听中...') return;
687
631
  stopMic();
688
- // 把识别结果填入控制台并传输
689
632
  const input = document.getElementById('consoleInput');
690
633
  if (input) { input.value = text; }
691
634
  if (typeof transmit === 'function') transmit();
@@ -3,7 +3,7 @@
3
3
  <head>
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <title>Foxclaw Deck · v3</title>
6
+ <title>Foxclaw</title>
7
7
  <!-- 内联 SVG 图标:不新增静态文件,也避免浏览器探 /favicon.ico 报 404 -->
8
8
  <link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><rect width='32' height='32' rx='9' fill='%23179d5f'/><circle cx='16' cy='16' r='6.5' fill='%23f4fbf0'/></svg>" />
9
9
  </head>
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "foxclaw-deck-next",
3
- "version": "3.0.0",
3
+ "version": "0.1.2",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "foxclaw-deck-next",
9
- "version": "3.0.0",
9
+ "version": "0.1.2",
10
10
  "dependencies": {
11
11
  "vue": "^3.5.13"
12
12
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "foxclaw-deck-next",
3
3
  "private": true,
4
- "version": "3.0.0",
4
+ "version": "0.1.4",
5
5
  "type": "module",
6
6
  "description": "Foxclaw Deck v3 - Vue 3 rendering layer (builds into frontend/v3)",
7
7
  "scripts": {
@@ -7,10 +7,8 @@ import Inspector from './components/Inspector.vue'
7
7
  import { onBeforeUnmount, onMounted, ref } from 'vue'
8
8
  import { state, setModule } from './stores/app.js'
9
9
  import { bootChat } from './stores/chat.js'
10
+ import { t } from './lib/i18n.js'
10
11
 
11
- /* 窄窗断点与 base.css 的 @media (max-width: 1180px) 是同一个数:
12
- 到这条线以下右栏变成抽屉(见 base.css),这里负责「变窄时自动收起」。
13
- 否则 900px 窗口里三栏并存,中栏只剩 252px,输入框被 + / 模型胶囊挤到 115px。 */
14
12
  const NARROW = 1180
15
13
  const narrow = ref(false)
16
14
 
@@ -21,7 +19,6 @@ function syncNarrow() {
21
19
  if (n) state.railOpen = false
22
20
  }
23
21
 
24
- /* 恢复上次会话历史(后端落盘) */
25
22
  onMounted(() => {
26
23
  bootChat()
27
24
  narrow.value = window.innerWidth < NARROW
@@ -34,7 +31,6 @@ onBeforeUnmount(() => {
34
31
  window.removeEventListener('resize', syncNarrow)
35
32
  })
36
33
 
37
- /* Ctrl+, 打开设置(顶栏 ⚙ 旁边写的那个键位)。输入框里也生效 —— 它是全局入口 */
38
34
  function onKey(e) {
39
35
  if (e.ctrlKey && (e.key === ',' || e.code === 'Comma')) {
40
36
  e.preventDefault()
@@ -55,8 +51,8 @@ function onKey(e) {
55
51
  <Inspector />
56
52
  </div>
57
53
  <div v-if="narrow && state.railOpen" class="rail-scrim" @click="state.railOpen = false" />
58
- <button v-if="!state.railOpen" class="rail-handle" title="展开检视栏" @click="state.railOpen = true">
59
- 检视
54
+ <button v-if="!state.railOpen" class="rail-handle" :title="t('app.expandInspector')" @click="state.railOpen = true">
55
+ {{ t('app.inspector') }}
60
56
  </button>
61
57
  </div>
62
58
  </template>
@@ -1,10 +1,5 @@
1
- /* 所有请求走相对路径:dev 由 vite proxy 透到 8000,prod 与后端同源 —— 无需区分环境。 */
2
1
  export const API = '/api'
3
2
 
4
- /* FastAPI 错误统一带 {detail},直接抛出来比 "xxx -> 400" 有用得多。
5
- detail 可能是字符串,也可能是结构化对象({error:"install_denied",reasons:[...]} /
6
- {needs_ack:true,warnings:[...]})—— 直接 String() 会得到 "[object Object]",
7
- 所以拆一层;原始 detail / status 挂到 err 上,调用方要区分「要确认」才有据可依。 */
8
3
  function detailMsg(d) {
9
4
  if (typeof d === 'string') return d
10
5
  if (d && typeof d === 'object') {
@@ -74,8 +69,6 @@ export async function postForm(path, form) {
74
69
  return r.json()
75
70
  }
76
71
 
77
- /* 少数端点在根路径、不带 /api 前缀 —— vendor/hkc 的 knowledge router 是直接
78
- include_router(knowledge.router) 挂上去的,实测 /api/knowledge/* 是 404。 */
79
72
  export async function getRaw(path, init) {
80
73
  const r = await fetch(path, init)
81
74
  if (!r.ok) throw await toError(r, path)