release: bump version to 0.51.0

This commit is contained in:
rayd1o
2026-05-11 09:49:08 +08:00
parent 455b8360d0
commit 1cb51b1172
52 changed files with 4440 additions and 279 deletions

View File

@@ -8,6 +8,21 @@ This project follows the repository versioning rule:
- `improvement` -> `+0.0.1`bugfix + 小功能混合)
- `bugfix` -> `+0.0.1`
## [0.51.0] — 2026-05-11
Released: 2026-05-11
### ✨ Highlights
- 新增 AI Settings 控制台页面与 `backend/app/services/ai_tools/` 工具层,串通 Web Search Provider 与轻量 Agent orchestrator。
- 重写 Earth 算力中心候选「预览 / 保存」交互:单一委托 click + 内存 candidate Map新增空心呼吸圈预览保存后即时生成正式图标后台刷新失败不再误报为保存失败。
- 重写动作捕捉 zoom 识别mirror-safe 的 trend + pose hold 双通道,张开/合拢手势直接对应 zoom_in/out 并支持持续触发;单臂 rotate 仅在另一只手明确静止时才允许。
### Improvements
- 同步中英文 `earth-frontend-context.md``frontend-admin-frontend-context.md``faq.md``manual.md``quickstart.md`
- Earth 模块多处优化bgp-cruise-adapter、interactable、satellites、presentation-controller、controls 调整与回归测试补全。
---
## [0.50.0] — 2026-05-10
Released: 2026-05-10

View File

