Files
storyforge/docs/superpowers/plans/2026-03-27-fnos-lan-delivery-stabilization.md
2026-03-27 13:49:30 +08:00

8.0 KiB
Raw Blame History

fnOS LAN Delivery Stabilization Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: 把 StoryForge 的 fnOS / NAS 局域网交付链做成仓库内一键可复现、可 smoke、可恢复的稳定版本。

Architecture: 继续采用“Windows 运行 cutvideo + fnOS 通过 SSH 隧道暴露 19186/19181 + fnOS collector 默认走 19186 + fnOS Web 默认走 fnOS collector”的交付路径。在仓库内新增统一部署入口、统一 LAN smoke、补齐 healthz 路由可见性与前端提示,并把运维说明统一到同一条主链。

Tech Stack: Bash, Python 3, FastAPI, vanilla JS, Docker Compose, fnOS SSH helpers


Task 1: 落地统一部署入口

Files:

  • Create: scripts/deploy_fnos_storyforge_lan_stack.sh

  • Modify: scripts/deploy_fnos_storyforge_web.sh

  • Modify: scripts/deploy_fnos_storyforge_collector.sh

  • Test: tests/test_production_baseline.py

  • Step 1: 写失败测试,约束统一部署入口存在并串联 tunnel / collector / web

def test_repo_contains_fnos_lan_stack_deploy_entrypoint(self) -> None:
    script_path = ROOT / "scripts" / "deploy_fnos_storyforge_lan_stack.sh"
    self.assertTrue(script_path.exists())
    content = script_path.read_text(encoding="utf-8")
    self.assertIn("deploy_fnos_cutvideo_tunnel.sh", content)
    self.assertIn("deploy_fnos_storyforge_collector.sh", content)
    self.assertIn("deploy_fnos_storyforge_web.sh", content)
  • Step 2: 跑测试确认当前失败

Run: python3 -m unittest tests.test_production_baseline.ProductionBaselineTests.test_repo_contains_fnos_lan_stack_deploy_entrypoint -v Expected: FAIL with missing deploy_fnos_storyforge_lan_stack.sh

  • Step 3: 写最小实现
#!/usr/bin/env bash
set -euo pipefail

ROOT="$(CDPATH= cd -- "$(dirname "$0")/.." && pwd)"

bash "$ROOT/scripts/deploy_fnos_cutvideo_tunnel.sh"
bash "$ROOT/scripts/deploy_fnos_storyforge_collector.sh"
bash "$ROOT/scripts/deploy_fnos_storyforge_web.sh"
  • Step 4: 跑测试确认通过

Run: python3 -m unittest tests.test_production_baseline.ProductionBaselineTests.test_repo_contains_fnos_lan_stack_deploy_entrypoint -v Expected: PASS

  • Step 5: 完成后补脚本语法校验

Run: bash -n scripts/deploy_fnos_storyforge_lan_stack.sh Expected: exit 0

Task 2: 落地统一 LAN smoke

Files:

  • Create: scripts/smoke_fnos_storyforge_lan.sh

  • Modify: tests/test_production_baseline.py

  • Modify: README.md

  • Modify: docs/LAN_E2E_GUIDE_2026-03-18.md

  • Step 1: 写失败测试,约束 LAN smoke 覆盖 web / healthz / auto-session / integrations / tunnel

def test_repo_contains_fnos_lan_smoke_script(self) -> None:
    script_path = ROOT / "scripts" / "smoke_fnos_storyforge_lan.sh"
    self.assertTrue(script_path.exists())
    content = script_path.read_text(encoding="utf-8")
    for expected in [
        "/healthz",
        "/v2/auth/auto-session",
        "/v2/integrations/health",
        "/api/bootstrap",
        "19181",
    ]:
        self.assertIn(expected, content)
  • Step 2: 跑测试确认当前失败

Run: python3 -m unittest tests.test_production_baseline.ProductionBaselineTests.test_repo_contains_fnos_lan_smoke_script -v Expected: FAIL with missing smoke_fnos_storyforge_lan.sh

  • Step 3: 写最小实现
#!/usr/bin/env bash
set -euo pipefail

FNOS_HOST="${FNOS_HOST:-192.168.31.188}"
WEB_PORT="${STORYFORGE_WEB_V4_DEV_PORT:-19192}"
COLLECTOR_PORT="${STORYFORGE_COLLECTOR_DEV_PORT:-19193}"
CUTVIDEO_PORT="${CUTVIDEO_FORWARD_PORT:-19186}"
COMPAT_PORT="${STORYFORGE_COMPAT_FORWARD_PORT:-19181}"

继续补:

  • 访问 http://$FNOS_HOST:$WEB_PORT/

  • 校验 storyforge-runtime-config.js

  • 访问 http://$FNOS_HOST:$COLLECTOR_PORT/healthz

  • POST /v2/auth/auto-session 获取 token

  • 带 token 调 GET /v2/integrations/health

  • 校验 cutvideo.reachable == true

  • 访问 http://$FNOS_HOST:$CUTVIDEO_PORT/api/bootstrap

  • 访问 http://$FNOS_HOST:$COMPAT_PORT/

  • Step 4: 跑测试确认通过

