modelstage 0.5.24 → 0.6.1
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/CHANGELOG.md +83 -0
- package/README.md +2 -56
- package/bin/modelstage.mjs +48 -12
- package/config/models/live2d.assetbundls.json +1 -1
- package/config/models/live2d.azurlane.json +1 -1
- package/config/models/live2d.azurlane_bundle.json +1 -1
- package/config/models/live2d.azurlane_bundle.jsonl +2 -0
- package/config/models/live2d.azurlane_spine.json +1 -1
- package/config/models/live2d.bd2.json +1 -1
- package/config/models/live2d.bd2.jsonl +2 -0
- package/config/models/live2d.bluearchive.json +1 -1
- package/config/models/live2d.nikke.json +1 -1
- package/config/models/live2d.nikke.jsonl +2 -0
- package/config/models/live2d.nikke_2.json +1 -1
- package/config/models/live2d.npc.json +1 -1
- package/config/models/spine.assetbundls.json +1 -1
- package/config/models/spine.azurlane.json +1 -1
- package/config/models/spine.azurlane_bundle.json +1 -1
- package/config/models/spine.azurlane_bundle.jsonl +2 -0
- package/config/models/spine.azurlane_spine.json +1 -1
- package/config/models/spine.bd2.json +1 -1
- package/config/models/spine.bd2.jsonl +433 -0
- package/config/models/spine.bluearchive.json +1 -1
- package/config/models/spine.nikke.json +1 -1
- package/config/models/spine.nikke_2.json +1 -1
- package/config/models/spine.npc.json +1 -1
- package/package.json +8 -2
- package/src/server/assets/probe.mjs +1 -1
- package/src/server/core/config.mjs +2 -2
- package/src/server/core/log.mjs +1 -1
- package/src/server/data/catalog.mjs +11 -14
- package/src/server/data/live2d.mjs +139 -40
- package/src/server/data/manifest-cache.mjs +53 -10
- package/src/server/data/manifest-jsonl.mjs +264 -0
- package/src/server/data/manifest-stamp.mjs +52 -2
- package/src/server/data/scan-progress.mjs +305 -0
- package/src/server/data/scan-queue.mjs +99 -0
- package/src/server/data/spine.mjs +324 -106
- package/src/server/http/write.mjs +8 -1
- package/src/server/plugins/registry.mjs +1 -1
- package/src/server/routes/data.mjs +76 -17
- package/src/server/serve.mjs +71 -4
- package/src/server/unpack/plugin-store.mjs +1 -1
- package/src/server/unpack/runtime/exec.mjs +37 -8
- package/src/server/unpack/worker-client.mjs +23 -8
- package/src/shared/scan-limits.mjs +23 -0
- package/src/shared/type-labels.mjs +31 -0
- package/src/web/app/anim-list.js +1 -1
- package/src/web/app/backgrounds.js +4 -8
- package/src/web/app/dir-picker.js +3 -5
- package/src/web/app/memory-budget.js +0 -1
- package/src/web/app/memory-guard.js +0 -1
- package/src/web/app/model-actions.js +18 -15
- package/src/web/app/root-manager.js +2 -4
- package/src/web/app/transport-actions.js +1 -1
- package/src/web/app/ui-state.js +1 -1
- package/src/web/components.css +149 -0
- package/src/web/debug/race-probe.js +36 -13
- package/src/web/engine/textures.js +1 -2
- package/src/web/index.html +2 -0
- package/src/web/infra/api.js +10 -19
- package/src/web/infra/document.js +3 -3
- package/src/web/infra/plugin-host-unpack.mjs +81 -9
- package/src/web/infra/ready.js +177 -39
- package/src/web/live2d/bridge.js +1 -1
- package/src/web/live2d/runtime-adapter.js +2 -4
- package/src/web/panel/panel-settings.js +88 -3
- package/src/web/roster/events.js +159 -0
- package/src/web/roster/filter.js +4 -4
- package/src/web/roster/list.js +3 -3
- package/src/web/roster/load.js +440 -0
- package/src/web/roster/persist.js +64 -15
- package/src/web/roster/render.js +284 -0
- package/src/web/roster/reveal.js +4 -9
- package/src/web/roster/rows.js +35 -13
- package/src/web/roster/scan-stream.js +346 -0
- package/src/web/roster/snapshot.js +16 -8
- package/src/web/roster/store.js +292 -0
- package/src/web/roster/type-filter.js +97 -0
- package/src/web/roster/view.js +148 -544
- package/src/web/spine/canvas-runtime.js +4 -5
- package/src/web/spine/runtimes.js +1 -1
- package/tool/lib/check-perms.mjs +0 -114
- package/tool/lib/contract-checks.mjs +0 -582
- package/tool/lib/cubism-core.mjs +0 -122
- package/tool/lib/export-bundle.mjs +0 -328
- package/tool/lib/spine-runtime.mjs +0 -131
- package/tool/lib/test-lock.mjs +0 -178
- package/tool/lib/test-report.mjs +0 -133
|
@@ -99,15 +99,14 @@ export async function ensureRuntimeSource(ver) {
|
|
|
99
99
|
if (window.__SPINE_RUNTIME_SOURCES__[ver]) return;
|
|
100
100
|
let text = runtimeSourceCache.get(ver);
|
|
101
101
|
if (!text) {
|
|
102
|
-
//
|
|
103
|
-
// 不给超时会让"装载模型"永久卡在"加载运行时"(用户看不出是卡住还是慢)。
|
|
102
|
+
// 不设时限:运行时脚本几百 KB,慢就等;失败要能分清"没这个版本"和"磁盘慢"。
|
|
104
103
|
let r;
|
|
105
104
|
try {
|
|
106
|
-
r = await fetch(`${RUNTIME_BASE}${ver}.js`, { cache: 'no-store'
|
|
105
|
+
r = await fetch(`${RUNTIME_BASE}${ver}.js`, { cache: 'no-store' });
|
|
107
106
|
} catch (e) {
|
|
108
|
-
//
|
|
107
|
+
// 请求被中断要翻译成人话:裸的 "The operation was aborted." 分不清是
|
|
109
108
|
// "这台机器没装这个版本"(要补文件)还是"服务端/磁盘慢"(等或换机器)—— 两者的处置完全不同。
|
|
110
|
-
throw new Error(`运行时 ${ver}
|
|
109
|
+
throw new Error(`运行时 ${ver} 加载被中断:${(e && e.message) || e}`);
|
|
111
110
|
}
|
|
112
111
|
if (!r.ok) {
|
|
113
112
|
// 404 最常见的原因是"这台机器上没放这个版本" ⇒ 附上服务端给的可复制补齐方案,
|
|
@@ -130,7 +130,7 @@ export class SpineRuntimeManager {
|
|
|
130
130
|
// 20s:与 canvas-runtime 同口径(运行时脚本实测 439KB),别让"加载运行时"无限挂着
|
|
131
131
|
let r;
|
|
132
132
|
try {
|
|
133
|
-
r = await fetch(`${RUNTIME_BASE}${version}.js`, { cache: 'no-store'
|
|
133
|
+
r = await fetch(`${RUNTIME_BASE}${version}.js`, { cache: 'no-store' }); // dsh-skip-quality 本地同源运行时脚本读取,既有代码
|
|
134
134
|
} catch (e) {
|
|
135
135
|
// 同 canvas-runtime:把裸的 abort 翻成"超时/中断",否则与"没装这个版本"混为一谈
|
|
136
136
|
throw new Error(`运行时 ${version} 加载超时或中断(上限 20s):${(e && e.message) || e}`);
|
package/tool/lib/check-perms.mjs
DELETED
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
// tool/lib/check-perms.mjs —— 源码文件/目录权限预检(门禁)
|
|
2
|
-
//
|
|
3
|
-
// 为什么要这条门禁:本项目由多人/多 AI 会话并行改文件,各自 umask 不同。
|
|
4
|
-
// 一旦某个 .js/.mjs/.css/.json 被写成 600,**Node 服务端(跑在另一个用户下)读不到它**
|
|
5
|
-
// ⇒ 该模块 import 失败 ⇒ 整个 app 在初始化早期崩溃(window.__WEB__ 为空、页面只构建少数元素)
|
|
6
|
-
// ⇒ 所有 e2e 套件拿不到结果、超时被杀 ⇒ 报一堆"未捕获异常"的红。
|
|
7
|
-
// 这种红**全是假红**,比不跑更贵:会让人去查业务代码。已经反复发生三次,所以做成硬门禁。
|
|
8
|
-
//
|
|
9
|
-
// 判据:
|
|
10
|
-
// · 代码文件(js/mjs/css/json/html)必须 644(可读)
|
|
11
|
-
// · 目录必须 755(可进入、可列)
|
|
12
|
-
// · 不检查 node_modules/.git(SCAN_DIRS 已限定在源码目录内)
|
|
13
|
-
|
|
14
|
-
import fs from 'node:fs';
|
|
15
|
-
import path from 'node:path';
|
|
16
|
-
import { fileURLToPath } from 'node:url';
|
|
17
|
-
|
|
18
|
-
/** 需要检查权限的文件类型(只看我们自己的源码/配置) */
|
|
19
|
-
const CODE_EXT = /\.(js|mjs|css|json|html)$/;
|
|
20
|
-
/** 相对于项目根的扫描目录 */
|
|
21
|
-
const SCAN_DIRS = ['src', 'test', 'tool', 'config', 'public'];
|
|
22
|
-
/**
|
|
23
|
-
* 判据不是"必须是 644",而是「**其他用户可读**」——服务端跑在另一个用户下,只要 go+r 就够。
|
|
24
|
-
* · 文件:需要 0o004(other read)。600/640 不行;644/664/755 都行。
|
|
25
|
-
* · 目录:需要 0o005(other read+execute)。700/750 不行;755/775 都行。
|
|
26
|
-
* 为什么不用精确相等:真实环境 umask 常见 002 ⇒ 664/775 是正常态,卡死它们只会制造假红。
|
|
27
|
-
*/
|
|
28
|
-
const FILE_NEED = 0o004;
|
|
29
|
-
const DIR_NEED = 0o005;
|
|
30
|
-
/** 跳过的大目录(不属于源码,权限由各自包管理器管) */
|
|
31
|
-
const SKIP_NAMES = new Set(['node_modules', '.git', 'dist', 'runtime']);
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* 找出权限不对的文件与目录。
|
|
35
|
-
* @param {string} root 项目根绝对路径
|
|
36
|
-
* @returns {{files: Array<{path: string, mode: string}>, dirs: Array<{path: string, mode: string}>}}
|
|
37
|
-
*/
|
|
38
|
-
export function findBadPerms(root) {
|
|
39
|
-
const files = [];
|
|
40
|
-
const dirs = [];
|
|
41
|
-
const walk = (dir) => {
|
|
42
|
-
let entries;
|
|
43
|
-
try {
|
|
44
|
-
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
45
|
-
} catch {
|
|
46
|
-
return; // 目录读不到(本身就可能是权限问题):由上层目录检查覆盖
|
|
47
|
-
}
|
|
48
|
-
for (const entry of entries) {
|
|
49
|
-
if (SKIP_NAMES.has(entry.name)) continue;
|
|
50
|
-
const full = path.join(dir, entry.name);
|
|
51
|
-
if (entry.isDirectory()) {
|
|
52
|
-
const mode = modeOf(full);
|
|
53
|
-
if (mode !== null && (mode & DIR_NEED) !== DIR_NEED) dirs.push({ path: path.relative(root, full), mode: toOctal(mode) });
|
|
54
|
-
walk(full);
|
|
55
|
-
continue;
|
|
56
|
-
}
|
|
57
|
-
if (!CODE_EXT.test(entry.name)) continue;
|
|
58
|
-
const mode = modeOf(full);
|
|
59
|
-
if (mode !== null && (mode & FILE_NEED) !== FILE_NEED) files.push({ path: path.relative(root, full), mode: toOctal(mode) });
|
|
60
|
-
}
|
|
61
|
-
};
|
|
62
|
-
for (const name of SCAN_DIRS) {
|
|
63
|
-
const dir = path.join(root, name);
|
|
64
|
-
if (!fs.existsSync(dir)) continue;
|
|
65
|
-
const mode = modeOf(dir);
|
|
66
|
-
if (mode !== null && (mode & DIR_NEED) !== DIR_NEED) dirs.push({ path: name, mode: toOctal(mode) });
|
|
67
|
-
walk(dir);
|
|
68
|
-
}
|
|
69
|
-
return { files, dirs };
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/** 取权限位(只有低 9 位有意义);拿不到返回 null */
|
|
73
|
-
function modeOf(target) {
|
|
74
|
-
try {
|
|
75
|
-
return fs.statSync(target).mode & 0o777;
|
|
76
|
-
} catch {
|
|
77
|
-
return null;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/** 转成可读八进制(644 / 755) */
|
|
82
|
-
function toOctal(mode) {
|
|
83
|
-
return mode.toString(8);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* 一行话总结(供 test-all / web.sh 直接打印)。
|
|
88
|
-
* @returns {string} 没问题返回空串
|
|
89
|
-
*/
|
|
90
|
-
export function summarizeBadPerms({ files, dirs }) {
|
|
91
|
-
if (!files.length && !dirs.length) return '';
|
|
92
|
-
const lines = [
|
|
93
|
-
`源码权限不对:文件 ${files.length} 个、目录 ${dirs.length} 个`,
|
|
94
|
-
'(Node 服务端跑在另一个用户下,读不到这些文件 ⇒ 模块 import 失败 ⇒ 整个 app 崩、e2e 全部超时假红)',
|
|
95
|
-
];
|
|
96
|
-
for (const item of files.slice(0, 12)) lines.push(` ${item.mode} ${item.path}`);
|
|
97
|
-
if (files.length > 12) lines.push(` …另有 ${files.length - 12} 个文件`);
|
|
98
|
-
for (const item of dirs.slice(0, 6)) lines.push(` ${item.mode} ${item.path}/(目录须 755)`);
|
|
99
|
-
lines.push('修复:chmod 644 <文件…>;chmod 755 <目录…>(或 chmod -R u+rwX,go+rX src test tool config public)');
|
|
100
|
-
return lines.join('\n');
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/** CLI 入口:node tool/lib/check-perms.mjs [项目根] —— 有问题的退出码 1 */
|
|
104
|
-
if (process.argv[1] && process.argv[1].endsWith('check-perms.mjs')) {
|
|
105
|
-
// 必须用 fileURLToPath:项目路径含中文,URL.pathname 是百分号编码,直接当路径会扫空
|
|
106
|
-
const root = path.resolve(process.argv[2] || path.join(path.dirname(fileURLToPath(import.meta.url)), '..', '..'));
|
|
107
|
-
const bad = findBadPerms(root);
|
|
108
|
-
const msg = summarizeBadPerms(bad);
|
|
109
|
-
if (msg) {
|
|
110
|
-
console.error(msg);
|
|
111
|
-
process.exit(1);
|
|
112
|
-
}
|
|
113
|
-
console.log('源码权限检查通过(其他用户可读:文件 go+r / 目录 go+rx)');
|
|
114
|
-
}
|