@@ -179,6 +179,217 @@ Secret resolution should follow the existing settings pattern:
2. provider-specific environment variable, for example `TAVILY_API_KEY`
3. generic fallback `WEB_SEARCH_API_KEY`
### Common WebSearch Providers
The first implementation should model WebSearch as a provider-specific adapter
behind one internal interface:
```text
SearchEvidenceProvider.search(query, max_results, domains, freshness_days)
-> list[SearchEvidence]
```
Recommended provider ids and environment variables:
| Provider | Provider id | Env key | Default base URL | Primary use |
| --- | --- | --- | --- | --- |
| Tavily | `tavily` | `TAVILY_API_KEY` | `https://api.tavily.com` | Default hosted search for agent/RAG style results |
| Brave Search API | `brave` | `BRAVE_SEARCH_API_KEY` | `https://api.search.brave.com` | Independent web index and low-level SERP results |
| SerpAPI | `serpapi` | `SERPAPI_API_KEY` | `https://serpapi.com` | Search-engine-backed SERP data with engine options |
| Exa | `exa` | `EXA_API_KEY` | `https://api.exa.ai` | Neural/semantic web search and result contents |
| Firecrawl Search / Scrape | `firecrawl` | `FIRECRAWL_API_KEY` | `https://api.firecrawl.dev` | Search plus page scrape/markdown extraction |
| SearXNG | `searxng` | optional `SEARXNG_API_KEY` | self-hosted instance URL | Self-hosted metasearch when external search APIs are undesirable |
The normalized configuration should support per-provider defaults while keeping
one active provider:
```text
external_integrations.web_search
enabled: true
default_provider: tavily
providers:
tavily:
base_url: https://api.tavily.com
api_key: <secret>
max_results: 5
search_depth: basic
include_answer: false
include_raw_content: false
brave:
base_url: https://api.search.brave.com
api_key: <secret>
endpoint_path: /res/v1/web/search
max_results: 5
serpapi:
base_url: https://serpapi.com
api_key: <secret>
endpoint_path: /search.json
engine: google
max_results: 5
exa:
base_url: https://api.exa.ai
api_key: <secret>
endpoint_path: /search
max_results: 5
include_text: false
firecrawl:
base_url: https://api.firecrawl.dev
api_key: <secret>
search_path: /v2/search
scrape_path: /v2/scrape
max_results: 5
scrape_formats: [markdown]
searxng:
base_url: http://localhost:8080
api_key: <optional secret>
endpoint_path: /
max_results: 5
categories: general
engines: []
```
Adapter notes:
- Tavily should call `/search` and normalize title, URL, snippet/content, score,
and optional raw content.
- Brave should call `/res/v1/web/search` and map web results into the same
`SearchEvidence` shape.
- SerpAPI should call `/search.json`, pass `engine`, and normalize organic
results. Search-engine-specific fields should remain in provider metadata.
- Exa should call `/search`; optional result text should be treated as fetched
content only when enabled.
- Firecrawl can be used both as `web_search` and `web_fetch`: `/v2/search`
returns result URLs/descriptions and may include scrape options, while
`/v2/scrape` can produce markdown for a selected URL.
- SearXNG should query the configured instance with `q` and `format=json`.
Public instances should not be assumed reliable for production; a controlled
self-hosted instance is preferred.
The settings UI should expose only provider, base URL, key, max results, and a
test button in the first version. Provider-specific advanced fields can stay
collapsed or backend-only until a real workflow needs them.
### Frontend Configuration Window
Add a WebSearch configuration panel to the existing settings page, next to the
LLM provider configuration. It should behave like the current AI provider secret
controls: clear configured state, masked preview, explicit show/hide, test
connection, and save feedback.
First-version visible fields:
```text
WebSearch Provider
API Base URL
API Key
Max Results
Timeout Seconds
Enable WebSearch
Test Connection
Save
```
Provider dropdown options:
```text
Tavily
Brave Search API
SerpAPI
Exa
Firecrawl Search / Scrape
SearXNG
```
Field behavior:
- Switching provider loads that provider's saved config and masked key preview.
- Empty key input means keep the existing saved or environment key.
- Typing a new key replaces only the selected provider's key.
- Show key reveals the full current input value when the backend reveal endpoint
allows it; hide key returns to the prefix-preserving masked preview.
- The configured badge should only show `已配置` or `未配置`, not repeat the
masked key text.
- `Test Connection` sends the current unsaved draft to the backend and should
not require a separate save first.
- A successful test may save the draft as the new WebSearch default only if the
API endpoint is explicitly designed to mirror the AI provider test behavior.
Otherwise, test should be read-only and the Save button should persist.
- Save success and test success must show visible feedback. Failures should show
provider-specific but secret-safe error messages.
Provider-specific UI hints:
| Provider | UI hint |
| --- | --- |
| Tavily | Good default for agent/RAG style search. |
| Brave Search API | Uses Brave's independent search index. |
| SerpAPI | Supports search-engine-specific parameters such as `engine`. |
| Exa | Good for semantic search and optional result text. |
| Firecrawl | Can search and scrape pages into markdown. |
| SearXNG | Requires a reachable self-hosted or trusted instance URL. |
Advanced fields can live in a collapsed section:
```text
Endpoint Path
Search Depth
Engine
Categories
Engines
Include Raw Content
Scrape Formats
Domain Allowlist
```
The first version should keep the UI conservative. It should not expose every
provider knob until backend workflows use those knobs.
### Web Fetch and Page Extraction
`web_fetch` is separate from `web_search`. Search finds candidate URLs; fetch
turns selected pages into clean, citable evidence.
Recommended extraction chain:
```text
1. plain httpx fetch
2. trafilatura extraction for static HTML
3. readability extraction as secondary cleanup
4. Playwright fetch only for allowlisted JS-heavy pages
5. Firecrawl scrape as hosted fallback when configured
```
Implementation guidance:
- Use `trafilatura` as the first local extractor because it is Python-native and
matches the backend stack.
- Prefer a Python readability implementation for local cleanup. Do not introduce
a Node-only readability dependency for backend fetch.
- Use Playwright sparingly for JavaScript-rendered pages. It should have domain
allowlists, low concurrency, strict timeouts, response size limits, and no
automatic form submission or login behavior.
- Store `content_hash`, `retrieved_at`, final URL, title, extracted text
preview, and extractor name in `ai_evidence`.
- Keep short quotes for UI review, but do not store huge page bodies directly in
every task record. Large extracted content should be truncated or stored once
by hash.
The local/self-hosted stack should look like this:
```text
SearXNG
-> SearchEvidence URLs
-> httpx fetch
-> trafilatura / readability
-> Playwright only when static extraction fails and the domain is allowed
-> normalized evidence
-> LLM structured output through AIProviderClient
```
This route gives Planet a lower-cost and more controllable search path, while
hosted providers remain available when search quality or maintenance effort
matters more than self-hosting.
## Phase 3: Limited Agent Loop

View File

