foxclaw 0.1.0 → 0.1.2
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 +54 -29
- package/bin/foxclaw.cjs +51 -0
- package/index.js +11 -10
- package/package.json +46 -38
- package/python/.dockerignore +35 -0
- package/python/.env.example +165 -0
- package/python/.gitattributes +46 -0
- package/python/.github/workflows/ci.yml +122 -0
- package/python/.github/workflows/release.yml +183 -0
- package/python/Dockerfile +64 -0
- package/python/LICENSE +21 -0
- package/python/MANIFEST.in +14 -0
- package/python/README.md +192 -0
- package/python/README_CN.md +324 -0
- package/python/bench/bm25_audit.py +199 -0
- package/python/bench/bm25_regression.py +178 -0
- package/python/bench/bm25_stress.py +159 -0
- package/python/bench/cd_audit.py +238 -0
- package/python/bench/chaos.py +142 -0
- package/python/bench/concurrency.py +103 -0
- package/python/bench/e2e_real_models.py +95 -0
- package/python/bench/llm_retry_test.py +248 -0
- package/python/bench/load_test.py +196 -0
- package/python/bench/vector_ivf_upgrade.py +134 -0
- package/python/docs/CHANNELS.md +195 -0
- package/python/docs/GOVERNANCE.md +164 -0
- package/python/docs/RELEASE.md +109 -0
- package/python/docs/RUN.md +75 -0
- package/python/docs/RUN_CN.md +121 -0
- package/python/docs//351/205/215/347/275/256/346/214/207/345/215/227.md +152 -0
- package/python/evals/__init__.py +0 -0
- package/python/evals/governance/README.md +61 -0
- package/python/evals/governance/__init__.py +0 -0
- package/python/evals/governance/cases.py +152 -0
- package/python/evals/governance/compare_embeddings.py +94 -0
- package/python/evals/governance/convergence.svg +11 -0
- package/python/evals/governance/report.json +371 -0
- package/python/evals/governance/run_evals.py +400 -0
- package/python/examples/governance_middleware_demo.py +77 -0
- package/python/examples/langgraph_governance_demo.py +108 -0
- package/python/examples/sample_itinerary.csv +6 -0
- package/python/examples/sample_knowledge.csv +9 -0
- package/python/examples/sample_transactions.csv +14 -0
- package/python/foxclaw/__init__.py +526 -0
- package/python/foxclaw/agents/__init__.py +12 -0
- package/python/foxclaw/agents/agents.py +35 -0
- package/python/foxclaw/agents/base.py +865 -0
- package/python/foxclaw/agents/impl/__init__.py +1 -0
- package/python/foxclaw/agents/impl/_helpers.py +78 -0
- package/python/foxclaw/agents/impl/finance.py +248 -0
- package/python/foxclaw/agents/impl/office.py +367 -0
- package/python/foxclaw/agents/impl/supervisor.py +659 -0
- package/python/foxclaw/agents/mixins/_context.py +151 -0
- package/python/foxclaw/agents/mixins/augment.py +149 -0
- package/python/foxclaw/agents/mixins/llm.py +66 -0
- package/python/foxclaw/agents/mixins/memory.py +35 -0
- package/python/foxclaw/agents/mixins/preference.py +81 -0
- package/python/foxclaw/agents/mixins/prompt.py +77 -0
- package/python/foxclaw/agents/mixins/skillbox.py +58 -0
- package/python/foxclaw/agents/mixins/tool_exec.py +443 -0
- package/python/foxclaw/api/__init__.py +283 -0
- package/python/foxclaw/api/app.py +123 -0
- package/python/foxclaw/api/middleware.py +317 -0
- package/python/foxclaw/api/routes/ahs.py +417 -0
- package/python/foxclaw/api/routes/attachments.py +117 -0
- package/python/foxclaw/api/routes/browser.py +39 -0
- package/python/foxclaw/api/routes/domain.py +314 -0
- package/python/foxclaw/api/routes/knowledge.py +163 -0
- package/python/foxclaw/api/routes/llm_admin.py +461 -0
- package/python/foxclaw/api/routes/memory.py +89 -0
- package/python/foxclaw/api/routes/orchestration.py +41 -0
- package/python/foxclaw/api/routes/recovery.py +43 -0
- package/python/foxclaw/api/routes/review.py +275 -0
- package/python/foxclaw/api/routes/search.py +37 -0
- package/python/foxclaw/api/routes/system.py +327 -0
- package/python/foxclaw/api/routes/task.py +281 -0
- package/python/foxclaw/api/routes/ws.py +68 -0
- package/python/foxclaw/api/spine.py +55 -0
- package/python/foxclaw/browser/__init__.py +13 -0
- package/python/foxclaw/browser/action/__init__.py +1 -0
- package/python/foxclaw/browser/action/action_executor.py +54 -0
- package/python/foxclaw/browser/action/action_types.py +45 -0
- package/python/foxclaw/browser/action/action_verifier.py +68 -0
- package/python/foxclaw/browser/action/url_policy.py +147 -0
- package/python/foxclaw/browser/adapter/__init__.py +1 -0
- package/python/foxclaw/browser/adapter/base_adapter.py +23 -0
- package/python/foxclaw/browser/adapter/web_adapter.py +46 -0
- package/python/foxclaw/browser/driver/__init__.py +1 -0
- package/python/foxclaw/browser/driver/base_driver.py +61 -0
- package/python/foxclaw/browser/driver/playwright_driver.py +166 -0
- package/python/foxclaw/browser/perception/__init__.py +1 -0
- package/python/foxclaw/browser/perception/dom_extractor.py +43 -0
- package/python/foxclaw/browser/perception/element_indexer.py +57 -0
- package/python/foxclaw/browser/perception/semantic_reducer.py +33 -0
- package/python/foxclaw/browser/perception/snapshot.py +29 -0
- package/python/foxclaw/browser/service.py +148 -0
- package/python/foxclaw/browser/session/__init__.py +1 -0
- package/python/foxclaw/browser/session/session_manager.py +57 -0
- package/python/foxclaw/capability/__init__.py +12 -0
- package/python/foxclaw/capability/embedding_service.py +67 -0
- package/python/foxclaw/channels/__init__.py +140 -0
- package/python/foxclaw/channels/discord.py +184 -0
- package/python/foxclaw/channels/dispatcher.py +204 -0
- package/python/foxclaw/channels/feishu.py +225 -0
- package/python/foxclaw/channels/openclaw_bridge.py +156 -0
- package/python/foxclaw/channels/signing.py +187 -0
- package/python/foxclaw/channels/telegram.py +121 -0
- package/python/foxclaw/channels/weixin.py +225 -0
- package/python/foxclaw/cli.py +154 -0
- package/python/foxclaw/cognitive/__init__.py +4 -0
- package/python/foxclaw/cognitive/reasoner.py +82 -0
- package/python/foxclaw/cognitive/state.py +147 -0
- package/python/foxclaw/core/__init__.py +9 -0
- package/python/foxclaw/core/audit_chain.py +133 -0
- package/python/foxclaw/core/audit_sink.py +100 -0
- package/python/foxclaw/core/compaction.py +283 -0
- package/python/foxclaw/core/config.py +65 -0
- package/python/foxclaw/core/confirm_nonce.py +157 -0
- package/python/foxclaw/core/dispatch.py +27 -0
- package/python/foxclaw/core/envfile.py +48 -0
- package/python/foxclaw/core/failure_policy.py +151 -0
- package/python/foxclaw/core/gateway.py +85 -0
- package/python/foxclaw/core/governance_audit.py +175 -0
- package/python/foxclaw/core/idempotency.py +289 -0
- package/python/foxclaw/core/injection_guard.py +221 -0
- package/python/foxclaw/core/memory_adapter.py +516 -0
- package/python/foxclaw/core/metrics.py +89 -0
- package/python/foxclaw/core/model_router.py +1097 -0
- package/python/foxclaw/core/paths.py +103 -0
- package/python/foxclaw/core/recovery.py +362 -0
- package/python/foxclaw/core/scheduler.py +144 -0
- package/python/foxclaw/core/secrets.py +134 -0
- package/python/foxclaw/core/sessions.py +157 -0
- package/python/foxclaw/core/settings.py +430 -0
- package/python/foxclaw/core/shared_write.py +198 -0
- package/python/foxclaw/core/steering_queue.py +153 -0
- package/python/foxclaw/core/system_bus.py +84 -0
- package/python/foxclaw/core/thought_chain.py +318 -0
- package/python/foxclaw/core/token_meter.py +235 -0
- package/python/foxclaw/core/tools.py +172 -0
- package/python/foxclaw/core/tracing.py +390 -0
- package/python/foxclaw/core/truth_pipeline.py +114 -0
- package/python/foxclaw/core/vec_recall_adapter.py +503 -0
- package/python/foxclaw/core/verify.py +181 -0
- package/python/foxclaw/core/web_search.py +242 -0
- package/python/foxclaw/finance/__init__.py +15 -0
- package/python/foxclaw/finance/evidence.py +153 -0
- package/python/foxclaw/finance/fx.py +100 -0
- package/python/foxclaw/finance/graph.py +188 -0
- package/python/foxclaw/finance/models.py +150 -0
- package/python/foxclaw/finance/nlparse.py +575 -0
- package/python/foxclaw/finance/policy.py +329 -0
- package/python/foxclaw/finance/reasoner.py +129 -0
- package/python/foxclaw/finance/reasoning.py +340 -0
- package/python/foxclaw/finance/reconcile.py +180 -0
- package/python/foxclaw/finance/runtime.py +527 -0
- package/python/foxclaw/governance/__init__.py +24 -0
- package/python/foxclaw/governance/middleware.py +184 -0
- package/python/foxclaw/harness/__init__.py +17 -0
- package/python/foxclaw/harness/_base.py +72 -0
- package/python/foxclaw/harness/_policy.py +359 -0
- package/python/foxclaw/harness/drift.py +89 -0
- package/python/foxclaw/harness/event_bus.py +31 -0
- package/python/foxclaw/harness/harness.py +446 -0
- package/python/foxclaw/harness/loop_detection.py +152 -0
- package/python/foxclaw/itinerary/__init__.py +10 -0
- package/python/foxclaw/itinerary/amap_client.py +117 -0
- package/python/foxclaw/itinerary/models.py +80 -0
- package/python/foxclaw/itinerary/reasoner.py +101 -0
- package/python/foxclaw/itinerary/reasoning.py +174 -0
- package/python/foxclaw/itinerary/runtime.py +148 -0
- package/python/foxclaw/kernel/__init__.py +13 -0
- package/python/foxclaw/kernel/capabilities.py +73 -0
- package/python/foxclaw/kernel/policy_gate.py +114 -0
- package/python/foxclaw/kernel/process_manager.py +205 -0
- package/python/foxclaw/kernel/scheduler.py +131 -0
- package/python/foxclaw/kernel/sentinel.py +453 -0
- package/python/foxclaw/kernel/sentinel_client.py +246 -0
- package/python/foxclaw/kernel/spine.py +97 -0
- package/python/foxclaw/knowledge/__init__.py +376 -0
- package/python/foxclaw/knowledge/truth_crystallizer.py +92 -0
- package/python/foxclaw/learning/__init__.py +12 -0
- package/python/foxclaw/learning/knowledge_graph.py +149 -0
- package/python/foxclaw/learning/models.py +93 -0
- package/python/foxclaw/learning/reasoner.py +70 -0
- package/python/foxclaw/learning/runtime.py +363 -0
- package/python/foxclaw/learning/spaced_repetition.py +96 -0
- package/python/foxclaw/mcp_server.py +189 -0
- package/python/foxclaw/orchestration/__init__.py +184 -0
- package/python/foxclaw/pipeline/__init__.py +23 -0
- package/python/foxclaw/pipeline/cognitive_pipeline.py +481 -0
- package/python/foxclaw/pipeline/supervisor_stages.py +230 -0
- package/python/foxclaw/planning/__init__.py +17 -0
- package/python/foxclaw/planning/llm_planner.py +232 -0
- package/python/foxclaw/planning/planner.py +99 -0
- package/python/foxclaw/search/__init__.py +24 -0
- package/python/foxclaw/search/agent_port.py +194 -0
- package/python/foxclaw/search/evidence.py +113 -0
- package/python/foxclaw/search/external_search.py +50 -0
- package/python/foxclaw/search/hkc_read_port.py +60 -0
- package/python/foxclaw/search/internal_search.py +73 -0
- package/python/foxclaw/search/models.py +129 -0
- package/python/foxclaw/search/planner.py +44 -0
- package/python/foxclaw/search/retriever.py +39 -0
- package/python/foxclaw/search/runtime.py +134 -0
- package/python/foxclaw/search/strategy.py +44 -0
- package/python/foxclaw/skills/__init__.py +613 -0
- package/python/foxclaw/skills/hooks_spec.py +73 -0
- package/python/foxclaw/skills/lifecycle.py +278 -0
- package/python/foxclaw/skills/markdown_skill.py +174 -0
- package/python/foxclaw/skills/markdown_store.py +39 -0
- package/python/foxclaw/skills/mcp_client.py +190 -0
- package/python/foxclaw/skills/mcp_store.py +43 -0
- package/python/foxclaw/skills/permission.py +180 -0
- package/python/foxclaw/skills/pipeline.py +386 -0
- package/python/foxclaw/skills/policy.py +80 -0
- package/python/foxclaw/skills/review.py +72 -0
- package/python/foxclaw/skills/store.py +57 -0
- package/python/frontend/assets/core_bg.jpg +0 -0
- package/python/frontend/deck/auth.js +32 -0
- package/python/frontend/deck/chat_model_picker.js +537 -0
- package/python/frontend/deck/domain.js +296 -0
- package/python/frontend/deck/main.js +1950 -0
- package/python/frontend/deck/markdown.js +48 -0
- package/python/frontend/deck/settings.js +324 -0
- package/python/frontend/deck/state.js +6 -0
- package/python/frontend/deck/util.js +6 -0
- package/python/frontend/deck/visuals.js +212 -0
- package/python/frontend/deck.css +855 -0
- package/python/frontend/deck.html +686 -0
- package/python/frontend/deck_v2.html +341 -0
- package/python/frontend/planet/index.html +266 -0
- package/python/frontend/planet/planet.css +579 -0
- package/python/frontend/planet/planet.js +1637 -0
- package/python/frontend/planet/vendor/three.core.min.js +6 -0
- package/python/frontend/planet/vendor/three.module.min.js +6 -0
- package/python/frontend/v3/assets/index-BD7VPbMV.js +33 -0
- package/python/frontend/v3/assets/index-CAvnYWGH.css +1 -0
- package/python/frontend/v3/index.html +15 -0
- package/python/frontend/widget_runtime.js +295 -0
- package/python/frontend/widgets.js +696 -0
- package/python/frontend_src/index.html +14 -0
- package/python/frontend_src/package-lock.json +1376 -0
- package/python/frontend_src/package.json +19 -0
- package/python/frontend_src/src/App.vue +62 -0
- package/python/frontend_src/src/api.js +89 -0
- package/python/frontend_src/src/components/ChatDock.vue +377 -0
- package/python/frontend_src/src/components/ComposerMenu.vue +75 -0
- package/python/frontend_src/src/components/DocViewer.vue +171 -0
- package/python/frontend_src/src/components/Inspector.vue +241 -0
- package/python/frontend_src/src/components/LeftRail.vue +394 -0
- package/python/frontend_src/src/components/MenuPopover.vue +130 -0
- package/python/frontend_src/src/components/MessageStream.vue +401 -0
- package/python/frontend_src/src/components/ModelPicker.vue +355 -0
- package/python/frontend_src/src/components/QuickIngest.vue +118 -0
- package/python/frontend_src/src/components/Stage.vue +226 -0
- package/python/frontend_src/src/components/ToolMenu.vue +144 -0
- package/python/frontend_src/src/components/TopBar.vue +90 -0
- package/python/frontend_src/src/components/panels/ActivityPanel.vue +84 -0
- package/python/frontend_src/src/components/panels/AgentsPanel.vue +86 -0
- package/python/frontend_src/src/components/panels/KnowledgePanel.vue +441 -0
- package/python/frontend_src/src/components/panels/MemoryPanel.vue +118 -0
- package/python/frontend_src/src/components/panels/PanelBox.vue +51 -0
- package/python/frontend_src/src/components/panels/ReviewPanel.vue +308 -0
- package/python/frontend_src/src/components/panels/SettingsPanel.vue +254 -0
- package/python/frontend_src/src/components/panels/TasksPanel.vue +74 -0
- package/python/frontend_src/src/components/panels/ToolsPanel.vue +640 -0
- package/python/frontend_src/src/components/panels/UsagePanel.vue +105 -0
- package/python/frontend_src/src/lib/fmt.js +48 -0
- package/python/frontend_src/src/lib/md.js +204 -0
- package/python/frontend_src/src/main.js +7 -0
- package/python/frontend_src/src/stores/app.js +106 -0
- package/python/frontend_src/src/stores/chat.js +484 -0
- package/python/frontend_src/src/stores/model.js +104 -0
- package/python/frontend_src/src/stores/panels.js +330 -0
- package/python/frontend_src/src/styles/base.css +138 -0
- package/python/frontend_src/src/styles/panel.css +117 -0
- package/python/frontend_src/src/styles/tokens.css +78 -0
- package/python/frontend_src/vite.config.js +46 -0
- package/python/pyproject.toml +265 -0
- package/python/skills/README.md +123 -0
- package/python/skills/execution-contract/SKILL.md +30 -0
- package/python/skills/general-assistant/SKILL.md +34 -0
- package/python/skills/planner/SKILL.md +33 -0
- package/python/skills/schedule-itinerary/SKILL.md +35 -0
- package/python/skills/skill-composition/SKILL.md +31 -0
- package/python/skills/skill-guard/SKILL.md +67 -0
- package/python/skills/skill-memory/SKILL.md +50 -0
- package/python/skills/skill-router/SKILL.md +35 -0
- package/python/tests/conftest.py +76 -0
- package/python/tests/run_all.py +263 -0
- package/python/tests/test_agent_lifecycle.py +360 -0
- package/python/tests/test_ahs_p2.py +414 -0
- package/python/tests/test_anthropic_tools.py +206 -0
- package/python/tests/test_api.py +87 -0
- package/python/tests/test_api_auth.py +83 -0
- package/python/tests/test_api_host_guard.py +69 -0
- package/python/tests/test_attachments.py +167 -0
- package/python/tests/test_audit_chain.py +121 -0
- package/python/tests/test_audit_sink.py +136 -0
- package/python/tests/test_browser.py +153 -0
- package/python/tests/test_browser_request_guard.py +184 -0
- package/python/tests/test_capabilities.py +158 -0
- package/python/tests/test_channel_signing.py +192 -0
- package/python/tests/test_channel_trust.py +109 -0
- package/python/tests/test_channels.py +184 -0
- package/python/tests/test_cli.py +46 -0
- package/python/tests/test_compaction_toolpair.py +119 -0
- package/python/tests/test_config_layering.py +65 -0
- package/python/tests/test_confirm_nonce.py +197 -0
- package/python/tests/test_context_compaction_wiring.py +130 -0
- package/python/tests/test_dispatch.py +95 -0
- package/python/tests/test_domain_evals.py +403 -0
- package/python/tests/test_domain_import.py +78 -0
- package/python/tests/test_domain_reasoners.py +148 -0
- package/python/tests/test_e2e.py +178 -0
- package/python/tests/test_emb_cache.py +107 -0
- package/python/tests/test_embedding_singleton.py +89 -0
- package/python/tests/test_envfile.py +83 -0
- package/python/tests/test_evals_smoke.py +114 -0
- package/python/tests/test_event_spine.py +182 -0
- package/python/tests/test_evidence_semantic.py +101 -0
- package/python/tests/test_evidence_sse.py +97 -0
- package/python/tests/test_file_ingest.py +146 -0
- package/python/tests/test_finance.py +153 -0
- package/python/tests/test_finance_ledger.py +252 -0
- package/python/tests/test_finance_persist.py +178 -0
- package/python/tests/test_gateway.py +144 -0
- package/python/tests/test_governance.py +468 -0
- package/python/tests/test_governance_audit.py +359 -0
- package/python/tests/test_governance_middleware.py +143 -0
- package/python/tests/test_hkc.py +286 -0
- package/python/tests/test_hkc_index_build_manager.py +339 -0
- package/python/tests/test_hkc_manifest_reuse.py +317 -0
- package/python/tests/test_hkc_search_event_sync.py +533 -0
- package/python/tests/test_import_path_guard.py +161 -0
- package/python/tests/test_injection_guard.py +211 -0
- package/python/tests/test_integration.py +141 -0
- package/python/tests/test_itinerary.py +103 -0
- package/python/tests/test_kee_semantic.py +89 -0
- package/python/tests/test_kernel.py +222 -0
- package/python/tests/test_learning.py +110 -0
- package/python/tests/test_llm_admin.py +160 -0
- package/python/tests/test_llm_planner.py +366 -0
- package/python/tests/test_markdown_skills.py +73 -0
- package/python/tests/test_markdown_skills_openclaw.py +71 -0
- package/python/tests/test_mcp_connectors.py +109 -0
- package/python/tests/test_mcp_guardian_discovery.py +91 -0
- package/python/tests/test_mcp_no_memory.py +43 -0
- package/python/tests/test_mcp_policy_gate.py +77 -0
- package/python/tests/test_mcp_server.py +95 -0
- package/python/tests/test_memory_recall.py +95 -0
- package/python/tests/test_metrics.py +66 -0
- package/python/tests/test_multi_chain.py +102 -0
- package/python/tests/test_multimodal.py +135 -0
- package/python/tests/test_negotiation.py +146 -0
- package/python/tests/test_orchestration.py +119 -0
- package/python/tests/test_orchestration_decoupled.py +109 -0
- package/python/tests/test_p3_p5_hardening.py +338 -0
- package/python/tests/test_parallel_dispatch.py +215 -0
- package/python/tests/test_pipeline.py +533 -0
- package/python/tests/test_plan_visible.py +86 -0
- package/python/tests/test_planner.py +95 -0
- package/python/tests/test_policy_gate.py +109 -0
- package/python/tests/test_reasoner.py +185 -0
- package/python/tests/test_release_check.py +263 -0
- package/python/tests/test_resume_checkpoint.py +156 -0
- package/python/tests/test_routing.py +95 -0
- package/python/tests/test_scheduler.py +141 -0
- package/python/tests/test_search.py +204 -0
- package/python/tests/test_secrets.py +159 -0
- package/python/tests/test_semantic.py +78 -0
- package/python/tests/test_sessions.py +91 -0
- package/python/tests/test_settings.py +281 -0
- package/python/tests/test_shadow_learning.py +330 -0
- package/python/tests/test_shared_write.py +207 -0
- package/python/tests/test_skill_taint_gate.py +154 -0
- package/python/tests/test_skills.py +133 -0
- package/python/tests/test_stats_and_events.py +165 -0
- package/python/tests/test_streaming.py +60 -0
- package/python/tests/test_supervisor_pipeline.py +177 -0
- package/python/tests/test_supervisor_triage.py +108 -0
- package/python/tests/test_taint.py +117 -0
- package/python/tests/test_task_stream.py +85 -0
- package/python/tests/test_thinking_sse.py +67 -0
- package/python/tests/test_thinking_stream.py +76 -0
- package/python/tests/test_tool_center.py +129 -0
- package/python/tests/test_tool_parallel.py +113 -0
- package/python/tests/test_tool_params.py +103 -0
- package/python/tests/test_tool_use.py +242 -0
- package/python/tests/test_toplevel_concurrency.py +117 -0
- package/python/tests/test_tracing.py +290 -0
- package/python/tests/test_truth_crystallizer.py +127 -0
- package/python/tests/test_truth_flow.py +93 -0
- package/python/tests/test_url_policy.py +53 -0
- package/python/tests/test_verify_evidence.py +225 -0
- package/python/tests/test_version_sync.py +71 -0
- package/python/tests/test_web_trigger.py +99 -0
- package/python/tools/check_faiss_type.py +31 -0
- package/python/tools/check_log.ps1 +46 -0
- package/python/tools/ivf_status.py +72 -0
- package/python/tools/mypy_baseline.txt +5 -0
- package/python/tools/mypy_ratchet.py +97 -0
- package/python/tools/pack.py +178 -0
- package/python/tools/release_check.py +349 -0
- package/python/tools/restart_foxclaw.ps1 +74 -0
- package/python/tools/shared_write_audit.py +204 -0
- package/python/tools/size_baseline.txt +28 -0
- package/python/tools/size_check.py +102 -0
- package/python/tools/verify_env.bat +2 -0
- package/python/tools/verify_env.py +25 -0
- package/python/vendor/HMR_LICENSE +37 -0
- package/python/vendor/HMR_README.md +65 -0
- package/python/vendor/hkc/LICENSE +37 -0
- package/python/vendor/hkc/hkc_ace/__init__.py +0 -0
- package/python/vendor/hkc/hkc_ace/ace.py +457 -0
- package/python/vendor/hkc/hkc_ace/skill_taxonomy.json +465 -0
- package/python/vendor/hkc/hkc_api/__init__.py +0 -0
- package/python/vendor/hkc/hkc_api/container.py +416 -0
- package/python/vendor/hkc/hkc_api/ingest_jobs.py +103 -0
- package/python/vendor/hkc/hkc_api/main.py +157 -0
- package/python/vendor/hkc/hkc_api/routes/__init__.py +0 -0
- package/python/vendor/hkc/hkc_api/routes/ability.py +151 -0
- package/python/vendor/hkc/hkc_api/routes/config.py +51 -0
- package/python/vendor/hkc/hkc_api/routes/knowledge.py +630 -0
- package/python/vendor/hkc/hkc_api/routes/search.py +88 -0
- package/python/vendor/hkc/hkc_api/schemas.py +153 -0
- package/python/vendor/hkc/hkc_api/synthesis.py +179 -0
- package/python/vendor/hkc/hkc_core/__init__.py +0 -0
- package/python/vendor/hkc/hkc_core/graph/__init__.py +0 -0
- package/python/vendor/hkc/hkc_core/graph/base.py +188 -0
- package/python/vendor/hkc/hkc_core/graph/sqlite_store.py +789 -0
- package/python/vendor/hkc/hkc_core/models/__init__.py +0 -0
- package/python/vendor/hkc/hkc_core/models/enums.py +65 -0
- package/python/vendor/hkc/hkc_core/models/ku.py +307 -0
- package/python/vendor/hkc/hkc_core/utils/__init__.py +0 -0
- package/python/vendor/hkc/hkc_core/utils/id_gen.py +113 -0
- package/python/vendor/hkc/hkc_crystallizer/__init__.py +44 -0
- package/python/vendor/hkc/hkc_crystallizer/adapters.py +94 -0
- package/python/vendor/hkc/hkc_crystallizer/candidate.py +145 -0
- package/python/vendor/hkc/hkc_crystallizer/crystallizer.py +179 -0
- package/python/vendor/hkc/hkc_crystallizer/ingress.py +90 -0
- package/python/vendor/hkc/hkc_kde/__init__.py +0 -0
- package/python/vendor/hkc/hkc_kde/chunker.py +197 -0
- package/python/vendor/hkc/hkc_kde/extractor.py +623 -0
- package/python/vendor/hkc/hkc_kde/kde.py +298 -0
- package/python/vendor/hkc/hkc_kde/loaders/__init__.py +0 -0
- package/python/vendor/hkc/hkc_kde/loaders/ebook_loader.py +153 -0
- package/python/vendor/hkc/hkc_kde/loaders/office_loader.py +194 -0
- package/python/vendor/hkc/hkc_kde/loaders/pdf_loader.py +162 -0
- package/python/vendor/hkc/hkc_kde/loaders/text_loader.py +157 -0
- package/python/vendor/hkc/hkc_kde/loaders/web_loader.py +91 -0
- package/python/vendor/hkc/hkc_kde/models.py +74 -0
- package/python/vendor/hkc/hkc_kde/packager.py +329 -0
- package/python/vendor/hkc/hkc_kde/relation_disc.py +151 -0
- package/python/vendor/hkc/hkc_kee/__init__.py +0 -0
- package/python/vendor/hkc/hkc_kee/dedup.py +227 -0
- package/python/vendor/hkc/hkc_kee/fingerprint.py +106 -0
- package/python/vendor/hkc/hkc_kee/kee.py +541 -0
- package/python/vendor/hkc/hkc_kep/__init__.py +0 -0
- package/python/vendor/hkc/hkc_kep/event_bus.py +205 -0
- package/python/vendor/hkc/hkc_sdk/__init__.py +63 -0
- package/python/vendor/hkc/hkc_sdk/base.py +151 -0
- package/python/vendor/hkc/hkc_sdk/direct_client.py +153 -0
- package/python/vendor/hkc/hkc_sdk/exceptions.py +39 -0
- package/python/vendor/hkc/hkc_sdk/http_client.py +166 -0
- package/python/vendor/hkc/hkc_sdk/models.py +171 -0
- package/python/vendor/hkc/hkc_search/__init__.py +0 -0
- package/python/vendor/hkc/hkc_search/bm25.py +325 -0
- package/python/vendor/hkc/hkc_search/embedding_backends.py +374 -0
- package/python/vendor/hkc/hkc_search/graph_search.py +132 -0
- package/python/vendor/hkc/hkc_search/hybrid.py +331 -0
- package/python/vendor/hkc/hkc_search/index_build_manager.py +378 -0
- package/python/vendor/hkc/hkc_search/vector_search.py +484 -0
- package/python/vendor/hmr/__init__.py +0 -0
- package/python/vendor/hmr/core/__init__.py +0 -0
- package/python/vendor/hmr/core/hmr.py +684 -0
- package/python/vendor/hmr/core/models.py +310 -0
- package/python/vendor/hmr/engines/__init__.py +0 -0
- package/python/vendor/hmr/engines/evolution.py +710 -0
- package/python/vendor/hmr/engines/jit_compiler.py +472 -0
- package/python/vendor/hmr/engines/lifecycle.py +264 -0
- package/python/vendor/hmr/engines/policy.py +625 -0
- package/python/vendor/hmr/engines/recall.py +260 -0
- package/python/vendor/hmr/engines/runtime_state.py +158 -0
- package/python/vendor/hmr/engines/scheduler.py +323 -0
- package/python/vendor/hmr/engines/semantic.py +150 -0
- package/python/vendor/hmr/engines/temporal.py +261 -0
- package/python/vendor/hmr/engines/thought_chain.py +635 -0
- package/python/vendor/hmr/graph/__init__.py +0 -0
- package/python/vendor/hmr/graph/cwg.py +338 -0
- package/python/vendor/hmr/graph/memory_graph.py +530 -0
- package/python/vendor/hmr/storage/__init__.py +0 -0
- package/python/vendor/hmr/storage/memory_fs.py +300 -0
- package/python/vendor/hmr/storage/vector_store.py +520 -0
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Foxclaw API — FastAPI 后端
|
|
3
|
+
前端控制台通过这里获取所有数据
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
import asyncio
|
|
8
|
+
import queue
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from contextlib import asynccontextmanager
|
|
11
|
+
|
|
12
|
+
# 加载项目根目录的 .env(若存在),让所有 Key 从配置文件读取。
|
|
13
|
+
#
|
|
14
|
+
# 【FOXCLAW_SKIP_DOTENV】跳过 .env,只用进程环境变量 —— 专供测试/CI 使用。
|
|
15
|
+
# 为什么必须有这个逃生口:load_dotenv(override=False) 只保护「进程里已存在的键」。
|
|
16
|
+
# 测试若先 pop 掉某键(如 test_gateway.py pop FOXCLAW_API_KEY 以验证「未设即开放」),
|
|
17
|
+
# 再 import 本模块,.env 就会把它灌回来 → 测试实际跑在开发者的真实配置上。
|
|
18
|
+
# 后果有三:(1) 同一份代码在不同机器上结果不同,回归不可复现;
|
|
19
|
+
# (2) .env 里的真实 Key 会让测试真的打到外部 API(产生费用);
|
|
20
|
+
# (3) FOXCLAW_DATA_DIR=./foxclaw_data 会让测试写进用户的真实数据目录。
|
|
21
|
+
# 设 FOXCLAW_SKIP_DOTENV=1 后,进程环境变量成为唯一来源,测试环境完全自洽。
|
|
22
|
+
try:
|
|
23
|
+
from ..core.envfile import load_env_file
|
|
24
|
+
_env_path = Path(__file__).parent.parent.parent / ".env"
|
|
25
|
+
if os.environ.get("FOXCLAW_SKIP_DOTENV", "").strip() in ("1", "true", "True", "yes"):
|
|
26
|
+
print("[Foxclaw] ○ FOXCLAW_SKIP_DOTENV=1 —— 跳过 .env,仅用进程环境变量")
|
|
27
|
+
elif _env_path.exists():
|
|
28
|
+
# 非空值优先:系统环境变量里的同名旧 key 不再压住 .env(见 core/envfile.py)
|
|
29
|
+
_shadows = load_env_file(_env_path)
|
|
30
|
+
print("[Foxclaw] ✓ 已加载配置文件 .env")
|
|
31
|
+
if _shadows:
|
|
32
|
+
print("[Foxclaw] ! .env 覆盖了系统环境变量: " + ", ".join(_shadows))
|
|
33
|
+
except ImportError:
|
|
34
|
+
pass # 未装 python-dotenv 则用系统环境变量
|
|
35
|
+
|
|
36
|
+
from fastapi import FastAPI
|
|
37
|
+
from typing import Optional
|
|
38
|
+
|
|
39
|
+
from foxclaw import Foxclaw, __version__
|
|
40
|
+
|
|
41
|
+
# 中间件实现已移至 middleware.py;此处保留符号作 re-export(测试/外部经 foxclaw.api 访问),
|
|
42
|
+
# 冗余别名 `X as X` 让 ruff 识别为显式 re-export(F401 硬门)。install_middleware 在下方调用。
|
|
43
|
+
from .middleware import (
|
|
44
|
+
install_middleware,
|
|
45
|
+
_constant_time_eq as _constant_time_eq,
|
|
46
|
+
_is_guarded_path as _is_guarded_path,
|
|
47
|
+
_gateway_on as _gateway_on,
|
|
48
|
+
_GATEWAY as _GATEWAY,
|
|
49
|
+
_MAX_BODY as _MAX_BODY,
|
|
50
|
+
_api_key_guard as _api_key_guard,
|
|
51
|
+
_gateway_mw as _gateway_mw,
|
|
52
|
+
_trace_mw as _trace_mw,
|
|
53
|
+
_metrics_mw as _metrics_mw,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
from .routes.ws import manager, harness_websocket as harness_websocket
|
|
57
|
+
|
|
58
|
+
# 事件脊柱辅助(WS 净化 / HKC 预热)已移至 spine.py;`_register_spine_projection` 因
|
|
59
|
+
# `_WS_MUTED` 源码级断言约束仍留本文件(见 test_governance_audit)。
|
|
60
|
+
from .spine import _ws_safe_payload, _prewarm_hkc
|
|
61
|
+
# app 挂载/配置(CORS / HKC 星球数据面 / 前端静态)已移至 app.py,在下方按原顺序调用。
|
|
62
|
+
from .app import _configure_cors, _mount_hkc, _mount_frontend
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
# ── 全局 OS 实例 ───────────────────────────────────────────
|
|
66
|
+
_os_instance: Optional[Foxclaw] = None
|
|
67
|
+
|
|
68
|
+
# 线程安全事件队列:harness 在任意线程触发的事件先入队,
|
|
69
|
+
# 由后台 async 任务统一广播到 WebSocket(解决跨线程 create_task 失败问题)
|
|
70
|
+
_event_queue: "queue.Queue" = queue.Queue()
|
|
71
|
+
_broadcast_task: Optional[asyncio.Task] = None
|
|
72
|
+
# HKC 后台预热守护线程:关闭 lifespan 前必须 join,否则 aclose() 关闭 SQLite 连接时
|
|
73
|
+
# 预热线程仍在 build_index() → use-after-free → Windows access violation(偶发崩溃)。
|
|
74
|
+
_prewarm_thread = None
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _register_spine_projection(os_instance: Foxclaw):
|
|
78
|
+
"""【P0·统一事件脊柱】注册**单一投影**:EventSpine 上的每一条事件 → 线程安全队列
|
|
79
|
+
→ WS 广播。取代旧的"逐 Agent 逐事件手工转发"(那时只有 harness 4 类事件能到前端,
|
|
80
|
+
truth/process/memory 等系统事件被漏掉)。现在 harness 事件与系统事件同经脊柱,
|
|
81
|
+
一个投影全量观测。payload 经 _ws_safe_payload 净化(JSON 安全 + 有界),避免脏/大
|
|
82
|
+
对象丢连接或泛洪。event/data 形状与旧版一致(前端不受影响,未知事件被忽略)。"""
|
|
83
|
+
# 高频事件:给重放/审计用(仍在脊柱 history + robot sink),不广播到 WS 以免泛洪。
|
|
84
|
+
# · agent.cognition.*:逐 stage 认知事件
|
|
85
|
+
# · knowledge.created/updated:HKC 批量摄入一本书→数百条,量大;merged/conflict/
|
|
86
|
+
# ability/agent.used_ability 等"有意义"的知识事件仍广播(低频、可观测知识演化)。
|
|
87
|
+
_WS_MUTED = ("knowledge.created", "knowledge.updated")
|
|
88
|
+
|
|
89
|
+
def project(evt):
|
|
90
|
+
if evt.topic.startswith("agent.cognition.") or evt.topic in _WS_MUTED:
|
|
91
|
+
return
|
|
92
|
+
try:
|
|
93
|
+
_event_queue.put_nowait(
|
|
94
|
+
{"event": evt.topic, "data": _ws_safe_payload(evt.payload)})
|
|
95
|
+
except Exception:
|
|
96
|
+
pass
|
|
97
|
+
os_instance.event_spine.project(project)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
@asynccontextmanager
|
|
101
|
+
async def lifespan(app: FastAPI):
|
|
102
|
+
global _os_instance, _broadcast_task
|
|
103
|
+
# 启动自检:未设 FOXCLAW_API_KEY 即等于无鉴权。此处只做提醒(绑定地址由启动方式决定,
|
|
104
|
+
# 拿不到 host);__main__ 入口对「非回环 + 无鉴权」有硬拦截。
|
|
105
|
+
if not os.environ.get("FOXCLAW_API_KEY", "").strip():
|
|
106
|
+
print("[Foxclaw API] ⚠ 未设置 FOXCLAW_API_KEY —— 当前无鉴权,"
|
|
107
|
+
"仅本地回环安全;对外暴露前务必设置。")
|
|
108
|
+
_os_instance = Foxclaw(
|
|
109
|
+
data_dir=os.environ.get("FOXCLAW_DATA_DIR", "./foxclaw_data"),
|
|
110
|
+
shadow_rounds=settings.get_int("FOXCLAW_SHADOW_ROUNDS"),
|
|
111
|
+
)
|
|
112
|
+
_register_spine_projection(_os_instance)
|
|
113
|
+
# trace 落库(与其它 SQLite 同放 data_dir):内存环只留最近若干条且重启即失,
|
|
114
|
+
# 落库后才能"查昨天那次慢请求"。只留慢的/出错的 + 采样,见 tracing._should_persist。
|
|
115
|
+
try:
|
|
116
|
+
from ..core.tracing import TRACES
|
|
117
|
+
TRACES.attach_db(Path(_os_instance.data_dir) / "traces.db")
|
|
118
|
+
except Exception as e:
|
|
119
|
+
print(f"[Foxclaw API] ⚠ trace 持久化未启用: {e}")
|
|
120
|
+
# 后台任务:把队列里的事件广播到所有 WebSocket
|
|
121
|
+
_broadcast_task = asyncio.create_task(_drain_event_queue())
|
|
122
|
+
print("[Foxclaw API] ✓ 事件广播任务已启动")
|
|
123
|
+
# 预热 HKC:开机后台加载知识核心(装配 + HKC 多语言 embedding 模型 + 搜索索引 over 全量 KU),
|
|
124
|
+
# 免得"重启后第一次知识查询"触发冷装配卡 ~1 分钟。后台守护线程跑、不阻塞启动;失败静默。
|
|
125
|
+
_prewarm_hkc(_os_instance)
|
|
126
|
+
if _foxclaw_mcp is not None:
|
|
127
|
+
# 内置 MCP 服务器的 session_manager 需在 app lifespan 内 run()
|
|
128
|
+
async with _foxclaw_mcp.session_manager.run():
|
|
129
|
+
print("[Foxclaw API] ✓ 内置 MCP 服务器已挂载 /api/mcp/")
|
|
130
|
+
yield
|
|
131
|
+
else:
|
|
132
|
+
yield
|
|
133
|
+
if _broadcast_task:
|
|
134
|
+
_broadcast_task.cancel()
|
|
135
|
+
try:
|
|
136
|
+
await _broadcast_task
|
|
137
|
+
except asyncio.CancelledError:
|
|
138
|
+
pass
|
|
139
|
+
# 释放持有原生资源的子系统(浏览器专用线程/会话),避免进程无法干净退出。
|
|
140
|
+
# 先 join HKC 预热线程:预热在后台线程对 SQLite 建索引,若不 join 就直接 aclose()
|
|
141
|
+
# 关闭 SQLite 连接,会与预热线程产生 use-after-free → Windows access violation。
|
|
142
|
+
if _prewarm_thread is not None and _prewarm_thread.is_alive():
|
|
143
|
+
_prewarm_thread.join(timeout=120.0)
|
|
144
|
+
if _os_instance is not None:
|
|
145
|
+
await _os_instance.aclose()
|
|
146
|
+
print("[Foxclaw API] 关闭中...")
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
async def _drain_event_queue():
|
|
150
|
+
"""后台 async 循环:从线程安全队列取事件,广播到 WebSocket。"""
|
|
151
|
+
import queue as _queue_mod
|
|
152
|
+
loop = asyncio.get_running_loop()
|
|
153
|
+
while True:
|
|
154
|
+
try:
|
|
155
|
+
# 带超时地阻塞取队列:超时让线程周期性醒来,
|
|
156
|
+
# 以便被 cancel 时 executor 线程能干净退出(否则进程无法结束)
|
|
157
|
+
try:
|
|
158
|
+
event = await loop.run_in_executor(
|
|
159
|
+
None, lambda: _event_queue.get(timeout=0.5))
|
|
160
|
+
except _queue_mod.Empty:
|
|
161
|
+
continue
|
|
162
|
+
await manager.broadcast(event)
|
|
163
|
+
except asyncio.CancelledError:
|
|
164
|
+
break
|
|
165
|
+
except Exception:
|
|
166
|
+
await asyncio.sleep(0.1)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
# ── 内置 MCP 服务器(默认关;FOXCLAW_MCP_SERVER=1 开)──────────
|
|
170
|
+
# 把只读知识+记忆暴露成 MCP 工具,挂 /api/mcp/(自动受 X-API-Key 中间件保护)。
|
|
171
|
+
_foxclaw_mcp = None
|
|
172
|
+
_foxclaw_mcp_app = None
|
|
173
|
+
if os.environ.get("FOXCLAW_MCP_SERVER", "0").strip() in ("1", "true", "True"):
|
|
174
|
+
from ..mcp_server import build_foxclaw_mcp
|
|
175
|
+
_mcp_agents = os.environ.get("FOXCLAW_MCP_AGENTS", "0").strip() in ("1", "true", "True")
|
|
176
|
+
_foxclaw_mcp = build_foxclaw_mcp(lambda: _os_instance, expose_agents=_mcp_agents)
|
|
177
|
+
_foxclaw_mcp_app = _foxclaw_mcp.streamable_http_app()
|
|
178
|
+
if _mcp_agents:
|
|
179
|
+
print("[Foxclaw API] ⚠ MCP 已暴露 Agent 调度(ask_foxclaw)——外部客户端可驱动你的认知 Agent")
|
|
180
|
+
if not os.environ.get("FOXCLAW_API_KEY", "").strip():
|
|
181
|
+
print("[Foxclaw API] ⚠ 内置 MCP 服务器已开启但未设 FOXCLAW_API_KEY —— "
|
|
182
|
+
"对外暴露前务必设置,否则任何人可读你的知识/记忆")
|
|
183
|
+
if os.environ.get("FOXCLAW_CORS_ORIGINS", "").strip() == "*":
|
|
184
|
+
print("[Foxclaw API] ⚠ MCP 已开启但 CORS=* —— 生产建议用 FOXCLAW_CORS_ORIGINS 收紧允许来源")
|
|
185
|
+
|
|
186
|
+
app = FastAPI(
|
|
187
|
+
title="Foxclaw API",
|
|
188
|
+
version=__version__,
|
|
189
|
+
description="Cognitive Agent Operating System",
|
|
190
|
+
lifespan=lifespan,
|
|
191
|
+
)
|
|
192
|
+
if _foxclaw_mcp_app is not None:
|
|
193
|
+
app.mount("/api/mcp", _foxclaw_mcp_app) # 受 _api_key_guard 保护(startswith /api)
|
|
194
|
+
|
|
195
|
+
# ── CORS 收紧:实现已移至 app.py ───────────────────────────
|
|
196
|
+
_configure_cors(app)
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
# ── 中间件注册(鉴权/网关限流/追踪/指标):实现已移至 middleware.py,这里按原顺序集中挂载 ──
|
|
200
|
+
install_middleware(app)
|
|
201
|
+
|
|
202
|
+
# ── WebSocket 路由:实现已移至 routes/ws.py ────────────────
|
|
203
|
+
from .routes import ws as _ws
|
|
204
|
+
_ws.register(app)
|
|
205
|
+
|
|
206
|
+
# ── 系统/监控/追踪路由:实现已移至 routes/system.py(import 即注册) ────────────────
|
|
207
|
+
from .routes import system # noqa: F401
|
|
208
|
+
|
|
209
|
+
# ── AHS 执行链路由:实现已移至 routes/ahs.py(import 即注册) ────────────────
|
|
210
|
+
from .routes import ahs # noqa: F401
|
|
211
|
+
|
|
212
|
+
# ── 记忆/会话/偏好:routes/memory.py(import 即注册) ────────────────
|
|
213
|
+
from .routes import memory # noqa: F401
|
|
214
|
+
|
|
215
|
+
# ── 任务执行/流式直答/流式任务:routes/task.py(import 即注册) ────────────────
|
|
216
|
+
from .routes import task # noqa: F401
|
|
217
|
+
|
|
218
|
+
# ── 领域内核(财务/学习/行程):routes/domain.py(import 即注册) ────────────────
|
|
219
|
+
from .routes import domain # noqa: F401
|
|
220
|
+
|
|
221
|
+
# ── 断点续跑现场:routes/recovery.py(import 即注册) ────────────────
|
|
222
|
+
from .routes import recovery # noqa: F401
|
|
223
|
+
|
|
224
|
+
# ── 主管编排:routes/orchestration.py(import 即注册) ────────────────
|
|
225
|
+
from .routes import orchestration # noqa: F401
|
|
226
|
+
|
|
227
|
+
# ── 浏览器子系统:routes/browser.py(import 即注册) ────────────────
|
|
228
|
+
from .routes import browser # noqa: F401
|
|
229
|
+
|
|
230
|
+
# ── 知识核心(HKC):routes/knowledge.py(import 即注册) ────────────────
|
|
231
|
+
from .routes import knowledge # noqa: F401
|
|
232
|
+
|
|
233
|
+
# ── SM-2 复习面:routes/review.py(import 即注册) ────────────────
|
|
234
|
+
from .routes import review # noqa: F401
|
|
235
|
+
|
|
236
|
+
# ── 认知搜索(HSR):routes/search.py(import 即注册) ────────────────
|
|
237
|
+
from .routes import search # noqa: F401
|
|
238
|
+
|
|
239
|
+
# ── 聊天附件上传/下载(多模态渲染·步骤7):routes/attachments.py(import 即注册) ────────────────
|
|
240
|
+
from .routes import attachments # noqa: F401
|
|
241
|
+
|
|
242
|
+
# ── 前端 LLM 管理(下拉式选模型 + 自定义):routes/llm_admin.py(import 即注册) ──────
|
|
243
|
+
from .routes import llm_admin # noqa: F401
|
|
244
|
+
|
|
245
|
+
# 搬移前这些是 api 模块级符号,测试/外部仍经 foxclaw.api 访问 → re-export
|
|
246
|
+
from .routes.task import _sanitize_images, _sanitize_attachments, _MAX_IMAGES, _MAX_IMG_B64, _STREAM_LIMITER # noqa: F401
|
|
247
|
+
from .routes.domain import _parse_import_rows # noqa: F401
|
|
248
|
+
from .routes.knowledge import knowledge_ability as knowledge_ability, _HKCInsuffCov as _HKCInsuffCov # noqa: F401
|
|
249
|
+
from ..core import settings
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
# ── HKC 星球数据面:实现已移至 app.py ─────────────────────
|
|
253
|
+
_mount_hkc(app)
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
# ── 前端静态文件:实现已移至 app.py ─────────────────────────
|
|
257
|
+
_mount_frontend(app)
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
if __name__ == "__main__":
|
|
261
|
+
import uvicorn
|
|
262
|
+
|
|
263
|
+
# 默认只绑回环地址(本地开发)。对外暴露必须显式设 FOXCLAW_HOST,且应同时设置 FOXCLAW_API_KEY——
|
|
264
|
+
# 否则该端口上任何可达者都能读写记忆、执行技能、摄入文件(这些端点全部无鉴权)。
|
|
265
|
+
# 用 `python -m uvicorn foxclaw.api:app --host ...` 启动时会绕过本检查,故 lifespan 另有软提醒。
|
|
266
|
+
_host = os.environ.get("FOXCLAW_HOST", "127.0.0.1").strip() or "127.0.0.1"
|
|
267
|
+
_port = settings.get_int("FOXCLAW_PORT")
|
|
268
|
+
_LOOPBACK = {"127.0.0.1", "localhost", "::1", "[::1]"}
|
|
269
|
+
if _host not in _LOOPBACK and not os.environ.get("FOXCLAW_API_KEY", "").strip():
|
|
270
|
+
print(
|
|
271
|
+
f"\n[Foxclaw API] ⚠⚠ 绑定 {_host} 但未设置 FOXCLAW_API_KEY:\n"
|
|
272
|
+
" 该端口上任何可达者都能读写记忆、执行技能、摄入文件。\n"
|
|
273
|
+
" 请设置 FOXCLAW_API_KEY,或改用 FOXCLAW_HOST=127.0.0.1。\n"
|
|
274
|
+
" 确认自担风险,可设 FOXCLAW_ALLOW_INSECURE=1 跳过此检查。\n"
|
|
275
|
+
)
|
|
276
|
+
if os.environ.get("FOXCLAW_ALLOW_INSECURE", "").strip() != "1":
|
|
277
|
+
raise SystemExit("[Foxclaw API] 已拒绝启动:非回环绑定 + 无鉴权。")
|
|
278
|
+
uvicorn.run(
|
|
279
|
+
"foxclaw.api:app",
|
|
280
|
+
host=_host,
|
|
281
|
+
port=_port,
|
|
282
|
+
reload=True,
|
|
283
|
+
)
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"""app 配置与挂载:CORS、HKC 星球数据面、前端静态文件。
|
|
2
|
+
|
|
3
|
+
从 `foxclaw/api/__init__.py` 搬移出来的「挂载类」逻辑,均为接收 `app` 参数的纯函数,
|
|
4
|
+
在 `__init__.py` 创建 `app` 之后按原顺序调用,保持行为不变。
|
|
5
|
+
"""
|
|
6
|
+
import os
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
from fastapi import HTTPException
|
|
10
|
+
from fastapi.middleware.cors import CORSMiddleware
|
|
11
|
+
from fastapi.staticfiles import StaticFiles
|
|
12
|
+
from fastapi.responses import FileResponse
|
|
13
|
+
|
|
14
|
+
import foxclaw.api as _api
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _configure_cors(app):
|
|
18
|
+
# ── 可选鉴权 + CORS 收紧(部署安全)──────────────────────────
|
|
19
|
+
# FOXCLAW_API_KEY 未设 → 开放(本地默认,保持现状,既有测试/前端不受影响)。
|
|
20
|
+
# 设了 → 所有 /api/* 请求必须带正确的 X-API-Key,否则 401。
|
|
21
|
+
# 注意:http 中间件**看不到 websocket scope**,故 /ws 的鉴权在 WS 端点内单独做
|
|
22
|
+
# (见 harness_websocket,用 ?key= 查询参数)。前端(静态)与 /docs 始终开放。
|
|
23
|
+
# 默认只放行回环来源(本地开发足够:前端与 API 是同源挂载,同源请求本就不经 CORS)。
|
|
24
|
+
# 需要跨源时设 FOXCLAW_CORS_ORIGINS=来源1,来源2(逗号分隔);显式设 "*" 才会全开(不推荐,
|
|
25
|
+
# 与未鉴权叠加等于把 API 交给任意网页)。收紧默认值属于「默认安全」:不再默认 allow_origins=["*"]。
|
|
26
|
+
_cors_env = os.environ.get("FOXCLAW_CORS_ORIGINS", "").strip()
|
|
27
|
+
if _cors_env == "*":
|
|
28
|
+
_CORS_ORIGINS = ["*"]
|
|
29
|
+
_CORS_ORIGIN_REGEX = None
|
|
30
|
+
elif _cors_env:
|
|
31
|
+
_CORS_ORIGINS = [o.strip() for o in _cors_env.split(",") if o.strip()]
|
|
32
|
+
_CORS_ORIGIN_REGEX = None
|
|
33
|
+
else:
|
|
34
|
+
_CORS_ORIGINS = []
|
|
35
|
+
_CORS_ORIGIN_REGEX = r"^https?://(localhost|127\.0\.0\.1|\[::1\])(:\d+)?$"
|
|
36
|
+
|
|
37
|
+
app.add_middleware(
|
|
38
|
+
CORSMiddleware,
|
|
39
|
+
allow_origins=_CORS_ORIGINS,
|
|
40
|
+
allow_origin_regex=_CORS_ORIGIN_REGEX,
|
|
41
|
+
allow_methods=["*"],
|
|
42
|
+
allow_headers=["*"],
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _mount_hkc(app):
|
|
47
|
+
# ── HKC 星球数据面:挂载 HKC 自带的 /knowledge REST 路由 ──────────────────
|
|
48
|
+
# 星球 UI(frontend/planet)需要 /knowledge/graph、/ku/{id}、/ku/{id}/neighbors、
|
|
49
|
+
# /ingest/*、/reset 等。HKC 在本进程装配,这里把它自带的 knowledge 路由挂上,并用
|
|
50
|
+
# dependency_overrides 把 get_container 指向 **Foxclaw 已装配的同一个容器**(依赖倒置,
|
|
51
|
+
# 不另开一套;保持惰性:首个 /knowledge 请求才触发 HKC 装配)。鉴权由上面中间件覆盖 /knowledge。
|
|
52
|
+
# 缺 vendor/hkc 或导入失败 → 静默跳过(星球无数据但不影响主应用)。
|
|
53
|
+
try:
|
|
54
|
+
from foxclaw.knowledge import _ensure_hkc_importable as _ensure_hkc
|
|
55
|
+
_ensure_hkc()
|
|
56
|
+
from hkc_api.routes.knowledge import router as _hkc_knowledge_router
|
|
57
|
+
from hkc_api.container import get_container as _hkc_get_container
|
|
58
|
+
|
|
59
|
+
def _resolve_hkc_container():
|
|
60
|
+
svc = _api._os_instance.hkc if _api._os_instance is not None else None
|
|
61
|
+
if svc is None or not getattr(svc, "available", False):
|
|
62
|
+
raise HTTPException(503, "HKC 未启用(未 vendor 或缺依赖)")
|
|
63
|
+
return svc.container
|
|
64
|
+
|
|
65
|
+
app.dependency_overrides[_hkc_get_container] = _resolve_hkc_container
|
|
66
|
+
app.include_router(_hkc_knowledge_router)
|
|
67
|
+
# /stats 系统统计:独立 hkc-api 在 main.py 顶层 @app.get("/stats") 定义,不在任何
|
|
68
|
+
# router 里,嵌入式挂载时会漏掉 → 星球 UI 的 updateDataInfo 调 /stats 404,
|
|
69
|
+
# 「数据目录 / 全库知识总数」提示失效。这里补挂,复用同一容器 override(惰性装配)。
|
|
70
|
+
@app.get("/stats", tags=["system"])
|
|
71
|
+
def _hkc_stats():
|
|
72
|
+
return _resolve_hkc_container().stats()
|
|
73
|
+
# 连接设置(星球 UI 左下的「连接设置」面板)POST /config/llm 做运行时 LLM 配置,
|
|
74
|
+
# 覆盖 .env 的 HKC_LLM_*。独立 hkc-api 有这个路由,嵌入式 foxclaw 之前没挂 → 前端
|
|
75
|
+
# 报「模型配置失败:404」且改配置无效。这里补挂 config 路由(同一容器 override 已覆盖),
|
|
76
|
+
# 让「连接设置」的「知识抽取模型」段在 foxclaw 下真正生效。
|
|
77
|
+
from hkc_api.routes.config import router as _hkc_config_router
|
|
78
|
+
app.include_router(_hkc_config_router) # /config/llm(GET 读配置 / POST 改配置)
|
|
79
|
+
# 星球副面板(冲突裁决/能力编译/最短路径)用的 HKC 路由。不挂的话 /conflicts、
|
|
80
|
+
# /abilities、/search/path 在 Foxclaw 下 404 → 星球「冲突裁决」面板拉不到数据、
|
|
81
|
+
# 永远显示 0(误导),能力/路径面板同样空。这里补挂(同一容器 override 已覆盖)。
|
|
82
|
+
try:
|
|
83
|
+
from hkc_api.routes.ability import router as _hkc_ability_router
|
|
84
|
+
from hkc_api.routes.search import router as _hkc_search_router
|
|
85
|
+
app.include_router(_hkc_ability_router) # /abilities, /conflicts
|
|
86
|
+
app.include_router(_hkc_search_router) # /search/path 等
|
|
87
|
+
_sub = "/abilities, /conflicts, /search/*"
|
|
88
|
+
except Exception as _e2:
|
|
89
|
+
_sub = f"(能力/搜索路由未挂: {_e2})"
|
|
90
|
+
print(f"[Foxclaw API] ✓ HKC 星球数据面已挂载 (/knowledge/*, /config/*, {_sub})")
|
|
91
|
+
except Exception as _e:
|
|
92
|
+
print(f"[Foxclaw API] · HKC 星球数据面未挂载(可选): {_e}")
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _mount_frontend(app):
|
|
96
|
+
# ── 前端静态文件 ───────────────────────────────────────────
|
|
97
|
+
# frontend/ 下现在并存三套:frontend/v3(Vue,当前主界面)、deck_v2.html(旧主界面)、
|
|
98
|
+
# deck.html(最早的一版)。根路径必须给 v3 —— 之前一直指向 deck_v2,新版只能手输 /v3/,
|
|
99
|
+
# 等于新界面根本没上线。
|
|
100
|
+
frontend_dir = Path(__file__).parent.parent.parent / "frontend"
|
|
101
|
+
if not frontend_dir.exists():
|
|
102
|
+
return
|
|
103
|
+
|
|
104
|
+
v3_index = frontend_dir / "v3" / "index.html"
|
|
105
|
+
|
|
106
|
+
@app.get("/")
|
|
107
|
+
async def serve_root():
|
|
108
|
+
"""主界面。v3 未构建(没跑 npm run build)时退回旧 deck_v2,不让根路径 404。"""
|
|
109
|
+
if v3_index.exists():
|
|
110
|
+
return FileResponse(str(v3_index))
|
|
111
|
+
legacy = frontend_dir / "deck_v2.html"
|
|
112
|
+
return FileResponse(str(legacy if legacy.exists() else frontend_dir / "deck.html"))
|
|
113
|
+
|
|
114
|
+
@app.get("/classic")
|
|
115
|
+
async def serve_classic():
|
|
116
|
+
"""旧界面(deck_v2,v5.34):新版出问题时的手动回退入口,也留着它的二级面板历史。"""
|
|
117
|
+
return FileResponse(str(frontend_dir / "deck_v2.html"))
|
|
118
|
+
|
|
119
|
+
@app.get("/old")
|
|
120
|
+
async def serve_deck_old():
|
|
121
|
+
return FileResponse(str(frontend_dir / "deck.html"))
|
|
122
|
+
|
|
123
|
+
app.mount("/", StaticFiles(directory=str(frontend_dir), html=True), name="frontend")
|