Files
dsh_shenxian/scripts/find-ui-scan.py
T
admin 3efd68517f chore: 并入已删除会话的在途成果(防丢失;原会话已删,未做功能验收)
**背景**:这些改动原属本工作区另外几个会话(T01/T02 等),**那些会话已被用户删除** ⇒
工作树里的成果处于"无主"状态,一次错误 checkout / 覆盖即**永久丢失** ⇒ 代入库保全。
口径遵循本项目**先例**(`39a1f2e` / `b617cdb`:**别人的活,代入库并在提交信息里注明**)。

**内容**:档案 101「能力管理」改名 + 页内 tab 分页|档案 102 语言切换搬入「用户设置」|
`07-实例UI分区登记表.md`|`scripts/find-ui*.mjs`(UI 元素定位工具)|`poc/portal-entry/`(0.5.3)|
`src/web/locale-pref.ts` + `home-files.ts`(语言偏好持久化)|`test/locale-pref.test.mjs`|
`package.json`|`BRIEF.md` / `INDEX.md` / `docs-manifest.json` / `03-路线图与待办.md` / 档案 100 增量。

**已做最小健全性检查**(⚠️ **未跑完整构建 / 单测** —— 那是原会话的验收职责,本次只求"不丢"):
- JSON 合法:`package.json` / `poc/business-plugins/package.json` / `docs-manifest.json` ✓
- 4 个 TS 文件 `{}`/`()` 配平 ✓;新增文件均非空 ✓
- 规模:13 文件改动 +340/−210,新增 12 条

**未 push**(按 §4 提交边界:用户说"提交",未说"推送")。
2026-09-15 21:17:12 +08:00

97 lines
3.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""find-ui-scan.py —— 在**服务器上**扫描「哪些包注册了实例 UI 分区」,输出 TSV。
由 `scripts/find-ui.cjs` 通过 `ssh <host> python3 -` 喂进来执行(本文件不在服务器上落地)。
为什么要落到服务器上跑:**权威事实是"活着的 profile 里装了什么"**,不是仓里的快照、
也不是文档库里的历史副本(2026-09-15 就是在这上面绕了很久)。
输出 TSV(每行一个分区):
pkg <TAB> origin <TAB> file <TAB> id <TAB> order <TAB> label_raw
scan 顺序:**自研(@dsh-local)在前**,官方在后 —— 排查时先看自己的。
"""
import glob
import json
import re
import sys
PROFILE_GLOB = '/var/lib/dshs/users/*/home/profiles/web/node_modules/@dsh-local/*/lib/client.js'
OFFICIAL_GLOB = '/usr/local/lib/node_modules/@deepseek-ai/dsh/node_modules/@deepseek-ai/*/lib/client.js'
# 注册对象形如: { name: "settings.section", id: "x", order: 100, label: … }
REG = re.compile(r'name:\s*"settings\.section"')
ID = re.compile(r'\bid:\s*"([^"]+)"')
ORDER = re.compile(r'\border:\s*(-?\d+)')
LABEL_LIT = re.compile(r'label:\s*(?:\([^)]*\)\s*=>\s*|function\s*\([^)]*\)\s*\{\s*return\s*)\s*"((?:[^"\\]|\\.)*)"')
LABEL_KEY = re.compile(r'label:\s*(?:\([^)]*\)\s*=>\s*|function\s*\([^)]*\)\s*\{\s*return\s*)\s*t\(\s*"([^"]+)"\s*\)')
def unique_files(pattern):
"""同一 profile 内容一致 ⇒ 只取**第一个** profile,避免同一分区重复几十行。"""
hits = sorted(glob.glob(pattern))
if not hits:
return []
if 'users' in pattern:
# 取第一个用户目录下的全部 @dsh-local 包
first_user = hits[0].split('/home/profiles/')[0]
return [h for h in hits if h.startswith(first_user + '/')]
return hits
def label_of(text, raw):
"""label 是字面量 ⇒ 解码转义;是 t("key") ⇒ 在同文件的词典里找值。"""
m = LABEL_LIT.search(raw)
if m:
lit = m.group(1)
# 关键:`\u7528\u6237\u8bbe\u7f6e` 这类**转义 unicode** 要还原成人能看的字
try:
return json.loads('"' + lit + '"')
except Exception:
return lit
m = LABEL_KEY.search(raw)
if m:
key = m.group(1)
d = re.search(r'"' + re.escape(key) + r'":\s*"((?:[^"\\]|\\.)*)"', text)
if d:
try:
return json.loads('"' + d.group(1) + '"')
except Exception:
return d.group(1)
return 't("%s")' % key
return '(无 label)'
def scan(files, origin):
for f in files:
try:
text = open(f, encoding='utf-8', errors='replace').read()
except Exception:
continue
if 'settings.section' not in text:
continue
# ⚠️ 用 removeprefix 而不是 lstrip:lstrip 是按**字符集**删,会把官方包的 "@deepseek" 也啃掉
k = f.split('/node_modules/')[-1].split('/lib/client.js')[0]
pkg = k.removeprefix('@dsh-local/')
for m in REG.finditer(text):
raw = text[m.start():m.start() + 420]
i = ID.search(raw)
if not i:
continue
o = ORDER.search(raw)
print('\t'.join([
pkg, origin, f, i.group(1), o.group(1) if o else '—', label_of(text, raw),
]))
def main():
mine = unique_files(PROFILE_GLOB)
if not mine:
sys.stderr.write('!! 没找到任何 profile 的 @dsh-local 包(集群模式下用户可能落别的 Worker)\n')
scan(mine, '自研')
scan(unique_files(OFFICIAL_GLOB), '官方')
if __name__ == '__main__':
main()