@@ -98,6 +98,18 @@ Gesture recognition may run locally in the browser or inside the local Agent, bu
[motion-debug-panel.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/motion-debug-panel.js) owns the debug panel. It listens for `earth:motion-debug-frame` and draws normalized skeleton joints and bones on a canvas. The Browser Camera provider also emits `earth:motion-debug-video-source` with the local `<video>` element so the panel can show a local preview behind the skeleton; `shared.motionDebugSkeletonOnly` switches the panel back to skeleton-only rendering. `Stop Matching Gestures` dispatches `earth:motion-recognition-pause`, which suppresses gesture execution while video and skeleton drawing continue. Unmatched skeletons are red; matched gestures turn green and display the gesture name. Settings are persisted under `shared.motionDebugEnabled`, `shared.motionProvider`, and `shared.motionDebugSkeletonOnly` in `planet.earth.settings.v2`, and both the switch and provider selector reserve `data-gatekeeper-permission="earth.motion_debug"`.
The Browser Camera provider's gesture pipeline lives in `recognizeGesture()` inside [motion-browser-provider.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/motion-browser-provider.js). Detectors are evaluated in this order, first match wins:
1. **`getZoomTrend` (trend zoom)** — derived from the per-frame change in `Math.abs(rightWrist.x - leftWrist.x)`. Both wrists must cross the noise floor (`ZOOM_TREND_MIN_WRIST_DELTA = 0.010`) and stay within `ZOOM_TREND_HEIGHT_TOLERANCE` of each other vertically. Growing span → `zoom_in`, shrinking span → `zoom_out`. Trend has highest priority so mid-motion frames cannot be hijacked by the layer/focus/rotate detectors.
2. **`getZoomHoldPose` (sustained zoom)** — after motion stops, keeps emitting `zoom_in` while the wrists stay at chest level or above with span > `ZOOM_HOLD_SPREAD_FACTOR × shoulderWidth` (default 1.30), and `zoom_out` while elbows sit visibly outward and span < `ZOOM_HOLD_CLOSE_FACTOR × shoulderWidth` (default 0.85).
3. **layer / focus** — left-wrist raise + vertical motion fires `layer_prev/next`; head tilt fires `focus_prev/next`.
4. **`getRightArmPattern` (single-arm rotate)** — only considered when both `!isZoomCandidatePose(...)` and `isLeftArmAtRest(...)` hold. `isLeftArmAtRest` requires the left wrist to hang clearly below the shoulder line (≥ 0.13) and both left elbow and left wrist to stay near the body — any ambiguous left-arm posture (mid-spread, raised, held at chest) blocks single-arm rotate.
Two non-obvious decisions worth preserving:
- **Mirror-safe**: all zoom checks use `Math.abs(rightWrist.x - leftWrist.x)` and never rely on per-side x direction. `getUserMedia` returns the raw camera feed without horizontal flip, so a subject's anatomical left arm appears on the image right. A direction-based detector (e.g. "left wrist moves left, right wrist moves right") inverts on non-mirrored feeds — span-based detection is invariant.
- **Continuous vs. discrete**: `rotate`, `layer`, `focus`, and `confirm` go through `applyPoseLatch`, which emits each gesture once until the pose returns to neutral (one wave = one rotation step). Zoom intentionally bypasses the latch and re-matches every frame; downstream `GESTURE_POLICIES.zoom_in/out.cooldownMs = 120` rate-limits to ~8 emits/sec, so holding a spread pose keeps zooming in until the user changes their pose. Do not reuse the latch for zoom — that semantic difference is the point.
[presentation-controller.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/presentation-controller.js) is the new Presentation layer. In the first stage only Motion uses it: `motion-cruise-adapter.js` uses a persistent presentation that reuses the cruise fixed-card placement and connector, but mouse movement does not auto-hide the card. The connector recalculates source and target anchors every frame so dragged cards, globe rotation, and moving targets stay connected. BGP/News still use the existing `CruiseSequencer` auto-advance path to preserve the old cruise experience.
### 6. Globe and Terrain
@@ -134,6 +146,10 @@ The compute-center layer row has a notification badge for GeoJSON `unresolved` r
Location candidate state in the details card is cached in [info-card.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/info-card.js) by `entityType:entityId`. If the user closes the details card or unresolved queue and reopens the same compute center / BGP collector, previously collected candidates and status text are restored. Header-level `一键采用` prefers cached candidates, avoiding repeated online geocoding or LLM factcheck calls. After a location is saved, that entity's candidate list is cleared to a "refreshing layer" status so stale candidates do not keep misleading the user.
The `预览 / 保存` buttons on each candidate row use a single delegated `click` handler per candidate root (the `[data-collect-cache-key]` block in the details card, or `[data-unresolved-item]` in the unresolved queue), guarded by a `data-candidate-actions-bound` flag so it cannot be double-bound. Direct `pointerup` / `click` listeners on individual buttons and overlapping delegated handlers were removed. Candidate objects are no longer JSON-stringified into an HTML attribute and parsed back; buttons only carry `data-candidate-index`, and the handler resolves the candidate object from a module-level `Map` keyed by cache-key. This removes the entire class of failures caused by HTML entity escaping of `&` / `<` / `"` in candidate fields. Clicking `预览` dispatches `earth:preview-location-candidate`; `main.js`'s `previewLocationCandidate()` calls `showComputeCenterLocationPreview()`, which attaches a hollow breathing-ring sprite pair at the candidate coordinates (visually mirroring the BGP event ring) and focuses the camera on the candidate. Previewing another candidate replaces the ring; saving clears it and `spawnSavedComputeCenterLocation()` immediately spawns the formal compute-center interactable. Note that `main.js` has no module-level `earth` variable — every location-save / preview handler must call `const earth = getEarth();` first, otherwise the event handler throws a `ReferenceError` that the surrounding `.catch` swallows, producing the failure mode where the button "does nothing".
The `earth:compute-center-location-saved` reconciliation pipeline is deliberately silent on background-refresh failures. `spawnComputeCenterAfterLocationSave()` already presents the success toast and locked state; `refreshComputeCentersAfterLocationSave()` only reloads backend data when the scene is ready and no longer emits its own `已保存` toast. `handleComputeCenterLocationSaved()` runs refresh in the background after a successful spawn; only when spawn returns `null` (scene not ready) or throws does refresh take over the success toast. A refresh error is only `console.warn`'d — it must never surface as a `保存失败` message, because the save itself succeeded and the refresh is a follow-up sync.
### AIS Vessel Layer
The vessel layer fetches `/api/v1/visualization/geo/vessels` and renders the aggregated AIS GeoJSON through `createInteractableLayer()`. By default it does not send a `limit` parameter, and `VESSEL_CONFIG.maxRenderedMarkers = 0` means the frontend does not clip the result to 5000 vessels. A positive `options.limit` or positive `maxRenderedMarkers` can still be used as an explicit temporary cap.