Run: python3 -m unittest tests.test_production_baseline.ProductionBaselineTests.test_repo_contains_fnos_lan_smoke_script -v Expected: PASS

  • Step 5: 完成后做脚本语法校验

Run: bash -n scripts/smoke_fnos_storyforge_lan.sh Expected: exit 0

Task 3: 收口 healthz 与前端依赖文案

Files:

  • Modify: collector-service/app/core_main.py

  • Modify: web/storyforge-web-v4/assets/app.js

  • Modify: tests/test_production_baseline.py

  • Step 1: 写失败测试,约束 healthz 暴露局域网路由信息

def test_healthz_exposes_lan_routing_summary(self) -> None:
    response = self.client.get("/healthz")
    self.assertEqual(response.status_code, 200, response.text)
    payload = response.json()
    self.assertIn("lanRouting", payload)
    self.assertIn("cutvideoRouteMode", payload["lanRouting"])
  • Step 2: 跑测试确认当前失败

Run: python3 -m unittest tests.test_production_baseline.ProductionBaselineTests.test_healthz_exposes_lan_routing_summary -v Expected: FAIL because lanRouting is absent

  • Step 3: 写最小实现
"lanRouting": {
    "collectorBaseUrl": DEFAULT_EXTERNAL_BASE_URL,
    "cutvideoBaseUrl": CUTVIDEO_BASE_URL,
    "liveRecorderBaseUrl": LIVE_RECORDER_BASE_URL,
    "cutvideoRouteMode": "fnos_tunnel" if ":19186" in CUTVIDEO_BASE_URL else "direct",
}

前端同步收口:

if (key === "cutvideo" && detail.baseUrl.includes(":19186")) {
  extra = "当前通过 fnOS NAS SSH 隧道访问 Windows cutvideo。";
}
  • Step 4: 跑测试确认通过

Run: python3 -m unittest tests.test_production_baseline.ProductionBaselineTests.test_healthz_exposes_lan_routing_summary -v Expected: PASS

  • Step 5: 做前端语法校验

Run: node --check web/storyforge-web-v4/assets/app.js Expected: exit 0

Task 4: 统一 README / LAN 运维手册并补最终回归

Files:

  • Modify: README.md

  • Modify: docs/LAN_E2E_GUIDE_2026-03-18.md

  • Create: docs/FNOS_LAN_DELIVERY_RUNBOOK_2026-03-27.md

  • Step 1: 更新主入口文档

把 README 收口成三条默认命令:

./scripts/deploy_fnos_cutvideo_tunnel.sh
./scripts/deploy_fnos_storyforge_lan_stack.sh
./scripts/smoke_fnos_storyforge_lan.sh
  • Step 2: 更新 LAN E2E

CUTVIDEO_BASE_URL=http://<windows-lan-ip>:7860 改成“主链默认使用 http://192.168.31.188:19186Windows 直连仅作自检”。

  • Step 3: 写新的运维 runbook

包含:

  • 默认端口

  • 默认路由

  • 故障分流

  • fnOS 重启后如何验证 tunnel / web / collector

  • smoke 命令与预期结果

  • Step 4: 跑最终验证

Run:

bash -n scripts/deploy_fnos_storyforge_lan_stack.sh
bash -n scripts/smoke_fnos_storyforge_lan.sh
python3 -m unittest tests.test_production_baseline -v
node --check web/storyforge-web-v4/assets/app.js
git diff --check

Expected:

  • scripts syntax all pass

  • unittest pass

  • JS syntax pass

  • git diff --check clean

  • Step 5: Commit

git add README.md docs/LAN_E2E_GUIDE_2026-03-18.md docs/FNOS_LAN_DELIVERY_RUNBOOK_2026-03-27.md docs/superpowers/plans/2026-03-27-fnos-lan-delivery-stabilization.md scripts/deploy_fnos_storyforge_lan_stack.sh scripts/smoke_fnos_storyforge_lan.sh tests/test_production_baseline.py collector-service/app/core_main.py web/storyforge-web-v4/assets/app.js
git commit -m "feat: stabilize fnos lan delivery flow"

Self-Review

Spec coverage

  • 统一部署入口Task 1 覆盖
  • LAN smokeTask 2 覆盖
  • 前后端状态收口Task 3 覆盖
  • 文档与运维统一Task 4 覆盖

Placeholder scan

  • 没有保留 TBD / TODO / “后续补”
  • 每个任务都给了明确文件和验证命令

Type consistency

  • 统一使用 deploy_fnos_storyforge_lan_stack.sh
  • 统一使用 smoke_fnos_storyforge_lan.sh
  • healthz 新字段统一命名为 lanRouting