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.
- package/index.js +1 -8
- package/package.json +1 -1
- package/python/.env.example +8 -0
- package/python/.github/workflows/ci.yml +47 -2
- package/python/.github/workflows/release.yml +5 -0
- package/python/CHANGELOG.md +100 -0
- package/python/CHANGELOG_CN.md +100 -0
- package/python/README.md +17 -2
- package/python/README_CN.md +38 -20
- package/python/bench/bm25_audit.py +1 -14
- package/python/bench/bm25_regression.py +0 -1
- package/python/bench/bm25_stress.py +0 -4
- package/python/bench/cd_audit.py +0 -23
- package/python/bench/chaos.py +6 -8
- package/python/bench/e2e_real_models.py +0 -1
- package/python/bench/llm_retry_test.py +2 -9
- package/python/bench/load_test.py +5 -8
- package/python/bench/vector_ivf_upgrade.py +1 -7
- package/python/docs/CONTENT_GUARD.md +189 -0
- package/python/docs/CONTENT_GUARD_CN.md +189 -0
- package/python/docs/RELEASE.md +78 -37
- package/python/docs/RUN_CN.md +2 -2
- package/python/evals/governance/cases.py +0 -37
- package/python/evals/governance/run_evals.py +5 -37
- package/python/examples/governance_middleware_demo.py +1 -6
- package/python/examples/langgraph_governance_demo.py +7 -11
- package/python/foxclaw/__init__.py +74 -152
- package/python/foxclaw/agents/base.py +82 -136
- package/python/foxclaw/agents/impl/_helpers.py +2 -10
- package/python/foxclaw/agents/impl/finance.py +4 -21
- package/python/foxclaw/agents/impl/office.py +7 -24
- package/python/foxclaw/agents/impl/supervisor.py +12 -79
- package/python/foxclaw/agents/mixins/_context.py +0 -23
- package/python/foxclaw/agents/mixins/augment.py +10 -16
- package/python/foxclaw/agents/mixins/llm.py +7 -6
- package/python/foxclaw/agents/mixins/memory.py +23 -3
- package/python/foxclaw/agents/mixins/preference.py +58 -8
- package/python/foxclaw/agents/mixins/prompt.py +59 -12
- package/python/foxclaw/agents/mixins/skillbox.py +6 -6
- package/python/foxclaw/agents/mixins/tool_exec.py +16 -42
- package/python/foxclaw/agents/rules/finance/RULES.md +131 -0
- package/python/foxclaw/api/__init__.py +18 -77
- package/python/foxclaw/api/_state.py +43 -0
- package/python/foxclaw/api/bootstrap.py +114 -0
- package/python/foxclaw/api/middleware.py +5 -28
- package/python/foxclaw/api/routes/ahs.py +60 -73
- package/python/foxclaw/api/routes/attachments.py +5 -6
- package/python/foxclaw/api/routes/browser.py +7 -11
- package/python/foxclaw/api/routes/domain.py +18 -26
- package/python/foxclaw/api/routes/knowledge.py +3 -21
- package/python/foxclaw/api/routes/llm_admin.py +21 -33
- package/python/foxclaw/api/routes/memory.py +50 -12
- package/python/foxclaw/api/routes/orchestration.py +3 -5
- package/python/foxclaw/api/routes/recovery.py +4 -3
- package/python/foxclaw/api/routes/review.py +2 -11
- package/python/foxclaw/api/routes/search.py +3 -7
- package/python/foxclaw/api/routes/system.py +33 -49
- package/python/foxclaw/api/routes/task.py +31 -49
- package/python/foxclaw/api/routes/ws.py +2 -6
- package/python/foxclaw/api/spine.py +1 -4
- package/python/foxclaw/behavior/__init__.py +20 -0
- package/python/foxclaw/behavior/behavior.yaml +62 -0
- package/python/foxclaw/behavior/spec.py +287 -0
- package/python/foxclaw/browser/action/action_executor.py +1 -5
- package/python/foxclaw/browser/action/action_types.py +0 -1
- package/python/foxclaw/browser/action/url_policy.py +1 -5
- package/python/foxclaw/browser/adapter/web_adapter.py +1 -1
- package/python/foxclaw/browser/driver/playwright_driver.py +20 -16
- package/python/foxclaw/browser/perception/dom_extractor.py +0 -1
- package/python/foxclaw/browser/perception/element_indexer.py +1 -2
- package/python/foxclaw/browser/service.py +7 -11
- package/python/foxclaw/capability/embedding_service.py +4 -6
- package/python/foxclaw/channels/__init__.py +9 -11
- package/python/foxclaw/channels/discord.py +4 -13
- package/python/foxclaw/channels/dispatcher.py +2 -27
- package/python/foxclaw/channels/feishu.py +10 -20
- package/python/foxclaw/channels/openclaw_bridge.py +4 -9
- package/python/foxclaw/channels/signing.py +9 -9
- package/python/foxclaw/channels/telegram.py +3 -4
- package/python/foxclaw/channels/weixin.py +7 -17
- package/python/foxclaw/cli.py +42 -13
- package/python/foxclaw/cognitive/reasoner.py +9 -10
- package/python/foxclaw/cognitive/state.py +18 -24
- package/python/foxclaw/core/audit_chain.py +1 -3
- package/python/foxclaw/core/audit_sink.py +3 -3
- package/python/foxclaw/core/compaction.py +8 -26
- package/python/foxclaw/core/config.py +2 -2
- package/python/foxclaw/core/confirm_nonce.py +6 -12
- package/python/foxclaw/core/content_guard.py +341 -0
- package/python/foxclaw/core/envfile.py +2 -2
- package/python/foxclaw/core/failure_policy.py +17 -26
- package/python/foxclaw/core/gateway.py +6 -10
- package/python/foxclaw/core/governance_audit.py +3 -7
- package/python/foxclaw/core/idempotency.py +15 -22
- package/python/foxclaw/core/injection_guard.py +10 -29
- package/python/foxclaw/core/memory_adapter.py +53 -60
- package/python/foxclaw/core/metrics.py +4 -5
- package/python/foxclaw/core/model_router.py +74 -150
- package/python/foxclaw/core/paths.py +1 -1
- package/python/foxclaw/core/recovery.py +21 -34
- package/python/foxclaw/core/scheduler.py +23 -26
- package/python/foxclaw/core/secrets.py +0 -3
- package/python/foxclaw/core/sensitive_write.py +283 -0
- package/python/foxclaw/core/sessions.py +4 -6
- package/python/foxclaw/core/settings.py +16 -32
- package/python/foxclaw/core/shared_write.py +11 -23
- package/python/foxclaw/core/steering_queue.py +12 -11
- package/python/foxclaw/core/system_bus.py +1 -1
- package/python/foxclaw/core/thought_chain.py +30 -32
- package/python/foxclaw/core/token_meter.py +9 -13
- package/python/foxclaw/core/tools.py +4 -12
- package/python/foxclaw/core/tracing.py +25 -40
- package/python/foxclaw/core/truth_pipeline.py +5 -9
- package/python/foxclaw/core/update_check.py +149 -0
- package/python/foxclaw/core/vec_recall_adapter.py +11 -37
- package/python/foxclaw/core/verify.py +6 -14
- package/python/foxclaw/core/web_search.py +4 -28
- package/python/foxclaw/finance/evidence.py +14 -18
- package/python/foxclaw/finance/fx.py +1 -3
- package/python/foxclaw/finance/graph.py +9 -18
- package/python/foxclaw/finance/models.py +20 -24
- package/python/foxclaw/finance/nlparse.py +12 -72
- package/python/foxclaw/finance/policy.py +8 -36
- package/python/foxclaw/finance/reasoner.py +3 -11
- package/python/foxclaw/finance/reasoning.py +13 -34
- package/python/foxclaw/finance/reconcile.py +5 -10
- package/python/foxclaw/finance/runtime.py +19 -46
- package/python/foxclaw/governance/middleware.py +13 -21
- package/python/foxclaw/harness/_base.py +2 -8
- package/python/foxclaw/harness/_policy.py +19 -38
- package/python/foxclaw/harness/drift.py +1 -13
- package/python/foxclaw/harness/event_bus.py +2 -5
- package/python/foxclaw/harness/harness.py +9 -97
- package/python/foxclaw/harness/loop_detection.py +11 -18
- package/python/foxclaw/itinerary/amap_client.py +2 -5
- package/python/foxclaw/itinerary/models.py +7 -7
- package/python/foxclaw/itinerary/reasoner.py +1 -6
- package/python/foxclaw/itinerary/reasoning.py +15 -21
- package/python/foxclaw/itinerary/runtime.py +8 -12
- package/python/foxclaw/kernel/capabilities.py +5 -6
- package/python/foxclaw/kernel/policy_gate.py +4 -10
- package/python/foxclaw/kernel/process_manager.py +17 -36
- package/python/foxclaw/kernel/scheduler.py +3 -9
- package/python/foxclaw/kernel/sentinel.py +13 -23
- package/python/foxclaw/kernel/sentinel_client.py +12 -15
- package/python/foxclaw/kernel/spine.py +9 -9
- package/python/foxclaw/knowledge/__init__.py +4 -20
- package/python/foxclaw/knowledge/truth_crystallizer.py +5 -8
- package/python/foxclaw/learning/knowledge_graph.py +4 -9
- package/python/foxclaw/learning/models.py +13 -13
- package/python/foxclaw/learning/reasoner.py +1 -4
- package/python/foxclaw/learning/runtime.py +19 -31
- package/python/foxclaw/learning/spaced_repetition.py +2 -7
- package/python/foxclaw/mcp_server.py +2 -3
- package/python/foxclaw/orchestration/__init__.py +16 -33
- package/python/foxclaw/pipeline/cognitive_pipeline.py +45 -97
- package/python/foxclaw/pipeline/stage_meta.py +21 -0
- package/python/foxclaw/pipeline/supervisor_stages.py +13 -42
- package/python/foxclaw/planning/llm_planner.py +3 -3
- package/python/foxclaw/planning/planner.py +6 -11
- package/python/foxclaw/search/agent_port.py +7 -11
- package/python/foxclaw/search/evidence.py +0 -4
- package/python/foxclaw/search/internal_search.py +3 -5
- package/python/foxclaw/search/models.py +11 -13
- package/python/foxclaw/search/planner.py +3 -3
- package/python/foxclaw/search/retriever.py +1 -1
- package/python/foxclaw/search/runtime.py +1 -8
- package/python/foxclaw/skills/__init__.py +46 -100
- package/python/foxclaw/skills/archive.py +111 -0
- package/python/foxclaw/skills/fetch.py +226 -0
- package/python/foxclaw/skills/hooks_spec.py +21 -25
- package/python/foxclaw/skills/install_cmd.py +216 -0
- package/python/foxclaw/skills/lifecycle.py +16 -42
- package/python/foxclaw/skills/markdown_skill.py +10 -9
- package/python/foxclaw/skills/mcp_client.py +5 -11
- package/python/foxclaw/skills/mcp_store.py +0 -3
- package/python/foxclaw/skills/permission.py +13 -48
- package/python/foxclaw/skills/pipeline.py +21 -53
- package/python/foxclaw/skills/policy.py +4 -10
- package/python/foxclaw/skills/review.py +3 -3
- package/python/frontend/deck/auth.js +0 -7
- package/python/frontend/deck/chat_model_picker.js +0 -54
- package/python/frontend/deck/domain.js +0 -20
- package/python/frontend/deck/main.js +0 -144
- package/python/frontend/deck/markdown.js +0 -4
- package/python/frontend/deck/settings.js +1 -18
- package/python/frontend/deck/state.js +0 -1
- package/python/frontend/deck/util.js +0 -2
- package/python/frontend/deck/visuals.js +0 -22
- package/python/frontend/planet/planet.js +18 -201
- package/python/frontend/v3/assets/index-JKfHlb-a.css +1 -0
- package/python/frontend/v3/assets/index-tN90dpUH.js +34 -0
- package/python/frontend/v3/index.html +3 -3
- package/python/frontend/widget_runtime.js +0 -43
- package/python/frontend/widgets.js +0 -57
- package/python/frontend_src/index.html +1 -1
- package/python/frontend_src/package-lock.json +2 -2
- package/python/frontend_src/package.json +1 -1
- package/python/frontend_src/src/App.vue +3 -7
- package/python/frontend_src/src/api.js +0 -7
- package/python/frontend_src/src/components/ChatDock.vue +27 -39
- package/python/frontend_src/src/components/DocViewer.vue +0 -9
- package/python/frontend_src/src/components/Inspector.vue +0 -6
- package/python/frontend_src/src/components/LeftRail.vue +60 -39
- package/python/frontend_src/src/components/MenuPopover.vue +9 -11
- package/python/frontend_src/src/components/MessageStream.vue +18 -30
- package/python/frontend_src/src/components/ModelPicker.vue +0 -5
- package/python/frontend_src/src/components/QuickIngest.vue +0 -6
- package/python/frontend_src/src/components/Stage.vue +71 -25
- package/python/frontend_src/src/components/ToolMenu.vue +0 -3
- package/python/frontend_src/src/components/TopBar.vue +38 -10
- package/python/frontend_src/src/components/panels/ActivityPanel.vue +0 -2
- package/python/frontend_src/src/components/panels/AgentsPanel.vue +2 -5
- package/python/frontend_src/src/components/panels/KnowledgePanel.vue +0 -16
- package/python/frontend_src/src/components/panels/MemoryPanel.vue +42 -3
- package/python/frontend_src/src/components/panels/PanelBox.vue +6 -7
- package/python/frontend_src/src/components/panels/ReviewPanel.vue +0 -16
- package/python/frontend_src/src/components/panels/SettingsPanel.vue +142 -38
- package/python/frontend_src/src/components/panels/TasksPanel.vue +0 -1
- package/python/frontend_src/src/components/panels/ToolsPanel.vue +125 -19
- package/python/frontend_src/src/lib/fmt.js +0 -4
- package/python/frontend_src/src/lib/i18n.js +53 -0
- package/python/frontend_src/src/lib/locales/en.js +173 -0
- package/python/frontend_src/src/lib/locales/zh.js +174 -0
- package/python/frontend_src/src/lib/md.js +0 -14
- package/python/frontend_src/src/main.js +2 -0
- package/python/frontend_src/src/stores/app.js +17 -32
- package/python/frontend_src/src/stores/chat.js +0 -34
- package/python/frontend_src/src/stores/model.js +0 -8
- package/python/frontend_src/src/stores/panels.js +16 -44
- package/python/frontend_src/src/stores/profile.js +31 -0
- package/python/frontend_src/src/stores/system.js +25 -0
- package/python/frontend_src/vite.config.js +0 -7
- package/python/pyproject.toml +17 -3
- package/python/requirements.txt +311 -0
- package/python/tests/conftest.py +0 -22
- package/python/tests/run_all.py +4 -66
- package/python/tests/test_agent_lifecycle.py +4 -38
- package/python/tests/test_agent_rules.py +81 -0
- package/python/tests/test_ahs_p2.py +4 -25
- package/python/tests/test_anthropic_tools.py +1 -15
- package/python/tests/test_api.py +19 -9
- package/python/tests/test_api_auth.py +1 -5
- package/python/tests/test_attachments.py +9 -25
- package/python/tests/test_audit_chain.py +2 -7
- package/python/tests/test_audit_sink.py +0 -6
- package/python/tests/test_behavior_spec.py +198 -0
- package/python/tests/test_browser.py +2 -6
- package/python/tests/test_browser_request_guard.py +0 -4
- package/python/tests/test_capabilities.py +0 -6
- package/python/tests/test_channel_signing.py +2 -9
- package/python/tests/test_channels.py +1 -11
- package/python/tests/test_config_layering.py +0 -4
- package/python/tests/test_confirm_nonce.py +1 -12
- package/python/tests/test_conflict_dismiss.py +143 -0
- package/python/tests/test_content_guard.py +177 -0
- package/python/tests/test_context_compaction_wiring.py +1 -7
- package/python/tests/test_dispatch.py +0 -6
- package/python/tests/test_domain_evals.py +11 -80
- package/python/tests/test_domain_import.py +0 -6
- package/python/tests/test_domain_reasoners.py +0 -14
- package/python/tests/test_e2e.py +0 -17
- package/python/tests/test_emb_cache.py +7 -17
- package/python/tests/test_embedding_singleton.py +1 -3
- package/python/tests/test_evals_smoke.py +0 -24
- package/python/tests/test_event_spine.py +10 -18
- package/python/tests/test_evidence_semantic.py +1 -14
- package/python/tests/test_evidence_sse.py +3 -6
- package/python/tests/test_file_ingest.py +0 -11
- package/python/tests/test_finance.py +0 -9
- package/python/tests/test_finance_ledger.py +5 -12
- package/python/tests/test_finance_persist.py +8 -12
- package/python/tests/test_gateway.py +3 -22
- package/python/tests/test_governance.py +5 -52
- package/python/tests/test_governance_audit.py +4 -39
- package/python/tests/test_governance_middleware.py +1 -7
- package/python/tests/test_hkc.py +6 -24
- package/python/tests/test_hkc_index_build_manager.py +0 -33
- package/python/tests/test_hkc_manifest_reuse.py +4 -27
- package/python/tests/test_hkc_search_event_sync.py +3 -54
- package/python/tests/test_import_path_guard.py +0 -6
- package/python/tests/test_injection_guard.py +18 -36
- package/python/tests/test_integration.py +5 -29
- package/python/tests/test_itinerary.py +0 -3
- package/python/tests/test_kee_semantic.py +0 -5
- package/python/tests/test_kernel.py +1 -11
- package/python/tests/test_learning.py +3 -12
- package/python/tests/test_llm_admin.py +4 -19
- package/python/tests/test_llm_planner.py +1 -23
- package/python/tests/test_markdown_skills.py +3 -14
- package/python/tests/test_markdown_skills_openclaw.py +4 -9
- package/python/tests/test_mcp_connectors.py +3 -15
- package/python/tests/test_mcp_guardian_discovery.py +2 -6
- package/python/tests/test_mcp_no_memory.py +3 -3
- package/python/tests/test_mcp_policy_gate.py +0 -1
- package/python/tests/test_mcp_server.py +6 -9
- package/python/tests/test_memory_recall.py +1 -4
- package/python/tests/test_metrics.py +0 -8
- package/python/tests/test_multi_chain.py +1 -13
- package/python/tests/test_multimodal.py +5 -18
- package/python/tests/test_negotiation.py +3 -13
- package/python/tests/test_orchestration.py +1 -11
- package/python/tests/test_orchestration_decoupled.py +0 -6
- package/python/tests/test_p3_p5_hardening.py +3 -17
- package/python/tests/test_parallel_dispatch.py +4 -12
- package/python/tests/test_pipeline.py +17 -58
- package/python/tests/test_plan_visible.py +4 -4
- package/python/tests/test_planner.py +0 -7
- package/python/tests/test_policy_gate.py +2 -3
- package/python/tests/test_profile.py +67 -0
- package/python/tests/test_readme_claims.py +163 -0
- package/python/tests/test_reasoner.py +2 -17
- package/python/tests/test_release_check.py +2 -13
- package/python/tests/test_resume_checkpoint.py +7 -15
- package/python/tests/test_routing.py +2 -12
- package/python/tests/test_scheduler.py +7 -10
- package/python/tests/test_search.py +1 -11
- package/python/tests/test_secrets.py +4 -10
- package/python/tests/test_semantic.py +1 -10
- package/python/tests/test_sensitive_write.py +181 -0
- package/python/tests/test_sessions.py +3 -4
- package/python/tests/test_settings.py +2 -18
- package/python/tests/test_shadow_learning.py +3 -43
- package/python/tests/test_shared_write.py +0 -7
- package/python/tests/test_skill_taint_gate.py +1 -10
- package/python/tests/test_skills.py +2 -18
- package/python/tests/test_skills_fetch.py +282 -0
- package/python/tests/test_stats_and_events.py +2 -5
- package/python/tests/test_streaming.py +2 -7
- package/python/tests/test_supervisor_pipeline.py +4 -5
- package/python/tests/test_supervisor_triage.py +4 -10
- package/python/tests/test_taint.py +2 -7
- package/python/tests/test_task_stream.py +1 -12
- package/python/tests/test_thinking_sse.py +0 -1
- package/python/tests/test_tool_center.py +1 -17
- package/python/tests/test_tool_parallel.py +2 -2
- package/python/tests/test_tool_params.py +1 -5
- package/python/tests/test_tool_use.py +0 -13
- package/python/tests/test_toplevel_concurrency.py +3 -4
- package/python/tests/test_tracing.py +7 -41
- package/python/tests/test_truth_crystallizer.py +4 -6
- package/python/tests/test_truth_flow.py +2 -7
- package/python/tests/test_update_check.py +95 -0
- package/python/tests/test_verify_evidence.py +8 -21
- package/python/tests/test_version_sync.py +35 -6
- package/python/tests/test_web_trigger.py +0 -17
- package/python/tools/build_content_lexicon.py +218 -0
- package/python/tools/bundle_web.py +98 -0
- package/python/tools/check_faiss_type.py +0 -1
- package/python/tools/check_log.ps1 +0 -8
- package/python/tools/mypy_baseline.txt +12 -4
- package/python/tools/mypy_ratchet.py +52 -6
- package/python/tools/pack.py +0 -5
- package/python/tools/recheck_conflicts.py +156 -0
- package/python/tools/release_check.py +24 -15
- package/python/tools/restart_foxclaw.ps1 +5 -8
- package/python/tools/shared_write_audit.py +1 -15
- package/python/tools/size_check.py +1 -1
- package/python/tools/strip_comments.py +228 -0
- package/python/vendor/hkc/hkc_api/routes/ability.py +31 -0
- package/python/vendor/hkc/hkc_api/schemas.py +11 -0
- package/python/vendor/hkc/hkc_kee/kee.py +124 -3
- package/python/vendor/hkc/hkc_sdk/base.py +22 -0
- package/python/vendor/hkc/hkc_sdk/direct_client.py +10 -0
- package/python/vendor/hkc/hkc_sdk/http_client.py +19 -0
- package/python/foxclaw/api/app.py +0 -123
- package/python/frontend/v3/assets/index-BD7VPbMV.js +0 -33
- 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
|
|
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-
|
|
10
|
-
<link rel="stylesheet" crossorigin href="/v3/assets/index-
|
|
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,'&').replace(/</g,'<').replace(/>/g,'>').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
|
|
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
|
+
"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": "
|
|
9
|
+
"version": "0.1.2",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"vue": "^3.5.13"
|
|
12
12
|
},
|
|
@@ -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="
|
|
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)
|