View File

@@ -102,7 +102,15 @@ If both localhost checks pass but a phone or another computer cannot connect, st
./planet.sh start --allow-lan
```
Then configure portproxy and firewall from Administrator PowerShell:
The flag must be written as `--allow-lan`. `allowlan` or `--allowlan` is not recognized by the startup script. If Planet is already running and you only need to reopen the frontend on the LAN, restart the frontend explicitly:
```bash
./planet.sh restart -f 3000 --allow-lan
```
If `ss -ltnp` shows the frontend listening on `0.0.0.0:3000`, but `Test-NetConnection <Windows LAN IP> -Port 3000` still fails from Windows PowerShell, the problem is usually Windows-side forwarding or firewall policy rather than Vite or `.zshrc`.
For traditional WSL NAT networking, configure portproxy and firewall from Administrator PowerShell:
```powershell
netsh interface portproxy add v4tov4 listenaddress=0.0.0.0 listenport=3000 connectaddress=127.0.0.1 connectport=3000
@@ -111,6 +119,20 @@ New-NetFirewallRule -DisplayName "WSL Planet 3000" -Direction Inbound -Action Al
New-NetFirewallRule -DisplayName "WSL Planet 8000" -Direction Inbound -Action Allow -Protocol TCP -LocalPort 8000
```
If `wslinfo --networking-mode` prints `mirrored`, also check Hyper-V firewall. Even when ordinary Windows Firewall rules exist, Hyper-V firewall can still block external devices from reaching WSL. From Administrator PowerShell, allow the required ports:
```powershell
New-NetFirewallHyperVRule -Name "Planet-Frontend-3000" -DisplayName "Planet Frontend 3000" -Direction Inbound -VMCreatorId "{40E0AC32-46A5-438A-A0B2-2B479E8F2E90}" -Protocol TCP -LocalPorts 3000 -Action Allow
New-NetFirewallHyperVRule -Name "Planet-Backend-8000" -DisplayName "Planet Backend 8000" -Direction Inbound -VMCreatorId "{40E0AC32-46A5-438A-A0B2-2B479E8F2E90}" -Protocol TCP -LocalPorts 8000 -Action Allow
```
Use these commands to inspect the current Hyper-V firewall state:
```powershell
Get-NetFirewallHyperVVMSetting -Name "{40E0AC32-46A5-438A-A0B2-2B479E8F2E90}"
Get-NetFirewallHyperVRule -VMCreatorId "{40E0AC32-46A5-438A-A0B2-2B479E8F2E90}"
```
LAN devices should open the Windows LAN IP, for example `http://<Windows LAN IP>:3000/earth`, not the internal WSL IP.
### How do `--allow-lan` and the Motion Agent LAN URL fit together?

View File

@@ -32,7 +32,7 @@ Current admin-related routes:
- `/alerts/bgp`
- `/alerts/situational`
- `/bgp`
- `/playground`
- `/ai`
- `/settings`
`/earth` is a standalone display page and is not part of the console shell.
@@ -196,7 +196,24 @@ Responsibilities:
`App.tsx` uses it to decide whether to redirect to the login page. `/docs` remains a public route, but the backend decides the visible catalog and content from the token; anonymous visitors only receive public docs.
### 2. Business Data Gateway
### 2. AI
File:
- [AISettings.tsx](/home/ray/dev/linkong/planet/frontend/src/pages/AISettings/AISettings.tsx)
Responsibilities:
- `/ai` now owns LLM Provider, AI Tool configuration, and the testbench instead of nesting them under `/settings`
- The `模型供应商` tab manages default provider, model, base URL, provider key, local `aiprovider` proxy, and connection test
- The `工具` tab manages WebSearch provider, search key, base URL, timeout, result count, and advanced provider options
- The `测试台` tab embeds the former Playground real session, preset prompts, and AI Provider status debugging
- The page reuses the Settings single-screen tabs, panel card, and internal scrolling style
Legacy `/settings?tab=ai` should redirect to `/ai?tab=providers`.
Legacy `/playground` should redirect to `/ai?tab=playground`.
### 3. Business Data Gateway
AI / situational awareness related services are currently in:

View File

