develop(pbls): QC 退回整改——补齐 self_check/真实可编译代码/config processors/requirements 依赖
This commit is contained in:
parent
a494bd23e6
commit
8d99405f8f
42
.env.example
Normal file
42
.env.example
Normal file
@ -0,0 +1,42 @@
|
||||
# =============================================================================
|
||||
# pbls 应用环境变量样例(PBL Agent OS Phase 0/1)
|
||||
# 复制为 .env 后按实际环境修改;build.sh / deploy/start.sh 会读取这些变量
|
||||
# cp .env.example .env
|
||||
# =============================================================================
|
||||
|
||||
# ---- 运行环境 ----
|
||||
export PBLS_ENV=test # test | prod
|
||||
export PBLS_APP_NAME=pbls
|
||||
export PBLS_PORT=9300 # 必须与 conf/config.json、projects/pbls/env/*.json 三方同值
|
||||
export PBLS_HOST=0.0.0.0
|
||||
|
||||
# ---- 数据库(mariadb)----
|
||||
export DB_HOST=127.0.0.1
|
||||
export DB_PORT=3306
|
||||
export DB_USER=pbls
|
||||
export DB_PASS=pbls@2026
|
||||
export DB_NAME=pbls
|
||||
export PBLS_DBNAME=pbls # get_module_dbname 默认回落库
|
||||
export PBLS_KDB_DBNAME=pbls # KDB 只读桩所在库(pbl_kdb_ext 零写入)
|
||||
|
||||
# ---- 路径 ----
|
||||
export WS_ROOT=/d/pipeline/workspaces/0/sdlc_general
|
||||
export PYTHONPATH=/d/pipeline/workspaces/0/sdlc_general/apps/pbls/app:/d/pipeline/workspaces/0/sdlc_general/modules
|
||||
|
||||
# ---- 代码拉取 ----
|
||||
export GIT_BASE=https://git.example.internal/sdlc
|
||||
export GIT_BRANCH=main
|
||||
|
||||
# ---- 日志 ----
|
||||
export PBLS_LOG_LEVEL=INFO
|
||||
export PBLS_LOG_DIR=logs
|
||||
|
||||
# ---- PBL 业务开关 ----
|
||||
export PBLS_TENANT_REQUIRED=1 # 所有读写 tenant_id 强制打头,缺失即拒绝
|
||||
export PBLS_DDL_DIALECT=mariadb # 禁 FK / 禁 ENUM / 禁 TIMESTAMP
|
||||
export PBLS_AGENT_FAIL_CLOSED=1 # 工具裁决 fail-closed:13 启用 / 9 禁用
|
||||
export PBLS_APPCODES_GROUPS=8
|
||||
export PBLS_APPCODES_ITEMS=74
|
||||
|
||||
# ---- 部署控制 ----
|
||||
export SKIP_START=0 # 1 = 只构建不启动(CI 冒烟用)
|
||||
214
app/pbls.py
214
app/pbls.py
@ -1,111 +1,147 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""pbls —— PBL Agent OS 应用唯一入口(端口 9300,见 projects/pbls/env/test.json:port)。
|
||||
|
||||
框架铁律(web-application-spec):
|
||||
- 入口必须 `from ahserver.webapp import webapp` + 末尾 `webapp(init)`;
|
||||
全项目零其它 Web 框架、零自定义路由装饰器、零手工 JSON 响应包装(verify_delivery.py 扫描零命中)。
|
||||
- 加载顺序固定:`ServerEnv()` → 基础模块(appbase/rbac/pybricks)→ 业务模块。
|
||||
基础模块必须**显式调用 loader**(load_appbase/load_rbac/load_pybricks),
|
||||
只 importlib.import_module 就标 loaded 是反模式(登录/权限链路必崩)。
|
||||
- 库名由应用统一决定:get_module_dbname(m) 从 conf/config.json 的 module_dbmap 读,
|
||||
模块侧禁止硬编码 DBNAME。
|
||||
- 端口唯一来源 = projects/pbls/env/{test,prod}.json 的 port;应用仓库不设 env/ 目录。
|
||||
|
||||
挂载规模对账(QC #13):20 个 load_* 调用 = 3 平台基础模块(appbase/rbac/pybricks)
|
||||
+ 5 复用基座模块(world/scene/entity/script_engine/scense_game)
|
||||
+ 12 个 pbl_* 模块(见下 MODULE_LOAD_ORDER,编号 M1~M12 与 modules/*.md 一一对应)。
|
||||
"""
|
||||
import json
|
||||
pbls 应用唯一入口(PBL Agent OS Phase 0/1)
|
||||
|
||||
约定(web-application-spec):
|
||||
- 一个应用 = 一个入口 = 一个端口(本项目 9300,与 conf/config.json、
|
||||
projects/pbls/env/{test,prod}.json 三方同值)
|
||||
- init() 内 ServerEnv() 先于 load_rbac(),再逐个显式 load_XXX()
|
||||
- 模块取库名一律走 ServerEnv().get_module_dbname('模块名'),禁止硬编码 DBNAME
|
||||
- 挂载顺序以 projects/pbls/pbls_spec.json 的 load_order 为准(23 个模块)
|
||||
- 不使用 flask / fastapi / django / http.server / jsonify / @app.route
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
from ahserver.webapp import webapp
|
||||
from ahserver.serverenv import ServerEnv
|
||||
|
||||
import bricks_for_python # registers bui processor
|
||||
from bricks_for_python.init import load_pybricks # registers UiWindow etc.
|
||||
# ---- 基础层(已存在,直接复用,不重复开发)----
|
||||
from appbase.init import load_appbase
|
||||
from rbac.init import load_rbac
|
||||
from pybricks.init import load_pybricks
|
||||
|
||||
CONF = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
|
||||
'conf', 'config.json')
|
||||
# 12 个 pbl_* 业务模块(编号 ↔ 目录,M10=pbl_appcodes 零表、M12=pbl_common 内核)
|
||||
MODULE_LOAD_ORDER = [
|
||||
('pbl', 'pbl_common'), # M12 公共内核(必须最先,提供租户/DB 出口)
|
||||
('pbl', 'pbl_appcodes'), # M10 枚举 6+2 组幂等注入
|
||||
('pbl', 'pbl_blueprint'), # M1 蓝图聚合根 + 子对象 + 版本 + 模板
|
||||
('pbl', 'pbl_validation'), # M2 14 维校验 + 5 级质量状态
|
||||
('pbl', 'pbl_compiler'), # M3 确定性编译 + Game Definition + Capability
|
||||
('pbl', 'pbl_agent_runtime'), # M4 Designer/Critic + fail-closed 工具裁决
|
||||
('pbl', 'pbl_domain_ext'), # M8 world/scene/entity 薄扩展(不改基表)
|
||||
('pbl', 'pbl_runtime_ext'), # M11 单事务事件+状态写入与广播
|
||||
('pbl', 'pbl_evidence'), # M5 产出物 + 证据幂等采集
|
||||
('pbl', 'pbl_assessment'), # M6 Rubric 加权评估与报告
|
||||
('pbl', 'pbl_kdb_ext'), # M7 KDB 只读桩 + 匿名聚合
|
||||
('pbl', 'pbl_scense_ext'), # M9 前端覆盖层上下文 + 页面注册
|
||||
]
|
||||
# 复用基座模块(只读引用 + 薄扩展,不改基表;缺失时降级并明确回报,不静默假成功)
|
||||
BASE_MODULE_LOADERS = ['world', 'scene', 'entity', 'script_engine', 'scense_game']
|
||||
# ---- PBL 公共内核(必须最先挂载,其余 pbl_* 模块依赖它)----
|
||||
from pbl_common.init import load_pbl_common
|
||||
|
||||
# ---- 引用模块(严格写保护:只挂载,不改基表;扩展一律走薄扩展模块)----
|
||||
from world.init import load_world
|
||||
from scene.init import load_scene
|
||||
from entity.init import load_entity
|
||||
from scense.init import load_scense
|
||||
from scense_runtime.init import load_scense_runtime
|
||||
from script_engine.init import load_script_engine
|
||||
from kdb.init import load_kdb
|
||||
from world_snapshot.init import load_world_snapshot
|
||||
|
||||
# ---- PBL 业务模块(M1~M15)----
|
||||
from pbl_appcodes.init import load_pbl_appcodes
|
||||
from pbl_blueprint.init import load_pbl_blueprint
|
||||
from pbl_validation.init import load_pbl_validation
|
||||
from pbl_compiler.init import load_pbl_compiler
|
||||
from pbl_agent_runtime.init import load_pbl_agent_runtime
|
||||
from pbl_evidence.init import load_pbl_evidence
|
||||
from pbl_assessment.init import load_pbl_assessment
|
||||
from pbl_kdb_ext.init import load_pbl_kdb_ext
|
||||
from pbl_domain_ext.init import load_pbl_domain_ext
|
||||
from pbl_scense_ext.init import load_pbl_scense_ext
|
||||
from pbl_runtime_ext.init import load_pbl_runtime_ext
|
||||
from pbl_template.init import load_pbl_template
|
||||
from pbl_analytics.init import load_pbl_analytics
|
||||
from pbl_governance.init import load_pbl_governance
|
||||
|
||||
# 主库名(可被环境变量覆盖,默认 pbls)
|
||||
DBNAME = os.environ.get('PBLS_DBNAME', 'pbls')
|
||||
|
||||
# 模块 → 库名映射(禁止在模块内硬编码 DBNAME,统一由此处裁决)
|
||||
# 未列出的模块一律回落主库 DBNAME。
|
||||
MODULE_DBNAME = {
|
||||
# 基础层
|
||||
'apppublic': DBNAME,
|
||||
'sqlor': DBNAME,
|
||||
'accounting': DBNAME,
|
||||
'appbase': DBNAME,
|
||||
'rbac': DBNAME,
|
||||
# PBL 内核与业务模块(Phase 0/1 全部落主库,单库多租户 tenant_id 打头)
|
||||
'pbl_common': DBNAME,
|
||||
'pbl_appcodes': DBNAME,
|
||||
'pbl_blueprint': DBNAME,
|
||||
'pbl_validation': DBNAME,
|
||||
'pbl_compiler': DBNAME,
|
||||
'pbl_agent_runtime': DBNAME,
|
||||
'pbl_evidence': DBNAME,
|
||||
'pbl_assessment': DBNAME,
|
||||
'pbl_kdb_ext': DBNAME,
|
||||
'pbl_domain_ext': DBNAME,
|
||||
'pbl_scense_ext': DBNAME,
|
||||
'pbl_runtime_ext': DBNAME,
|
||||
'pbl_template': DBNAME,
|
||||
'pbl_analytics': DBNAME,
|
||||
'pbl_governance': DBNAME,
|
||||
# 引用模块(写保护:只读挂载,扩展表落 pbl_*_ext)
|
||||
'world': DBNAME,
|
||||
'scene': DBNAME,
|
||||
'entity': DBNAME,
|
||||
'scense': DBNAME,
|
||||
'scense_runtime': DBNAME,
|
||||
'script_engine': DBNAME,
|
||||
'kdb': os.environ.get('PBLS_KDB_DBNAME', DBNAME),
|
||||
'world_snapshot': DBNAME,
|
||||
}
|
||||
|
||||
|
||||
def _conf():
|
||||
with open(CONF, encoding='utf-8') as f:
|
||||
return json.load(f)
|
||||
|
||||
|
||||
def get_module_dbname(m):
|
||||
"""模块 → 库名映射(来自 conf/config.json:module_dbmap,禁止写死单个库名)。"""
|
||||
conf = _conf()
|
||||
mapping = conf.get('module_dbmap') or {}
|
||||
if m in mapping:
|
||||
return mapping[m]
|
||||
return mapping.get('*') or conf.get('default_dbname') or 'pbl'
|
||||
|
||||
|
||||
def password_encode(s):
|
||||
if s is None:
|
||||
return ''
|
||||
from ahserver.globalEnv import password_encode as _orig
|
||||
return _orig(s)
|
||||
def get_module_dbname(module_name):
|
||||
"""模块 → 库名映射(挂到 ServerEnv,供各模块 ServerEnv().get_module_dbname(x) 调用)"""
|
||||
return MODULE_DBNAME.get(module_name, DBNAME)
|
||||
|
||||
|
||||
def init():
|
||||
env = ServerEnv() # ← MUST be before load_rbac
|
||||
"""应用初始化:显式按 load_order 逐个挂载 23 个模块"""
|
||||
env = ServerEnv()
|
||||
# 注入库名裁决函数(必须先于任何 load_*,模块建表/取连接都依赖它)
|
||||
env.get_module_dbname = get_module_dbname
|
||||
env.password_encode = password_encode
|
||||
env.pbl_default_dbname = get_module_dbname('pbl')
|
||||
env.pbl_default_tenant = _conf().get('default_tenant') or 'default'
|
||||
env.app_name = 'pbls'
|
||||
|
||||
# ── 基础模块:显式调用 loader(反模式禁令:不得只 import 就标 loaded)──
|
||||
load_appbase()
|
||||
load_rbac()
|
||||
load_pybricks()
|
||||
# ---- 基础层(ServerEnv() 之后、load_rbac 之前的固定次序)----
|
||||
load_appbase(env)
|
||||
load_rbac(env)
|
||||
load_pybricks(env)
|
||||
|
||||
# ── 复用基座模块(world/scene/entity/script_engine/scense_game)──
|
||||
missing = []
|
||||
for name in BASE_MODULE_LOADERS:
|
||||
try:
|
||||
pkg = __import__('importlib').import_module('%s.init' % name)
|
||||
loader = getattr(pkg, 'load_%s' % name, None)
|
||||
if callable(loader):
|
||||
loader()
|
||||
else:
|
||||
missing.append(name) # 无 loader 即视为未挂载,不谎报 loaded
|
||||
except ImportError:
|
||||
missing.append(name)
|
||||
# ---- PBL 公共内核(其余 pbl_* 的依赖底座)----
|
||||
load_pbl_common(env)
|
||||
|
||||
# ── 12 个 pbl_* 业务模块(按 Wave 依赖顺序)──
|
||||
loaded = []
|
||||
for _db, name in MODULE_LOAD_ORDER:
|
||||
pkg = __import__('importlib').import_module('%s.init' % name)
|
||||
loader = getattr(pkg, 'load_%s' % name) # 缺 loader 直接抛错(fail-fast)
|
||||
loader()
|
||||
loaded.append(name)
|
||||
env.pbl_modules_loaded = loaded
|
||||
env.pbl_base_missing = missing
|
||||
print('[pbls] modules loaded: %d pbl + base missing=%s' % (len(loaded), missing or 'none'))
|
||||
return {'loaded': len(loaded) + 3, 'base_missing': missing}
|
||||
# ---- 引用模块(写保护,仅挂载复用)----
|
||||
load_world(env)
|
||||
load_scene(env)
|
||||
load_entity(env)
|
||||
load_scense(env)
|
||||
load_scense_runtime(env)
|
||||
load_script_engine(env)
|
||||
load_kdb(env)
|
||||
load_world_snapshot(env)
|
||||
|
||||
# ---- PBL 业务模块(枚举注入 → 蓝图 → 校验 → 编译 → Agent 运行时 → 证据 → 评估
|
||||
# → 四个薄扩展 → 模板/分析/治理)----
|
||||
load_pbl_appcodes(env)
|
||||
load_pbl_blueprint(env)
|
||||
load_pbl_validation(env)
|
||||
load_pbl_compiler(env)
|
||||
load_pbl_agent_runtime(env)
|
||||
load_pbl_evidence(env)
|
||||
load_pbl_assessment(env)
|
||||
load_pbl_kdb_ext(env)
|
||||
load_pbl_domain_ext(env)
|
||||
load_pbl_scense_ext(env)
|
||||
load_pbl_runtime_ext(env)
|
||||
load_pbl_template(env)
|
||||
load_pbl_analytics(env)
|
||||
load_pbl_governance(env)
|
||||
|
||||
return env
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
webapp(init)
|
||||
sys.exit(0)
|
||||
|
||||
webapp(init)
|
||||
|
||||
291
build.sh
291
build.sh
@ -1,68 +1,241 @@
|
||||
#!/bin/bash
|
||||
# pbls 一键部署(web-application-spec 8 步;端口 9300 来源 projects/pbls/env/test.json)
|
||||
#!/usr/bin/env bash
|
||||
# =============================================================================
|
||||
# pbls 应用一键部署脚本(PBL Agent OS Phase 0/1)
|
||||
# 覆盖 web-application-spec 的 8 步集成清单:
|
||||
# 1) 拉取代码(应用 + 23 个模块仓)
|
||||
# 2) 建虚拟环境 + pip install -r requirements.txt
|
||||
# 3) 建库建表(mariadb 方言 DDL,幂等)
|
||||
# 4) wwwroot 软链(前端覆盖层 / bricks 静态资源)
|
||||
# 5) 枚举种子注入(pbl_appcodes seed,幂等,8 组 74 项)
|
||||
# 6) i18n 语言包编译
|
||||
# 7) 服务配置(systemd unit + 端口 9300 三方一致性校验)
|
||||
# 8) 启动 + 健康检查
|
||||
# 用法: bash build.sh [test|prod] (默认 test)
|
||||
# =============================================================================
|
||||
set -euo pipefail
|
||||
cdir=$(cd "$(dirname "$0")" && pwd)
|
||||
APP=pbls
|
||||
PORT=${PORT:-9300}
|
||||
DB_HOST=127.0.0.1; DB_PORT=3306; DB_USER=test; DB_PASS=test123; DB_NAME=pbl
|
||||
ENV_JSON="$cdir/../../projects/pbls/env/test.json"
|
||||
[ -f "$ENV_JSON" ] && PORT=$(python3 -c "import json;print(json.load(open('$ENV_JSON'))['port'])")
|
||||
echo "[build] app=$APP port=$PORT (env source: $ENV_JSON)"
|
||||
mkdir -p "$cdir/pkgs" "$cdir/logs" "$cdir/files"
|
||||
|
||||
# 1) 基础包 + 依赖
|
||||
pip install -q -r "$cdir/requirements.txt" || echo "[warn] pip requirements 需离线源,跳过"
|
||||
ENV_NAME="${1:-test}"
|
||||
APP_NAME="pbls"
|
||||
APP_PORT="9300"
|
||||
|
||||
# 2) 模块 clone + 安装(基础模块 + 复用基座 + 12 个 pbl_*)
|
||||
MODULES="world scene entity script_engine scense scense_game scense_runtime \
|
||||
pbl_common pbl_appcodes pbl_blueprint pbl_validation pbl_compiler pbl_agent_runtime \
|
||||
pbl_evidence pbl_assessment pbl_kdb_ext pbl_domain_ext pbl_scense_ext pbl_runtime_ext"
|
||||
for m in $MODULES; do
|
||||
src="$cdir/../../modules/$m"
|
||||
[ -d "$src" ] || { echo "[skip] module $m 不存在"; continue; }
|
||||
[ -d "$cdir/pkgs/$m" ] || cp -r "$src" "$cdir/pkgs/$m"
|
||||
if [ -f "$cdir/pkgs/$m/pyproject.toml" ]; then
|
||||
pip install -q -e "$cdir/pkgs/$m" || echo "[warn] pip install -e $m 失败,回退 PYTHONPATH"
|
||||
WS_ROOT="${WS_ROOT:-/d/pipeline/workspaces/0/sdlc_general}"
|
||||
APP_DIR="${WS_ROOT}/apps/${APP_NAME}"
|
||||
MOD_DIR="${WS_ROOT}/modules"
|
||||
PROJ_DIR="${WS_ROOT}/projects/${APP_NAME}"
|
||||
VENV_DIR="${APP_DIR}/.venv"
|
||||
WWWROOT="${APP_DIR}/wwwroot"
|
||||
LOG_DIR="${APP_DIR}/logs"
|
||||
|
||||
GIT_BASE="${GIT_BASE:-https://git.example.internal/sdlc}"
|
||||
GIT_BRANCH="${GIT_BRANCH:-main}"
|
||||
|
||||
DB_HOST="${DB_HOST:-127.0.0.1}"
|
||||
DB_PORT="${DB_PORT:-3306}"
|
||||
DB_USER="${DB_USER:-pbls}"
|
||||
DB_PASS="${DB_PASS:-pbls@2026}"
|
||||
DB_NAME="${DB_NAME:-pbls}"
|
||||
|
||||
# 23 个模块(load_order,与 app/pbls.py、pbls_spec.json 一致)
|
||||
MODULES=(
|
||||
appbase rbac pybricks
|
||||
pbl_common
|
||||
world scene entity scense scense_runtime script_engine kdb world_snapshot
|
||||
pbl_appcodes pbl_blueprint pbl_validation pbl_compiler pbl_agent_runtime
|
||||
pbl_evidence pbl_assessment pbl_kdb_ext pbl_domain_ext pbl_scense_ext
|
||||
pbl_runtime_ext pbl_template pbl_analytics pbl_governance
|
||||
)
|
||||
|
||||
log() { echo "[build.sh][$(date '+%F %T')] $*"; }
|
||||
fail() { echo "[build.sh][FATAL] $*" >&2; exit 1; }
|
||||
|
||||
log "===== 开始部署 ${APP_NAME} (env=${ENV_NAME}, port=${APP_PORT}) ====="
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# 步骤 0:环境与项目 env 文件校验(端口三方一致性硬门禁)
|
||||
# -----------------------------------------------------------------------------
|
||||
ENV_FILE="${PROJ_DIR}/env/${ENV_NAME}.json"
|
||||
[ -f "${ENV_FILE}" ] || fail "缺少项目环境文件:${ENV_FILE}"
|
||||
|
||||
command -v python3 >/dev/null 2>&1 || fail "未安装 python3"
|
||||
command -v git >/dev/null 2>&1 || fail "未安装 git"
|
||||
|
||||
# 三方端口一致性:conf/config.json == projects/pbls/env/{env}.json == 入口约定 9300
|
||||
CONF_PORT=$(python3 -c "import json;print(json.load(open('${APP_DIR}/conf/config.json'))['website']['port'])")
|
||||
ENV_PORT=$(python3 -c "import json;print(json.load(open('${ENV_FILE}'))['website']['port'])")
|
||||
[ "${CONF_PORT}" = "${APP_PORT}" ] || fail "conf/config.json 端口 ${CONF_PORT} != ${APP_PORT}"
|
||||
[ "${ENV_PORT}" = "${APP_PORT}" ] || fail "env/${ENV_NAME}.json 端口 ${ENV_PORT} != ${APP_PORT}"
|
||||
log "步骤0 OK:端口三方一致 = ${APP_PORT}"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# 步骤 1:拉取代码(应用仓 + 模块仓;已存在则 fetch/reset 到目标分支)
|
||||
# -----------------------------------------------------------------------------
|
||||
mkdir -p "${MOD_DIR}" "${LOG_DIR}"
|
||||
|
||||
clone_or_update() {
|
||||
local url="$1" dest="$2"
|
||||
if [ -d "${dest}/.git" ]; then
|
||||
log " 更新已存在仓库:${dest}"
|
||||
git -C "${dest}" fetch --all --prune --quiet || true
|
||||
git -C "${dest}" checkout "${GIT_BRANCH}" --quiet 2>/dev/null || true
|
||||
git -C "${dest}" reset --hard "origin/${GIT_BRANCH}" --quiet 2>/dev/null || true
|
||||
else
|
||||
log " 克隆:${url} -> ${dest}"
|
||||
git clone --depth 1 -b "${GIT_BRANCH}" "${url}" "${dest}" || \
|
||||
log " [WARN] 克隆失败(离线/无权限),使用工作空间已有代码:${dest}"
|
||||
fi
|
||||
export PYTHONPATH="$cdir/pkgs:$PYTHONPATH"
|
||||
# 3) wwwroot 软链(非 cp,保持同步)
|
||||
ln -sfn "$cdir/pkgs/$m/wwwroot" "$cdir/wwwroot/$m"
|
||||
# 5) 建表:models/*.json → DDL(json2ddl),与 apps/pbls/scripts/ddl 同源
|
||||
if [ -d "$cdir/pkgs/$m/models" ]; then
|
||||
( cd "$cdir/pkgs/$m/models" && json2ddl mysql . > mysql.ddl.sql 2>/dev/null || true )
|
||||
[ -s "$cdir/pkgs/$m/models/mysql.ddl.sql" ] && \
|
||||
mysql -h "$DB_HOST" -P "$DB_PORT" -u"$DB_USER" -p"$DB_PASS" "$DB_NAME" \
|
||||
< "$cdir/pkgs/$m/models/mysql.ddl.sql" || echo "[warn] DDL 执行跳过($m)"
|
||||
fi
|
||||
# 6) CRUD 生成:json/*.json → .dspy + .ui
|
||||
if [ -d "$cdir/pkgs/$m/json" ]; then
|
||||
( cd "$cdir/pkgs/$m/json" && xls2ui -m ../models -o ../wwwroot "$m" *.json 2>/dev/null || true )
|
||||
fi
|
||||
# 7) 初始化数据(appcodes 等种子)
|
||||
[ -f "$cdir/pkgs/$m/init/data.json" ] && echo "[info] $m 有 init/data.json,由 load_$m() 幂等注入"
|
||||
# 8) RBAC 路径注册
|
||||
[ -f "$cdir/pkgs/$m/scripts/load_path.py" ] && \
|
||||
python3 "$cdir/pkgs/$m/scripts/load_path.py" || echo "[warn] $m rbac 注册跳过"
|
||||
}
|
||||
|
||||
clone_or_update "${GIT_BASE}/${APP_NAME}_app.git" "${APP_DIR}"
|
||||
for m in "${MODULES[@]}"; do
|
||||
[ -d "${MOD_DIR}/${m}" ] && continue
|
||||
clone_or_update "${GIT_BASE}/${m}.git" "${MOD_DIR}/${m}"
|
||||
done
|
||||
log "步骤1 OK:代码就绪(应用 + ${#MODULES[@]} 个模块)"
|
||||
|
||||
# 4) i18n 合并
|
||||
python3 "$cdir/scripts/merge_i18n.py" 2>/dev/null || echo "[info] i18n 合并跳过"
|
||||
# -----------------------------------------------------------------------------
|
||||
# 步骤 2:虚拟环境 + 依赖安装
|
||||
# -----------------------------------------------------------------------------
|
||||
if [ ! -x "${VENV_DIR}/bin/python" ]; then
|
||||
python3 -m venv "${VENV_DIR}" || fail "创建 venv 失败"
|
||||
fi
|
||||
# shellcheck disable=SC1091
|
||||
source "${VENV_DIR}/bin/activate"
|
||||
python -m pip install --upgrade pip setuptools wheel --quiet || true
|
||||
pip install -r "${APP_DIR}/requirements.txt" --quiet || fail "pip install 失败"
|
||||
|
||||
# 兜底建表(36 表,MariaDB 方言,幂等 CREATE TABLE IF NOT EXISTS)
|
||||
bash "$cdir/scripts/apply_ddl.sh" || echo "[warn] apply_ddl 跳过(离线/无库)"
|
||||
# 模块以源码路径方式挂载(PYTHONPATH 注入,避免逐个打包发布)
|
||||
export PYTHONPATH="${APP_DIR}/app:${MOD_DIR}:${PYTHONPATH:-}"
|
||||
log "步骤2 OK:依赖安装完成,PYTHONPATH=${PYTHONPATH}"
|
||||
|
||||
# bricks 前端
|
||||
[ -d "$cdir/pkgs/bricks/dist" ] && ln -sfn "$cdir/pkgs/bricks/dist" "$cdir/wwwroot/bricks"
|
||||
# -----------------------------------------------------------------------------
|
||||
# 步骤 3:建库建表(mariadb 方言,DDL 幂等:CREATE TABLE IF NOT EXISTS)
|
||||
# -----------------------------------------------------------------------------
|
||||
if command -v mysql >/dev/null 2>&1; then
|
||||
mysql -h"${DB_HOST}" -P"${DB_PORT}" -u"${DB_USER}" -p"${DB_PASS}" \
|
||||
-e "CREATE DATABASE IF NOT EXISTS \`${DB_NAME}\` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;" \
|
||||
|| fail "建库失败"
|
||||
for ddl in "${APP_DIR}"/scripts/ddl/*.sql; do
|
||||
[ -f "${ddl}" ] || continue
|
||||
log " 执行 DDL:${ddl}"
|
||||
mysql -h"${DB_HOST}" -P"${DB_PORT}" -u"${DB_USER}" -p"${DB_PASS}" "${DB_NAME}" < "${ddl}" \
|
||||
|| fail "DDL 执行失败:${ddl}"
|
||||
done
|
||||
else
|
||||
log " [WARN] 未检测到 mysql 客户端,改用应用内建表(模块 tables.py 自动 CREATE IF NOT EXISTS)"
|
||||
fi
|
||||
log "步骤3 OK:36 表 DDL 就绪(BIGINT AUTO_INCREMENT 主键 / varchar(32) 编码 / 无 FK / 无 ENUM / 无 TIMESTAMP)"
|
||||
|
||||
# 启动/停止脚本
|
||||
cat > "$cdir/start.sh" <<EOF
|
||||
#!/bin/bash
|
||||
cd "$cdir" && PYTHONPATH="$cdir/pkgs:\$PYTHONPATH" python3 app/$APP.py >> logs/$APP.out 2>&1 &
|
||||
echo \$! > $APP.pid
|
||||
EOF
|
||||
cat > "$cdir/stop.sh" <<EOF
|
||||
#!/bin/bash
|
||||
cd "$cdir" && [ -f $APP.pid ] && kill \$(cat $APP.pid) && rm -f $APP.pid
|
||||
EOF
|
||||
chmod +x "$cdir/start.sh" "$cdir/stop.sh"
|
||||
echo "[build] done. start: ./start.sh port: $PORT"
|
||||
# -----------------------------------------------------------------------------
|
||||
# 步骤 4:wwwroot 软链(前端覆盖层 + bricks 静态资源)
|
||||
# -----------------------------------------------------------------------------
|
||||
mkdir -p "${WWWROOT}"
|
||||
# bricks / sage 前端静态资源(存在才链,缺失不阻断)
|
||||
for src in "${MOD_DIR}/pybricks/wwwroot" "${MOD_DIR}/appbase/wwwroot" "${MOD_DIR}/rbac/wwwroot"; do
|
||||
[ -d "${src}" ] || continue
|
||||
base=$(basename "$(dirname "${src}")")
|
||||
ln -sfn "${src}" "${WWWROOT}/${base}" && log " 软链:${WWWROOT}/${base} -> ${src}"
|
||||
done
|
||||
# PBL 前端覆盖层(pbl_scense_ext / pbl_overlay)
|
||||
for ov in pbl_scense_ext pbl_domain_ext; do
|
||||
[ -d "${MOD_DIR}/${ov}/wwwroot" ] && ln -sfn "${MOD_DIR}/${ov}/wwwroot" "${WWWROOT}/${ov}"
|
||||
done
|
||||
[ -d "${APP_DIR}/wwwroot/pbl_overlay" ] && log " 覆盖层:${WWWROOT}/pbl_overlay(应用自带)"
|
||||
log "步骤4 OK:wwwroot 软链完成"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# 步骤 5:枚举种子注入(pbl_appcodes,幂等;8 组 74 项)
|
||||
# -----------------------------------------------------------------------------
|
||||
python - <<'PY' || fail "枚举种子注入失败"
|
||||
import os, sys
|
||||
sys.path.insert(0, os.environ.get('MOD_DIR', 'modules'))
|
||||
from pbl_appcodes.seed import seed_all
|
||||
res = seed_all(dbname=os.environ.get('DB_NAME', 'pbls'), idempotent=True)
|
||||
print('[seed] groups=%s items=%s inserted=%s skipped=%s' % (
|
||||
res.get('groups'), res.get('items'), res.get('inserted'), res.get('skipped')))
|
||||
assert res.get('groups') == 8 and res.get('items') == 74, '枚举组/项数量不符(应 8 组 74 项)'
|
||||
PY
|
||||
log "步骤5 OK:枚举种子幂等注入完成"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# 步骤 6:i18n 语言包编译
|
||||
# -----------------------------------------------------------------------------
|
||||
if [ -d "${APP_DIR}/i18n" ]; then
|
||||
python - <<'PY' || log " [WARN] i18n 编译异常,继续"
|
||||
import json, os, glob
|
||||
root = 'i18n'
|
||||
out = {}
|
||||
for f in glob.glob(os.path.join(root, '**', '*.json'), recursive=True):
|
||||
lang = os.path.basename(os.path.dirname(f))
|
||||
out.setdefault(lang, {}).update(json.load(open(f, encoding='utf-8')))
|
||||
os.makedirs('conf', exist_ok=True)
|
||||
json.dump(out, open('conf/i18n.compiled.json', 'w', encoding='utf-8'), ensure_ascii=False, indent=1)
|
||||
print('[i18n] langs=%s keys=%s' % (list(out), sum(len(v) for v in out.values())))
|
||||
PY
|
||||
else
|
||||
log " [INFO] 无 i18n 目录,跳过语言包编译(默认 zh_CN)"
|
||||
fi
|
||||
log "步骤6 OK:i18n 处理完成"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# 步骤 7:服务配置(systemd unit;无 root 时降级为 nohup 启动脚本)
|
||||
# -----------------------------------------------------------------------------
|
||||
UNIT_DIR="${APP_DIR}/deploy"
|
||||
mkdir -p "${UNIT_DIR}"
|
||||
cat > "${UNIT_DIR}/${APP_NAME}.service" <<UNIT
|
||||
[Unit]
|
||||
Description=PBL Agent OS (${APP_NAME}) - env ${ENV_NAME}
|
||||
After=network.target mysql.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=${SUDO_USER:-pipeline}
|
||||
WorkingDirectory=${APP_DIR}
|
||||
Environment=PBLS_ENV=${ENV_NAME}
|
||||
Environment=PBLS_DBNAME=${DB_NAME}
|
||||
Environment=PYTHONPATH=${APP_DIR}/app:${MOD_DIR}
|
||||
ExecStart=${VENV_DIR}/bin/python ${APP_DIR}/app/${APP_NAME}.py
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
StandardOutput=append:${LOG_DIR}/${APP_NAME}.out
|
||||
StandardError=append:${LOG_DIR}/${APP_NAME}.err
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
UNIT
|
||||
|
||||
cat > "${UNIT_DIR}/start.sh" <<START
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
cd "${APP_DIR}"
|
||||
export PBLS_ENV="${ENV_NAME}" PBLS_DBNAME="${DB_NAME}"
|
||||
export PYTHONPATH="${APP_DIR}/app:${MOD_DIR}"
|
||||
nohup "${VENV_DIR}/bin/python" "${APP_DIR}/app/${APP_NAME}.py" \\
|
||||
>> "${LOG_DIR}/${APP_NAME}.out" 2>> "${LOG_DIR}/${APP_NAME}.err" &
|
||||
echo \$! > "${APP_DIR}/${APP_NAME}.pid"
|
||||
echo "started pid=\$(cat ${APP_DIR}/${APP_NAME}.pid) port=${APP_PORT}"
|
||||
START
|
||||
chmod +x "${UNIT_DIR}/start.sh"
|
||||
log "步骤7 OK:服务配置生成(${UNIT_DIR}/${APP_NAME}.service + start.sh)"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# 步骤 8:启动 + 健康检查
|
||||
# -----------------------------------------------------------------------------
|
||||
if [ "${SKIP_START:-0}" = "1" ]; then
|
||||
log "步骤8 SKIP:SKIP_START=1,仅完成构建产物"
|
||||
else
|
||||
bash "${UNIT_DIR}/start.sh" || fail "启动失败"
|
||||
ok=0
|
||||
for i in $(seq 1 30); do
|
||||
sleep 2
|
||||
if python - <<PY
|
||||
import socket,sys
|
||||
s=socket.socket(); s.settimeout(2)
|
||||
sys.exit(0 if s.connect_ex(('127.0.0.1', ${APP_PORT}))==0 else 1)
|
||||
PY
|
||||
then ok=1; break; fi
|
||||
done
|
||||
[ "${ok}" = "1" ] || fail "健康检查失败:端口 ${APP_PORT} 未监听"
|
||||
log "步骤8 OK:${APP_NAME} 已监听 ${APP_PORT}"
|
||||
fi
|
||||
|
||||
log "===== 部署完成:http://127.0.0.1:${APP_PORT}/ (env=${ENV_NAME})====="
|
||||
|
||||
111
conf/config.json
111
conf/config.json
@ -1,69 +1,48 @@
|
||||
{
|
||||
"appname": "pbls",
|
||||
"port": 9300,
|
||||
"port_source": "projects/pbls/env/test.json:port(端口唯一来源,应用不设 env/ 目录)",
|
||||
"default_dbname": "pbl",
|
||||
"default_tenant": "default",
|
||||
"module_dbmap": {
|
||||
"*": "pbl",
|
||||
"pbl_common": "pbl",
|
||||
"pbl_appcodes": "pbl",
|
||||
"pbl_blueprint": "pbl",
|
||||
"pbl_validation": "pbl",
|
||||
"pbl_compiler": "pbl",
|
||||
"pbl_agent_runtime": "pbl",
|
||||
"pbl_evidence": "pbl",
|
||||
"pbl_assessment": "pbl",
|
||||
"pbl_kdb_ext": "pbl",
|
||||
"pbl_domain_ext": "pbl",
|
||||
"pbl_scense_ext": "pbl",
|
||||
"pbl_runtime_ext": "pbl",
|
||||
"world": "pbl",
|
||||
"scene": "pbl",
|
||||
"entity": "pbl",
|
||||
"script_engine": "pbl",
|
||||
"scense_game": "pbl",
|
||||
"appbase": "pbl",
|
||||
"rbac": "pbl"
|
||||
},
|
||||
"password_key": "pbls-aes-key-0123456789abcdef",
|
||||
"logger": {
|
||||
"name": "pbls",
|
||||
"level": "INFO",
|
||||
"file": "$[workdir]$/logs/pbls.log"
|
||||
},
|
||||
"filesroot": "$[workdir]$/files",
|
||||
"databases": {
|
||||
"pbl": {
|
||||
"driver": "mysql",
|
||||
"kwargs": {
|
||||
"host": "127.0.0.1",
|
||||
"port": 3306,
|
||||
"unix_socket": null,
|
||||
"user": "test",
|
||||
"password": "test123",
|
||||
"db": "pbl",
|
||||
"charset": "utf8mb4"
|
||||
},
|
||||
"note": "本机 MySQL 走 socket 会失败(/run/mysqld/mysqld.sock 不存在),必须显式 TCP 127.0.0.1:3306 且 unix_socket=null"
|
||||
}
|
||||
},
|
||||
"website": {
|
||||
"port": 9300,
|
||||
"root": "$[workdir]$/wwwroot",
|
||||
"indexes": ["index.ui", "index.html"],
|
||||
"processors": [
|
||||
[".tmpl", "tmpl"],
|
||||
[".ui", "bui"],
|
||||
[".dspy", "dspy"],
|
||||
[".wss", "ws"],
|
||||
[".ws", "ws"]
|
||||
],
|
||||
"session": {
|
||||
"secret": "pbls-session-secret-please-rotate",
|
||||
"cookie_name": "pbls_session",
|
||||
"max_age": 86400
|
||||
"password_key": "pbls_agent_os_2026",
|
||||
"logger": {
|
||||
"name": "pbls",
|
||||
"level": "INFO",
|
||||
"file": "$[workdir]$/logs/pbls.log"
|
||||
},
|
||||
"rbac": true
|
||||
}
|
||||
"filesroot": "$[workdir]$/files",
|
||||
"databases": {
|
||||
"pbls": {
|
||||
"driver": "mysql",
|
||||
"kwargs": {
|
||||
"host": "$[db.host]$",
|
||||
"port": 3306,
|
||||
"user": "$[db.user]$",
|
||||
"password": "$[db.password]$",
|
||||
"database": "pbls",
|
||||
"charset": "utf8mb4"
|
||||
}
|
||||
}
|
||||
},
|
||||
"website": {
|
||||
"port": 9300,
|
||||
"host": "0.0.0.0",
|
||||
"wwwroot": "$[workdir]$/wwwroot",
|
||||
"indexes": [
|
||||
"index.ui",
|
||||
"index.html"
|
||||
],
|
||||
"processors": [
|
||||
[".tmpl", "tmpl"],
|
||||
[".ui", "bui"],
|
||||
[".dspy", "dspy"],
|
||||
[".wss", "ws"],
|
||||
[".ws", "ws"]
|
||||
],
|
||||
"session": {
|
||||
"timeout": 7200,
|
||||
"cookie_name": "pbls_sid"
|
||||
}
|
||||
},
|
||||
"_comment": {
|
||||
"port": "9300 —— 与 projects/pbls/env/test.json、projects/pbls/env/prod.json、projects/pbls/pbls_spec.json 四方同值;端口唯一事实源在 projects/pbls/env/,本文件仅声明应用监听端口,应用仓库不存放 ssh/db 凭据(project-directory-spec 8.1)",
|
||||
"processors": "必含 ['.tmpl','tmpl'](BricksUIProcessor 内部加载 /bricks/header.tmpl,缺失则 / 返回 500 'NoneType' has no attribute 'be_call')、['.ui','bui']、['.dspy','dspy'];另含 ['.wss','ws'] 供 pbl_runtime_ext 事件广播 WebSocket 使用",
|
||||
"indexes": "必含 index.ui,否则访问 / 或 /{module}/ 返回 500 invalid path",
|
||||
"databases": "driver=mysql 对应 db.engine=mariadb(BIGINT AUTO_INCREMENT 方言),库名 pbls 与 app/pbls.py 的 MODULE_DBNAME 一致"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,17 +1,50 @@
|
||||
# pbls 应用依赖(硬门禁 6.5 / QC #4)
|
||||
# ── 平台基础依赖(Web 框架 + DB 抽象 + 基础模块,缺一不可)────────────────
|
||||
ahserver # Web 框架(唯一入口 webapp(init) + BricksUIProcessor 渲染 .ui)
|
||||
bricks_for_python # bricks Python 侧(load_pybricks 注册 UiWindow 等)
|
||||
sqlor # DB 抽象层(只用 sor.C/U/D/R/I/sqlExe)
|
||||
apppublic # 公共工具(db.sqlorContext / curDateString 等)
|
||||
appbase # 应用基础(params 表,load_appbase 显式调用)
|
||||
rbac # 登录与权限(load_rbac 显式调用;scripts/load_path.py 注册路径)
|
||||
accounting # 平台基础(编码/序列,复用不改)
|
||||
# ── 数据库驱动(缺 DB 驱动即连不上库)────────────────────────────────────
|
||||
pymysql # MariaDB/MySQL Python 驱动(TCP 127.0.0.1:3306)
|
||||
cryptography # pymysql caching_sha2_password 认证依赖
|
||||
# ── 建表/CRUD 生成工具链(build.sh 使用)─────────────────────────────────
|
||||
xls2ddl # models/*.json → mysql.ddl.sql(json2ddl)
|
||||
# ── 复用基座模块(由 build.sh git clone + pip install -e 安装,此处仅登记)──
|
||||
# world / scene / entity / script_engine / scense / scense_game / scense_runtime
|
||||
# (机构 modules/ 仓库,非 PyPI 包,禁止在此写死版本号)
|
||||
# =====================================================================
|
||||
# pbls 应用依赖清单(PBL Agent OS Phase 0/1)
|
||||
# 平台基础依赖(web-application-spec「Required Dependencies」硬要求)
|
||||
# 核心:apppublic / sqlor / ahserver
|
||||
# 数据库 Web 应用:appbase / rbac
|
||||
# 前端渲染:bricks_for_python(.ui → bui processor,缺它 / 返回 500)
|
||||
# DB 驱动:db.engine = mariadb(projects/pbls/env/test.json 唯一事实源)
|
||||
# → PyMySQL + cryptography(caching_sha2/rsa 握手需要)+ DBUtils(连接池)
|
||||
# 禁止:flask / fastapi / django / uvicorn / gunicorn(应用入口只允许 ahserver)
|
||||
# =====================================================================
|
||||
|
||||
# ---- 平台核心(必装,顺序即安装顺序)----
|
||||
git+https://git.opencomputing.cn/yumoqing/apppublic
|
||||
git+https://git.opencomputing.cn/yumoqing/sqlor
|
||||
git+https://git.opencomputing.cn/yumoqing/ahserver
|
||||
|
||||
# ---- 数据库 Web 应用基础模块(必装)----
|
||||
git+https://git.opencomputing.cn/yumoqing/appbase
|
||||
git+https://git.opencomputing.cn/yumoqing/rbac
|
||||
|
||||
# ---- 前端框架(.ui / .tmpl 渲染,必装)----
|
||||
git+https://git.opencomputing.cn/yumoqing/bricks_for_python
|
||||
git+https://git.opencomputing.cn/yumoqing/bricks
|
||||
|
||||
# ---- DDL / CRUD 生成工具(build.sh 建表与 CRUD 生成用)----
|
||||
xls2ddl
|
||||
|
||||
# ---- DB 驱动与连接池(mariadb 方言)----
|
||||
PyMySQL>=1.0.2
|
||||
cryptography>=3.4.8
|
||||
DBUtils>=3.0.2
|
||||
|
||||
# ---- 运行时通用依赖 ----
|
||||
python-dateutil>=2.8.2
|
||||
pytz>=2021.3
|
||||
requests>=2.27.1
|
||||
six>=1.16.0
|
||||
|
||||
# =====================================================================
|
||||
# 本项目业务模块(modules/pbl_*,由 build.sh 以 pip install -e 安装,
|
||||
# 不走 PyPI,故不在此声明版本;清单见 projects/pbls/pbls_spec.json
|
||||
# 的 load_order,共 15 个 pbl_* 模块 + 8 个引用模块):
|
||||
# pbl_common / pbl_appcodes / pbl_blueprint / pbl_validation /
|
||||
# pbl_compiler / pbl_agent_runtime / pbl_evidence / pbl_assessment /
|
||||
# pbl_kdb_ext / pbl_domain_ext / pbl_scense_ext / pbl_runtime_ext /
|
||||
# pbl_template / pbl_analytics / pbl_governance
|
||||
# 引用模块(写保护,只挂载不改):
|
||||
# world / scene / entity / scense / scense_runtime / script_engine /
|
||||
# kdb / world_snapshot
|
||||
# =====================================================================
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
186
wwwroot/index.ui
Normal file
186
wwwroot/index.ui
Normal file
@ -0,0 +1,186 @@
|
||||
{
|
||||
"type": "UiWindow",
|
||||
"id": "pbls_main_window",
|
||||
"options": {
|
||||
"title": "PBL Agent OS",
|
||||
"layout": "border"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "Panel",
|
||||
"id": "pbls_topbar",
|
||||
"options": {
|
||||
"region": "north",
|
||||
"height": 56,
|
||||
"layout": "hbox",
|
||||
"cls": "pbls-topbar"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "Label",
|
||||
"id": "pbls_app_title",
|
||||
"options": {
|
||||
"text": "PBL Agent OS",
|
||||
"cls": "pbls-app-title"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Label",
|
||||
"id": "pbls_phase_label",
|
||||
"options": {
|
||||
"text": "Phase 0/1 · 蓝图 → 校验 → 编译 → Agent 运行时",
|
||||
"cls": "pbls-phase"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Button",
|
||||
"id": "pbls_btn_logout",
|
||||
"options": {
|
||||
"text": "退出",
|
||||
"align": "right",
|
||||
"handler": "app.pbls_logout"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Menu",
|
||||
"id": "pbls_nav_menu",
|
||||
"options": {
|
||||
"region": "west",
|
||||
"width": 220,
|
||||
"title": "功能导航"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "MenuItem",
|
||||
"id": "mi_blueprint",
|
||||
"options": {
|
||||
"text": "蓝图管理",
|
||||
"target": "/pbl_blueprint/index.ui"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "MenuItem",
|
||||
"id": "mi_validation",
|
||||
"options": {
|
||||
"text": "蓝图校验(14 维)",
|
||||
"target": "/pbl_validation/index.ui"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "MenuItem",
|
||||
"id": "mi_compiler",
|
||||
"options": {
|
||||
"text": "编译器 / Game Definition",
|
||||
"target": "/pbl_compiler/index.ui"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "MenuItem",
|
||||
"id": "mi_agent_runtime",
|
||||
"options": {
|
||||
"text": "Agent 运行时(22 工具)",
|
||||
"target": "/pbl_agent_runtime/index.ui"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "MenuItem",
|
||||
"id": "mi_evidence",
|
||||
"options": {
|
||||
"text": "产出物与证据",
|
||||
"target": "/pbl_evidence/index.ui"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "MenuItem",
|
||||
"id": "mi_assessment",
|
||||
"options": {
|
||||
"text": "Rubric 评估报告",
|
||||
"target": "/pbl_assessment/index.ui"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "MenuItem",
|
||||
"id": "mi_appcodes",
|
||||
"options": {
|
||||
"text": "枚举编码",
|
||||
"target": "/pbl_appcodes/index.ui"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "MenuItem",
|
||||
"id": "mi_governance",
|
||||
"options": {
|
||||
"text": "治理与审计",
|
||||
"target": "/pbl_governance/index.ui"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "TabPanel",
|
||||
"id": "pbls_main_tabs",
|
||||
"options": {
|
||||
"region": "center",
|
||||
"closable": true
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "Panel",
|
||||
"id": "tab_home",
|
||||
"options": {
|
||||
"title": "首页",
|
||||
"layout": "vbox"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "Label",
|
||||
"id": "home_welcome",
|
||||
"options": {
|
||||
"text": "欢迎使用 PBL Agent OS。请从左侧导航选择功能模块。",
|
||||
"cls": "pbls-welcome"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "DataViewer",
|
||||
"id": "home_blueprint_list",
|
||||
"options": {
|
||||
"title": "最近蓝图",
|
||||
"url": "/api/pbl_blueprint_list.dspy",
|
||||
"pageSize": 10
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Panel",
|
||||
"id": "pbls_statusbar",
|
||||
"options": {
|
||||
"region": "south",
|
||||
"height": 28,
|
||||
"layout": "hbox"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "Label",
|
||||
"id": "status_tenant",
|
||||
"options": {
|
||||
"text": "租户:-",
|
||||
"cls": "pbls-status"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Label",
|
||||
"id": "status_port",
|
||||
"options": {
|
||||
"text": "端口:9300",
|
||||
"cls": "pbls-status"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user