@@ -19,7 +19,7 @@ After a default startup, the common URLs are:
| Docs | `http://localhost:3000/docs` | Partly | Usage docs are public; developer, backend, and operations docs require Gatekeeper groups |
| FAQ | `http://localhost:3000/docs/faq` | No | Windows / WSL, ports, dependencies, motion capture, credentials, and permission troubleshooting |
| Console | `http://localhost:3000/admin` | Yes | Data, config, alerts, logs, and situational observation |
| AI Playground | `http://localhost:3000/playground` | Yes | AI Provider status and debugging |
| AI | `http://localhost:3000/ai` | Yes | Model providers, AI tools, and testbench |
| Backend API Docs | `http://localhost:8000/docs` | Depends on endpoint | FastAPI / OpenAPI documentation |
## planet.sh
@@ -427,7 +427,7 @@ Common pages:
| System Alerts | `/alerts/system` | System-level alerts |
| BGP Alerts | `/alerts/bgp` | BGP-related alerts |
| Situational Alerts | `/alerts/situational` | Situational assessment alerts |
| AI Playground | `/playground` | AI Provider debugging |
| AI | `/ai` | Model providers, WebSearch-style tools, and testbench |
| System Logs | `/logs` | View system logs (typically super admin only) |
| Users | `/users` | User management |
| Settings | `/settings` | System config and TV live stream sources |
@@ -487,7 +487,18 @@ Current common uses:
- System settings
- TV live stream source configuration
- Collector settings
- External integrations and AI Provider configuration
### AI
`/ai` manages the AI runtime chain and is now separate from system settings. Legacy `/playground` redirects to `/ai?tab=playground`.
It currently contains:
- `模型供应商`: default LLM provider, model, base URL, API key, local `aiprovider` proxy, and connection test
- `工具`: WebSearch provider, search API key, base URL, max results, timeout, and advanced provider options
- `测试台`: AI Provider status, preset prompts, and real analysis-chain debugging
Legacy `/settings?tab=ai` redirects to `/ai?tab=providers`.
Available configuration depends on the current user's role.

View File

@@ -57,7 +57,7 @@ After startup, the key URLs are:
| Earth | `http://localhost:3000/earth` | Public 3D Earth visualization |
| Console | `http://localhost:3000/admin` | Admin console (login required) |
| Docs | `http://localhost:3000/docs` | Usage docs are public; developer and operations docs require Gatekeeper groups |
| AI Playground | `http://localhost:3000/playground` | AI debugging (login required) |
| AI | `http://localhost:3000/ai` | Model provider, tool, and testbench entry (login required) |
| Backend API Docs | `http://localhost:8000/docs` | FastAPI / OpenAPI interface docs |
If the default ports are taken, specify custom ports:
@@ -114,6 +114,7 @@ First-time inspection checklist:
- `/datasources`: data source directory and collection triggers; endpoint, headers, and credentials are configured under `/settings` collector settings
- `/data`: collected data
- `/bgp`: BGP situational view
- `/ai`: AI page for model providers, WebSearch-style tools, and the testbench
- `/alerts/system`: system alerts
- `/settings`: system configuration

View File

@@ -105,6 +105,18 @@ React 路由入口:
动捕调试面板由 [motion-debug-panel.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/motion-debug-panel.js) 负责。它监听 `earth:motion-debug-frame`,用 canvas 绘制归一化骨架点和连线Browser Camera provider 会额外通过 `earth:motion-debug-video-source` 提供本机 `<video>` 作为调试预览底图,`shared.motionDebugSkeletonOnly` 可切换为只显示骨骼。`停止匹配动作` 通过 `earth:motion-recognition-pause` 暂停 gesture 执行,但继续显示视频和骨架。未匹配动作为红色,匹配后变绿并显示动作名。设置项持久化在 `planet.earth.settings.v2``shared.motionDebugEnabled``shared.motionProvider``shared.motionDebugSkeletonOnly`switch 和输入源控件都预留 `data-gatekeeper-permission="earth.motion_debug"`
Browser Camera provider 的手势识别管线在 [motion-browser-provider.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/motion-browser-provider.js) 的 `recognizeGesture()`,按以下顺序匹配,前者命中即返回:
1. **`getZoomTrend`(趋势 zoom**:基于上一帧到当前帧两腕 x 间距的变化量(`Math.abs(rightWrist.x - leftWrist.x)` 的 delta。两腕都越过噪声地板`ZOOM_TREND_MIN_WRIST_DELTA = 0.010`)且高度差不超过 `ZOOM_TREND_HEIGHT_TOLERANCE`spread 增加 → `zoom_in`spread 减少 → `zoom_out`。趋势优先级最高,避免中间帧被 layer/focus/rotate 抢先误判。
2. **`getZoomHoldPose`(姿态 zoom**:动作停止后,只要两腕仍保持在胸口及以上、且 span > `ZOOM_HOLD_SPREAD_FACTOR × shoulderWidth`(默认 1.30)就持续派发 `zoom_in`;两肘明显外展且 span < `ZOOM_HOLD_CLOSE_FACTOR × shoulderWidth`(默认 0.85)就持续派发 `zoom_out`
3. **layer / focus**:左手抬起 + 上下移动派发 `layer_prev/next`;头部左右倾斜派发 `focus_prev/next`
4. **`getRightArmPattern`(单臂 rotate**:仅当 `!isZoomCandidatePose(...) && isLeftArmAtRest(...)` 同时成立时才考虑。`isLeftArmAtRest` 要求左腕明显垂在肩下 13% 以下且左肘/左腕都不外伸,把「张臂中间帧」「单手举起」「左手扶在胸前」等所有模糊状态都判为非静止 —— 单臂 rotate 严格要求另一只手处于静止。
两个关键设计:
- **mirror-safe**:所有 zoom 检测都基于 `Math.abs(rightWrist.x - leftWrist.x)`,不依赖单侧 x 方向。无论摄像头是否做镜像翻转(浏览器默认不翻转,`getUserMedia` 返回的就是原始帧),张臂始终 → zoom_in合手始终 → zoom_out。早期基于「左腕往左 + 右腕往右」的判定会在非镜像视图里把方向判反。
- **连续 vs 离散**rotate / layer / focus / confirm 都过 `applyPoseLatch`同一手势只发一次必须先回到中性位才能再发挥一下转一格。zoom 故意 bypass latch每帧匹配都返回 → 下游 `GESTURE_POLICIES.zoom_in/out.cooldownMs = 120` 节流到 ~8 次/秒,张臂保持就一直放大直到姿势变化。这是 zoom 跟其它手势在交互语义上的本质区别,不要复用 latch 给 zoom。
[presentation-controller.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/presentation-controller.js) 是新的 Presentation 层。第一阶段只接入 Motion`motion-cruise-adapter.js` 通过 persistent presentation 复用巡航固定卡片位置和 connector但不会让鼠标移动触发自动隐藏connector 每帧重算 source/target anchor让卡片拖动、地球旋转和目标移动时端点继续跟随。BGP/News 仍保持原有 `CruiseSequencer` 自动轮播路径,避免改变既有巡航体验。
### 6. 地球与地形
@@ -330,6 +342,10 @@ AISStream 的 `PositionReport` 常带实时位置和 `MetaData.ShipName`,但
详情卡里的坐标候选状态由 [info-card.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/info-card.js) 按 `entityType:entityId` 缓存在模块内存中。用户关闭详情卡或待定位列表后再次打开同一个算力中心 / BGP 观测站,已经采集到的候选和状态文案会恢复;`一键采用` 会优先使用缓存候选,避免重复调用在线地理编码或 LLM factcheck。保存成功后该实体的候选列表会清空为“正在刷新图层”状态避免旧候选在刷新后继续误导用户。
候选行的 `预览 / 保存` 按钮采用单一的事件委托模型:每个候选根(详情卡里的 `[data-collect-cache-key]` 块,或待定位列表里的 `[data-unresolved-item]`)只挂一个 `click` 监听,由 `data-candidate-actions-bound` 幂等标记,不再混用 `pointerup` / `click` 直绑或重复委托。候选对象不再以 JSON 字符串塞进 HTML 属性后再 `JSON.parse`,按钮只携带 `data-candidate-index`handler 通过 cache-key 在模块内存的 `Map` 里取出原对象,避开 HTML 实体转义对 `&` / `<` / `"` 的破坏。点击 `预览` 会派发 `earth:preview-location-candidate`,由 `main.js``previewLocationCandidate()` 调用 `showComputeCenterLocationPreview()`:在候选经纬度上挂双层空心呼吸 sprite视觉参考 BGP 事件 ring并把视角聚焦到候选坐标切换到另一个候选会替换为新呼吸圈保存时立即清除并由 `spawnSavedComputeCenterLocation()` 即时生成正式算力中心交互图标。注意 `main.js` 没有模块级 `earth` 变量,所有 location-save / preview 处理函数必须先 `const earth = getEarth();`,否则会在事件 handler 里抛 `ReferenceError``.catch` 静默掉,外观上等同于按钮“没有反应”。
`earth:compute-center-location-saved` 之后的图层校准链路对后台刷新失败保持沉默:`spawnComputeCenterAfterLocationSave()` 已经把 toast 和 locked 状态都给了用户,`refreshComputeCentersAfterLocationSave()` 只在场景就绪时重新拉取后端数据,本身不再吐 `已保存` toast`handleComputeCenterLocationSaved()` 在 spawn 成功路径让 refresh 静默后台运行,只在 spawn 返回 `null`(场景未就绪)或抛错时才让 refresh 接管成功 toastrefresh 自身报错只走 `console.warn`,绝不冒泡成 `保存失败` 文案——保存请求本身已经成功,刷新失败属于后续同步问题。
asset 图标大小由 `Interactable``icon.fitSize` 控制。SVG / 图片文件应尽量保持原始 viewBox 和路径,不要为了在地球上显示成 60x60 而手写 `transform``drawAssetIcon()` 会把资源等比 contain 到指定尺寸并居中绘制到 atlas canvas。
`Interactable` 默认使用固定屏幕像素尺寸适合船只、BGP 事件、BGP 观测站、算力中心这类需要稳定识别的图标。如果某类图标需要跟随相机距离缩放,可以把 `sizeMode` 设为非 `"fixed"`,并用 `sizeScale.min / max / referenceFov` 控制缩放范围;单个 marker 的业务尺寸差异可以通过 `getPointSizeMultiplier()` 表达,例如 BGP 事件按严重级别调整点大小BGP 观测站按活跃度调整点大小。

View File

@@ -104,7 +104,15 @@ curl http://localhost:8000/health
./planet.sh start --allow-lan
```
管理员 PowerShell 中配置 portproxy 和防火墙
参数必须写成 `--allow-lan``allowlan``--allowlan` 不会被启动脚本识别。如果服务已经启动,只想重新开放前端,需要显式重启前端
```bash
./planet.sh restart -f 3000 --allow-lan
```
如果 `ss -ltnp` 显示前端已经监听 `0.0.0.0:3000`,但 Windows PowerShell 中 `Test-NetConnection <Windows局域网IP> -Port 3000` 仍失败,问题通常不在 Vite 或 `.zshrc`,而是在 Windows 侧转发或防火墙。
传统 WSL NAT 场景下,管理员 PowerShell 中配置 portproxy 和防火墙:
```powershell
netsh interface portproxy add v4tov4 listenaddress=0.0.0.0 listenport=3000 connectaddress=127.0.0.1 connectport=3000
@@ -113,6 +121,20 @@ New-NetFirewallRule -DisplayName "WSL Planet 3000" -Direction Inbound -Action Al
New-NetFirewallRule -DisplayName "WSL Planet 8000" -Direction Inbound -Action Allow -Protocol TCP -LocalPort 8000
```
如果 `wslinfo --networking-mode` 输出 `mirrored`,还需要检查 Hyper-V firewall。普通 Windows 防火墙规则存在时Hyper-V firewall 仍可能拦截外部设备进入 WSL。管理员 PowerShell 中按端口放行:
```powershell
New-NetFirewallHyperVRule -Name "Planet-Frontend-3000" -DisplayName "Planet Frontend 3000" -Direction Inbound -VMCreatorId "{40E0AC32-46A5-438A-A0B2-2B479E8F2E90}" -Protocol TCP -LocalPorts 3000 -Action Allow
New-NetFirewallHyperVRule -Name "Planet-Backend-8000" -DisplayName "Planet Backend 8000" -Direction Inbound -VMCreatorId "{40E0AC32-46A5-438A-A0B2-2B479E8F2E90}" -Protocol TCP -LocalPorts 8000 -Action Allow
```
也可以用下面命令确认当前 Hyper-V firewall 状态:
```powershell
Get-NetFirewallHyperVVMSetting -Name "{40E0AC32-46A5-438A-A0B2-2B479E8F2E90}"
Get-NetFirewallHyperVRule -VMCreatorId "{40E0AC32-46A5-438A-A0B2-2B479E8F2E90}"
```
局域网设备访问的是 Windows 的局域网 IP例如 `http://<Windows局域网IP>:3000/earth`,不是 WSL 内部 IP。
### `--allow-lan` 和 Motion Agent 局域网地址怎么配?

View File

@@ -32,7 +32,7 @@
- `/alerts/bgp`
- `/alerts/situational`
- `/bgp`
- `/playground`
- `/ai`
- `/settings`
`/earth` 是独立展示页,不属于控制台骨架。
@@ -196,7 +196,24 @@
`App.tsx` 用它判断是否进入登录页。`/docs` 仍是公开路由,但目录和正文由后端按 token 决定;未登录时只返回公开文档。
### 2. 业务数据网关
### 2. AI
文件:
- [AISettings.tsx](/home/ray/dev/linkong/planet/frontend/src/pages/AISettings/AISettings.tsx)
职责:
- `/ai` 独立承载 LLM Provider、AI Tool 配置和测试台,不再放在 `/settings` 的系统配置 tabs 中
- `模型供应商` tab 管理默认 provider、模型、base URL、provider key、本地 `aiprovider` 代理和连接测试
- `工具` tab 管理 WebSearch provider、搜索 key、base URL、超时、结果数和高级 provider 参数
- `测试台` tab 嵌入原 Playground 的真实会话、预设请求和 AI Provider 状态调试
- 页面复用 Settings 的单屏 tabs、panel card 和内部滚动样式
旧的 `/settings?tab=ai` 应跳转到 `/ai?tab=providers`
旧的 `/playground` 应跳转到 `/ai?tab=playground`
### 3. 业务数据网关
目前 AI / 态势感知相关服务集中在:

View File

@@ -19,7 +19,7 @@
| Docs | `http://localhost:3000/docs` | 部分需要 | 使用手册公开;开发、后端、运维文档按 Gatekeeper 权限组开放 |
| FAQ | `http://localhost:3000/docs/faq` | 否 | Windows / WSL、端口、依赖、动捕、凭证和权限排障 |
| 控制台 | `http://localhost:3000/admin` | 是 | 数据、配置、告警、日志和专题观测 |
| AI Playground | `http://localhost:3000/playground` | 是 | AI Provider 状态和调试 |
| AI | `http://localhost:3000/ai` | 是 | 模型供应商、AI 工具和测试台 |
| 后端 API 文档 | `http://localhost:8000/docs` | 视接口而定 | FastAPI / OpenAPI 文档 |
## planet.sh
@@ -458,7 +458,7 @@ http://localhost:3000/admin
| 系统告警 | `/alerts/system` | 系统级告警 |
| BGP 告警 | `/alerts/bgp` | BGP 相关告警 |
| 态势告警 | `/alerts/situational` | 态势研判告警 |
| AI Playground | `/playground` | AI Provider 调试 |
| AI | `/ai` | 模型供应商、WebSearch 等工具和测试台 |
| 系统日志 | `/logs` | 查看系统日志,通常仅 super admin 可见 |
| 用户管理 | `/users` | 管理用户 |
| 系统配置 | `/settings` | 系统配置和电视直播源等设置 |
@@ -518,7 +518,18 @@ http://localhost:3000/admin
- 系统设置
- 电视直播源配置
- 采集器设置
- 外部集成和 AI Provider 配置
### AI
`/ai` 用于管理 AI 运行链路,已经从系统配置中独立出来。旧链接 `/playground` 会跳转到 `/ai?tab=playground`
当前包含:
- `模型供应商`:默认 LLM provider、模型、Base URL、API Key、本地 `aiprovider` 代理和连接测试
- `工具`WebSearch provider、搜索 API Key、Base URL、最大结果数、超时和高级 provider 参数
- `测试台`AI Provider 状态、预设请求和真实分析链路调试
旧链接 `/settings?tab=ai` 会跳转到 `/ai?tab=providers`
具体可用配置取决于当前登录用户权限。

View File

@@ -57,7 +57,7 @@ export BARENTSWATCH_CLIENT_SECRET="..."
| Earth | `http://localhost:3000/earth` | 公开 3D Earth 可视化页面 |
| 控制台 | `http://localhost:3000/admin` | 登录后的管理后台 |
| 文档站 | `http://localhost:3000/docs` | 使用手册公开;开发/运维文档按 Gatekeeper 权限组开放 |
| AI Playground | `http://localhost:3000/playground` | 登录后的 AI 调试入口 |
| AI | `http://localhost:3000/ai` | 登录后的模型供应商、工具和测试台入口 |
| 后端 API 文档 | `http://localhost:8000/docs` | FastAPI / OpenAPI 接口文档 |
如果默认端口被占用,可以指定端口:
@@ -114,6 +114,7 @@ http://localhost:3000/admin
- `/datasources`:数据源目录和采集触发;接口、请求头和凭证配置在 `/settings` 的“采集器设置”
- `/data`:已采集数据
- `/bgp`BGP 专题观测
- `/ai`AI管理模型供应商、WebSearch 等工具和测试台
- `/alerts/system`:系统告警
- `/settings`:系统配置

View File

@@ -16,12 +16,13 @@
## Current Version
- `main` 当前主线历史推导到:`0.16.5`
- `dev` 当前开发分支历史推导到:`0.50.0`
- `dev` 当前开发分支历史推导到:`0.51.0`
## Timeline
| Version | Type | Branch | Commit | Summary |
| --- | --- | --- | --- | --- |
| `0.51.0` | feature | `dev` | `pending` | 新增 AI Settings 控制台与 ai_tools 工具层;重写算力中心候选预览/保存交互(呼吸圈 + 即时图标);动作捕捉 zoom 改为 mirror-safe trend + pose hold 双通道,支持持续触发 |
| `0.50.0` | feature | `dev` | `pending` | 新增 Earth 动捕双通道控制、Motion Agent、Presentation 持久展示、AI Provider 多 provider 设置、位置候选 LLM 兜底与 FAQ |
| `0.49.0` | feature | `dev` | `pending` | 新增位置解析 Pipeline、BGP/算力中心地理定位、Docs Gatekeeper、Earth 新闻栏与 Mobile 国家高亮 |
| `0.48.0` | feature | `dev` | `pending` | 新增自定义源 REST/WebSocket 实时 mock 链路,完善 AIS 多源聚合/船舶 enrichment并将 Earth 全球态势统计改为轻量 SQL 聚合 |