@tbox.cn/app-cli 0.1.3 → 0.4.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/dist/index.js CHANGED
@@ -1,481 +1,29 @@
1
1
  #!/usr/bin/env node
2
-
3
- // src/index.ts
4
- import { Command } from "commander";
5
- import { createRequire as createRequire3 } from "module";
6
- import { fileURLToPath } from "url";
7
- import { realpathSync as realpathSync3 } from "fs";
8
-
9
- // src/commands/create.ts
10
- import { existsSync as existsSync3, readdirSync, realpathSync } from "fs";
11
- import { appendFile, cp, readFile as readFile2, rename, writeFile as writeFile2 } from "fs/promises";
12
- import path4 from "path";
13
-
14
- // src/manifest.ts
15
- import { existsSync, readFileSync } from "fs";
16
- import { mkdir, writeFile } from "fs/promises";
17
- import path from "path";
18
- function manifestPath(appDir) {
19
- return path.join(appDir, ".tbox", "app.json");
20
- }
21
- function readAppManifest(appDir) {
22
- const file = manifestPath(appDir);
23
- if (!existsSync(file)) return null;
24
- try {
25
- const raw = JSON.parse(readFileSync(file, "utf8"));
26
- const templateVersion = raw?.templateVersion;
27
- if (typeof templateVersion !== "string" || templateVersion.trim().length === 0) return null;
28
- return {
29
- templateVersion: templateVersion.trim(),
30
- npmModules: Array.isArray(raw?.npmModules) ? raw.npmModules : []
31
- };
32
- } catch {
33
- return null;
34
- }
35
- }
36
- async function writeAppManifest(appDir, manifest) {
37
- const file = manifestPath(appDir);
38
- await mkdir(path.dirname(file), { recursive: true });
39
- await writeFile(file, JSON.stringify(manifest, null, 2) + "\n", "utf8");
40
- }
41
- function upsertNpmModule(manifest, entry) {
42
- const rest = manifest.npmModules.filter((m) => m.id !== entry.id);
43
- return { ...manifest, npmModules: [...rest, entry] };
44
- }
45
- function removeNpmModule(manifest, id) {
46
- return { ...manifest, npmModules: manifest.npmModules.filter((m) => m.id !== id) };
47
- }
48
- function updateNpmModule(manifest, id, patch) {
49
- return {
50
- ...manifest,
51
- npmModules: manifest.npmModules.map((m) => m.id === id ? { ...m, ...patch } : m)
52
- };
53
- }
54
- function isValidModuleId(id) {
55
- return /^[a-z0-9]+(-[a-z0-9]+)*$/.test(id) && !id.includes("..");
56
- }
57
- function assertValidModuleId(id, source) {
58
- if (!isValidModuleId(id)) {
59
- throw new Error(`\u975E\u6CD5\u6A21\u5757 id "${id}"\uFF08\u6765\u81EA ${source}\uFF09\uFF1A\u4EC5\u5141\u8BB8\u5C0F\u5199\u5B57\u6BCD/\u6570\u5B57/\u4E2D\u5212\u7EBF`);
60
- }
61
- }
62
-
63
- // src/source.ts
64
- import { existsSync as existsSync2, readFileSync as readFileSync2, mkdtempSync, rmSync } from "fs";
65
- import { execFileSync as execFileSync2 } from "child_process";
66
- import { tmpdir } from "os";
67
- import { readFile, readdir, stat } from "fs/promises";
68
- import path3 from "path";
69
-
70
- // src/registry.ts
71
- import { execFileSync } from "child_process";
72
- import path2 from "path";
73
- var cachedRegistry = null;
74
- function resolveRegistry(pkgName) {
75
- if (pkgName?.startsWith("@tbox.cn/")) {
76
- try {
77
- const scopeOut = execFileSync("pnpm", ["config", "get", "@tbox.cn:registry"], { encoding: "utf8" });
78
- const scopeReg = scopeOut.trim().split(/\r?\n/).pop() ?? "";
79
- if (scopeReg) return scopeReg;
80
- } catch {
81
- }
82
- }
83
- if (cachedRegistry) return cachedRegistry;
84
- try {
85
- const out = execFileSync("pnpm", ["config", "get", "registry"], { encoding: "utf8" });
86
- cachedRegistry = out.trim().split(/\r?\n/).pop() ?? "https://registry.npmjs.org/";
87
- } catch {
88
- cachedRegistry = "https://registry.npmjs.org/";
89
- }
90
- return cachedRegistry;
91
- }
92
- function isRegistrySpec(spec) {
93
- return /^@[^/]+\/[^@/]+(@.+)?$/.test(spec);
94
- }
95
- function specName(spec) {
96
- return spec.replace(/@[^/]+$/, "");
97
- }
98
- function packFromRegistry(spec, outDir, registry) {
99
- const reg = registry ?? resolveRegistry(specName(spec));
100
- const viewArgs = ["view", spec, "dist.tarball"];
101
- if (registry) viewArgs.push("--registry", registry);
102
- const out = execFileSync("pnpm", viewArgs, { encoding: "utf8" });
103
- const url = out.trim().split(/\r?\n/).pop() ?? "";
104
- if (!url) throw new Error(`registry \u62C9\u53D6\u5931\u8D25\uFF1A${spec} \u65E0 dist.tarball\uFF08registry=${reg}\uFF09`);
105
- const file = url.split("/").pop() ?? `${spec.replace(/^@/, "").replace(/\//g, "-").split("@")[0]}.tgz`;
106
- const dest = path2.join(path2.resolve(outDir), file);
107
- execFileSync("curl", ["-fsSL", "-o", dest, url], { stdio: "ignore" });
108
- return dest;
109
- }
110
-
111
- // src/source.ts
112
- var EXCLUDED_DIRS = /* @__PURE__ */ new Set(["node_modules", "dist", "dist-ssr", ".git", ".tbox"]);
113
- async function walkDir(dir, base, out) {
114
- const entries = await readdir(dir, { withFileTypes: true });
115
- for (const entry of entries) {
116
- const full = path3.join(dir, entry.name);
117
- const rel = path3.relative(base, full).split(path3.sep).join("/");
118
- if (entry.isDirectory()) {
119
- if (EXCLUDED_DIRS.has(entry.name)) continue;
120
- await walkDir(full, base, out);
121
- } else if (entry.isFile()) {
122
- out.push(rel);
123
- }
124
- }
125
- }
126
- var LocalDirSource = class {
127
- constructor(dir) {
128
- this.dir = dir;
129
- this.name = path3.basename(path3.resolve(dir));
130
- }
131
- dir;
132
- name;
133
- getDir() {
134
- return this.dir;
135
- }
136
- async listFiles() {
137
- const files = [];
138
- await walkDir(this.dir, this.dir, files);
139
- return files.sort();
140
- }
141
- async readFile(rel) {
142
- return readFile(path3.join(this.dir, rel), "utf8");
143
- }
144
- /** 目录是否可读(存在且含 package.json) */
145
- static isUsable(dir) {
146
- return existsSync2(path3.join(dir, "package.json"));
147
- }
148
- resolveVersion() {
149
- return readPackageNameVersion(this.dir).version;
150
- }
151
- };
152
- function readPackageNameVersion(pkgDir) {
153
- let raw;
154
- try {
155
- raw = readFileSync2(path3.join(pkgDir, "package.json"), "utf8");
156
- } catch {
157
- return { version: "0.0.0" };
158
- }
159
- try {
160
- const pkg = JSON.parse(raw);
161
- return { name: pkg?.name, version: pkg?.version ?? "0.0.0" };
162
- } catch {
163
- return { version: "0.0.0" };
164
- }
165
- }
166
- var RegistrySource = class {
167
- constructor(spec, registry) {
168
- this.spec = spec;
169
- this.registry = registry;
170
- this.name = spec;
171
- this.packDir = mkdtempSync(path3.join(tmpdir(), "tbox-reg-"));
172
- }
173
- spec;
174
- registry;
175
- name;
176
- inner = null;
177
- packDir;
178
- resolvedName = "";
179
- resolvedVersion = "";
180
- resolve() {
181
- if (this.inner) return this.inner;
182
- const pkgName = this.spec.replace(/@[^/]+$/, "");
183
- const reg = this.registry ?? resolveRegistry(pkgName);
184
- console.log(`\u{1F4E6} \u4ECE registry \u62C9\u53D6 ${this.spec}\uFF08${reg}\uFF09`);
185
- const tgzPath = packFromRegistry(this.spec, this.packDir, this.registry);
186
- this.inner = new TarballSource(tgzPath);
187
- let name;
188
- let version = "";
189
- try {
190
- const pkg = JSON.parse(readFileSync2(path3.join(this.inner.getDir(), "package.json"), "utf8"));
191
- name = pkg.name;
192
- version = pkg.version ?? "";
193
- } catch {
194
- }
195
- this.resolvedName = name ?? this.spec;
196
- this.resolvedVersion = version;
197
- console.log(` \u2192 ${this.resolvedName}@${this.resolvedVersion}`);
198
- return this.inner;
199
- }
200
- listFiles() {
201
- return Promise.resolve(this.resolve()).then((s) => s.listFiles());
202
- }
203
- readFile(rel) {
204
- return Promise.resolve(this.resolve()).then((s) => s.readFile(rel));
205
- }
206
- resolveVersion() {
207
- this.resolve();
208
- return this.resolvedVersion;
209
- }
210
- getDir() {
211
- return this.resolve().getDir();
212
- }
213
- get resolvedPackage() {
214
- return { name: this.resolvedName, version: this.resolvedVersion };
215
- }
216
- dispose() {
217
- if (this.inner) this.inner.dispose();
218
- try {
219
- if (this.packDir.startsWith(tmpdir())) {
220
- rmSync(this.packDir, { recursive: true, force: true });
221
- }
222
- } catch {
223
- }
224
- }
225
- };
226
- function openSource(sourcePath, registry) {
227
- if (sourcePath.endsWith(".tgz") && existsSync2(sourcePath)) {
228
- const tarball = new TarballSource(sourcePath);
229
- return { sourceDir: tarball.getDir(), source: tarball };
230
- }
231
- if (!existsSync2(sourcePath) && isRegistrySpec(sourcePath)) {
232
- const regSource = new RegistrySource(sourcePath, registry);
233
- return { sourceDir: regSource.getDir(), source: regSource };
234
- }
235
- return { sourceDir: sourcePath, source: new LocalDirSource(sourcePath) };
236
- }
237
- var TarballSource = class {
238
- name;
239
- inner;
240
- tmpRoot;
241
- constructor(tgzPath) {
242
- const abs = path3.resolve(tgzPath);
243
- this.name = path3.basename(abs, ".tgz");
244
- this.tmpRoot = mkdtempSync(path3.join(tmpdir(), "tbox-tgz-"));
245
- execFileSync2("tar", ["-xzf", abs, "-C", this.tmpRoot], { stdio: "ignore" });
246
- const pkgDir = path3.join(this.tmpRoot, "package");
247
- this.inner = new LocalDirSource(existsSync2(pkgDir) ? pkgDir : this.tmpRoot);
248
- }
249
- getDir() {
250
- return this.inner.getDir();
251
- }
252
- listFiles() {
253
- return this.inner.listFiles();
254
- }
255
- readFile(rel) {
256
- return this.inner.readFile(rel);
257
- }
258
- resolveVersion() {
259
- return this.inner.resolveVersion();
260
- }
261
- /** 释放临时目录(仅删本实例创建的 tmpRoot,防误删系统 tmp 父目录) */
262
- dispose() {
263
- try {
264
- if (this.tmpRoot.startsWith(tmpdir())) {
265
- rmSync(this.tmpRoot, { recursive: true, force: true });
266
- }
267
- } catch {
268
- }
269
- }
270
- };
271
-
272
- // src/commands/create.ts
273
- var EXCLUDE_DIRS = /* @__PURE__ */ new Set(["node_modules", "dist", "dist-ssr", ".git", ".DS_Store"]);
274
- var EXCLUDE_FILES = /* @__PURE__ */ new Set(["pnpm-lock.yaml"]);
275
- var TEMPLATE_WORKSPACE_FILE = "pnpm-workspace.template.yaml";
276
- var APP_WORKSPACE_FILE = "pnpm-workspace.yaml";
277
- function buildPlatformOverrides(tgzDir, appDir) {
278
- const files = readdirSync(tgzDir).filter((f) => /^tbox\.cn-app-(sdk|contracts)-.+\.tgz$/.test(f)).sort();
279
- if (files.length === 0) return null;
280
- const appReal = realpathSync(appDir);
281
- const seen = /* @__PURE__ */ new Set();
282
- const lines = [];
283
- for (const f of files) {
284
- const rest = f.replace(/^tbox\.cn-app-/, "").replace(/\.tgz$/, "");
285
- const i = rest.lastIndexOf("-");
286
- const name = "@tbox.cn/app-" + rest.slice(0, i);
287
- if (seen.has(name)) {
288
- throw new Error(`--local-deps \u76EE\u5F55\u542B\u540C\u4E00\u5E73\u53F0\u5305\u591A\u7248\u672C\uFF08overrides \u91CD\u590D key \u6B67\u4E49\uFF0C\u987B\u5355\u7248\u672C\u76EE\u5F55\uFF09: ${name}`);
289
- }
290
- seen.add(name);
291
- const rel = path4.relative(appReal, realpathSync(path4.join(tgzDir, f)));
292
- lines.push(` ${JSON.stringify(name)}: ${JSON.stringify(`file:${rel}`)}`);
293
- }
294
- return "\n# create --local-deps \u6CE8\u5165\uFF1A\u5E73\u53F0\u5305\u7ECF file: \u672C\u5730\u53D1\u5E03\u6001 tgz \u6D88\u8D39\uFF08005 v0.4.0 \u9A8C\u8BC1\u6001\uFF1B\u751F\u4EA7 B \u6863\u79FB\u9664\u672C\u5757\u8D70 registry\uFF09\noverrides:\n" + lines.join("\n") + "\n";
295
- }
296
- function validateLocalDeps(tgzDir) {
297
- const files = readdirSync(tgzDir).filter((f) => /^tbox\.cn-app-(sdk|contracts)-.+\.tgz$/.test(f));
298
- if (files.length === 0) {
299
- throw new Error(`--local-deps \u76EE\u5F55\u65E0\u5E73\u53F0\u5305\u4EA7\u7269\uFF08\u7F3A tbox.cn-app-(sdk|contracts)-*.tgz\uFF09: ${tgzDir}`);
300
- }
301
- const seen = /* @__PURE__ */ new Set();
302
- for (const f of files) {
303
- const rest = f.replace(/^tbox\.cn-app-/, "").replace(/\.tgz$/, "");
304
- const i = rest.lastIndexOf("-");
305
- const name = "@tbox.cn/app-" + rest.slice(0, i);
306
- if (seen.has(name)) {
307
- throw new Error(`--local-deps \u76EE\u5F55\u542B\u540C\u4E00\u5E73\u53F0\u5305\u591A\u7248\u672C\uFF08overrides \u91CD\u590D key \u6B67\u4E49\uFF0C\u987B\u5355\u7248\u672C\u76EE\u5F55\uFF09: ${name}`);
308
- }
309
- seen.add(name);
310
- }
311
- }
312
- async function createApp(opts) {
313
- const target = path4.resolve(process.cwd(), opts.name);
314
- if (existsSync3(target) && !opts.force) {
315
- throw new Error(`\u76EE\u5F55\u5DF2\u5B58\u5728: ${opts.name}\uFF08\u52A0 --force \u8986\u76D6\uFF09`);
316
- }
317
- const rawSource = opts.source;
318
- const source = path4.resolve(rawSource);
319
- const isReg = !existsSync3(source) && isRegistrySpec(rawSource);
320
- const fromTgz = source.endsWith(".tgz");
321
- if (!isReg && !fromTgz && !LocalDirSource.isUsable(source)) {
322
- throw new Error(`\u6A21\u677F\u6E90\u65E0\u6548\uFF08\u7F3A package.json\uFF09: ${source}`);
323
- }
324
- if (!isReg && fromTgz && !existsSync3(source)) {
325
- throw new Error(`\u6A21\u677F\u6E90\u65E0\u6548\uFF08tgz \u4E0D\u5B58\u5728\uFF09: ${source}`);
326
- }
327
- if (isReg && opts.localDeps) {
328
- throw new Error("registry \u6E90\uFF08--source @scope/pkg\uFF09\u4E0E --local-deps \u4E92\u65A5\uFF1Aregistry \u5B89\u88C5\u65E0\u9700/\u4E0D\u5141\u8BB8\u672C\u5730\u4F9D\u8D56\u6CE8\u5165");
329
- }
330
- if (opts.localDeps) {
331
- validateLocalDeps(path4.resolve(opts.localDeps));
332
- }
333
- const opened = openSource(isReg ? rawSource : source, opts.registry);
334
- const sourceDir = opened.sourceDir;
335
- try {
336
- const srcManifest = readAppManifest(sourceDir);
337
- if (!srcManifest) {
338
- throw new Error(`\u6A21\u677F\u7ED3\u6784\u5F02\u5E38: ${source} \u7684 .tbox/app.json \u7F3A\u5931\u3001\u635F\u574F\u6216\u7F3A\u5C11 templateVersion\uFF08\u51FA\u751F\u7248\u672C\uFF09\u5B57\u6BB5`);
339
- }
340
- await cp(sourceDir, target, {
341
- recursive: true,
342
- force: true,
343
- filter: (src) => {
344
- const name = path4.basename(src);
345
- if (EXCLUDE_FILES.has(name)) return false;
346
- if (EXCLUDE_DIRS.has(name)) return false;
347
- return true;
348
- }
349
- });
350
- const tplWs = path4.join(target, TEMPLATE_WORKSPACE_FILE);
351
- if (existsSync3(tplWs)) {
352
- await rename(tplWs, path4.join(target, APP_WORKSPACE_FILE));
353
- }
354
- if (opts.localDeps) {
355
- const block = buildPlatformOverrides(path4.resolve(opts.localDeps), target);
356
- await appendFile(path4.join(target, APP_WORKSPACE_FILE), block, "utf8");
357
- }
358
- const pkgFile = path4.join(target, "package.json");
359
- if (existsSync3(pkgFile)) {
360
- const pkg = JSON.parse(await readFile2(pkgFile, "utf8"));
361
- pkg.name = opts.name;
362
- await writeFile2(pkgFile, JSON.stringify(pkg, null, 2) + "\n", "utf8");
363
- }
364
- const manifest = { templateVersion: srcManifest.templateVersion, npmModules: [] };
365
- await writeAppManifest(target, manifest);
366
- console.log(`\u2705 \u5DF2\u521B\u5EFA\u5E94\u7528 ${opts.name}`);
367
- if (isReg) {
368
- console.log(` \u26A1 \u5DF2\u4ECE registry \u6A21\u677F\u5305 ${opened.source.name} \u5C55\u5F00\uFF08\u53D1\u5E03\u6001\uFF09`);
369
- } else if (fromTgz) {
370
- console.log(` \u26A1 \u5DF2\u4ECE\u6A21\u677F\u5305 ${path4.basename(source)} \u5C55\u5F00\uFF08\u53D1\u5E03\u6001\uFF09`);
371
- } else {
372
- console.log(` \u26A1 \u5DF2\u4ECE\u6A21\u677F\u76EE\u5F55\u5C55\u5F00\uFF08\u672C\u5730\u5F00\u53D1\u6E90\uFF09`);
373
- }
374
- if (opts.localDeps) {
375
- console.log(` \u26A1 \u5DF2\u6CE8\u5165\u5E73\u53F0 overrides\uFF08--local-deps: ${opts.localDeps}\uFF0Cinstall \u79BB\u7EBF\u6D88\u8D39\u672C\u5730\u53D1\u5E03\u6001\u4EA7\u7269\uFF09`);
376
- }
377
- console.log(` \u26A1 \u5DF2\u751F\u6210 pnpm-workspace.yaml\uFF08\u5E94\u7528 workspace\uFF09`);
378
- console.log(` \u4E0B\u4E00\u6B65: cd ${opts.name} && pnpm install`);
379
- console.log(` \u{1F510} \u9274\u6743\uFF08006\uFF09: \u751F\u4EA7\u8BF7\u7531\u5E73\u53F0\u6CE8\u5165 AUTH_TOKEN_SECRET \u4E14\u786E\u4FDD NODE_ENV=production\uFF08dev-login \u5F3A\u5236\u5173\u95ED\uFF09\uFF1B`);
380
- console.log(` \u672C\u5730\u5F00\u53D1\u9ED8\u8BA4\u5F00\u542F dev-login\uFF08\u975E production\uFF09\uFF1B\u5982\u9700\u5173\u95ED\u8BBE AUTH_DEV_LOGIN_DISABLED=true\u3002`);
381
- } finally {
382
- if (opened.source instanceof TarballSource) opened.source.dispose();
383
- }
384
- }
385
-
386
- // src/commands/add.ts
387
- import { existsSync as existsSync20, readFileSync as readFileSync9, readdirSync as readdirSync2 } from "fs";
388
- import path24 from "path";
389
-
390
- // src/expand.ts
391
- import { mkdir as mkdir2, rm, writeFile as writeFile3 } from "fs/promises";
392
- import path5 from "path";
393
-
394
- // src/pkg-family.ts
395
- var PLATFORM_RE = /^@tbox\.cn\/app-(sdk|contracts)/;
396
- function isPlatform(name) {
397
- return PLATFORM_RE.test(name);
398
- }
399
-
400
- // src/expand.ts
401
- function isWorkspacePkg(name) {
402
- return isPlatform(name);
403
- }
404
- function rewritePlatformRefs(deps) {
405
- for (const name of Object.keys(deps)) {
406
- if (isWorkspacePkg(name)) deps[name] = "catalog:";
407
- }
408
- }
409
- function deriveModuleId(pkgName) {
410
- return pkgName.replace(/^@tbox\.cn\/app-/, "").replace(/^@[^/]+\//, "");
411
- }
412
- function escapeRegExp(s) {
413
- return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
414
- }
415
- function rewriteLoadModuleSkillsCall(content, fromName, toName) {
416
- return content.replaceAll(
417
- new RegExp(`loadModuleSkills\\(\\s*['"]${escapeRegExp(fromName)}['"]`, "g"),
418
- `loadModuleSkills('${toName}'`
419
- );
420
- }
421
- function rewriteExportsToSrc(pkg) {
422
- const exports = pkg.exports;
423
- if (!exports || typeof exports !== "object") return;
424
- const next = {};
425
- for (const [sub, val] of Object.entries(exports)) {
426
- if (sub === "./package.json") {
427
- next[sub] = val;
428
- continue;
429
- }
430
- if (typeof val !== "object" || val === null) continue;
431
- const base = sub === "." ? "./src/index.ts" : `./src/${sub.replace(/^\.\//, "")}/index.ts`;
432
- next[sub] = { types: base, import: base };
433
- }
434
- if (next["./package.json"] === void 0) next["./package.json"] = "./package.json";
435
- pkg.exports = next;
436
- }
437
- async function expandCodegen(appDir, sourceDir, entry) {
438
- const src = new LocalDirSource(sourceDir);
439
- const targetDir = path5.join(appDir, "packages", entry.id);
440
- await rm(targetDir, { recursive: true, force: true });
441
- await mkdir2(targetDir, { recursive: true });
442
- const rawPkg = JSON.parse(await src.readFile("package.json"));
443
- const fromName = rawPkg.name ?? "";
444
- const toName = `@app/${entry.id}`;
445
- for (const rel of await src.listFiles()) {
446
- if (rel === "package.json") continue;
447
- let content = await src.readFile(rel);
448
- if (fromName && fromName !== toName && rel.endsWith(".ts")) {
449
- content = rewriteLoadModuleSkillsCall(content, fromName, toName);
450
- }
451
- const out = path5.join(targetDir, rel);
452
- await mkdir2(path5.dirname(out), { recursive: true });
453
- await writeFile3(out, content);
454
- }
455
- const pkg = rawPkg;
456
- pkg.name = toName;
457
- pkg.private = true;
458
- if (pkg.dependencies && typeof pkg.dependencies === "object") {
459
- rewritePlatformRefs(pkg.dependencies);
460
- }
461
- if (pkg.peerDependencies && typeof pkg.peerDependencies === "object") {
462
- rewritePlatformRefs(pkg.peerDependencies);
463
- }
464
- rewriteExportsToSrc(pkg);
465
- await writeFile3(path5.join(targetDir, "package.json"), JSON.stringify(pkg, null, 2) + "\n", "utf8");
466
- }
467
- var LOCAL_SERVER_ENTRY = (id) => `import type { ServerContext, ServerModule } from '@tbox.cn/app-sdk/server';
2
+ import{a as Ee,b as Pr,c as Re,d as Lr,e as Fr,f as _r,g as Nr,h as Vr,i as Jr,j as zr,k as Ur,l as Br,m as Hr,n as Wr,o as Kr,p as Ne,q as nt,r as Kt,s as z,t as ue}from"./chunk-GY4SXKTS.js";import{d as rt}from"./chunk-SGF4P5YN.js";import{Command as lp}from"commander";import{createRequire as pp}from"module";import{fileURLToPath as up}from"url";import{realpathSync as ui}from"fs";import{existsSync as Y,readdirSync as ct,realpathSync as at,statSync as Yt,chmodSync as bi}from"fs";import{appendFile as ki,cp as Xt,readFile as xi,rename as sn,writeFile as Si}from"fs/promises";import T from"path";function B(e){return Nr(e)}async function te(e,t){_r(e,t)}function Gr(e,t){return Vr(e,t)}function qr(e,t){return Jr(e,t)}function Xr(e,t){return zr(e,t)}function Yr(e,t,r){return Ur(e,t,r)}function ke(e,t){Br(e,t)}import{existsSync as di}from"fs";import{execFileSync as fi}from"child_process";import mi from"path";function De(e){if(!di(mi.join(e,"pnpm-lock.yaml")))return!1;try{return fi("pnpm",["install","--lockfile-only"],{cwd:e,stdio:"pipe",encoding:"utf8"}),!0}catch{return console.warn(" ⚠️ lockfile 刷新失败(pnpm install --lockfile-only)——请手动 pnpm install 后提交 pnpm-lock.yaml"),!1}}import{existsSync as ot,readFileSync as en,mkdtempSync as tn,rmSync as rn}from"fs";import{execFileSync as yi}from"child_process";import{tmpdir as it}from"os";import{readFile as hi,readdir as vi,stat as Tp}from"fs/promises";import K from"path";import{execFileSync as st}from"child_process";import Zr from"path";var Ve=null;function Gt(e){if(e?.startsWith("@tbox.cn/"))try{let r=st("pnpm",["config","get","@tbox.cn:registry"],{encoding:"utf8"}).trim().split(/\r?\n/).pop()??"";if(r)return r}catch{}if(Ve)return Ve;try{Ve=st("pnpm",["config","get","registry"],{encoding:"utf8"}).trim().split(/\r?\n/).pop()??"https://registry.npmjs.org/"}catch{Ve="https://registry.npmjs.org/"}return Ve}function Ce(e){return/^@[^/]+\/[^@/]+(@.+)?$/.test(e)}function gi(e){return e.replace(/@[^/]+$/,"")}function Qr(e,t,r){let n=r??Gt(gi(e)),s=["view",e,"dist.tarball"];r&&s.push("--registry",r);let o=st("pnpm",s,{encoding:"utf8"}).trim().split(/\r?\n/).pop()??"";if(!o)throw new Error(`registry 拉取失败:${e} 无 dist.tarball(registry=${n})`);let a=o.split("/").pop()??`${e.replace(/^@/,"").replace(/\//g,"-").split("@")[0]}.tgz`,c=Zr.join(Zr.resolve(t),a);return st("curl",["-fsSL","-o",c,o],{stdio:"ignore"}),c}var wi=new Set(["node_modules","dist","dist-ssr",".git",".tbox"]);async function nn(e,t,r){let n=await vi(e,{withFileTypes:!0});for(let s of n){let i=K.join(e,s.name),o=K.relative(t,i).split(K.sep).join("/");if(s.isDirectory()){if(wi.has(s.name))continue;await nn(i,t,r)}else s.isFile()&&r.push(o)}}var re=class{constructor(t){this.dir=t;this.name=K.basename(K.resolve(t))}dir;name;getDir(){return this.dir}async listFiles(){let t=[];return await nn(this.dir,this.dir,t),t.sort()}async readFile(t){return hi(K.join(this.dir,t),"utf8")}static isUsable(t){return ot(K.join(t,"package.json"))}resolveVersion(){return qt(this.dir).version}};function qt(e){let t;try{t=en(K.join(e,"package.json"),"utf8")}catch{return{version:"0.0.0"}}try{let r=JSON.parse(t);return{name:r?.name,version:r?.version??"0.0.0"}}catch{return{version:"0.0.0"}}}var de=class{constructor(t,r){this.spec=t;this.registry=r;this.name=t,this.packDir=tn(K.join(it(),"tbox-reg-"))}spec;registry;name;inner=null;packDir;resolvedName="";resolvedVersion="";resolve(){if(this.inner)return this.inner;let t=this.spec.replace(/@[^/]+$/,""),r=this.registry??Gt(t);console.log(`📦 从 registry 拉取 ${this.spec}(${r})`);let n=Qr(this.spec,this.packDir,this.registry);this.inner=new U(n);let s,i="";try{let o=JSON.parse(en(K.join(this.inner.getDir(),"package.json"),"utf8"));s=o.name,i=o.version??""}catch{}return this.resolvedName=s??this.spec,this.resolvedVersion=i,console.log(` → ${this.resolvedName}@${this.resolvedVersion}`),this.inner}listFiles(){return Promise.resolve(this.resolve()).then(t=>t.listFiles())}readFile(t){return Promise.resolve(this.resolve()).then(r=>r.readFile(t))}resolveVersion(){return this.resolve(),this.resolvedVersion}getDir(){return this.resolve().getDir()}get resolvedPackage(){return{name:this.resolvedName,version:this.resolvedVersion}}dispose(){this.inner&&this.inner.dispose();try{this.packDir.startsWith(it())&&rn(this.packDir,{recursive:!0,force:!0})}catch{}}};function fe(e,t){if(e.endsWith(".tgz")&&ot(e)){let r=new U(e);return{sourceDir:r.getDir(),source:r}}if(!ot(e)&&Ce(e)){let r=new de(e,t);return{sourceDir:r.getDir(),source:r}}return{sourceDir:e,source:new re(e)}}var U=class{name;inner;tmpRoot;constructor(t){let r=K.resolve(t);this.name=K.basename(r,".tgz"),this.tmpRoot=tn(K.join(it(),"tbox-tgz-")),yi("tar",["-xzf",r,"-C",this.tmpRoot],{stdio:"ignore"});let n=K.join(this.tmpRoot,"package");this.inner=new re(ot(n)?n:this.tmpRoot)}getDir(){return this.inner.getDir()}listFiles(){return this.inner.listFiles()}readFile(t){return this.inner.readFile(t)}resolveVersion(){return this.inner.resolveVersion()}dispose(){try{this.tmpRoot.startsWith(it())&&rn(this.tmpRoot,{recursive:!0,force:!0})}catch{}}};var $i=new Set(["node_modules","dist","dist-ssr",".git",".DS_Store"]),ji=new Set(["pnpm-lock.yaml"]),Ei="pnpm-workspace.template.yaml",on="pnpm-workspace.yaml",Ri="pnpm-lock.template.yaml",Di="pnpm-lock.yaml";function Ci(e,t){let r=ct(e).filter(o=>/^tbox\.cn-app-(sdk|toolkit|contracts)-.+\.tgz$/.test(o)).sort();if(r.length===0)return null;let n=at(t),s=new Set,i=[];for(let o of r){let a=o.replace(/^tbox\.cn-app-/,"").replace(/\.tgz$/,""),c=a.lastIndexOf("-"),l="@tbox.cn/app-"+a.slice(0,c);if(s.has(l))throw new Error(`--local-deps 目录含同一平台包多版本(overrides 重复 key 歧义,须单版本目录): ${l}`);s.add(l);let p=T.relative(n,at(T.join(e,o)));i.push(` ${JSON.stringify(l)}: ${JSON.stringify(`file:${p}`)}`)}return`
3
+ # create --local-deps 注入:平台包经 file: 本地发布态 tgz 消费(005 v0.4.0 验证态;生产 B 档移除本块走 registry)
4
+ overrides:
5
+ `+i.join(`
6
+ `)+`
7
+ `}function Ti(e){let t=ct(e).filter(n=>/^tbox\.cn-app-(sdk|contracts)-.+\.tgz$/.test(n));if(t.length===0)throw new Error(`--local-deps 目录无平台包产物(缺 sdk/contracts tgz): ${e}`);let r=new Set;for(let n of t){let s=n.replace(/^tbox\.cn-app-/,"").replace(/\.tgz$/,""),i=s.lastIndexOf("-"),o="@tbox.cn/app-"+s.slice(0,i);if(r.has(o))throw new Error(`--local-deps 目录含同一平台包多版本(overrides 重复 key 歧义,须单版本目录): ${o}`);r.add(o)}}function an(e){let t=0;for(let r of ct(e,{withFileTypes:!0}))r.name===".DS_Store"||r.name==="node_modules"||(r.isDirectory()?t+=an(T.join(e,r.name)):t+=1);return t}function Mi(e){if(!Y(e)||!Yt(e).isDirectory())throw new Error(`--config-dir 不是目录: ${e}`);if(an(e)===0)throw new Error(`--config-dir 目录为空(无任何文件可合并): ${e}`)}var Ii=/^(?:@[a-z0-9._-]+\/)?[a-z0-9][a-z0-9._-]*$/;function Ai(e){let t=T.join(e,"scripts");if(!Y(t))return;function r(n){for(let s of ct(n,{withFileTypes:!0})){let i=T.join(n,s.name);s.isDirectory()?r(i):s.isFile()&&bi(i,493)}}r(t)}function Oi(e){if(!Ii.test(e))throw new Error(`应用包名无效: ${e}(需为 npm 包名;使用 --name 指定)`)}function Pi(e){return e===T.resolve(process.cwd())}function Li(e,t){let r=T.relative(e,t);return r!==""&&r!==".."&&!r.startsWith(`..${T.sep}`)&&!T.isAbsolute(r)}function Fi(e,t){if(!Y(e))return;let r=Yt(e),n=at(e),s=Y(t)?at(t):T.resolve(t);if(n===s||r.isDirectory()&&Li(n,s))throw new Error(`模板源与目标目录重合或目标位于模板源内,无法初始化: ${e}`)}async function cn(e){let t=T.resolve(process.cwd(),e.name),r=Y(t),n=Pi(t);if(r&&!Yt(t).isDirectory())throw new Error(`目标不是目录: ${e.name}`);if(r&&!n&&!e.force)throw new Error(`目录已存在: ${e.name}(加 --force 覆盖)`);if(n&&Y(T.join(t,".tbox","app.json"))&&!e.force)throw new Error("当前目录已有 tbox-app 应用(存在 .tbox/app.json;重新初始化请加 --force)");let s=e.appName??(n?T.basename(t):e.name);if(!s)throw new Error("无法从目标目录推导应用包名,请使用 --name 指定");e.appName&&Oi(s);let i=Array.isArray(e.configDir)?e.configDir:e.configDir?[e.configDir]:[],o=e.source,a=T.resolve(o),c=!Y(a)&&Ce(o),l=a.endsWith(".tgz");if(!c&&!l&&!re.isUsable(a))throw new Error(`模板源无效(缺 package.json): ${a}`);if(!c&&l&&!Y(a))throw new Error(`模板源无效(tgz 不存在): ${a}`);if(Fi(a,t),c&&e.localDeps)throw new Error("registry 源(--source @scope/pkg)与 --local-deps 互斥:registry 安装无需/不允许本地依赖注入");e.localDeps&&Ti(T.resolve(e.localDeps));for(let d of i)Mi(T.resolve(d));let p=fe(c?o:a,e.registry),u=p.sourceDir;try{let d=B(u);if(!d)throw new Error(`模板结构异常: ${a} 的 .tbox/app.json 缺失、损坏或缺少 templateVersion(出生版本)字段`);await Xt(u,t,{recursive:!0,force:!0,filter:S=>{let $=T.basename(S);return!(ji.has($)||$i.has($))}}),Ai(t);let f=T.join(t,Ei);Y(f)&&await sn(f,T.join(t,on));let g=T.join(t,Ri),m=Y(g);if(m&&await sn(g,T.join(t,Di)),e.localDeps){let S=Ci(T.resolve(e.localDeps),t);await ki(T.join(t,on),S,"utf8"),m&&De(t)}for(let S of i)await Xt(T.resolve(S),T.join(t,"config"),{recursive:!0,force:!0,filter:$=>T.basename($)!==".DS_Store"&&T.basename($)!=="node_modules"});e.envFile&&await Xt(T.resolve(e.envFile),T.join(t,"config","env.sh"),{force:!0});let h=T.join(t,"package.json");if(Y(h)){let S=JSON.parse(await xi(h,"utf8"));S.name=s,await Si(h,JSON.stringify(S,null,2)+`
8
+ `,"utf8")}let v={templateVersion:d.templateVersion,npmModules:[]};await te(t,v),console.log(`✅ 已创建应用 ${s}`),console.log(c?` ⚡ 已从 registry 模板包 ${p.source.name} 展开(发布态)`:l?` ⚡ 已从模板包 ${T.basename(a)} 展开(发布态)`:" ⚡ 已从模板目录展开(本地开发源)"),e.localDeps?(console.log(` ⚡ 已注入平台 overrides(--local-deps: ${e.localDeps},install 离线消费本地发布态产物)`),m&&console.log(" 🔒 携带锁已增量刷新为 file: 形态(仅平台三包换形态,间接依赖保钉)——B 档移除 overrides 块后 plain install 回 registry 形态、frozen 恢复可用")):m&&console.log(" 🔒 依赖已由 pnpm-lock.yaml 冻结(直接+间接全精确)——请随应用提交,安装可 --frozen-lockfile"),i.length>0&&console.log(` ⚡ 已合并应用配置(--config-dir: ${i.join(" + ")} → config/)`),console.log(" ⚡ 已生成 pnpm-workspace.yaml(应用 workspace)"),console.log(n?" 下一步: pnpm install":` 下一步: cd ${e.name} && pnpm install`),console.log(" 📱 支付宝小程序壳: tbox-app miniapp-container init(web-view 承载 H5 + JSAPI 桥代理)"),console.log(" 🔐 鉴权(006): 生产请由平台注入 AUTH_TOKEN_SECRET 且确保 NODE_ENV=production(dev-login 强制关闭);"),console.log(" 本地开发默认开启 dev-login(非 production);如需关闭设 AUTH_DEV_LOGIN_DISABLED=true。")}finally{p.source instanceof U&&p.source.dispose()}}import{existsSync as ve,readFileSync as yr,readdirSync as sc}from"fs";import Q from"path";import{copyFile as Gi,mkdir as wt,rm as vn,writeFile as ne}from"fs/promises";import H from"path";import{existsSync as ze,readFileSync as dt}from"fs";import{readFile as pt,readdir as Vi,writeFile as Ji}from"fs/promises";import _ from"path";import V from"typescript";var _i=/^@tbox\.cn\/app-(?:sdk|toolkit|contracts(?:-|$))/,Ni=new Set(["@tbox.cn/app-contracts-mall"]);function lt(e){return _i.test(e)}function Je(e){return Ni.has(e)}var pn=["dependencies","optionalDependencies","peerDependencies","devDependencies"],zi=new Set(["name","version","private","exports"]),ft=/\.(?:ts|tsx|mts|cts|js|jsx|mjs|cjs)$/,Ui=new Set(["node_modules","dist",".git",".tbox"]),Bi=new Map([["@tbox.cn/app-contracts-mall",{exports:{".":{types:"./src/index.ts",import:"./src/runtime.ts"},"./server":{types:"./src/server/index.ts",import:"./src/server/index.ts"},"./package.json":"./package.json"},files:{"src/index.ts":`export * from '@tbox.cn/app-contracts-mall';
9
+ `,"src/runtime.ts":`export * from '@tbox.cn/app-contracts-mall';
10
+ `,"src/server/index.ts":`export * from '@tbox.cn/app-contracts-mall/server';
11
+ `}}]]);function mt(e){return`@app/${e}`}function un(e,t){return Je(e)||t?"codegen":"sdk"}function xe(e){return{id:e.id,publicName:e.package,localName:mt(e.id),version:e.version}}function Hi(e){let t=new Map;for(let r of e?.npmModules??[]){if(r.mode!=="codegen")continue;let n=xe(r);t.set(n.publicName,n)}return t}function dn(e,t){let r=new Map(e);return r.set(t.publicName,t),r}function Se(e){return Hi(B(e))}function gt(e){try{return JSON.parse(dt(_.join(e,"package.json"),"utf8")).tboxCodegenShim===!0}catch{return!1}}function Wi(e,t,r){let n=[...t].sort((s,i)=>{let o=r==="local"?s.publicName:s.localName;return(r==="local"?i.publicName:i.localName).length-o.length});for(let s of n){let i=r==="local"?s.publicName:s.localName,o=r==="local"?s.localName:s.publicName;if(e===i)return o;if(e.startsWith(`${i}/`))return`${o}${e.slice(i.length)}`}return e}function Ue(e,t,r,n="local"){let s=[...r];if(s.length===0)return e;let i=t.endsWith(".tsx")?V.ScriptKind.TSX:t.endsWith(".jsx")?V.ScriptKind.JSX:t.endsWith(".json")?V.ScriptKind.JSON:V.ScriptKind.TS,o=V.createSourceFile(t,e,V.ScriptTarget.Latest,!0,i),a=[];function c(u){let d=Wi(u.text,s,n);if(d===u.text)return;let f=e.slice(u.getStart(o),u.end),g=f[0];(g==="'"||g==='"'||g==="`")&&f.at(-1)===g&&a.push({start:u.getStart(o),end:u.end,value:`${g}${d}${g}`})}function l(u){if(V.isImportDeclaration(u)&&V.isStringLiteralLike(u.moduleSpecifier))c(u.moduleSpecifier);else if(V.isExportDeclaration(u)&&u.moduleSpecifier&&V.isStringLiteralLike(u.moduleSpecifier))c(u.moduleSpecifier);else if(V.isImportTypeNode(u)&&V.isLiteralTypeNode(u.argument)&&V.isStringLiteralLike(u.argument.literal))c(u.argument.literal);else if(V.isCallExpression(u)&&u.arguments.length>0&&V.isStringLiteralLike(u.arguments[0])){let d=u.expression.kind===V.SyntaxKind.ImportKeyword,f=V.isIdentifier(u.expression)&&u.expression.text==="loadModuleSkills";(d||f)&&c(u.arguments[0])}else if(V.isPropertyAssignment(u)&&V.isArrayLiteralExpression(u.initializer)){let d=u.name.getText(o).replace(/["']/g,"");if(d==="external"||d==="noExternal")for(let f of u.initializer.elements)V.isStringLiteralLike(f)&&c(f)}V.forEachChild(u,l)}l(o);let p=e;for(let u of a.sort((d,f)=>f.start-d.start))p=p.slice(0,u.start)+u.value+p.slice(u.end);return p}function fn(e,t,r,n){let s=[...t];for(let i of pn){let o=e[i];if(!o||typeof o!="object"||Array.isArray(o))continue;let a={...o};for(let c of s){let l=r==="local"?c.publicName:c.localName,p=r==="local"?c.localName:c.publicName;l in a&&(delete a[l],a[p]=r==="local"?"workspace:*":"catalog:")}for(let c of Object.keys(a))lt(c)?a[c]="catalog:":a[c]==="catalog:"&&n?.has(c)&&(a[c]=n.get(c));e[i]=a}}function Ki(e){if(e==="./package.json"||!e.startsWith("./dist/"))return e;let t=e.replace(/^\.\/dist\//,"./src/");return t.endsWith(".d.ts")?`${t.slice(0,-5)}.ts`:t.endsWith(".js")?`${t.slice(0,-3)}.ts`:t}function Zt(e){return typeof e=="string"?Ki(e):Array.isArray(e)?e.map(Zt):!e||typeof e!="object"?e:Object.fromEntries(Object.entries(e).map(([t,r])=>[t,Zt(r)]))}function mn(e){if(!e.exports||typeof e.exports!="object")return;let t=Zt(e.exports);"./package.json"in t||(t["./package.json"]="./package.json"),e.exports=t}function Te(e,t,r,n){let s=JSON.parse(JSON.stringify(e));return s.name=mt(t.id),s.private=!0,fn(s,r,"local",n),mn(s),s}function ce(e){let t=/^\s+["']?([^"':#\s]+)["']?\s*:\s*(.+?)\s*(?:#.*)?$/,r=_.resolve(e);for(;;){let n=_.join(r,"pnpm-workspace.yaml");if(ze(n)){let i=new Map,o=!1;for(let a of dt(n,"utf8").split(`
12
+ `)){let c=a.replace(/\r$/,"");if(/^\S/.test(c)){o=/^catalog\s*:/.test(c);continue}if(!o)continue;let l=c.match(t);l&&i.set(l[1],l[2].replace(/^['"]|['"]$/g,""))}return i.size>0?i:void 0}let s=_.dirname(r);if(s===r)return;r=s}}function ln(e,t){return JSON.stringify(e)===JSON.stringify(t)}function gn(e,t,r,n={}){let s={},i=new Set([...Object.keys(e),...Object.keys(t),...Object.keys(r)]);for(let o of i){if(zi.has(o)){let a=n.ours?t[o]:r[o];a!==void 0&&(s[o]=a);continue}if(n.ours){t[o]!==void 0&&(s[o]=t[o]);continue}if(n.theirs){r[o]!==void 0&&(s[o]=r[o]);continue}ln(t[o],e[o])?r[o]!==void 0&&(s[o]=r[o]):ln(r[o],e[o])?t[o]!==void 0&&(s[o]=t[o]):r[o]!==void 0?s[o]=r[o]:t[o]!==void 0&&(s[o]=t[o])}return s}function yt(e){if(!ze(_.join(e,"package.json")))throw new Error(`codegen 源无效(缺 package.json): ${e}`);let t;try{t=JSON.parse(dt(_.join(e,"package.json"),"utf8")).name}catch{throw new Error(`codegen 源 package.json 无法解析: ${e}`)}if(t&&Je(t)&&!ze(_.join(e,"src","index.ts")))throw new Error(`codegen 源缺少 src/index.ts:请使用包含源码载荷的契约包版本(${e})`)}function ht(e,t){let r=[...t];return new Map([...e].map(([n,s])=>[n,ft.test(n)?Ue(s,n,r):s]))}async function yn(e,t){let r=Bi.get(t.publicName);if(!r)return!1;let n=_.join(e,"packages",t.id),{mkdir:s,rm:i,writeFile:o}=await import("fs/promises");await i(n,{recursive:!0,force:!0}),await s(n,{recursive:!0});let a={name:t.localName,version:"0.0.0",private:!0,type:"module",exports:r.exports,scripts:{typecheck:"tsc --noEmit",test:"vitest run --passWithNoTests"},dependencies:{[t.publicName]:"catalog:"},devDependencies:{"@types/node":"catalog:",typescript:"catalog:",vitest:"catalog:"},tboxCodegenShim:!0};await o(_.join(n,"package.json"),JSON.stringify(a,null,2)+`
13
+ `,"utf8");for(let[c,l]of Object.entries(r.files)){let p=_.join(n,c);await s(_.dirname(p),{recursive:!0}),await o(p,l,"utf8")}return!0}async function ut(e,t){if(ze(e))for(let r of await Vi(e,{withFileTypes:!0})){if(Ui.has(r.name))continue;let n=_.join(e,r.name);r.isDirectory()?await ut(n,t):r.isFile()&&(ft.test(r.name)||r.name==="package.json")&&t.push(n)}}async function vt(e,t,r="local"){let n=[...t],s=ce(e),i=[];await ut(_.join(e,"packages"),i);let o=[];for(let a of i){let c=_.relative(e,a).split(_.sep).join("/"),l=await pt(a,"utf8"),p=l;if(_.basename(a)==="package.json")try{let u=JSON.parse(l);fn(u,n,r,s),p=JSON.stringify(u,null,2)+`
14
+ `}catch{continue}else p=Ue(l,c,n,r);p!==l&&(await Ji(a,p,"utf8"),o.push(c))}return o}async function hn(e){let t=Se(e),r=[],n=[];await ut(_.join(e,"apps"),n),await ut(_.join(e,"packages"),n);for(let s of t.values()){let i=_.join(e,"packages",s.id);if(!ze(i))r.push(`packages/${s.id}: codegen 包缺失`);else{let o=_.join(i,"package.json");try{JSON.parse(await pt(o,"utf8")).name!==s.localName&&r.push(`packages/${s.id}/package.json: name 应为 ${s.localName}`)}catch{r.push(`packages/${s.id}/package.json: 无法解析`)}}}for(let s of n){let i=_.relative(e,s).split(_.sep).join("/");if(_.basename(s)==="package.json"){try{let a=JSON.parse(await pt(s,"utf8"));for(let c of pn){let l=a[c];if(!l||typeof l!="object"||Array.isArray(l))continue;let p=l;for(let u of t.values())(u.publicName in p||u.localName in p&&p[u.localName]!=="workspace:*")&&r.push(`${i}: codegen 依赖未归一为 ${u.localName}: workspace:*`)}}catch{}continue}let o=await pt(s,"utf8");Ue(o,i,t.values())!==o&&r.push(`${i}: 源码仍引用 codegen 发布包名`)}return[...new Set(r)]}function Be(e){return JSON.parse(dt(_.join(e,"package.json"),"utf8"))}function wn(e){return e.replace(/^@tbox\.cn\/app-/,"").replace(/^@[^/]+\//,"")}async function bn(e,t,r,n=[]){let s=new re(t),i=H.join(e,"packages",r.id);await vn(i,{recursive:!0,force:!0}),await wt(i,{recursive:!0});let o=Be(t),a=typeof o.name=="string"?o.name:r.package,c=xe({id:r.id,package:a,version:r.version}),l=dn(new Map([...n].map(u=>[u.publicName,u])),c);for(let u of await s.listFiles()){if(u==="package.json")continue;let d=H.join(i,u);if(await wt(H.dirname(d),{recursive:!0}),!ft.test(u)){await Gi(H.join(t,u),d);continue}let f=await s.readFile(u);await ne(d,Ue(f,u,l.values()))}let p=Te(o,r,l.values(),ce(t));await ne(H.join(i,"package.json"),JSON.stringify(p,null,2)+`
15
+ `,"utf8")}var qi=e=>`import type { ServerContext, ServerModule } from '@tbox.cn/app-sdk/server';
468
16
 
469
17
  /**
470
- * ${id} \u672C\u5730\u6A21\u5757\uFF08local \u6A21\u5F0F\uFF0Capp \u81EA\u6709\u5305\uFF09\u6CE8\u518C\u5165\u53E3\u3002
471
- * \u5728\u6B64\u6CE8\u518C\u5DE5\u5177 / \u5361\u7247 meta / handler / \u8DEF\u7531 / Service\uFF08\u53EA\u6CE8\u518C\u4E0D\u89E3\u6790\uFF0CresolveService \u4EC5 handle/\u4E8B\u4EF6\u56DE\u8C03\u5185\uFF09\u3002
472
- * cards map \u7684 key \u7EA6\u5B9A = meta.cardType\uFF1B\u65E0\u5361\u7247\u6A21\u5757\u5199\u7A7A map\u3002
18
+ * ${e} 本地模块(local 模式,app 自有包)注册入口。
19
+ * 在此注册工具 / 卡片 meta / handler / 路由 / Service(只注册不解析,resolveService handle/事件回调内)。
20
+ * cards map key 约定 = meta.cardType;无卡片模块写空 map
473
21
  */
474
22
  const cards = {} as const;
475
23
 
476
24
  export const serverModule = {
477
25
  cards,
478
- // \u53C2\u6570\u547D\u540D _ctx\uFF1A\u9AA8\u67B6\u65B9\u6CD5\u4F53\u4E3A\u7A7A\uFF0C\u89C4\u907F tsconfig.base noUnusedParameters\uFF08TS6133\uFF09
26
+ // 参数命名 _ctx:骨架方法体为空,规避 tsconfig.base noUnusedParameters(TS6133)
479
27
  register(_ctx: ServerContext): void {
480
28
  // _ctx.tools.register(...);
481
29
  // _ctx.cards.registerMap(cards);
@@ -483,25 +31,22 @@ export const serverModule = {
483
31
  // _ctx.services.register('member', member);
484
32
  },
485
33
  } satisfies ServerModule;
486
- `;
487
- var LOCAL_SERVICE = `/**
488
- * Service \u5C42\uFF08\u4E1A\u52A1\u4E13\u5C5E\u5B9E\u73B0\uFF0C\u76F4\u63A5 import \u6216\u7ECF ctx.services \u6CE8\u518C\u4F9B\u8DE8\u6A21\u5757\u6D88\u8D39\uFF09\u3002
34
+ `,Xi=`/**
35
+ * Service 层(业务专属实现,直接 import 或经 ctx.services 注册供跨模块消费)。
489
36
  */
490
37
  export {};
491
- `;
492
- var LOCAL_HANDLER = `/**
493
- * Handler \u5B9A\u4E49\uFF08001 \xA73.2\uFF09\u3002
38
+ `,Yi=`/**
39
+ * Handler 定义(001 §3.2)。
494
40
  */
495
41
  import type { HandlerDefinition } from '@tbox.cn/app-sdk/server';
496
42
 
497
43
  export const exampleHandler: HandlerDefinition = {
498
44
  id: 'example',
499
- handle: async () => ({ cards: [], text: '\u672C\u5730\u6A21\u5757\u793A\u4F8B' }),
45
+ handle: async () => ({ cards: [], text: '本地模块示例' }),
500
46
  };
501
- `;
502
- var LOCAL_CLIENT_INDEX = `/**
503
- * \u5BA2\u6237\u7AEF\u5165\u53E3\uFF1A\u5BFC\u51FA clientModule\uFF08CLI sync \u88C5\u914D\u8BFB\u53D6\uFF1B\u4E0E serverModule \u9010\u884C\u5BF9\u79F0\uFF09\u3002
504
- * cards map \u7684 key \u7EA6\u5B9A = cardType\uFF1B\u65E0\u5361\u7247\u6A21\u5757\u5199\u7A7A map\u3002
47
+ `,Zi=`/**
48
+ * 客户端入口:导出 clientModule(CLI sync 装配读取;与 serverModule 逐行对称)。
49
+ * cards map key 约定 = cardType;无卡片模块写空 map。
505
50
  */
506
51
  import type { ClientContext, ClientModule } from '@tbox.cn/app-sdk/client';
507
52
 
@@ -509,20 +54,18 @@ const cards = {} as const;
509
54
 
510
55
  export const clientModule = {
511
56
  cards,
512
- // \u53C2\u6570\u547D\u540D _ctx\uFF1A\u9AA8\u67B6\u65B9\u6CD5\u4F53\u4E3A\u7A7A\uFF0C\u89C4\u907F tsconfig.base noUnusedParameters\uFF08TS6133\uFF09
57
+ // 参数命名 _ctx:骨架方法体为空,规避 tsconfig.base noUnusedParameters(TS6133)
513
58
  register(_ctx: ClientContext): void {
514
59
  // _ctx.cards.registerMap(cards);
515
60
  },
516
61
  } satisfies ClientModule;
517
- `;
518
- var LOCAL_ROOT_INDEX = `/**
519
- * \u672C\u5730\u6A21\u5757\u4E3B\u5165\u53E3\uFF08package.json exports "." \u6307\u5411\uFF09\u3002
520
- * \u6D88\u8D39\u65B9\u901A\u5E38\u8D70 ./server / ./client \u5B50\u8DEF\u5F84\uFF1B\u4E3B\u5165\u53E3\u805A\u5408\u5BFC\u51FA\u4FBF\u4E8E\u76F4\u63A5\u5F15\u7528\u3002
62
+ `,Qi=`/**
63
+ * 本地模块主入口(package.json exports "." 指向)。
64
+ * 消费方通常走 ./server / ./client 子路径;主入口聚合导出便于直接引用。
521
65
  */
522
66
  export * from './server';
523
67
  export * from './client';
524
- `;
525
- var LOCAL_TSCONFIG = `{
68
+ `,ea=`{
526
69
  "extends": "../../tsconfig.base.json",
527
70
  "compilerOptions": {
528
71
  "jsx": "react-jsx",
@@ -530,2849 +73,179 @@ var LOCAL_TSCONFIG = `{
530
73
  },
531
74
  "include": ["src"]
532
75
  }
533
- `;
534
- async function createLocalSkeleton(appDir, shortName) {
535
- const id = shortName.startsWith("module-") ? shortName : `module-${shortName}`;
536
- const dir = path5.join(appDir, "packages", id);
537
- await rm(dir, { recursive: true, force: true });
538
- await mkdir2(path5.join(dir, "src/server"), { recursive: true });
539
- await mkdir2(path5.join(dir, "src/client"), { recursive: true });
540
- const pkg = {
541
- name: `@app/${id}`,
542
- version: "0.1.0",
543
- private: true,
544
- type: "module",
545
- exports: {
546
- ".": { types: "./src/index.ts", import: "./src/index.ts" },
547
- "./server": { types: "./src/server/index.ts", import: "./src/server/index.ts" },
548
- "./client": { types: "./src/client/index.ts", import: "./src/client/index.ts" },
549
- // 009 模块文件化前置:loadModuleSkills 包名解析需 exports 放行元数据子路径
550
- "./package.json": "./package.json"
551
- },
552
- // 本地模块自带工具链:typecheck/test 可在生成应用内直接跑(pnpm verify @app/* 范围)
553
- scripts: { typecheck: "tsc --noEmit", test: "vitest run --passWithNoTests" },
554
- dependencies: {
555
- "@tbox.cn/app-sdk": "catalog:",
556
- "@tbox.cn/app-contracts": "catalog:"
557
- },
558
- peerDependencies: {
559
- zod: "catalog:",
560
- react: "catalog:",
561
- "@mastra/core": "catalog:"
562
- },
563
- devDependencies: {
564
- typescript: "catalog:",
565
- vitest: "catalog:",
566
- "@types/react": "catalog:",
567
- "@types/node": "catalog:"
568
- }
569
- };
570
- const component = {
571
- schemaVersion: 1,
572
- name: id,
573
- version: "0.1.0",
574
- kind: "business",
575
- risk: { level: "low", writeBoundary: "source" },
576
- distribution: { defaultMode: "local" },
577
- contributes: {
578
- handlers: [],
579
- tools: [],
580
- cards: [],
581
- routes: [],
582
- pages: [],
583
- tabs: []
584
- },
585
- dependencies: { modules: [] },
586
- env: []
587
- };
588
- await writeFile3(path5.join(dir, "package.json"), JSON.stringify(pkg, null, 2) + "\n", "utf8");
589
- await writeFile3(
590
- path5.join(dir, "tbox.component.json"),
591
- JSON.stringify(component, null, 2) + "\n",
592
- "utf8"
593
- );
594
- await writeFile3(path5.join(dir, "tsconfig.json"), LOCAL_TSCONFIG, "utf8");
595
- await writeFile3(path5.join(dir, "src/server/index.ts"), LOCAL_SERVER_ENTRY(id), "utf8");
596
- await writeFile3(path5.join(dir, "src/server/service.ts"), LOCAL_SERVICE, "utf8");
597
- await writeFile3(path5.join(dir, "src/server/handler.ts"), LOCAL_HANDLER, "utf8");
598
- await writeFile3(path5.join(dir, "src/client/index.ts"), LOCAL_CLIENT_INDEX, "utf8");
599
- await writeFile3(path5.join(dir, "src/index.ts"), LOCAL_ROOT_INDEX, "utf8");
600
- return id;
601
- }
602
-
603
- // src/apps-deps.ts
604
- import { existsSync as existsSync4 } from "fs";
605
- import { readFileSync as readFileSync3 } from "fs";
606
- import { writeFile as writeFile4 } from "fs/promises";
607
- import path6 from "path";
608
- var APP_TARGETS = ["apps/server/package.json", "apps/client/package.json"];
609
- async function updateAppsDependency(appDir, pkgName, action) {
610
- let changed = false;
611
- for (const rel of APP_TARGETS) {
612
- const file = path6.join(appDir, rel);
613
- if (!existsSync4(file)) continue;
614
- const pkg = JSON.parse(readFileSync3(file, "utf8"));
615
- const deps = pkg.dependencies ??= {};
616
- if (action === "add" && !deps[pkgName]) {
617
- deps[pkgName] = "workspace:*";
618
- changed = true;
619
- } else if (action === "remove" && deps[pkgName]) {
620
- delete deps[pkgName];
621
- changed = true;
622
- }
623
- if (changed) await writeFile4(file, JSON.stringify(pkg, null, 2) + "\n", "utf8");
624
- }
625
- return changed;
626
- }
627
- async function removePackageDir(appDir, id) {
628
- const dir = path6.join(appDir, "packages", id);
629
- if (!existsSync4(dir)) return false;
630
- const { rm: rm2 } = await import("fs/promises");
631
- await rm2(dir, { recursive: true, force: true });
632
- return true;
633
- }
634
-
635
- // src/sync-all.ts
636
- import { existsSync as existsSync6, readFileSync as readFileSync6 } from "fs";
637
- import { readFile as readFile3, writeFile as writeFile6 } from "fs/promises";
638
- import path9 from "path";
639
-
640
- // src/assembly.ts
641
- var ASSEMBLY_FILES = [
642
- { path: "apps/server/src/modules.ts", kind: "server-modules" },
643
- { path: "apps/client/src/modules.ts", kind: "client-modules" }
644
- ];
645
- function camelCaseId(id) {
646
- return id.split(/[^A-Za-z0-9]+/).filter(Boolean).map(
647
- (part, i) => i === 0 ? part.toLowerCase() : part.charAt(0).toUpperCase() + part.slice(1).toLowerCase()
648
- ).join("");
649
- }
650
- function importAlias(kind, id) {
651
- return camelCaseId(id);
652
- }
653
- function contributesTo(kind, m) {
654
- return KINDS[kind].contributes(m);
655
- }
656
- function idFromPkg(pkg) {
657
- return pkg.replace(/^@[^/]+\//, "").replace(/\/(server|client)$/, "").replace(/\/$/, "");
658
- }
659
- var KINDS = {
660
- "server-modules": {
661
- mapMarker: "register",
662
- importExport: "serverModule",
663
- importSuffix: "server",
664
- entryPattern: /^\s*([A-Za-z_$][\w$]*),\s*$/,
665
- header: "const modules = [",
666
- footer: "];",
667
- contributes: (m) => m.hasServerEntry
668
- },
669
- "client-modules": {
670
- mapMarker: "register",
671
- importExport: "clientModule",
672
- importSuffix: "client",
673
- entryPattern: /^\s*([A-Za-z_$][\w$]*),\s*$/,
674
- header: "const modules = [",
675
- footer: "];",
676
- // 客户端装配仅收录"有卡片 + 有 clientModule 入口"的模块;老形态(无 clientModule)跳过并由
677
- // assembly-drift 告警提示升级(D8:新 app + 老模块 → 探测缺失跳过)
678
- contributes: (m) => m.hasClientEntry && m.hasCards
679
- }
680
- };
681
- var IMPORT_LINE_PATTERN = /^import\s*\{\s*([\w$]+)\s+as\s+([\w$]+)\s*\}\s*from\s*['"]([^'"]+)['"];\s*$/;
682
- function extractBlock(content, marker) {
683
- const begin = `// @tbox:${marker}-begin`;
684
- const end = `// @tbox:${marker}-end`;
685
- const bIdx = content.indexOf(begin);
686
- if (bIdx === -1) return null;
687
- const eIdx = content.indexOf(end, bIdx);
688
- if (eIdx === -1) return null;
689
- const start = bIdx + begin.length;
690
- return { block: content.slice(start, eIdx), start, end: eIdx };
691
- }
692
- function replaceBlock(content, range, newBlock) {
693
- return content.slice(0, range.start) + newBlock + content.slice(range.end);
694
- }
695
- function parseAssemblyFile(content, kind) {
696
- const spec = KINDS[kind];
697
- const moduleIds = [];
698
- const nonStandardLines = [];
699
- const broken = [];
700
- const imports = extractBlock(content, "imports");
701
- if (imports) {
702
- for (const line of imports.block.split(/\r?\n/)) {
703
- const m = line.match(IMPORT_LINE_PATTERN);
704
- if (m) {
705
- moduleIds.push(idFromPkg(m[3]));
706
- } else if (line.trim()) {
707
- nonStandardLines.push(`imports: ${line.trim()}`);
708
- }
709
- }
710
- } else {
711
- broken.push("imports");
712
- }
713
- const map = extractBlock(content, spec.mapMarker);
714
- if (map) {
715
- const lines = map.block.split(/\r?\n/);
716
- const headIdx = lines.findIndex((l) => l.trim().startsWith(spec.header));
717
- const tailIdx = [...lines].reverse().findIndex((l) => l.trim() === spec.footer);
718
- const lastIdx = tailIdx === -1 ? -1 : lines.length - 1 - tailIdx;
719
- if (headIdx === -1 || lastIdx <= headIdx) {
720
- broken.push(spec.mapMarker);
721
- } else {
722
- for (const line of lines.slice(headIdx + 1, lastIdx)) {
723
- if (!line.match(spec.entryPattern) && line.trim()) {
724
- nonStandardLines.push(`${spec.mapMarker}: ${line.trim()}`);
725
- }
726
- }
727
- }
728
- } else {
729
- broken.push(spec.mapMarker);
730
- }
731
- const aliasToId = /* @__PURE__ */ new Map();
732
- if (imports) {
733
- for (const line of imports.block.split(/\r?\n/)) {
734
- const m = line.match(IMPORT_LINE_PATTERN);
735
- if (m) aliasToId.set(m[2], idFromPkg(m[3]));
736
- }
737
- }
738
- const mapIds = [];
739
- if (map) {
740
- const lines = map.block.split(/\r?\n/);
741
- const headIdx = lines.findIndex((l) => l.trim().startsWith(spec.header));
742
- const tailIdx = [...lines].reverse().findIndex((l) => l.trim() === spec.footer);
743
- const lastIdx = tailIdx === -1 ? -1 : lines.length - 1 - tailIdx;
744
- if (headIdx !== -1 && lastIdx > headIdx) {
745
- for (const line of lines.slice(headIdx + 1, lastIdx)) {
746
- const m = line.match(spec.entryPattern);
747
- if (m) {
748
- const id = aliasToId.get(m[1]);
749
- if (id) mapIds.push(id);
750
- }
751
- }
752
- }
753
- }
754
- return {
755
- moduleIds: [.../* @__PURE__ */ new Set([...moduleIds, ...mapIds])],
756
- nonStandardLines,
757
- broken
758
- };
759
- }
760
- function syncAssemblyFile(content, kind, declared, force = false) {
761
- const spec = KINDS[kind];
762
- const warnings = [];
763
- const desired = declared.filter(spec.contributes);
764
- const desiredByAlias = new Map(desired.map((m) => [importAlias(kind, m.id), m]));
765
- let next = content;
766
- let changed = false;
767
- const importsRange = extractBlock(next, "imports");
768
- if (!importsRange) {
769
- warnings.push(`[${kind}] imports \u951A\u533A\u7F3A\u5931/\u7834\u574F\uFF0C\u8DF3\u8FC7`);
770
- return { content: next, changed, warnings, moduleIds: [] };
771
- }
772
- const importLines = importsRange.block.split(/\r?\n/);
773
- const keptImports = [];
774
- for (const line of importLines) {
775
- const m = line.match(IMPORT_LINE_PATTERN);
776
- if (!m) {
777
- if (line.trim() === "") continue;
778
- if (!force) warnings.push(`[${kind}] imports \u951A\u533A\u975E\u6807\u884C\u4FDD\u7559: ${line.trim()}`);
779
- if (!force) keptImports.push(line);
780
- continue;
781
- }
782
- const localName = m[2];
783
- const pkg = m[3];
784
- const target = desiredByAlias.get(localName);
785
- if (target && idFromPkg(target.pkg) === idFromPkg(pkg)) {
786
- keptImports.push(line);
787
- } else if (target) {
788
- keptImports.push(specImportLine(kind, localName, target.pkg));
789
- changed = true;
790
- } else {
791
- changed = true;
792
- }
793
- }
794
- for (const m of desired) {
795
- const localName = importAlias(kind, m.id);
796
- if (!keptImports.some((l) => l.includes(`as ${localName} } from`))) {
797
- keptImports.push(specImportLine(kind, localName, m.pkg));
798
- changed = true;
799
- }
800
- }
801
- const newImportsBlock = normalizeBlock(keptImports.join("\n"));
802
- if (newImportsBlock !== importsRange.block) {
803
- next = replaceBlock(next, importsRange, newImportsBlock);
804
- changed = true;
805
- }
806
- const mapRange = extractBlock(next, spec.mapMarker);
807
- if (!mapRange) {
808
- warnings.push(`[${kind}] ${spec.mapMarker} \u951A\u533A\u7F3A\u5931/\u7834\u574F\uFF0C\u8DF3\u8FC7`);
809
- return { content: next, changed, warnings, moduleIds: [] };
810
- }
811
- const mapLines = mapRange.block.split(/\r?\n/);
812
- const headIdx = mapLines.findIndex((l) => l.trim().startsWith(spec.header));
813
- const reverseTailIdx = [...mapLines].reverse().findIndex((l) => l.trim() === spec.footer);
814
- const tailIdx = reverseTailIdx === -1 ? -1 : mapLines.length - 1 - reverseTailIdx;
815
- if (headIdx === -1 || tailIdx <= headIdx) {
816
- if (force) {
817
- const forcedBlock = normalizeBlock([spec.header, ...desired.map((m) => specEntryLine(kind, importAlias(kind, m.id))), spec.footer].join("\n"));
818
- if (forcedBlock !== mapRange.block) {
819
- next = replaceBlock(next, mapRange, forcedBlock);
820
- changed = true;
821
- }
822
- return { content: next, changed, warnings, moduleIds: desired.map((m) => m.id) };
823
- }
824
- warnings.push(`[${kind}] ${spec.mapMarker} \u951A\u533A\u7ED3\u6784\u4E0D\u7B26\u5408\u9884\u671F\uFF0C\u964D\u7EA7\u4E3A\u544A\u8B66\u4E0D\u8986\u76D6`);
825
- return { content: next, changed, warnings, moduleIds: [] };
826
- }
827
- const preservedHead = mapLines.slice(0, headIdx);
828
- const preservedTail = mapLines.slice(tailIdx + 1);
829
- const body = mapLines.slice(headIdx + 1, tailIdx);
830
- const keptEntries = [];
831
- for (const line of body) {
832
- const m = line.match(spec.entryPattern);
833
- if (!m) {
834
- if (line.trim() === "") continue;
835
- if (!force) warnings.push(`[${kind}] ${spec.mapMarker} \u951A\u533A\u975E\u6807\u884C\u4FDD\u7559: ${line.trim()}`);
836
- if (!force) keptEntries.push(line);
837
- continue;
838
- }
839
- const localName = m[1];
840
- if (desiredByAlias.has(localName)) {
841
- keptEntries.push(specEntryLine(kind, localName));
842
- } else {
843
- changed = true;
844
- }
845
- }
846
- for (const m of desired) {
847
- const localName = importAlias(kind, m.id);
848
- if (!keptEntries.some((l) => l.includes(localName))) {
849
- keptEntries.push(specEntryLine(kind, localName));
850
- changed = true;
851
- }
852
- }
853
- const newMapBlock = normalizeBlock(
854
- [...preservedHead, spec.header, ...keptEntries, spec.footer, ...preservedTail].join("\n")
855
- );
856
- if (newMapBlock !== mapRange.block) {
857
- next = replaceBlock(next, mapRange, newMapBlock);
858
- changed = true;
859
- }
860
- return { content: next, changed, warnings, moduleIds: desired.map((m) => m.id) };
861
- }
862
- function normalizeBlock(block) {
863
- const lines = block.split(/\r?\n/);
864
- while (lines.length > 0 && lines[0].trim() === "") lines.shift();
865
- while (lines.length > 0 && lines[lines.length - 1].trim() === "") lines.pop();
866
- return "\n" + lines.join("\n") + "\n";
867
- }
868
- function specImportLine(kind, localName, pkg) {
869
- const spec = KINDS[kind];
870
- return `import { ${spec.importExport} as ${localName} } from '${pkg}/${spec.importSuffix}';`;
871
- }
872
- function specEntryLine(kind, localName) {
873
- return ` ${localName},`;
874
- }
875
-
876
- // src/declared.ts
877
- import { existsSync as existsSync5 } from "fs";
878
- import { readFileSync as readFileSync4 } from "fs";
879
- import { readdir as readdir2 } from "fs/promises";
880
- import path7 from "path";
881
-
882
- // src/component-schema.ts
883
- import { z } from "zod";
884
- var cardContribSchema = z.object({
885
- cardType: z.string(),
886
- schemaVersion: z.number().optional()
887
- });
888
- var pageContribSchema = z.object({
889
- route: z.string(),
890
- component: z.string()
891
- });
892
- var tabContribSchema = z.object({
893
- route: z.string(),
894
- label: z.string(),
895
- icon: z.string().optional(),
896
- order: z.number().optional()
897
- });
898
- var moduleDependencySchema = z.object({
899
- id: z.string(),
900
- range: z.string().optional(),
901
- required: z.boolean().default(true)
902
- });
903
- var componentDescriptorSchema = z.object({
904
- schemaVersion: z.number().default(1),
905
- name: z.string(),
906
- version: z.string().default("0.0.0"),
907
- kind: z.enum(["platform", "business", "third-party"]).default("business"),
908
- risk: z.object({
909
- level: z.enum(["high", "medium", "low"]).default("low"),
910
- writeBoundary: z.enum(["sdk-enforced", "source"]).default("source")
911
- }).default({}),
912
- distribution: z.object({
913
- defaultMode: z.enum(["hybrid", "sdk", "codegen", "local"]).default("codegen")
914
- }).default({}),
915
- contributes: z.object({
916
- handlers: z.array(z.string()).default([]),
917
- tools: z.array(z.string()).default([]),
918
- cards: z.array(cardContribSchema).default([]),
919
- routes: z.array(z.string()).default([]),
920
- pages: z.array(pageContribSchema).default([]),
921
- tabs: z.array(tabContribSchema).default([])
922
- }).default({}),
923
- dependencies: z.object({
924
- modules: z.array(moduleDependencySchema).default([])
925
- }).default({}),
926
- env: z.array(z.string()).default([])
927
- }).strict();
928
- function parseComponentDescriptor(raw) {
929
- const result = componentDescriptorSchema.safeParse(raw);
930
- if (result.success) return { ok: true, value: result.data };
931
- return {
932
- ok: false,
933
- errors: result.error.issues.map(
934
- (issue) => `${issue.path.join(".") || "(root)"}: ${issue.message}`
935
- )
936
- };
937
- }
938
-
939
- // src/declared.ts
940
- function readComponentFile(dir) {
941
- const file = path7.join(dir, "tbox.component.json");
942
- if (!existsSync5(file)) return null;
943
- try {
944
- const parsed = parseComponentDescriptor(JSON.parse(readFileSync4(file, "utf8")));
945
- return parsed.ok ? parsed.value : null;
946
- } catch {
947
- return null;
948
- }
949
- }
950
- function probeModuleObjectExport(dir, sub, symbol) {
951
- const file = path7.join(dir, "src", sub, "index.ts");
952
- if (!existsSync5(file)) return false;
953
- try {
954
- const src = readFileSync4(file, "utf8");
955
- return new RegExp(`export\\s+const\\s+${symbol}\\b`).test(src);
956
- } catch {
957
- return false;
958
- }
959
- }
960
- function readServerSource(dir) {
961
- const file = path7.join(dir, "src", "server", "index.ts");
962
- if (!existsSync5(file)) return "";
963
- try {
964
- return readFileSync4(file, "utf8");
965
- } catch {
966
- return "";
967
- }
968
- }
969
- function extractComponentId(source) {
970
- const match = source.match(/declaration\s*:\s*\{[\s\S]*?componentId\s*:\s*['"]([^'"]+)['"]/);
971
- return match?.[1];
972
- }
973
- async function listLocalModules(appDir) {
974
- const packagesDir = path7.join(appDir, "packages");
975
- const result = [];
976
- if (!existsSync5(packagesDir)) return result;
977
- const entries = await readdir2(packagesDir, { withFileTypes: true });
978
- for (const entry of entries) {
979
- if (!entry.isDirectory()) continue;
980
- const dir = path7.join(packagesDir, entry.name);
981
- if (!existsSync5(path7.join(dir, "package.json"))) continue;
982
- const descriptor = readComponentFile(dir);
983
- if (descriptor) {
984
- result.push({ id: descriptor.name ?? entry.name, dir, descriptor });
985
- }
986
- }
987
- return result.sort((a, b) => a.id.localeCompare(b.id));
988
- }
989
- function readDescriptorFromNodeModules(appDir, pkg) {
990
- try {
991
- const p = path7.join(appDir, "node_modules", ...pkg.split("/"), "tbox.component.json");
992
- if (!existsSync5(p)) return null;
993
- const parsed = parseComponentDescriptor(JSON.parse(readFileSync4(p, "utf8")));
994
- return parsed.ok ? parsed.value : null;
995
- } catch {
996
- return null;
997
- }
998
- }
999
- async function collectDeclared(appDir) {
1000
- const manifest = readAppManifest(appDir);
1001
- const declared = [];
1002
- const seen = /* @__PURE__ */ new Set();
1003
- for (const entry of manifest?.npmModules ?? []) {
1004
- seen.add(entry.id);
1005
- const local = path7.join(appDir, "packages", entry.id);
1006
- const descriptor = readComponentFile(local) ?? readDescriptorFromNodeModules(appDir, entry.package);
1007
- const probeDir = existsSync5(path7.join(local, "src")) ? local : path7.join(appDir, "node_modules", ...entry.package.split("/"));
1008
- const c = descriptor?.contributes;
1009
- const hasServerEntry = descriptor !== null && probeModuleObjectExport(probeDir, "server", "serverModule");
1010
- declared.push({
1011
- id: entry.id,
1012
- pkg: entry.mode === "sdk" ? entry.package : `@app/${entry.id}`,
1013
- mode: entry.mode,
1014
- hasServerEntry,
1015
- hasClientEntry: descriptor !== null && probeModuleObjectExport(probeDir, "client", "clientModule"),
1016
- hasCards: (c?.cards?.length ?? 0) > 0,
1017
- // 011 评审 M3:declaration.componentId(deployment 对账真源;老模块无 declaration → undefined)
1018
- ...hasServerEntry ? { componentId: extractComponentId(readServerSource(probeDir)) } : {}
1019
- });
1020
- }
1021
- for (const mod of await listLocalModules(appDir)) {
1022
- if (seen.has(mod.id)) continue;
1023
- const c = mod.descriptor.contributes;
1024
- const hasServerEntry = probeModuleObjectExport(mod.dir, "server", "serverModule");
1025
- declared.push({
1026
- id: mod.id,
1027
- pkg: `@app/${mod.id}`,
1028
- mode: "local",
1029
- hasServerEntry,
1030
- hasClientEntry: probeModuleObjectExport(mod.dir, "client", "clientModule"),
1031
- hasCards: (c.cards?.length ?? 0) > 0,
1032
- // 011 评审 M3:declaration.componentId(同 npmModules 路径)
1033
- ...hasServerEntry ? { componentId: extractComponentId(readServerSource(mod.dir)) } : {}
1034
- });
1035
- }
1036
- return declared;
1037
- }
1038
-
1039
- // src/deps-merge.ts
1040
- import { writeFile as writeFile5 } from "fs/promises";
1041
- import path8 from "path";
1042
- import { readFileSync as readFileSync5 } from "fs";
1043
- function readJsonOrNull(p) {
1044
- try {
1045
- return JSON.parse(readFileSyncSafe(p) ?? "{}");
1046
- } catch {
1047
- return null;
1048
- }
1049
- }
1050
- async function mergeModuleDeps(appDir, targetPackageJson, moduleDeps) {
1051
- const file = path8.join(appDir, targetPackageJson);
1052
- const pkg = readJsonOrNull(file) ?? {};
1053
- const deps = pkg.dependencies ?? {};
1054
- const merged = {};
1055
- const conflicts = [];
1056
- for (const [name, spec] of Object.entries(moduleDeps)) {
1057
- if (isPlatform(name)) continue;
1058
- if (spec === "workspace:*" || spec.startsWith("catalog:")) continue;
1059
- const existing = deps[name];
1060
- if (!existing) {
1061
- deps[name] = spec;
1062
- merged[name] = spec;
1063
- continue;
1064
- }
1065
- if (existing === spec) continue;
1066
- const inter = intersectRanges(existing, spec);
1067
- if (inter) {
1068
- if (inter !== existing) {
1069
- deps[name] = inter;
1070
- merged[name] = inter;
1071
- }
1072
- } else {
1073
- conflicts.push(`${name}: \u5DF2\u6709 ${existing}\uFF0C\u6A21\u5757\u8981\u6C42 ${spec}`);
1074
- }
1075
- }
1076
- pkg.dependencies = deps;
1077
- await writeFile5(file, JSON.stringify(pkg, null, 2) + "\n", "utf8");
1078
- return { merged, conflicts };
1079
- }
1080
- function intersectRanges(a, b) {
1081
- const clean = (s) => s.replace(/^\^/, "");
1082
- const pa = parseVer(clean(a));
1083
- const pb = parseVer(clean(b));
1084
- if (!pa || !pb) return null;
1085
- if (pa.major !== pb.major) return null;
1086
- if (pa.minor > pb.minor) return a;
1087
- if (pb.minor > pa.minor) return b;
1088
- return pa.patch > pb.patch ? a : b;
1089
- }
1090
- function parseVer(v) {
1091
- const m = v.match(/^(\d+)\.(\d+)(?:\.(\d+))?/);
1092
- if (!m) return null;
1093
- return { major: Number(m[1]), minor: Number(m[2]), patch: Number(m[3] ?? 0) };
1094
- }
1095
- function readFileSyncSafe(p) {
1096
- try {
1097
- return readFileSync5(p, "utf8");
1098
- } catch {
1099
- return void 0;
1100
- }
1101
- }
1102
-
1103
- // src/sync-all.ts
1104
- async function syncAll(appDir, opts = {}) {
1105
- const declared = await collectDeclared(appDir);
1106
- const warnings = [];
1107
- const changedFiles = [];
1108
- const depConflicts = [];
1109
- for (const file of ASSEMBLY_FILES) {
1110
- const full = path9.join(appDir, file.path);
1111
- if (!existsSync6(full)) {
1112
- warnings.push(`\u7F3A\u5C11\u88C5\u914D\u6587\u4EF6 ${file.path}`);
1113
- continue;
1114
- }
1115
- const content = await readFile3(full, "utf8");
1116
- const result = syncAssemblyFile(content, file.kind, declared, opts.force ?? false);
1117
- warnings.push(...result.warnings.map((w) => `${file.path}: ${w}`));
1118
- if (result.changed) {
1119
- await writeFile6(full, result.content, "utf8");
1120
- changedFiles.push(file.path);
1121
- }
1122
- }
1123
- const targets = ["apps/server/package.json", "apps/client/package.json"];
1124
- for (const mod of declared) {
1125
- const pkgFile = path9.join(appDir, "packages", mod.id, "package.json");
1126
- if (!existsSync6(pkgFile)) continue;
1127
- let deps = {};
1128
- try {
1129
- deps = JSON.parse(readFileSync6(pkgFile, "utf8")).dependencies ?? {};
1130
- } catch {
1131
- continue;
1132
- }
1133
- for (const target of targets) {
1134
- if (!existsSync6(path9.join(appDir, target))) continue;
1135
- const result = await mergeModuleDeps(appDir, target, deps);
1136
- depConflicts.push(...result.conflicts.map((c) => `${mod.id} \u2192 ${target}: ${c}`));
1137
- }
1138
- }
1139
- return { declared, changedFiles, warnings, depConflicts };
1140
- }
1141
-
1142
- // src/diff.ts
1143
- import { existsSync as existsSync7 } from "fs";
1144
- import { readFile as readFile4 } from "fs/promises";
1145
- import path10 from "path";
1146
- async function collectActual(appDir) {
1147
- const result = {};
1148
- for (const file of ASSEMBLY_FILES) {
1149
- const full = path10.join(appDir, file.path);
1150
- if (!existsSync7(full)) {
1151
- result[file.kind] = { moduleIds: [], nonStandardLines: [], broken: ["missing-file"] };
1152
- continue;
1153
- }
1154
- const content = await readFile4(full, "utf8");
1155
- const parsed = parseAssemblyFile(content, file.kind);
1156
- result[file.kind] = {
1157
- moduleIds: parsed.moduleIds,
1158
- nonStandardLines: parsed.nonStandardLines,
1159
- broken: parsed.broken
1160
- };
1161
- }
1162
- return result;
1163
- }
1164
-
1165
- // src/doctor/rules/workspace-dag.ts
1166
- import { existsSync as existsSync8 } from "fs";
1167
- import { readdir as readdir4 } from "fs/promises";
1168
- import path12 from "path";
1169
-
1170
- // src/doctor/util.ts
1171
- import { readFileSync as readFileSync7 } from "fs";
1172
- import { readFile as readFile5, readdir as readdir3 } from "fs/promises";
1173
- import path11 from "path";
1174
- async function walkFiles(dir, base, out) {
1175
- const entries = await readdir3(dir, { withFileTypes: true });
1176
- for (const entry of entries) {
1177
- if (entry.name === "node_modules" || entry.name === "dist" || entry.name === ".git") continue;
1178
- const full = path11.join(dir, entry.name);
1179
- if (entry.isDirectory()) await walkFiles(full, base, out);
1180
- else if (entry.isFile() && /\.(ts|tsx|mjs|js)$/.test(entry.name)) {
1181
- out.push(path11.relative(base, full).split(path11.sep).join("/"));
1182
- }
1183
- }
1184
- }
1185
- async function readTextOrEmpty(p) {
1186
- try {
1187
- return await readFile5(p, "utf8");
1188
- } catch {
1189
- return "";
1190
- }
1191
- }
1192
- function readJsonOrNull2(p) {
1193
- try {
1194
- return JSON.parse(readFileSync7(p, "utf8"));
1195
- } catch {
1196
- return null;
1197
- }
1198
- }
1199
- function readPkgJson(dir) {
1200
- return readJsonOrNull2(path11.join(dir, "package.json"));
1201
- }
1202
- function intersectRanges2(a, b) {
1203
- const clean = (s) => s.replace(/^\^/, "");
1204
- const pa = parseVer2(clean(a));
1205
- const pb = parseVer2(clean(b));
1206
- if (!pa || !pb) return null;
1207
- if (pa.major !== pb.major) return null;
1208
- if (pa.minor > pb.minor) return a;
1209
- if (pb.minor > pa.minor) return b;
1210
- return pa.patch > pb.patch ? a : b;
1211
- }
1212
- function parseVer2(v) {
1213
- const m = v.match(/^(\d+)\.(\d+)(?:\.(\d+))?/);
1214
- if (!m) return null;
1215
- return { major: Number(m[1]), minor: Number(m[2]), patch: Number(m[3] ?? 0) };
1216
- }
1217
-
1218
- // src/doctor/rules/workspace-dag.ts
1219
- async function workspaceDagRule(ctx) {
1220
- const issues = [];
1221
- const packagesDir = path12.join(ctx.appDir, "packages");
1222
- if (!existsSync8(packagesDir)) return issues;
1223
- for (const entry of await readdir4(packagesDir, { withFileTypes: true })) {
1224
- if (!entry.isDirectory()) continue;
1225
- const dir = path12.join(packagesDir, entry.name);
1226
- if (!existsSync8(path12.join(dir, "tbox.component.json"))) continue;
1227
- const pkg = readPkgJson(dir);
1228
- const allDeps = { ...pkg?.dependencies ?? {}, ...pkg?.peerDependencies ?? {} };
1229
- for (const name of Object.keys(allDeps)) {
1230
- if (/^@app\/(module|scenario)-/.test(name) || /^@tbox\.cn\/app-(module|scenario)-/.test(name)) {
1231
- issues.push({
1232
- rule: "workspace-dag",
1233
- level: "error",
1234
- message: `${entry.name} \u4F9D\u8D56\u4E1A\u52A1\u6A21\u5757 ${name}\uFF08module \u4E0D\u5F97\u4F9D\u8D56 module/scenario\uFF09`
1235
- });
1236
- }
1237
- }
1238
- }
1239
- return issues;
1240
- }
1241
-
1242
- // src/doctor/rules/register-pure.ts
1243
- import { existsSync as existsSync9 } from "fs";
1244
- import { readdir as readdir5 } from "fs/promises";
1245
- import path13 from "path";
1246
-
1247
- // src/doctor/ast.ts
1248
- import ts from "typescript";
1249
- function parseSource(src) {
1250
- return ts.createSourceFile("entry.ts", src, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
1251
- }
1252
- function unwrapAs(expr) {
1253
- if (!expr) return expr;
1254
- if (ts.isAsExpression(expr) || ts.isSatisfiesExpression(expr)) return expr.expression;
1255
- return expr;
1256
- }
1257
- function findFunctionBody(src, name) {
1258
- const sf = parseSource(src);
1259
- let result = null;
1260
- function visit(node) {
1261
- if (result) return;
1262
- if (ts.isFunctionDeclaration(node) && node.name?.text === name && node.body) {
1263
- result = node.body;
1264
- return;
1265
- }
1266
- if (ts.isVariableStatement(node) && node.declarationList.declarations.some(
1267
- (d) => ts.isIdentifier(d.name) && d.name.text === name
1268
- )) {
1269
- const decl = node.declarationList.declarations.find(
1270
- (d) => ts.isIdentifier(d.name) && d.name.text === name
1271
- );
1272
- const init = unwrapAs(decl.initializer);
1273
- if (init && ts.isArrowFunction(init) && init.body && ts.isBlock(init.body)) {
1274
- result = init.body;
1275
- return;
1276
- }
1277
- const methodBody = objectMethodBody(init, "register");
1278
- if (methodBody) {
1279
- result = methodBody;
1280
- return;
1281
- }
1282
- }
1283
- ts.forEachChild(node, visit);
1284
- }
1285
- visit(sf);
1286
- return result;
1287
- }
1288
- function objectMethodBody(init, methodName) {
1289
- if (!init || !ts.isObjectLiteralExpression(init)) return null;
1290
- for (const prop of init.properties) {
1291
- if (ts.isMethodDeclaration(prop) && prop.name.getText() === methodName && prop.body) {
1292
- return prop.body;
1293
- }
1294
- if (ts.isPropertyAssignment(prop) && prop.name.getText() === methodName && prop.initializer) {
1295
- const fn = prop.initializer;
1296
- if (ts.isArrowFunction(fn) && fn.body && ts.isBlock(fn.body)) return fn.body;
1297
- if (ts.isFunctionExpression(fn) && fn.body) return fn.body;
1298
- }
76
+ `;async function kn(e,t){let r=t.startsWith("module-")?t:`module-${t}`,n=H.join(e,"packages",r);await vn(n,{recursive:!0,force:!0}),await wt(H.join(n,"src/server"),{recursive:!0}),await wt(H.join(n,"src/client"),{recursive:!0});let s={name:`@app/${r}`,version:"0.1.0",private:!0,type:"module",exports:{".":{types:"./src/index.ts",import:"./src/index.ts"},"./server":{types:"./src/server/index.ts",import:"./src/server/index.ts"},"./client":{types:"./src/client/index.ts",import:"./src/client/index.ts"},"./package.json":"./package.json"},scripts:{typecheck:"tsc --noEmit",test:"vitest run --passWithNoTests"},dependencies:{"@tbox.cn/app-sdk":"catalog:","@tbox.cn/app-contracts":"catalog:"},peerDependencies:{zod:"catalog:",react:"catalog:","@mastra/core":"catalog:"},devDependencies:{typescript:"catalog:",vitest:"catalog:","@types/react":"catalog:","@types/node":"catalog:"}},i={schemaVersion:1,name:r,version:"0.1.0",kind:"business",risk:{level:"low",writeBoundary:"source"},distribution:{defaultMode:"local"},contributes:{handlers:[],tools:[],cards:[],routes:[],pages:[],tabs:[],resources:[]},dependencies:{modules:[]},env:[]};return await ne(H.join(n,"package.json"),JSON.stringify(s,null,2)+`
77
+ `,"utf8"),await ne(H.join(n,"tbox.module.json"),JSON.stringify(i,null,2)+`
78
+ `,"utf8"),await ne(H.join(n,"tsconfig.json"),ea,"utf8"),await ne(H.join(n,"src/server/index.ts"),qi(r),"utf8"),await ne(H.join(n,"src/server/service.ts"),Xi,"utf8"),await ne(H.join(n,"src/server/handler.ts"),Yi,"utf8"),await ne(H.join(n,"src/client/index.ts"),Zi,"utf8"),await ne(H.join(n,"src/index.ts"),Qi,"utf8"),r}import{existsSync as xn}from"fs";import{readFileSync as ta}from"fs";import{writeFile as ra}from"fs/promises";import Sn from"path";var na=["apps/server/package.json","apps/client/package.json"];async function He(e,t,r){let n=!1;for(let s of na){let i=Sn.join(e,s);if(!xn(i))continue;let o=JSON.parse(ta(i,"utf8")),a=o.dependencies??={};r==="add"&&!a[t]?(a[t]="workspace:*",n=!0):r==="remove"&&a[t]&&(delete a[t],n=!0),n&&await ra(i,JSON.stringify(o,null,2)+`
79
+ `,"utf8")}return n}async function $n(e,t){let r=Sn.join(e,"packages",t);if(!xn(r))return!1;let{rm:n}=await import("fs/promises");return await n(r,{recursive:!0,force:!0}),!0}import{existsSync as sr,readFileSync as pa}from"fs";import{readFile as ua,writeFile as da}from"fs/promises";import or from"path";var se=[{path:"apps/server/src/modules.ts",kind:"server-modules"},{path:"apps/client/src/modules.ts",kind:"client-modules"}];function sa(e){return e.split(/[^A-Za-z0-9]+/).filter(Boolean).map((t,r)=>r===0?t.toLowerCase():t.charAt(0).toUpperCase()+t.slice(1).toLowerCase()).join("")}function bt(e,t){return sa(t)}function St(e,t){return $t[e].contributes(t)}function kt(e){return e.replace(/^@[^/]+\//,"").replace(/\/(server|client)$/,"").replace(/\/$/,"")}var $t={"server-modules":{mapMarker:"register",importExport:"serverModule",importSuffix:"server",entryPattern:/^\s*([A-Za-z_$][\w$]*),\s*$/,header:"const modules = [",footer:"];",contributes:e=>e.hasServerEntry},"client-modules":{mapMarker:"register",importExport:"clientModule",importSuffix:"client",entryPattern:/^\s*([A-Za-z_$][\w$]*),\s*$/,header:"const modules = [",footer:"];",contributes:e=>e.hasClientEntry&&e.hasCards}},rr=/^import\s*\{\s*([\w$]+)\s+as\s+([\w$]+)\s*\}\s*from\s*['"]([^'"]+)['"];\s*$/,nr=/^\]\s*(?:as\s+const)?;\s*$/;function xt(e,t){let r=`// @tbox:${t}-begin`,n=`// @tbox:${t}-end`,s=e.indexOf(r);if(s===-1)return null;let i=e.indexOf(n,s);if(i===-1)return null;let o=s+r.length;return{block:e.slice(o,i),start:o,end:i}}function Qt(e,t,r){return e.slice(0,t.start)+r+e.slice(t.end)}function En(e,t){let r=$t[t],n=[],s=[],i=[],o=xt(e,"imports");if(o)for(let p of o.block.split(/\r?\n/)){let u=p.match(rr);u?n.push(kt(u[3])):p.trim()&&s.push(`imports: ${p.trim()}`)}else i.push("imports");let a=xt(e,r.mapMarker);if(a){let p=a.block.split(/\r?\n/),u=p.findIndex(g=>g.trim().startsWith(r.header)),d=[...p].reverse().findIndex(g=>nr.test(g.trim())),f=d===-1?-1:p.length-1-d;if(u===-1||f<=u)i.push(r.mapMarker);else for(let g of p.slice(u+1,f))!g.match(r.entryPattern)&&g.trim()&&s.push(`${r.mapMarker}: ${g.trim()}`)}else i.push(r.mapMarker);let c=new Map;if(o)for(let p of o.block.split(/\r?\n/)){let u=p.match(rr);u&&c.set(u[2],kt(u[3]))}let l=[];if(a){let p=a.block.split(/\r?\n/),u=p.findIndex(g=>g.trim().startsWith(r.header)),d=[...p].reverse().findIndex(g=>nr.test(g.trim())),f=d===-1?-1:p.length-1-d;if(u!==-1&&f>u)for(let g of p.slice(u+1,f)){let m=g.match(r.entryPattern);if(m){let h=c.get(m[1]);h&&l.push(h)}}}return{moduleIds:[...new Set([...n,...l])],nonStandardLines:s,broken:i}}function jt(e,t,r,n=!1){let s=$t[t],i=[],o=r.filter(s.contributes),a=new Map(o.map(w=>[bt(t,w.id),w])),c=e,l=!1,p=xt(c,"imports");if(!p)return i.push(`[${t}] imports 锚区缺失/破坏,跳过`),{content:c,changed:l,warnings:i,moduleIds:[]};let u=p.block.split(/\r?\n/),d=[];for(let w of u){let D=w.match(rr);if(!D){if(w.trim()==="")continue;n||i.push(`[${t}] imports 锚区非标行保留: ${w.trim()}`),n||d.push(w);continue}let M=D[2],b=D[3],k=a.get(M);k&&kt(k.pkg)===kt(b)?d.push(w):(k&&d.push(jn(t,M,k.pkg)),l=!0)}for(let w of o){let D=bt(t,w.id);d.some(M=>M.includes(`as ${D} } from`))||(d.push(jn(t,D,w.pkg)),l=!0)}let f=er(d.join(`
80
+ `));f!==p.block&&(c=Qt(c,p,f),l=!0);let g=xt(c,s.mapMarker);if(!g)return i.push(`[${t}] ${s.mapMarker} 锚区缺失/破坏,跳过`),{content:c,changed:l,warnings:i,moduleIds:[]};let m=g.block.split(/\r?\n/),h=m.findIndex(w=>w.trim().startsWith(s.header)),v=[...m].reverse().findIndex(w=>nr.test(w.trim())),S=v===-1?-1:m.length-1-v;if(h===-1||S<=h){if(n){let w=er([s.header,...o.map(D=>tr(t,bt(t,D.id))),s.footer].join(`
81
+ `));return w!==g.block&&(c=Qt(c,g,w),l=!0),{content:c,changed:l,warnings:i,moduleIds:o.map(D=>D.id)}}return i.push(`[${t}] ${s.mapMarker} 锚区结构不符合预期,降级为告警不覆盖`),{content:c,changed:l,warnings:i,moduleIds:[]}}let $=m.slice(0,h),R=m.slice(S+1),F=m.slice(h+1,S),A=[];for(let w of F){let D=w.match(s.entryPattern);if(!D){if(w.trim()==="")continue;n||i.push(`[${t}] ${s.mapMarker} 锚区非标行保留: ${w.trim()}`),n||A.push(w);continue}let M=D[1];a.has(M)?A.push(tr(t,M)):l=!0}for(let w of o){let D=bt(t,w.id);A.some(M=>M.includes(D))||(A.push(tr(t,D)),l=!0)}let N=m[S],P=er([...$,s.header,...A,N,...R].join(`
82
+ `));return P!==g.block&&(c=Qt(c,g,P),l=!0),{content:c,changed:l,warnings:i,moduleIds:o.map(w=>w.id)}}function er(e){let t=e.split(/\r?\n/);for(;t.length>0&&t[0].trim()==="";)t.shift();for(;t.length>0&&t[t.length-1].trim()==="";)t.pop();return`
83
+ `+t.join(`
84
+ `)+`
85
+ `}function jn(e,t,r){let n=$t[e];return`import { ${n.importExport} as ${t} } from '${r}/${n.importSuffix}';`}function tr(e,t){return` ${t},`}import{writeFile as oa}from"fs/promises";import ia from"path";import{readFileSync as ca}from"fs";function aa(e){try{return JSON.parse(la(e)??"{}")}catch{return null}}async function Cn(e,t,r){let n=ia.join(e,t),s=aa(n)??{},i=s.dependencies??{},o={},a=[];for(let[c,l]of Object.entries(r)){if(lt(c)||l==="workspace:*"||l.startsWith("catalog:"))continue;let p=i[c];if(!p){i[c]=l,o[c]=l;continue}if(p===l)continue;if(p==="catalog:"){let d=ce(e)?.get(c);d&&!Rn(d,l)&&a.push(`${c}: 应用 catalog ${d} 与模块要求 ${l} 无交集`);continue}let u=Rn(p,l);u?u!==p&&(i[c]=u,o[c]=u):a.push(`${c}: 已有 ${p},模块要求 ${l}`)}return s.dependencies=i,await oa(n,JSON.stringify(s,null,2)+`
86
+ `,"utf8"),{merged:o,conflicts:a}}function Rn(e,t){let r=Dn(e),n=Dn(t);if(!r||!n||r.lower.major!==n.lower.major)return null;let s=(a,c)=>a.major-c.major||a.minor-c.minor||a.patch-c.patch,i=s(r.lower,n.lower)>=0?r.lower:n.lower,o=s(r.upper,n.upper)<=0?r.upper:n.upper;return s(i,o)>=0?null:s(r.lower,n.lower)>0?e:s(n.lower,r.lower)>0?t:s(r.upper,n.upper)<=0?e:t}function Dn(e){let t=e.match(/^([\^~]?)(\d+)\.(\d+)(?:\.(\d+))?/);if(!t)return null;let r={major:Number(t[2]),minor:Number(t[3]),patch:Number(t[4]??0)},n;return t[1]==="~"?n={major:r.major,minor:r.minor+1,patch:0}:t[1]==="^"?n=r.major===0?{major:0,minor:r.minor+1,patch:0}:{major:r.major+1,minor:0,patch:0}:n={...r,patch:r.patch+1},{lower:r,upper:n}}function la(e){try{return ca(e,"utf8")}catch{return}}async function me(e,t={}){let r=await ue(e),n=[],s=[],i=[];for(let c of se){let l=or.join(e,c.path);if(!sr(l)){n.push(`缺少装配文件 ${c.path}`);continue}let p=await ua(l,"utf8"),u=jt(p,c.kind,r,t.force??!1);n.push(...u.warnings.map(d=>`${c.path}: ${d}`)),u.changed&&(await da(l,u.content,"utf8"),s.push(c.path))}let o=["apps/server/package.json","apps/client/package.json"];for(let c of r){let l=or.join(e,"packages",c.id,"package.json");if(!sr(l))continue;let p={};try{p=JSON.parse(pa(l,"utf8")).dependencies??{}}catch{continue}for(let u of o){if(!sr(or.join(e,u)))continue;let d=await Cn(e,u,p);i.push(...d.conflicts.map(f=>`${c.id} → ${u}: ${f}`))}}let a=De(e);return{declared:r,changedFiles:s,warnings:n,depConflicts:i,lockfileRefreshed:a}}import{existsSync as fa}from"fs";import{readFile as ma}from"fs/promises";import ga from"path";async function G(e){let t={};for(let r of se){let n=ga.join(e,r.path);if(!fa(n)){t[r.kind]={moduleIds:[],nonStandardLines:[],broken:["missing-file"]};continue}let s=await ma(n,"utf8"),i=En(s,r.kind);t[r.kind]={moduleIds:i.moduleIds,nonStandardLines:i.nonStandardLines,broken:i.broken}}return t}import{existsSync as Mn}from"fs";import{readdir as ba}from"fs/promises";import ir from"path";import{readFileSync as ya}from"fs";import{readFile as ha,readdir as va}from"fs/promises";import Et from"path";async function $e(e,t,r){let n=await va(e,{withFileTypes:!0});for(let s of n){if(s.name==="node_modules"||s.name==="dist"||s.name===".git")continue;let i=Et.join(e,s.name);s.isDirectory()?await $e(i,t,r):s.isFile()&&/\.(ts|tsx|mjs|js)$/.test(s.name)&&r.push(Et.relative(t,i).split(Et.sep).join("/"))}}async function L(e){try{return await ha(e,"utf8")}catch{return""}}function wa(e){try{return JSON.parse(ya(e,"utf8"))}catch{return null}}function Z(e){return wa(Et.join(e,"package.json"))}function Rt(e,t){let r=Tn(e),n=Tn(t);if(!r||!n||r.lower.major!==n.lower.major)return null;let s=(a,c)=>a.major-c.major||a.minor-c.minor||a.patch-c.patch,i=s(r.lower,n.lower)>=0?r.lower:n.lower,o=s(r.upper,n.upper)<=0?r.upper:n.upper;return s(i,o)>=0?null:s(r.lower,n.lower)>0?e:s(n.lower,r.lower)>0?t:s(r.upper,n.upper)<=0?e:t}function Tn(e){let t=e.match(/^([\^~]?)(\d+)\.(\d+)(?:\.(\d+))?/);if(!t)return null;let r={major:Number(t[2]),minor:Number(t[3]),patch:Number(t[4]??0)},n;return t[1]==="~"?n={major:r.major,minor:r.minor+1,patch:0}:t[1]==="^"?n=r.major===0?{major:0,minor:r.minor+1,patch:0}:{major:r.major+1,minor:0,patch:0}:n={...r,patch:r.patch+1},{lower:r,upper:n}}async function In(e){let t=[],r=ir.join(e.appDir,"packages");if(!Mn(r))return t;for(let n of await ba(r,{withFileTypes:!0})){if(!n.isDirectory())continue;let s=ir.join(r,n.name);if(!Mn(ir.join(s,"tbox.module.json")))continue;let i=/^provider-/.test(n.name),o=Z(s),a={...o?.dependencies??{},...o?.peerDependencies??{}};for(let c of Object.keys(a))(/^@app\/(module|scenario)-/.test(c)||/^@tbox\.cn\/app-(module|scenario)-/.test(c))&&t.push({rule:"workspace-dag",level:"error",message:`${n.name} 依赖业务模块 ${c}(module 不得依赖 module/scenario)`}),i&&(/^@app\/(module|scenario)-/.test(c)||/^@tbox\.cn\/app-(module|scenario)-/.test(c))&&t.push({rule:"workspace-dag",level:"error",message:`${n.name} 依赖业务模块 ${c}(provider 不得依赖 module/scenario——实现自持)`})}return t}import{existsSync as Fn}from"fs";import{readdir as xa}from"fs/promises";import _n from"path";import C from"typescript";function oe(e){return C.createSourceFile("entry.ts",e,C.ScriptTarget.Latest,!0,C.ScriptKind.TS)}function An(e){return e&&(C.isAsExpression(e)||C.isSatisfiesExpression(e)?e.expression:e)}function ka(e,t){let r=oe(e),n=null;function s(i){if(!n){if(C.isFunctionDeclaration(i)&&i.name?.text===t&&i.body){n=i.body;return}if(C.isVariableStatement(i)&&i.declarationList.declarations.some(o=>C.isIdentifier(o.name)&&o.name.text===t)){let o=i.declarationList.declarations.find(l=>C.isIdentifier(l.name)&&l.name.text===t),a=An(o.initializer);if(a&&C.isArrowFunction(a)&&a.body&&C.isBlock(a.body)){n=a.body;return}let c=On(a,"register");if(c){n=c;return}}C.forEachChild(i,s)}}return s(r),n}function On(e,t){if(!e||!C.isObjectLiteralExpression(e))return null;for(let r of e.properties){if(C.isMethodDeclaration(r)&&r.name.getText()===t&&r.body)return r.body;if(C.isPropertyAssignment(r)&&r.name.getText()===t&&r.initializer){let n=r.initializer;if(C.isArrowFunction(n)&&n.body&&C.isBlock(n.body)||C.isFunctionExpression(n)&&n.body)return n.body}}return null}function We(e,t){let r=oe(e),n=null;function s(i){if(!n){if(C.isVariableStatement(i)&&i.declarationList.declarations.some(o=>C.isIdentifier(o.name)&&o.name.text===t)){let o=i.declarationList.declarations.find(c=>C.isIdentifier(c.name)&&c.name.text===t),a=On(An(o.initializer),"register");if(a){n=a;return}}C.forEachChild(i,s)}}return s(r),n??ka(e,"registerServer")}function Pn(e){let t=!1;function r(n){if(!t&&!C.isArrowFunction(n)){if(C.isCallExpression(n)){let s=n.expression;if(C.isIdentifier(s)&&s.text==="resolveService"){t=!0;return}let i=s.getText();if(i==="ctx.resolveService"||i.endsWith(".services.resolve")){t=!0;return}}C.forEachChild(n,r)}}return r(e),t}function ar(e){let t=!1;function r(n){if(!t){if(C.isCallExpression(n)){let s=n.expression.getText();if(s.endsWith(".cards.registerMap")||s.endsWith(".cards.register")){t=!0;return}}C.forEachChild(n,r)}}return r(e),t}function Ln(e){let t=oe(e),r=[];function n(s){if(C.isCallExpression(s)&&s.expression.getText().endsWith(".cards.register")){let o=s.arguments[0];o&&r.push(o.getText().replace(/['"]/g,""))}C.forEachChild(s,n)}return n(t),r}function cr(e){let t=oe(e),r={};function n(s){if(C.isImportDeclaration(s)&&C.isStringLiteral(s.moduleSpecifier)){let i=s.moduleSpecifier.text;if(i.startsWith("./")||i.startsWith("../")){let o=s.importClause;o?.name&&(r[o.name.text]=i)}}C.forEachChild(s,n)}return n(t),r}async function Nn(e){let t=[],r=_n.join(e.appDir,"packages");if(!Fn(r))return t;for(let n of await xa(r,{withFileTypes:!0})){if(!n.isDirectory())continue;let s=_n.join(r,n.name,"src/server/index.ts");if(!Fn(s))continue;let i=await L(s),o=We(i,"serverModule");o&&Pn(o)&&t.push({rule:"register-pure",level:"error",message:`${n.name}: 注册入口(serverModule.register)内出现 resolveService(只允许在 handle/事件回调内)`})}return t}import{existsSync as Ge}from"fs";import ye from"path";import E from"typescript";function ge(e){return E.isAsExpression(e)||E.isSatisfiesExpression(e)?e.expression:e}function Ct(e,t){let r=oe(e),n=null;function s(i){let o={};for(let a of i.properties){if(!E.isPropertyAssignment(a))continue;let c=a.name,l=E.isIdentifier(c)||E.isStringLiteral(c)?c.text:"";l&&(o[l]=a.initializer)}return o}for(let i of r.statements)if(E.isVariableStatement(i)){for(let o of i.declarationList.declarations)if(!(!E.isIdentifier(o.name)||!o.initializer)){if(o.name.text==="cards"){let a=ge(o.initializer);if(E.isObjectLiteralExpression(a))return n=s(a),n}if(t&&o.name.text===t){let a=ge(o.initializer);if(E.isObjectLiteralExpression(a)){for(let c of a.properties)if(E.isPropertyAssignment(c)&&E.isIdentifier(c.name)&&c.name.text==="cards"){let l=ge(c.initializer);if(E.isObjectLiteralExpression(l))return n=s(l),n}}}}}return n}function Sa(e){let t=Ct(e,"clientModule");return t?Object.keys(t):[]}function Ke(e){let t=e.match(/cardType:\s*['"]([^'"]+)['"]/),r=e.match(/schemaVersion:\s*(\d+)/);return{cardType:t?.[1],schemaVersion:r?Number(r[1]):void 0}}function Jn(e,t){let r=[t,`${t}.ts`,`${t}.tsx`,`${t}/index.ts`,`${t}/index.tsx`];for(let n of r)if(Ge(ye.join(e,n)))return ye.join(e,n);return null}function zn(e,t,r){let n=ge(e);if(E.isObjectLiteralExpression(n)){let s=Ke(n.getText());return{cardType:s.cardType,schemaVersion:s.schemaVersion}}if(E.isStringLiteral(n))return{cardType:n.text};if(E.isIdentifier(n)){let s=t[n.text];if(!s)return null;let i=Jn(r,s.replace(/^\.\//,""));return i?{resolved:i}:null}return null}async function $a(e){let t=ye.join(e,"src/server/index.ts");if(!Ge(t))return{cardTypes:[],schemaVersions:{},keyMismatches:[],registerMissing:!1};let r=await L(t),n=cr(r),s=ye.join(e,"src/server"),i=[],o={},a=[],c=Ct(r,"serverModule");if(c){let l=!1;if(Object.keys(c).length>0){let p=We(r,"serverModule");l=!p||!ar(p)}for(let[p,u]of Object.entries(c)){let d=zn(u,n,s);if(d)if(d.resolved){let f=await L(d.resolved),g=Ke(f);g.cardType&&(i.push(g.cardType),g.schemaVersion&&(o[g.cardType]=g.schemaVersion),p!==g.cardType&&a.push({key:p,cardType:g.cardType}))}else d.cardType&&(i.push(d.cardType),d.schemaVersion&&(o[d.cardType]=d.schemaVersion),p!==d.cardType&&a.push({key:p,cardType:d.cardType}))}return{cardTypes:[...new Set(i)],schemaVersions:o,keyMismatches:a,registerMissing:l}}for(let l of Ln(r)){if(l.includes("cardType:")){let g=Ke(l);g.cardType&&(i.push(g.cardType),g.schemaVersion&&(o[g.cardType]=g.schemaVersion));continue}if(/^[a-zA-Z0-9][\w-]*$/.test(l)&&!n[l]){i.push(l);continue}let p=n[l];if(!p)continue;let u=Jn(s,p.replace(/^\.\//,""));if(!u)continue;let d=await L(u),f=Ke(d);f.cardType&&(i.push(f.cardType),f.schemaVersion&&(o[f.cardType]=f.schemaVersion))}return{cardTypes:[...new Set(i)],schemaVersions:o,keyMismatches:a,registerMissing:!1}}function ja(e,t){return oe(e).statements.some(n=>E.isVariableStatement(n)&&n.declarationList.declarations.some(s=>E.isIdentifier(s.name)&&s.name.text===t))}async function Ea(e){let t=ye.join(e,"src/client/index.ts");if(!Ge(t))return!1;let r=await L(t);if(!ja(r,"clientModule"))return!1;let n=Ct(r,"clientModule");if(!n||Object.keys(n).length===0)return!1;let s=We(r,"clientModule");return!s||!ar(s)}async function qe(e){let t=await z(e.appDir),r=[];for(let n of t){let s={};for(let c of n.descriptor.contributes.cards??[])s[c.cardType]=c.schemaVersion??1;let i=ye.join(n.dir,"src/client/index.ts"),o=Ge(i)?Sa(await L(i)):[],a=await $a(n.dir);r.push({id:n.id,declared:s,clientMap:o,serverRegistered:a.cardTypes,serverSchemaVersions:a.schemaVersions,serverKeyMismatches:a.keyMismatches,serverRegisterMissing:a.registerMissing,clientRegisterMissing:await Ea(n.dir)})}return r}function Dt(e){return E.isStringLiteral(e)||E.isNumericLiteral(e)||e.kind===E.SyntaxKind.NullKeyword||e.kind===E.SyntaxKind.TrueKeyword||e.kind===E.SyntaxKind.FalseKeyword?!0:E.isPrefixUnaryExpression(e)&&e.operator===E.SyntaxKind.MinusToken?Dt(e.operand):E.isArrayLiteralExpression(e)?e.elements.every(t=>E.isOmittedExpression(t)||Dt(t)):E.isObjectLiteralExpression(e)?e.properties.every(t=>E.isPropertyAssignment(t)&&Dt(t.initializer)):!1}function Vn(e){for(let t of e.properties)if(E.isPropertyAssignment(t)&&E.isIdentifier(t.name)&&t.name.text==="sampleData"){let r=ge(t.initializer);return E.isObjectLiteralExpression(r)?r.properties.length===0?"empty":Dt(r)?"ok":"non-literal":"non-literal"}return"missing"}function Ra(e){let t=oe(e),r={};for(let n of t.statements){if(E.isVariableStatement(n)){for(let s of n.declarationList.declarations)if(E.isIdentifier(s.name)&&s.initializer){let i=ge(s.initializer);E.isObjectLiteralExpression(i)&&(r[s.name.text]=i)}}if(E.isExportAssignment(n)&&!n.isExportEquals){let s=ge(n.expression);if(E.isObjectLiteralExpression(s))return s;if(E.isIdentifier(s)&&r[s.text])return r[s.text]}}return null}async function Un(e){let t=ye.join(e,"src/server/index.ts");if(!Ge(t))return[];let r=await L(t),n=cr(r),s=ye.join(e,"src/server"),i=Ct(r,"serverModule");if(!i)return[];let o=[];for(let[a,c]of Object.entries(i)){let l=zn(c,n,s);if(l){if(l.resolved){let p=await L(l.resolved),u=Ke(p).cardType??a,d=Ra(p);o.push({cardType:u,status:d?Vn(d):"missing",source:l.resolved})}else if(l.cardType){let p=ge(c),u=E.isObjectLiteralExpression(p)?Vn(p):"missing";o.push({cardType:l.cardType,status:u,source:"inline"})}}}return o}async function Bn(e){let t=[];for(let r of await qe(e)){let n=Object.keys(r.declared);if(n.length===0&&r.serverKeyMismatches.length===0&&!r.serverRegisterMissing&&!r.clientRegisterMissing)continue;for(let c of r.serverKeyMismatches)t.push({rule:"card-type-consistent",level:"error",message:`${r.id}: serverModule.cards key "${c.key}" !== meta.cardType "${c.cardType}"(key 约定 = cardType)`});r.serverRegisterMissing&&t.push({rule:"card-type-consistent",level:"error",message:`${r.id}: serverModule 声明了 cards 但 register 内未调用 ctx.cards.registerMap(cards)(注册动作缺失,运行时缺卡)`}),r.clientRegisterMissing&&t.push({rule:"card-type-consistent",level:"error",message:`${r.id}: clientModule 声明了 cards 但 register 内未调用 ctx.cards.registerMap(cards)(注册动作缺失,运行时缺卡)`});let s=n.filter(c=>!r.clientMap.includes(c));s.length>0&&t.push({rule:"card-type-consistent",level:"error",message:`${r.id}: 声明卡片 ${s.join(",")} 未在客户端 cards map 导出`});let i=n.filter(c=>!r.serverRegistered.includes(c));i.length>0&&t.push({rule:"card-type-consistent",level:"error",message:`${r.id}: 声明卡片 ${i.join(",")} 未在服务端注册(serverModule.cards meta / cards.register)`});let o=r.clientMap.filter(c=>!n.includes(c));o.length>0&&t.push({rule:"card-type-consistent",level:"warning",message:`${r.id}: 客户端导出未声明的卡片 ${o.join(",")}`});let a=r.serverRegistered.filter(c=>!n.includes(c));a.length>0&&t.push({rule:"card-type-consistent",level:"warning",message:`${r.id}: 服务端注册未声明的卡片 ${a.join(",")}`})}return t}async function Hn(e){let t=[],r=await qe(e),n={};for(let i of r)for(let o of i.serverRegistered)o in n?n[o]+=`,${i.id}`:n[o]=i.id;if(Object.keys(n).length===0)return t;let s=new Set;for(let i of r)for(let o of i.clientMap)s.add(o);for(let[i,o]of Object.entries(n))s.has(i)||t.push({rule:"client-card-cover",level:"error",message:`服务端注册卡片 ${i}(模块 ${o})未出现在应用客户端装配(漏装配)`});return t}import{existsSync as lr}from"fs";import{readdir as pr,stat as Da}from"fs/promises";import Me from"path";async function Wn(e){let t=[],r=Me.join(e.appDir,"packages");if(!lr(r))return t;let n=new Map;for(let i of await pr(r,{withFileTypes:!0})){if(!i.isDirectory())continue;let o=Z(Me.join(r,i.name));o?.name&&o.name.startsWith("@tbox.cn/app-contracts")&&n.set(o.name,o.version??"0.0.0")}let s=Me.join(e.appDir,"node_modules","@tbox.cn");if(lr(s))for(let i of await pr(s,{withFileTypes:!0})){let o=Me.join(s,i.name),a=i.isDirectory();if(!a&&i.isSymbolicLink())try{a=(await Da(o)).isDirectory()}catch{a=!1}if(!a)continue;let c=Z(o);c?.name&&c.name.startsWith("@tbox.cn/app-contracts")&&n.set(c.name,c.version??"0.0.0")}for(let i of await pr(r,{withFileTypes:!0})){if(!i.isDirectory())continue;let o=Me.join(r,i.name);if(!lr(Me.join(o,"tbox.module.json")))continue;let a=Z(o),c={...a?.dependencies??{},...a?.peerDependencies??{}};for(let[l,p]of Object.entries(c)){if(!l.startsWith("@tbox.cn/app-contracts"))continue;let u=n.get(l);if(!u||p==="*"||p.startsWith("workspace:")||p.startsWith("catalog:")||p.replace(/^[\^~]/,"")===u)continue;Rt(p,u)||t.push({rule:"contract-version",level:"error",message:`${i.name}: 契约 ${l} 范围 ${p} 与实装 ${u} 无交集`})}}return t}import{existsSync as Ca}from"fs";import{readdir as Ta}from"fs/promises";import Ma from"path";async function Kn(e){let t=[],r=new Set,n=Ma.join(e.appDir,"packages");if(Ca(n))for(let f of await Ta(n,{withFileTypes:!0}))f.isDirectory()&&r.add(f.name);for(let f of e.declared)r.add(f.id);let s=new Set;for(let f of se)for(let g of e.actual[f.kind]?.moduleIds??[])s.add(g);let i=new Map,o=await z(e.appDir);for(let f of o){let g=(f.descriptor.dependencies?.modules??[]).map(m=>m.id);i.set(f.id,g)}let a=0,c=1,l=2,p=new Map,u=[];function d(f,g){p.set(f,c),g.push(f);for(let m of i.get(f)??[]){let h=p.get(m)??a;if(h===c){let v=g.indexOf(m);v>=0&&u.push([...g.slice(v),m])}else h===a&&d(m,g)}g.pop(),p.set(f,l)}for(let f of i.keys())(p.get(f)??a)===a&&d(f,[]);for(let f of u)t.push({rule:"module-deps-assembled",level:"error",message:`dependencies.modules 声明层存在循环依赖:${f.join(" → ")}`});for(let f of o)for(let g of f.descriptor.dependencies?.modules??[]){if(g.required&&!r.has(g.id)){t.push({rule:"module-deps-assembled",level:"error",message:`${f.id}: 依赖模块 ${g.id} 未安装(缺失 packages/${g.id} 或 npm 清单)`});continue}o.some(h=>h.id===g.id)&&g.required&&!s.has(g.id)&&t.push({rule:"module-deps-assembled",level:"error",message:`${f.id}: 依赖业务模块 ${g.id} 已安装但未装配(modules.ts 缺行,运行 sync)`})}return t}import{existsSync as ur}from"fs";import{readdir as Ia}from"fs/promises";import{createRequire as Aa}from"module";import{realpathSync as Oa}from"fs";import Ie from"path";async function Gn(e){let t=[],r=["react","zod","@mastra/core"],n=["apps/client","apps/server","packages/app-sdk","packages/contracts"],s=Ie.join(e.appDir,"packages");if(ur(s))for(let i of await Ia(s,{withFileTypes:!0}))i.isDirectory()&&ur(Ie.join(s,i.name,"package.json"))&&n.push(Ie.join("packages",i.name));for(let i of r){let o=new Map;for(let a of n){let c=Ie.join(e.appDir,a);if(!ur(Ie.join(c,"package.json")))continue;let l=Pa(c,i);if(l){let p=o.get(l)??[];p.push(a),o.set(l,p)}}o.size>1&&t.push({rule:"singleton",level:"error",message:`${i} 解析结果不唯一:${[...o.entries()].map(([a,c])=>`${a}(${c.join(",")})`).join(" vs ")}`})}return t}function Pa(e,t){try{let r=Aa(Ie.join(e,"__noop__.js"));return Oa(r.resolve(t))}catch{return null}}import{existsSync as qn}from"fs";import dr from"path";var La=new Set(["@tbox.cn/app-sdk","@tbox.cn/app-sdk/server","@tbox.cn/app-sdk/client","@tbox.cn/app-sdk/platform","@tbox.cn/app-sdk/platform/code-inspect"]),Fa=[{re:/^@mastra\/core/,label:"@mastra/core*(框架内部,改经 @tbox.cn/app-sdk)"},{re:/^@ag-ui\//,label:"@ag-ui/*(框架内部,改经 @tbox.cn/app-sdk)"},{re:/^@ai-sdk\//,label:"@ai-sdk/*(框架内部)"},{re:/^ai$/,label:"ai(框架内部)"},{re:/^zustand$/,label:"zustand(框架内部)"}];async function Xn(e){let t=[],r=["apps","packages"];for(let n of r){let s=dr.join(e.appDir,n);if(!qn(s))continue;let i=[];await $e(s,e.appDir,i);for(let o of i){if(o.startsWith("packages/app-sdk/")||o.startsWith("packages/contracts/"))continue;let a=await L(dr.join(e.appDir,o));for(let c of a.matchAll(/from\s+['"]([^'"]+)['"]/g)){let l=c[1];if(l.startsWith("@tbox.cn/app-sdk/")&&!La.has(l))t.push({rule:"sdk-internal-import",level:"error",message:`${o}: 不允许 import SDK 内部路径 ${l}`});else if(l.startsWith("@tbox.cn/app-contracts/"))t.push({rule:"sdk-internal-import",level:"error",message:`${o}: 不允许 import 契约子路径 ${l}(仅 @tbox.cn/app-contracts)`});else if(_a(e.appDir,o))for(let p of Fa)p.re.test(l)&&t.push({rule:"sdk-internal-import",level:"error",message:`${o}: 模块不允许直接 import ${p.label}`})}}}return t}function _a(e,t){let r=t.match(/^packages\/([^/]+)\//);return r?qn(dr.join(e,"packages",r[1],"tbox.module.json")):!1}function Yn(e){let t=[];for(let r of se){let n=new Set(e.declared.filter(c=>St(r.kind,c)).map(c=>c.id)),s=new Set(e.actual[r.kind]?.moduleIds??[]),i=[...n].filter(c=>!s.has(c)),o=[...s].filter(c=>!n.has(c));i.length>0&&t.push({rule:"assembly-drift",level:"warning",message:`${r.path}: 已声明未装配 → ${i.join(", ")}`}),o.length>0&&t.push({rule:"assembly-drift",level:"warning",message:`${r.path}: 已装配未声明 → ${o.join(", ")}`});let a=e.actual[r.kind]?.broken??[];a.length>0&&t.push({rule:"assembly-drift",level:"warning",message:`${r.path}: 锚区破坏 → ${a.join(", ")}`})}return t}async function Zn(e){let t=[];for(let r of await qe(e))for(let[n,s]of Object.entries(r.declared)){let i=r.serverSchemaVersions[n]??1;i!==s&&t.push({rule:"schema-version",level:"error",message:`${r.id}: 卡片 ${n} contributes.schemaVersion=${s} 与 meta schemaVersion=${i} 不一致`})}return t}import{existsSync as Na}from"fs";import Qn from"path";var Va=["/webhook","/upload","/demo","/run"];async function es(e){let t=[];for(let r of await z(e.appDir)){let n=Qn.join(r.dir,"src/server");if(!Na(n))continue;let s=[];await $e(n,r.dir,s);for(let i of s){let o=await L(Qn.join(r.dir,i));for(let a of o.matchAll(/\.(?:post|put|patch|delete)\(\s*['"]([^'"]+)['"]/g)){let c=a[1];if(!c.startsWith("/"))continue;Va.some(p=>c.startsWith(p))||t.push({rule:"naked-write-route",level:"warning",message:`${r.id}/${i}: 裸写路由 ${a[0].replace("("," (")} 建议改走 ctx.actions.registerAction(全局动作分发器)`})}}}return t}import{existsSync as ts}from"fs";import{readdir as Ja}from"fs/promises";import rs from"path";async function ns(e){let t=[],r=rs.join(e.appDir,"packages");if(!ts(r))return t;for(let n of await Ja(r,{withFileTypes:!0})){if(!n.isDirectory())continue;let s=rs.join(r,n.name,"tbox.module.json");if(ts(s))try{let i=Ee(JSON.parse(await L(s)));i.ok||t.push({rule:"descriptor-schema",level:"error",message:`${n.name}/tbox.module.json: ${i.errors.join("; ")}`})}catch{t.push({rule:"descriptor-schema",level:"error",message:`${n.name}/tbox.module.json: JSON 解析失败`})}}return t}import{existsSync as ss}from"fs";import{readdir as za}from"fs/promises";import Xe from"path";async function os(e){let t=[];if(ss(Xe.join(e.appDir,"pnpm-workspace.template.yaml")))return t;let r=new Map;for(let n of["apps","packages"]){let s=Xe.join(e.appDir,n);if(ss(s))for(let i of await za(s,{withFileTypes:!0})){if(!i.isDirectory())continue;let o=Xe.join(s,i.name),a=Z(o);a?.name&&r.set(a.name,Xe.join(n,i.name))}}if(r.size===0)return t;for(let[n,s]of r){let i=Z(Xe.join(e.appDir,s));if(i)for(let o of["dependencies","peerDependencies","devDependencies"]){let a=i[o];if(!(!a||typeof a!="object"))for(let[c,l]of Object.entries(a))typeof l!="string"||!l.startsWith("workspace:")||r.has(c)||t.push({rule:"workspace-ref-resolved",level:"error",message:`${s}: workspace:* 引用 ${c} 未命中任何成员(packages/ 或 apps/)`})}}return t}import{existsSync as Ua}from"fs";import Ba from"path";var Ha=["apps/server/src/auth","apps/server/src/agent","apps/server/src/gateway","apps/server/src/http","apps/server/src/plugins/integration.ts","apps/server/src/config/tts-config.ts","apps/server/src/config/model-providers.ts","apps/client/src/env","apps/client/src/bridge","apps/client/src/adapters","apps/client/src/services/http.ts","apps/client/src/services/auth-client.ts","apps/client/src/services/user-id.ts","apps/client/src/services/tbox-session.ts"];function is(e){let t=[];for(let r of Ha)Ua(Ba.join(e.appDir,r))&&t.push({rule:"legacy-platform-files",level:"error",message:`${r} 为旧形态平台文件(007:平台能力已下沉 @tbox.cn/app-sdk)。请删除并改用 SDK API(server: createAuthRuntime/createPlatformApiRouter/createTtsProxy/createModelProvider/createPluginIntegration;client: createAuthClient/createPlatformAdapters/createPlatformApi/configureRuntime),详见 .agents/notes/archived/architecture/2026-08-12-platform-capabilities-sink.md。`});return t}import{existsSync as as,readFileSync as Wa}from"fs";import{readdir as Ka}from"fs/promises";import{createRequire as Ga}from"module";import Ye from"path";function cs(e,t){try{let n=Ga(Ye.join(e.appDir,"package.json")).resolve(`${t}/package.json`);return Ye.dirname(n)}catch{return}}function qa(e){let t=[Ye.join(e,"src/server/index.ts"),Ye.join(e,"src/server/index.tsx")];for(let r of t)if(as(r))return Wa(r,"utf8");return""}function Xa(e){let t=[];for(let r of e.matchAll(/patterns\s*:\s*\[([^\]]*)\]/g))for(let n of r[1].matchAll(/['"]([^'"]+)['"]/g))t.push(n[1]);return t}async function ls(e){let t=[],r=new Map;for(let n of e.declared){if(!n.hasServerEntry)continue;let s=cs(e,n.pkg);if(!s)continue;let i=Xa(qa(s));for(let o of i){let a=r.get(o)??[];a.push(n.id),r.set(o,a)}}for(let[n,s]of r)s.length>1&&t.push({rule:"intent-overlap",level:"warning",message:`意图词 "${n}" 在多个模块声明(${s.join(", ")})——先注册先命中`});return t}async function ps(e){let t=[],r=new Map;for(let n of e.declared){if(!n.hasServerEntry)continue;let s=cs(e,n.pkg);if(!s)continue;let i=Ye.join(s,"skills");if(as(i))try{let o=(await Ka(i,{withFileTypes:!0})).filter(a=>a.isDirectory()).map(a=>a.name);for(let a of o){let c=r.get(a)??[];c.push(n.id),r.set(a,c)}}catch{}}for(let[n,s]of r)s.length>1&&t.push({rule:"skill-name-conflict",level:"warning",message:`skill "${n}" 在多个模块声明(${s.join(", ")})——后注册覆盖`});return t}async function us(e){let t=[];for(let r of await z(e.appDir))for(let n of await Un(r.dir)){if(n.status==="ok")continue;let s=n.status==="missing"?"缺失(dev 卡片预览页 / 模块单测不可用)":n.status==="empty"?"为空对象(至少一组可展示数据)":"非字面量(引用常量/函数调用,AST 无法离线求值——静态检查无法求值,运行时预览不受影响)";t.push({rule:"sample-data-valid",level:"warning",message:`${r.id}/${n.cardType}: sampleData ${s}`})}return t}import{existsSync as ds}from"fs";import{readdir as fs}from"fs/promises";import Tt from"path";async function ms(e){let t=[],r=Tt.join(e.appDir,"packages");if(!ds(r))return t;for(let n of await fs(r,{withFileTypes:!0})){if(!n.isDirectory()||n.name.startsWith("contracts-"))continue;let s=Tt.join(r,n.name,"src");if(!ds(s))continue;let i=[];await gs(s,s,i),i.length>0&&t.push({rule:"module-console-logger",level:"warning",message:`${n.name}: 源码含裸 console 调用 ${i.length} 处(${i.slice(0,3).join("、")}${i.length>3?"…":""})——建议改用 ctx.logger(见 docs/reference/modules.md 模块日志规范)`})}return t}async function gs(e,t,r){for(let n of await fs(e,{withFileTypes:!0})){let s=Tt.join(e,n.name);if(n.isDirectory()){if(n.name==="__tests__"||n.name==="node_modules")continue;await gs(s,t,r);continue}if(!/\.tsx?$/.test(n.name)||/\.test\.tsx?$/.test(n.name))continue;let i=await L(s),o=Tt.relative(t,s);i.split(`
87
+ `).forEach((c,l)=>{/console\.(log|warn|error|info|debug)\s*\(/.test(c)&&r.push(`${o}:${l+1}`)})}}import{existsSync as ys}from"fs";import{readdir as hs}from"fs/promises";import Mt from"path";async function vs(e){let t=[],r=Mt.join(e.appDir,"packages");if(!ys(r))return t;for(let n of await hs(r,{withFileTypes:!0})){if(!n.isDirectory()||n.name.startsWith("contracts-"))continue;let s=Mt.join(r,n.name,"src");if(!ys(s))continue;let i=[];await ws(s,s,i),i.length>0&&t.push({rule:"module-mall-scope",level:"warning",message:`${n.name}: 源码含手解 attributes.mall ${i.length} 处(${i.slice(0,3).join("、")}${i.length>3?"…":""})——改用 parseMallScope/extractMallScope/withMallScopeFromQuery(见 docs/reference/modules.md 读取入口族)`})}return t}var Ya=/\.attributes\??\.?\bmall\b|\.attributes\??\.?\[\s*['"]mall['"]\s*\]/,Za=/as\s*\{[^}]*\bmall\b/,Qa=/^\s*(\*|\/\/|\/\*)/;async function ws(e,t,r){for(let n of await hs(e,{withFileTypes:!0})){let s=Mt.join(e,n.name);if(n.isDirectory()){if(n.name==="__tests__"||n.name==="node_modules")continue;await ws(s,t,r);continue}if(!/\.tsx?$/.test(n.name)||/\.test\.tsx?$/.test(n.name))continue;let i=await L(s),o=Mt.relative(t,s);i.split(`
88
+ `).forEach((c,l)=>{Qa.test(c)||(Ya.test(c)||Za.test(c))&&r.push(`${o}:${l+1}`)})}}import{existsSync as bs,readFileSync as ks}from"fs";import xs from"path";function ec(e,t=process.env,r="<config>"){let n=Pr(e,t);if(n.missing.length>0)throw new Error(`配置文件 ${r} 引用的环境变量缺失:${n.missing.join("、")}(先 source .real-env/env.sh <vendor>[-<variant>] 后重试)`);return n.text??e}async function Ss(e){let t=xs.join(e.appDir,"config","deployments","development.json");if(bs(t))try{let m=JSON.parse(ks(t,"utf8"));if(m&&typeof m=="object"&&"malls"in m)return[{rule:"integration-services",level:"error",message:"config/deployments/development.json 已退役(部署控制面并入 config/integrations.json 的 instances 注册表 + defaultInstanceId)——迁移后删除该文件(malls → instances[].id/attributes,defaultMallId → defaultInstanceId)"}]}catch{}let r=xs.join(e.appDir,"config","integrations.json");if(!bs(r))return[];let n=await rt(e.appDir);if(!n.strictValidation){let m=n.version===null;return[{rule:"integration-services",level:m?"warning":"error",message:m?"integrations: contracts 未安装——pnpm install 后重试(安装后启用严格校验与求值诊断)":`integrations: contracts 严格校验面不可用(${n.version},需 ≥0.9)——${n.notes[0]??"升级应用 @tbox.cn/app-contracts 依赖后可用"}`}]}let{resolveEffectiveService:s,buildSupplyLookup:i,moduleDomainOf:o,normalizeInstances:a,RESOURCE_TYPE_IDS:c}=n.module,l;try{l=JSON.parse(ec(ks(r,"utf8"),process.env,r))}catch(m){return[{rule:"integration-services",level:"error",message:`config/integrations.json 非法 JSON:${m.message}`}]}let p=l?.services;if(!p||typeof p!="object"||Array.isArray(p))return[{rule:"integration-services",level:"error",message:"config/integrations.json 缺 services(Record<service, {provider, instances}>)"}];let u=Hr(e.appDir),d=i(Wr(e.appDir)),f=Fr(e.appDir,u,l,process.env);return Lr({contracts:{resolveEffectiveService:s,buildSupplyLookup:i,moduleDomainOf:o,normalizeInstances:a,RESOURCE_TYPE_IDS:c},integrations:l,supplies:d,...f}).map(m=>({rule:"integration-services",level:m.severity,message:m.message,...m.code!==void 0?{code:m.code}:{},...m.service!==void 0?{service:m.service}:{},...m.module!==void 0?{module:m.module}:{},...m.instance!==void 0?{instance:m.instance}:{}}))}async function $s(e){return(await hn(e.appDir)).map(r=>({rule:"codegen-ref-resolved",level:"error",message:r}))}import{existsSync as js}from"fs";import{readdir as Es}from"fs/promises";import It from"path";async function Rs(e){let t=[],r=It.join(e.appDir,"packages");if(!js(r))return t;for(let n of await Es(r,{withFileTypes:!0})){if(!n.isDirectory()||n.name.startsWith("contracts-"))continue;let s=It.join(r,n.name,"src");if(!js(s))continue;let i=[];await Ds(s,s,i),i.length>0&&t.push({rule:"module-auth-context",level:"error",message:`${n.name}: 源码读 ctx.auth 全局单例 ${i.length} 处(${i.slice(0,3).join("、")}${i.length>3?"…":""})——ctx.auth 已于 app-sdk 0.11.0 拆除,改用显式通道(handle 第四参 reqCtx / getToolRequestContext,见 docs/reference/modules.md)`})}return t}async function Ds(e,t,r){for(let n of await Es(e,{withFileTypes:!0})){let s=It.join(e,n.name);if(n.isDirectory()){if(n.name==="__tests__"||n.name==="node_modules")continue;await Ds(s,t,r);continue}if(!/\.tsx?$/.test(n.name)||/\.test\.tsx?$/.test(n.name))continue;let i=await L(s),o=It.relative(t,s);i.split(`
89
+ `).forEach((c,l)=>{/ctx\.auth\.(getContext|setContext|requireIdentity|isAnonymous)\s*\(/.test(c)&&r.push(`${o}:${l+1}`)})}}import fr from"path";import he from"typescript";var Cs="presentation",Ts="labelsEmbedded",tc=new Set([".ts",".tsx"]);function rc(e){let t;try{t=oe(e)}catch{return[]}let r=[],n=s=>{if(he.isPropertyAssignment(s)&&(he.isIdentifier(s.name)||he.isStringLiteral(s.name))&&s.name.text===Cs&&he.isObjectLiteralExpression(s.initializer)){let i=s.initializer.properties.some(c=>c.name!==void 0&&he.isIdentifier(c.name)&&c.name.text==="variant"),o=s.initializer.properties.some(c=>c.name!==void 0&&he.isIdentifier(c.name)&&c.name.text===Ts),a=s.initializer.properties.some(he.isSpreadAssignment);i&&!o&&!a&&r.push(t.getLineAndCharacterOfPosition(s.getStart(t)).line+1)}he.forEachChild(s,n)};return n(t),r}async function Ms(e){let t=[];for(let r of await z(e.appDir)){let n=fr.join(r.dir,"src"),s=[];await $e(n,n,s);for(let i of s){if(!tc.has(fr.extname(i)))continue;let o=await L(fr.join(n,i));if(o.includes(Cs))for(let a of rc(o))t.push({rule:"card-face-labels",level:"error",message:`${r.id}: src/${i}:${String(a)} 的 presentation 缺 ${Ts}——卡面图自带卡名与「我的积分」字样,缺省时前端会再叠一层导致文字重叠;卡图确实无字请显式写 false`})}}return t}import{existsSync as Is}from"fs";import{readdir as As}from"fs/promises";import mr from"path";async function Os(e){let t=[],r=mr.join(e.appDir,"packages");if(!Is(r))return t;for(let n of await As(r,{withFileTypes:!0})){if(!n.isDirectory()||n.name.startsWith("contracts-"))continue;let s=mr.join(r,n.name,"src");if(!Is(s))continue;let i=await Ps(s);if(i.length===0)continue;let o=i.map(u=>u.text).join(`
90
+ `),a=new Set;for(let u of o.matchAll(/internalTool\s*\(\s*(\w+)\s*\)/g))a.add(u[1]);let c=[...o.matchAll(/const\s+(\w+)\s*=\s*createTool\s*\(/g)],l=[],p=[];for(let u=0;u<c.length;u++){let d=c[u],f=d[1],g=d.index??0,m=u+1<c.length?c[u+1].index??g+4e3:g+4e3,v=o.slice(g,Math.min(m,g+8e3)).includes("模型勿调用"),S=a.has(f);v&&!S?l.push(f):S&&!v&&p.push(f)}l.length>0&&t.push({rule:"module-internal-tool",level:"warning",message:`${n.name}: 工具 description 含「模型勿调用」但未 internalTool 标记(${l.join("、")})——模型面照常可见,撑大静态前缀且有幻觉调用面(见 docs/reference/modules.md 内部工具规约)`}),p.length>0&&t.push({rule:"module-internal-tool",level:"warning",message:`${n.name}: internalTool 标记的工具 description 缺「模型勿调用」标注(${p.join("、")})——标注是 doctor 静态面唯一可查锚点,补齐描述`})}return t}async function Ps(e,t=""){let r=[];for(let n of await As(e,{withFileTypes:!0})){let s=mr.join(e,n.name),i=t?`${t}/${n.name}`:n.name;if(n.isDirectory()){if(n.name==="__tests__"||n.name==="node_modules")continue;r.push(...await Ps(s,i));continue}/\.tsx?$/.test(n.name)&&(/\.test\.tsx?$/.test(n.name)||r.push({rel:i,text:await L(s)}))}return r}var nc=[{name:"workspace-dag",run:In},{name:"register-pure",run:Nn},{name:"card-type-consistent",run:Bn},{name:"client-card-cover",run:Hn},{name:"contract-version",run:Wn},{name:"module-deps-assembled",run:Kn},{name:"singleton",run:Gn},{name:"sdk-internal-import",run:Xn},{name:"assembly-drift",run:Yn},{name:"schema-version",run:Zn},{name:"naked-write-route",run:es},{name:"descriptor-schema",run:ns},{name:"workspace-ref-resolved",run:os},{name:"legacy-platform-files",run:is},{name:"intent-overlap",run:ls},{name:"skill-name-conflict",run:ps},{name:"sample-data-valid",run:us},{name:"module-console-logger",run:ms},{name:"module-mall-scope",run:vs},{name:"integration-services",run:Ss},{name:"codegen-ref-resolved",run:$s},{name:"module-auth-context",run:Rs},{name:"card-face-labels",run:Ms},{name:"module-internal-tool",run:Os}];async function q(e,t){let r=[],n=t?.only?.length?new Set(t.only):null,s=new Set(t?.skip??[]);for(let i of nc)if(!(n&&!n.has(i.name))&&!s.has(i.name))try{r.push(...await i.run(e))}catch(o){r.push({rule:i.name,level:"error",message:`规则执行异常: ${o?.message??String(o)}`})}return r}function Ls(e,t){return!t.has(e)||e.startsWith("contracts-")}async function Fs(e,t,r,n=[]){let s=await z(e),i=new Set(s.map(b=>b.id)),o=new Set(r?.map(b=>b.id)??[]),a=new Map;for(let b of s)a.set(b.id,b.descriptor.version??"0.0.0");let c=new Map(n.map(b=>[b.id,b.deps])),l=new Set,p=[],u=[...t,...n.map(b=>b.id)];for(;u.length>0;){let b=u.shift();if(l.has(b))continue;l.add(b);let k=s.find(j=>j.id===b),O=c.get(b)??k?.descriptor.dependencies?.modules??[];for(let j of O)p.push(j),!l.has(j.id)&&(i.has(j.id)||c.has(j.id))&&u.push(j.id)}let d=0,f=1,g=2,m=new Map,h=[];function v(b,k){m.set(b,f),k.push(b);for(let O of S.get(b)??[]){let j=m.get(O)??d;if(j===f){let ee=k.indexOf(O);ee>=0&&h.push([...k.slice(ee),O])}else j===d&&v(O,k)}k.pop(),m.set(b,g)}let S=new Map,$=new Map(s.map(b=>[b.id,b]));for(let b of l){let k=$.get(b),O=c.get(b)??k?.descriptor.dependencies?.modules??[];S.set(b,O.filter(j=>l.has(j.id)).map(j=>j.id))}for(let b of l)(m.get(b)??d)===d&&v(b,[]);let R=[...l].filter(b=>!o.has(b)),F=new Map;for(let b of R)F.set(b,0);let A=new Map;for(let b of R)for(let k of S.get(b)??[])if(F.has(k)){F.set(b,(F.get(b)??0)+1);let O=A.get(k);O?O.push(b):A.set(k,[b])}let N=R.filter(b=>(F.get(b)??0)===0),P=[];for(;N.length>0;){let b=N.shift();P.push(b);for(let k of A.get(b)??[]){let O=(F.get(k)??0)-1;F.set(k,O),O===0&&N.push(k)}}P.length,R.length;let w=new Set,D=new Set,M=[];for(let b of p){let k=b.id;if((!o.has(k)||!a.has(k))&&(Ls(k,i)?w.add(k):Ls(k,i)||D.add(k)),b.range&&a.has(k)){let O=a.get(k);b.range!=="*"&&!b.range.startsWith("workspace:")&&(Rt(b.range,O)||M.push(`${k}: 依赖范围 ${b.range} 与目标版本 ${O} 无交集`))}}return{order:P,contracts:[...w],business:[...D],cycles:h,conflicts:M}}function oc(e){let t=Q.join(e,"tbox.module.json");if(!ve(t))return null;try{let r=Ee(JSON.parse(yr(t,"utf8")));return r.ok?r.value:null}catch{return null}}function ic(e,t){if(!e)return null;let r=Q.dirname(e),n=Q.join(r,t);if(ve(Q.join(n,"package.json")))return n;try{let s=sc(r).find(i=>i.endsWith(".tgz")&&i.includes(`-${t}-`));if(s)return Q.join(r,s)}catch{}return null}function gr(e,t){let r=Q.join(e,"packages",t);return ve(r)&&!gt(r)?!0:B(e)?.npmModules.some(s=>s.id===t&&s.mode==="sdk")??!1}async function _s(e){await Ns(e,new Set,!1)}async function Ns(e,t,r){let n=[];try{await ac(e,t,r,n)}finally{for(let s of n)s.dispose()}}async function ac(e,t,r,n){let s=process.cwd();if(!B(s))throw new Error("当前目录不是 tbox-app 应用(缺 .tbox/app.json)");let i,o=null,a=null,c=!!e.source&&Ce(e.source);if(e.mode==="local"){let h=e.pkg;i=h.startsWith("module-")?h:`module-${h}`,ke(i,`--mode local ${e.pkg}`)}else{if(!e.source)throw new Error(`--mode ${e.mode} 需要 --source <目录或 tgz 或 registry 包>`);if(!(c&&!ve(Q.resolve(e.source)))&&!e.source.endsWith(".tgz")&&!re.isUsable(e.source))throw new Error(`模块源无效(缺 package.json): ${e.source}`);a=fe(e.source,e.registry),(a.source instanceof U||a.source instanceof de)&&n.push(a.source);let v=e.pkg.replace(/@[^/]+$/,"");i=wn(v),ke(i,`--mode ${e.mode} ${e.pkg}`),o=oc(a.sourceDir)}if(r&&gr(s,i)){console.log(` ⚠️ 依赖 ${i} 已安装,跳过`);return}if(!r&&gr(s,i)){let h=ve(Q.join(s,"packages",i))?`packages/${i}`:"manifest 已登记(sdk 包,无本地目录)";throw new Error(`模块 ${i} 已存在(${h})。若要重新展开请先 remove 或手动处理;装配唯一覆盖入口为 sync --force`)}if(t.has(i))return;if(t.add(i),!e.noDeps){let h=o?.dependencies?.modules??[],v=await Fs(s,[],void 0,[{id:i,deps:h}]);if(v.cycles.length>0)throw new Error(`依赖环:${v.cycles.map($=>$.join(" → ")).join(";")}`);for(let $ of v.conflicts)console.warn(` ⚠️ 依赖版本冲突: ${$}`);let S=[...v.contracts,...v.order].filter($=>$!==i&&!gr(s,$));for(let $ of[...new Set(S)]){let R=/^(module|scenario|contracts|provider)-/.test($)?`@tbox.cn/app-${$}`:`@tbox.cn/${$}`,F=c?R:ic(e.source,$);if(!F){console.warn(` ⚠️ 依赖 ${$} 未安装且未找到源(--source 兄弟目录/tgz),可 --no-deps 跳过`);continue}let A=fe(F,e.registry);(A.source instanceof U||A.source instanceof de)&&n.push(A.source);let N=ve(Q.join(A.sourceDir,"tbox.module.json")),P=un(R,N);await Ns({pkg:R,mode:P,source:F,registry:e.registry},t,!0)}}let l;if(e.mode==="local"){let h=e.pkg,v=h.startsWith("module-")?h:`module-${h}`;await kn(s,h),l={id:v,package:`@app/${v}`,version:"0.1.0",mode:"local"},await He(s,`@app/${v}`,"add")}else{let h=a.sourceDir,v=a.source.resolveVersion();e.mode==="codegen"&&(yt(h),await bn(s,h,{id:i,package:e.pkg.replace(/@[^/]+$/,""),version:v,mode:"codegen",baseVersion:v},Se(s).values()),await He(s,mt(i),"add")),l={id:i,package:e.pkg.replace(/@[^/]+$/,""),version:v,mode:e.mode,...e.mode==="codegen"?{baseVersion:v}:{}}}let p=B(s);if(await te(s,Gr(p,l)),l.mode==="codegen"){let h=await vt(s,Se(s).values());h.length>0&&console.log(` codegen 引用同步: ${h.join(", ")}`)}let u=await me(s);u.changedFiles.length>0&&console.log(` 装配更新: ${u.changedFiles.join(", ")}`);for(let h of u.warnings)console.warn(` ⚠️ ${h}`);for(let h of u.depConflicts)console.warn(` ⚠️ 依赖冲突: ${h}`);let d=await G(s),g=(await q({appDir:s,declared:u.declared,actual:d},{skip:["integration-services"]})).filter(h=>h.level==="error");if(g.length>0){console.error("❌ doctor 错误:");for(let h of g)console.error(` - [${h.rule}] ${h.message}`);process.exitCode=1}let m=await Ne(s);m.generated?console.log(` ⚡ 已生成 mock 槽绑定(config/integrations.json:${m.services.join(", ")})`):m.reason==="in-place"&&console.log(" ℹ config/integrations.json 已在场,跳过 mock 绑定生成"),console.log(`✅ 已安装 ${l.package}(mode=${e.mode}${r?", 依赖":""})`),cc(s,l.id),e.mode!=="local"&&console.log(" 下一步: pnpm install && tbox-app module doctor")}function cc(e,t){try{let r=Q.join(e,"packages",t,"package.json");if(!ve(r))return;let s=JSON.parse(yr(r,"utf8")).dependencies?.["@tbox.cn/app-contracts-mall"],i=typeof s=="string"?s.replace(/[\^~]/,""):void 0;if(!i)return;let o=Q.join(e,"pnpm-workspace.yaml");if(!ve(o))return;let l=yr(o,"utf8").match(/['"]?@tbox\.cn\/app-contracts-mall['"]?\s*:\s*['"]([^'"]+)['"]/)?.[1]?.replace(/[\^~]/,"");if(!l)return;lc(i,l)>0&&console.warn(` ⚠️ 版本配套:${t} 依赖 contracts-mall ${i}+,当前应用 catalog 为 ${l}——请升级模板或应用 catalog(015 W1)`)}catch{}}function lc(e,t){let r=e.split(".").map(Number),n=t.split(".").map(Number),s=(r[0]??0)-(n[0]??0);return Math.sign(s!==0?s:(r[1]??0)-(n[1]??0))}import pc from"path";async function Vs(e,t={}){let r=process.cwd();ke(e,"remove 参数");let n=B(r);if(!n)throw new Error("当前目录不是 tbox-app 应用(缺 .tbox/app.json)");if(!t.force){let m=[];for(let h of await z(r)){if(h.id===e)continue;let v=(h.descriptor.dependencies?.modules??[]).find(S=>S.id===e);v&&v.required&&m.push(h.id)}if(m.length>0)throw new Error(`模块 ${e} 被依赖,无法移除:${m.join(", ")}(--force 忽略)`)}let s=n.npmModules.find(m=>m.id===e),i=!!s;i&&await te(r,qr(n,e));let o=pc.join(r,"packages",e),c=gt(o)&&(s?.mode==="sdk"||!s);(c?!1:await $n(r,e))?console.log(` 已删除 packages/${e}`):c?console.log(" 已移除 manifest 登记(保留 contracts-mall SDK shim)"):s?.mode==="sdk"&&console.log(" 已移除 manifest 登记(sdk 包,无本地目录)");let p=s?.mode==="codegen"&&Je(s.package)?await yn(r,xe(s)):!1;p&&console.log(" 已恢复 contracts-mall SDK shim(@app/contracts-mall)"),!p&&!c&&(s?.mode==="codegen"||s?.mode==="local"||!i)&&await He(r,`@app/${e}`,"remove");let u=await me(r);u.changedFiles.length>0&&console.log(` 装配更新: ${u.changedFiles.join(", ")}`);for(let m of u.warnings)console.warn(` ⚠️ ${m}`);for(let m of u.depConflicts)console.warn(` ⚠️ 依赖冲突: ${m}`);let d=await G(r),g=(await q({appDir:r,declared:u.declared,actual:d})).filter(m=>m.level==="error");if(g.length>0){console.error("❌ doctor 错误:");for(let m of g)console.error(` - [${m.rule}] ${m.message}`);process.exitCode=1}console.log(c&&!i?`✅ ${e} 未登记,保留 SDK shim`:`✅ 已移除 ${e}`)}async function Js(e={}){let t=process.cwd(),r=await me(t,{force:e.force??!1});r.changedFiles.length===0?console.log("装配已同步,无变更"):console.log(`已更新装配文件: ${r.changedFiles.join(", ")}`);for(let o of r.warnings)console.warn(` ⚠️ ${o}`);for(let o of r.depConflicts)console.warn(` ⚠️ 依赖冲突: ${o}`);let n=await G(t),s=await q({appDir:t,declared:r.declared,actual:n}),i=s.filter(o=>o.level==="error");if(i.length>0){console.error("❌ doctor 错误:");for(let o of i)console.error(` - [${o.rule}] ${o.message}`);process.exitCode=1}else console.log("✅ doctor 无错误");for(let o of s.filter(a=>a.level==="warning"))console.warn(` ⚠️ [${o.rule}] ${o.message}`)}async function zs(){let e=process.cwd(),t=B(e),r=await ue(e);if(console.log(`模板版本: ${t?.templateVersion??"未知"}`),r.length===0){console.log("(无已装模块)");return}for(let i of r)console.log(` ${i.id.padEnd(28)} ${i.pkg} [${i.mode}]`);let n=new Set((t?.npmModules??[]).map(i=>i.id)),s=(await z(e)).filter(i=>!n.has(i.id));if(s.length>0){console.log(" 本地模块(未在 app.json 登记):");for(let i of s)console.log(` ${i.id}`)}}async function Us(e){let t=process.cwd(),r=await ue(t),n=await G(t),s=!1;for(let i of se){let o=i.kind,a=r.filter(g=>St(o,g)).map(g=>g.id),c=n[o]?.moduleIds??[],l=a.filter(g=>!c.includes(g)),p=c.filter(g=>!a.includes(g));e&&(l=l.filter(g=>g===e),p=p.filter(g=>g===e));let u=n[o]?.broken??[],d=n[o]?.nonStandardLines??[],f=[];u.length>0&&f.push(`⚠️ 锚区破坏: ${u.join(", ")}`),l.length>0&&f.push(` - 已声明未装配: ${l.join(", ")}`),p.length>0&&f.push(` + 已装配未声明: ${p.join(", ")}`);for(let g of d)f.push(` ? 非标行: ${g}`);if(f.length>0){s=!0,console.log(`${i.path}`);for(let g of f)console.log(` ${g}`)}}s||console.log("无漂移:声明集合与实际装配集合一致")}async function Bs(e={}){let t=process.cwd(),r=await ue(t),n=await G(t),s=await q({appDir:t,declared:r,actual:n},{only:e.only?e.only.split(",").map(a=>a.trim()).filter(Boolean):void 0,skip:e.skip?e.skip.split(",").map(a=>a.trim()).filter(Boolean):void 0}),i=s.filter(a=>a.level==="error"),o=s.filter(a=>a.level==="warning");if(e.json)return console.log(JSON.stringify({errors:i,warnings:o,rules:s},null,2)),i.length>0&&(process.exitCode=1),{issues:s};if(s.length===0)console.log("✅ doctor 全绿(结构校验通过)");else{for(let a of i)console.error(`❌ [${a.rule}] ${a.message}`);for(let a of o)console.warn(`⚠️ [${a.rule}] ${a.message}`);i.length>0?(console.error(`doctor 失败: ${i.length} 个错误`),process.exitCode=1):console.log(`doctor 通过(${o.length} 个告警)`)}return{issues:s}}import{existsSync as vr}from"fs";import{readdir as dc,readFile as Gs,unlink as fc,writeFile as mc}from"fs/promises";import Ae from"path";import{diffArrays as uc}from"diff";function Hs(e,t){let r=uc(e,t),n=[],s=0,i=null;for(let o of r)o.removed?(i?i.oldLines.push(...o.value):i={start:s,oldLines:[...o.value],newLines:[]},s+=o.value.length):o.added?i?i.newLines.push(...o.value):i={start:s,oldLines:[],newLines:[...o.value]}:(i&&(n.push(i),i=null),s+=o.value.length);return i&&n.push(i),n}function Ws(e,t,r){let n=e===""?[]:e.split(`
91
+ `),s=t===""?[]:t.split(`
92
+ `),i=r===""?[]:r.split(`
93
+ `),o=Hs(n,s),a=Hs(n,i),c=new Map;for(let m of o)c.set(m.start,{ours:m});for(let m of a){let h=c.get(m.start);h?h.theirs=m:c.set(m.start,{theirs:m})}let l=[],p=[],u=new Set,d=(m,h)=>{let v=Math.max(m?.oldLines.length??0,h?.oldLines.length??0);return m&&h?JSON.stringify(m.oldLines)===JSON.stringify(h.oldLines)&&JSON.stringify(m.newLines)===JSON.stringify(h.newLines)?l.push(...m.newLines):(p.push([...m.newLines,"--",...h.newLines].join(`
94
+ `)),l.push("<<<<<<< ours",...m.newLines,"=======",...h.newLines,">>>>>>> theirs")):m?l.push(...m.newLines):l.push(...h.newLines),v},f=0;for(;f<n.length;){let m=c.get(f);if(!m||u.has(f)){l.push(n[f]),f++;continue}u.add(f);let h=d(m.ours,m.theirs);h>0&&(f+=h)}let g=c.get(n.length);return g&&!u.has(n.length)&&d(g.ours,g.theirs),{merged:l.join(`
95
+ `),conflicts:p}}var gc=new Set(["node_modules","dist",".git",".tbox"]);function Ks(e){if(e.endsWith(".tgz")&&vr(e)){let t=new U(e);return{dir:t.getDir(),tarball:t}}return{dir:Ae.resolve(e),tarball:null}}async function hr(e){let t=new Map;async function r(n){let s=Ae.join(e,n),i=await dc(s,{withFileTypes:!0});for(let o of i){if(gc.has(o.name))continue;let a=n?`${n}/${o.name}`:o.name,c=Ae.join(s,o.name);if(o.isDirectory())await r(a);else try{t.set(a,await Gs(c,"utf8"))}catch{}}}return await r(""),t}async function qs(e){let t=process.cwd(),r=B(t);if(!r)throw new Error("当前目录不是 tbox-app 应用(缺 .tbox/app.json)");let n=r.npmModules.find(a=>a.id===e.module);if(!n)throw new Error(`模块 ${e.module} 未在 .tbox/app.json 登记`);if(n.mode==="sdk"){if(e.dryRun){console.log(`[dry-run] pnpm update ${n.package}`);return}let{execFileSync:a}=await import("child_process");a("pnpm",["update",n.package],{stdio:"inherit",cwd:t}),console.log(`✅ 已升级 ${n.package}(sdk 模式)`);return}let s=Ae.join(t,"packages",e.module);if(!vr(s))throw new Error(`缺少 packages/${e.module}`);let i=await hr(s),o=[];try{let a,c,l;if(e.source){let k=Ks(e.source);k.tarball&&o.push(k.tarball),n.mode==="codegen"&&yt(k.dir),l=k.dir,a=await hr(k.dir),c=k.tarball?k.tarball.resolveVersion():Z(k.dir)?.version??n.version}else throw new Error("codegen 升级需要 --source <新版本地目录或 tgz>(npm 拉取为 manual,真实发布后回落)");let p=Se(t),u=n.mode==="codegen"?xe(n):null,d=u?new Map([...p,[u.publicName,u]]):p,f=Be(l),g=Be(s),m=ce(l),h=n.mode==="codegen"&&f?Te(f,n,d.values(),m):f,v=new Map,S=null;if(e.baseSource){let k=Ks(e.baseSource);k.tarball&&o.push(k.tarball),S=k.dir,v=await hr(k.dir)}let $=n.mode==="codegen"?ht(i,d.values()):i,R=n.mode==="codegen"?ht(v,d.values()):v,F=n.mode==="codegen"?ht(a,d.values()):a,A=new Set([...R.keys(),...$.keys(),...F.keys()]);A.delete("package.json");let N=new Map,P=[];for(let k of A){let O=R.get(k)??"",j=$.get(k)??"",ee=F.get(k)??"";if(e.ours){j&&N.set(k,j);continue}if(e.theirs){ee&&N.set(k,ee);continue}let Wt=Ws(O,j,ee);N.set(k,Wt.merged),Wt.conflicts.length>0&&P.push(`${k}: ${Wt.conflicts.length} 处冲突`)}if(n.mode==="codegen"&&h){let k={};try{k=JSON.parse(v.get("package.json")??"{}")}catch{k={}}let O=Te(k,n,d.values(),S?ce(S):void 0),j=Te(g,n,d.values(),ce(t)),ee=gn(O,j,h,{ours:e.ours,theirs:e.theirs});N.set("package.json",JSON.stringify(ee,null,2)+`
96
+ `)}else N.set("package.json",await Gs(Ae.join(s,"package.json"),"utf8"));if(e.dryRun){console.log(`[dry-run] ${e.module}: ${A.size} 文件合并,冲突 ${P.length} 处`);for(let k of P)console.log(` ⚠️ ${k}`);return}for(let[k,O]of N){let j=Ae.join(s,k);if(O===""&&vr(j)){await fc(j),console.log(` 🗑 已删除 ${k}(升级源删除该文件)`);continue}await mc(j,O,"utf8")}await te(t,Yr(r,e.module,{baseVersion:c,version:c})),n.mode==="codegen"&&await vt(t,Se(t).values());let w=await me(t),D=await G(t),b=(await q({appDir:t,declared:w.declared,actual:D})).filter(k=>k.level==="error");if(b.length>0){console.error("❌ doctor 错误:");for(let k of b)console.error(` - [${k.rule}] ${k.message}`);process.exitCode=1}if(P.length>0){console.warn("⚠️ 升级完成,存在冲突标记(<<<<<<< ours / >>>>>>> theirs),请人工/Agent 解决后运行 doctor:");for(let k of P)console.warn(` - ${k}`)}else console.log(`✅ 已升级 ${e.module} → ${c}`)}finally{for(let a of o)a.dispose()}}import{existsSync as xr,readFileSync as xc,mkdtempSync as eo,rmSync as Sc}from"fs";import{tmpdir as to}from"os";import le from"path";import{existsSync as ie,readFileSync as Xs,readdirSync as yc}from"fs";import{execFileSync as hc}from"child_process";import J from"path";function Ys(e,t){return`${e.replace(/^@/,"").replace("/","-")}-${t}.tgz`}function vc(e){if(!ie(e)||!ie(J.join(e,"package.json")))throw new Error(`模块目录无效(缺 package.json): ${e}`);let t;try{t=JSON.parse(Xs(J.join(e,"package.json"),"utf8"))}catch{throw new Error(`package.json 解析失败: ${J.join(e,"package.json")}`)}let r=typeof t.name=="string"?t.name:"";if(!/^@tbox\.cn\//.test(r))throw new Error(`发布包名必须为 @tbox.cn/* scope: ${r||"(缺失)"}`);let n=typeof t.version=="string"?t.version:"";if(!/^\d+\.\d+\.\d+/.test(n))throw new Error(`版本号无效(需 semver x.y.z): ${n||"(缺失)"}`);return{name:r,version:n}}function wr(e,t){let{name:r,version:n}=vc(e),s=J.resolve(t);return hc("pnpm",["pack","--pack-destination",s],{stdio:"inherit",cwd:e}),J.join(s,Ys(r,n))}function Zs(e,t){return ie(J.join(e,"pnpm-workspace.template.yaml"))?"template":ie(J.join(e,"tbox.module.json"))?"module":/^@[^/]+\/app-contracts(?:-|$)/.test(t)?"contract":"generic"}function wc(e){let t=[];function r(n){let s=J.join(e,n);for(let i of yc(s,{withFileTypes:!0})){if(i.name==="node_modules"||i.name===".git")continue;let o=n?`${n}/${i.name}`:i.name;i.isDirectory()?r(o):t.push(o)}}return r(""),t}function bc(e){let t=[],r=n=>{typeof n=="string"&&t.push(n)};if(typeof e=="string")r(e);else if(e&&typeof e=="object"){for(let n of Object.values(e))if(typeof n=="string")r(n);else if(n&&typeof n=="object")for(let s of Object.values(n))r(s)}return t}function kc(e,t){let r=[],n=J.join(e,"package.json"),s;try{s=JSON.parse(Xs(n,"utf8"))}catch{return r.push({rule:"pack-manifest",level:"error",message:"产物缺少可解析的 package.json"}),r}let i=t??Zs(e,s.name??""),o=wc(e);for(let p of["dependencies","optionalDependencies","peerDependencies","devDependencies"]){let u=s[p];if(!(!u||typeof u!="object"))for(let[d,f]of Object.entries(u))typeof f=="string"&&/^(workspace:|catalog:)/.test(f)&&r.push({rule:"pack-protocol-residue",level:"error",message:`${d}: 发布态残留 pnpm 私有协议 "${f}"(应已归一化为实装版本)`})}if(s.exports!==void 0)for(let p of bc(s.exports))p.includes("/src/")||p==="src"?r.push({rule:"pack-exports",level:"error",message:`exports 指向开发态源码 "${p}"(发布态应指向 dist/)`}):p.startsWith("./")&&!ie(J.join(e,p))&&r.push({rule:"pack-exports",level:"error",message:`exports 目标缺失(产物内不存在): ${p}`});let a=s.files;if(Array.isArray(a)&&a.length>0){let p=new Set([...a.map(d=>String(d)),"package.json"]),u=o.filter(d=>![...p].some(f=>d===f||d.startsWith(`${f}/`)));u.length>0&&r.push({rule:"pack-whitelist",level:"error",message:`产物含白名单外文件: ${u.slice(0,5).join(", ")}${u.length>5?` 等 ${u.length} 个`:""}`})}else r.push({rule:"pack-whitelist",level:"warning",message:"未声明 files 白名单,产物范围不可控(建议声明 files 限制打包内容)"});let c=o.filter(p=>/(^|\/)\.env$/.test(p)||/(^|\/)\.npmrc$/.test(p));if(c.length>0&&r.push({rule:"pack-leak",level:"error",message:`产物含敏感文件: ${c.join(", ")}`}),i==="module")for(let p of["src","dist","tbox.module.json"])!o.includes(p)&&!ie(J.join(e,p))&&r.push({rule:"pack-files",level:"error",message:`业务模块产物缺少 ${p}`});else if(i==="template"){for(let p of["package.json","pnpm-workspace.template.yaml","apps/server/package.json","apps/client/package.json",".tbox/app.json"])ie(J.join(e,p))||r.push({rule:"pack-files",level:"error",message:`模板产物缺少 ${p}`});for(let p of["pnpm-workspace.yaml","pnpm-lock.yaml"])ie(J.join(e,p))&&r.push({rule:"pack-files",level:"error",message:`模板产物泄漏 ${p}(应仅含 pnpm-workspace.template.yaml / pnpm-lock.template.yaml)`});ie(J.join(e,"pnpm-lock.template.yaml"))||r.push({rule:"pack-files",level:"warning",message:"模板产物未携带 pnpm-lock.template.yaml(间接依赖未冻结——平台包发布后跑 sync-template-lockfile 生成)"})}else if(i==="contract")o.filter(u=>u.startsWith("dist/")&&u.endsWith(".d.ts")).length===0&&r.push({rule:"pack-files",level:"error",message:"契约包产物缺少 dist/*.d.ts"}),s.name==="@tbox.cn/app-contracts-mall"&&!ie(J.join(e,"src","index.ts"))&&r.push({rule:"pack-files",level:"error",message:"contracts-mall codegen 载荷缺少 src/index.ts"});else{let p=s.bin,u=typeof p=="string"?[p]:p&&typeof p=="object"?Object.values(p):[];for(let d of u)typeof d=="string"&&!o.includes(d)&&r.push({rule:"pack-files",level:"error",message:`bin 指向文件缺失: ${d}`})}let l=Array.isArray(a)&&a.length>0;for(let p of["LICENSE","README.md"])o.includes(p)||r.push({rule:"pack-docs",level:"warning",message:`产物缺少 ${p}${l?"(files 白名单已声明;发布完备性 backlog)":"(建议补充以完备发布元数据)"}`});return r}async function br(e){let t=new U(e);try{let r=t.getDir(),n=JSON.parse(await t.readFile("package.json")),s=Zs(r,n.name??""),i=kc(r,s);if(n.name&&n.version){let o=Ys(n.name,n.version);J.basename(e)!==o&&i.push({rule:"pack-filename",level:"error",message:`tgz 文件名 ${J.basename(e)} 与包 ${n.name}@${n.version} 期望 ${o} 不一致`})}return i}finally{t.dispose()}}function kr(e){for(let t of e)console.log(` ${t.level==="error"?"❌":"⚠️"} [${t.rule}] ${t.message}`)}async function Qs(e){let t=J.resolve(process.cwd(),e.module),r=wr(t,e.out);if(console.log(`✅ 已打包 ${J.basename(r)} → ${J.resolve(e.out)}`),e.check){let n=await br(r);kr(n),n.some(s=>s.level==="error")&&(process.exitCode=1)}}async function ro(e){let t=process.cwd(),r=le.resolve(t,e.module);if(!xr(r)||!xr(le.join(r,"package.json")))throw new Error(`模块目录无效(缺 package.json): ${e.module}`);let n=(()=>{try{return JSON.parse(xc(le.join(r,"package.json"),"utf8"))}catch{throw new Error(`模块 package.json 解析失败: ${e.module}`)}})();if(n.private===!0)throw new Error(`模块 ${e.module}(${n.name??"?"})声明 private:true——防误发布门禁拦截(本地 tgz/目录分发;恢复发布见仓库 docs/reference/operations.md 版本策略)`);let s,i=null;if(e.tgz){if(s=le.resolve(e.tgz),!xr(s))throw new Error(`tgz 产物不存在: ${e.tgz}`);let o=new U(s);try{let a=JSON.parse(await o.readFile("package.json"));if(a.name!==n.name)throw new Error(`--tgz 包名 ${a.name??"(未知)"} 与模块 ${n.name??"(未知)"} 不一致(请确认 --tgz 与模块匹配)`);if(a.private===!0)throw new Error(`--tgz 产物 ${le.basename(s)} 标记 private:true——防误发布门禁拦截`)}finally{o.dispose()}}else i=eo(le.join(to(),"tbox-publish-")),s=wr(r,i);try{let o=await br(s);o.length>0&&kr(o);let a=o.filter(p=>p.level==="error");if(a.length>0)throw new Error(`产物门禁失败(${a.length} 处 error),已中止发布: ${le.basename(s)}`);i||(i=eo(le.join(to(),"tbox-publish-")));let{execFileSync:c}=await import("child_process"),l=e.registry?["--registry",e.registry]:[];c("pnpm",["publish",s,"--access",e.access??"public",...l,...e.dryRun?["--dry-run"]:[]],{stdio:"inherit",cwd:i}),console.log(`✅ 已发布 ${le.basename(s)}${e.registry?` → ${e.registry}`:""}${e.dryRun?"(dry-run)":""}`)}finally{if(i)try{Sc(i,{recursive:!0,force:!0})}catch{}}}import{existsSync as At}from"fs";import{cp as $c,readFile as jc,rm as Ec,writeFile as Rc}from"fs/promises";import Oe from"path";function Dc(e){return At(Oe.join(e,"mini.project.json"))&&At(Oe.join(e,"app.json"))&&At(Oe.join(e,"pages"))}async function no(e){let t=e.platform??"alipay";ke(t,"miniapp-container init");let r=process.cwd(),n=B(r);if(!n)throw new Error("当前目录不是 tbox-app 应用(缺 .tbox/app.json)");let s=`@tbox.cn/app-miniapp-container-${t}`,i;if(e.source)i=fe(e.source,e.registry);else try{i=fe(s,e.registry)}catch(o){throw new Error(`未找到 ${s}(registry 拉取失败:${o.message})。离线场景用 --source <容器目录或 tgz>`)}try{let o=i.sourceDir;if(!Dc(o))throw new Error(`源不是小程序容器工程(缺 mini.project.json/app.json/pages): ${e.source??s}`);let a=Oe.join(r,"apps",`miniapp-container-${t}`);if(At(a)){if(!e.force)throw new Error(`容器已存在: apps/miniapp-container-${t}(--force 删除重展开)`);await Ec(a,{recursive:!0,force:!0})}await $c(o,a,{recursive:!0,force:!0,filter:d=>{let f=Oe.basename(d);return!["node_modules","dist",".git",".DS_Store",".tbox"].includes(f)}});let c=Oe.join(a,"package.json"),l=JSON.parse(await jc(c,"utf8")),p=typeof l.name=="string"?l.name:s;l.name=`@app/miniapp-container-${t}`,l.private=!0,await Rc(c,JSON.stringify(l,null,2)+`
97
+ `,"utf8");let u=qt(o);await te(r,Xr(n,{id:t,package:u.name??s,version:i.source.resolveVersion()||u.version,dir:`apps/miniapp-container-${t}`})),De(r),console.log(`✅ 已初始化容器 @app/miniapp-container-${t}(v${i.source.resolveVersion()||u.version},源 ${p})`),console.log(` ⚡ 展开至 apps/miniapp-container-${t}(workspace apps/* 自动纳管)`),console.log(` 下一步: pnpm install && pnpm --filter @app/miniapp-container-${t} dev`)}finally{(i.source instanceof U||i.source instanceof de)&&i.source.dispose()}}import{existsSync as so,readFileSync as Cc}from"fs";import Ze from"path";function Sr(e){return Ze.join(e,"config","integrations.json")}async function oo(e){if(so(Sr(e)))return;let t=await Ne(e);t.generated?console.log(` ⚡ 已生成 mock 槽绑定(config/integrations.json:${t.services.join(", ")})`):(Re(Sr(e),JSON.stringify({services:{}},null,2)+`
98
+ `),console.log(" ⚡ 已创建 config/integrations.json 空骨架(无 local 供给可派生 mock 绑定)"))}function Ot(e){let t=Sr(e);if(!so(t))throw new Error("config/integrations.json 不存在(provider bind 需已初始化的槽位配置面——create 产物或手动创建)");let r=Kr(e);if(r===null){try{JSON.parse(Cc(t,"utf8"))}catch(n){throw new Error(`config/integrations.json 解析失败:${n.message}`)}throw new Error("config/integrations.json 解析失败")}if(!r.services||typeof r.services!="object")throw new Error("integrations.json 缺 services");return r}async function we(e,t){await nt(e).writeIntegrationsConfig(t)}async function Pt(e){let{collectDeclared:t}=await import("./declared-4SBML2FU.js"),r=await t(e),n=await G(e),s=await q({appDir:e,declared:r,actual:n},{only:["integration-services"]}),i=s.filter(o=>o.level==="error");for(let o of i)console.error(` ❌ [${o.rule}] ${o.message}`);for(let o of s.filter(a=>a.level==="warning"))console.warn(` ⚠️ [${o.rule}] ${o.message}`);i.length>0&&(process.exitCode=1)}async function Lt(e){let t=await rt(e);if(!t.strictValidation){let r=t.notes[0]??"升级应用 @tbox.cn/app-contracts 依赖后可用";throw new Error(`integrations: contracts 严格校验面不可用(${t.version??"未安装"},需 ≥0.9)——${r}`)}}async function io(e,t,r,n={}){let s=Ze.resolve(n.appDir??process.cwd());await Lt(s),await oo(s);let i=Ot(s);if(n.instance){let o=i.services[e]??{instances:{}};o.instances??={};let a={...o.instances[n.instance],provider:t};r?a.implementation=r:delete a.implementation,o.instances[n.instance]=a,i.services[e]=o,await we(s,i),console.log(r?`✅ 已绑定 ${e}@${n.instance} → ${t}/${r}(实例覆盖位)`:`✅ 已绑定 ${e}@${n.instance} → ${t}(实例覆盖位——implementation 派生:catalog 唯一供给)`)}else{let o=i.services[e];o?(o.provider=t,r?o.implementation=r:delete o.implementation):i.services[e]=r?{provider:t,implementation:r}:{provider:t},await we(s,i),console.log(r?`✅ 已绑定 ${e} → ${t}/${r}${o?"(实例集保留)":"(新槽——instances 缺席 = 通配激活,立即生效;限定实例集请补 instances 显式键)"}`:`✅ 已绑定 ${e} → ${t}(implementation 派生:catalog 唯一供给)${o?"(实例集保留)":"(新槽——instances 缺席 = 通配激活,立即生效;限定实例集请补 instances 显式键)"}`)}await Pt(s)}async function ao(e,t={}){let r=Ze.resolve(t.appDir??process.cwd());await Lt(r),await oo(r);let n=Ot(r);if(t.domain)n.domains??={},n.domains[t.domain]={...n.domains[t.domain],provider:e},await we(r,n),console.log(`✅ 域默认厂商 ${t.domain} → ${e}(该域全部槽生效——槽级显式绑定除外)`);else{n.provider=e,await we(r,n),console.log(`✅ 应用默认厂商 → ${e}(全部未显式绑定槽生效——instances 缺席槽即通配激活)`);let s=Object.entries(n.domains??{}).filter(([,i])=>typeof i.provider=="string"&&i.provider!==e).map(([i])=>i);s.length>0&&console.log(` ℹ 域级覆盖仍优先生效:${s.join(", ")}`)}await Pt(r)}async function co(e={}){let t=Ze.resolve(e.appDir??process.cwd());await Lt(t);let r=Ot(t);if(e.domain){let n=r.domains?.[e.domain];if(!n||n.provider===void 0)throw new Error(`域 ${e.domain} 无默认厂商(无可删项)`);delete n.provider,Object.keys(n).length===0&&delete r.domains?.[e.domain],r.domains&&Object.keys(r.domains).length===0&&delete r.domains,await we(t,r),console.log(`✅ 已清除域默认厂商 ${e.domain}(该域槽回落槽级/root 继承)`)}else{if(r.provider===void 0)throw new Error("root 无默认厂商(无可删项——provider use 设置)");delete r.provider,await we(t,r),console.log("✅ 已清除应用默认厂商(未显式绑定槽将 binding-unresolved——doctor 即时反馈)")}await Pt(t)}async function lo(e,t={}){let r=Ze.resolve(t.appDir??process.cwd());await Lt(r);let n=Ot(r),s=n.services[e];if(!s)throw new Error(`服务槽 ${e} 未配置(无可解绑项)`);if(t.instance){if(!s.instances?.[t.instance])throw new Error(`服务槽 ${e} 无实例 ${t.instance}(无可解绑项)`);delete s.instances[t.instance],await we(r,n),console.log(`✅ 已解绑 ${e}@${t.instance}(实例覆盖删除——回落槽级默认)`)}else delete n.services[e],await we(r,n),console.log(`✅ 已解绑 ${e}(槽与实例集删除——模块走降级链)`);await Pt(r)}import{existsSync as po,readFileSync as uo}from"fs";import je from"path";async function Tc(e){let t=new Map,r=s=>{typeof s.provider!="string"||typeof s.implementation!="string"||t.set(`${s.provider}-${s.implementation}`,{provider:s.provider,implementation:s.implementation})};for(let s of await z(e))for(let i of s.descriptor.contributes.providers?.slots??[])r(i);let n=je.join(e,".tbox","app.json");if(po(n))try{let s=JSON.parse(uo(n,"utf8"));for(let i of s.npmModules??[]){if(typeof i.id!="string"||typeof i.package!="string")continue;let o=Kt(je.join(e,"packages",i.id))??Kt(je.join(Mc(e),...i.package.split("/")));for(let a of o?.contributes.providers?.slots??[])r(a)}}catch{}return t}function Mc(e){return je.join(e,"node_modules")}function Ic(e,t){return t.get(e)}async function fo(e={}){let t=je.resolve(e.appDir??process.cwd()),r=je.join(t,"config","integrations.json");if(!po(r))throw new Error(`config/integrations.json 不存在:${r}`);let n=JSON.parse(uo(r,"utf8"));if(!n.services||typeof n.services!="object")throw new Error("integrations.json 缺 services");let s=await Tc(t),i=[],o=[],a=0,c=(l,p)=>{let u=Ic(p,s);return u?(i.push(` ${l}: '${p}' → provider='${u.provider}', implementation='${u.implementation}'`),a++):o.push(`${l}: '${p}'`),u};for(let[l,p]of Object.entries(n.services)){if(typeof p.provider=="string"&&p.provider){let u=c(`services.${l}.provider`,p.provider);u&&(p.provider=u.provider,p.implementation=u.implementation)}else if(p.provider&&typeof p.provider=="object"){let u=p.provider;typeof u.providerId=="string"&&(u.provider=u.providerId,delete u.providerId,i.push(` services.${l}.provider: 内联声明 providerId → provider(直译)`),a++)}for(let[u,d]of Object.entries(p.instances??{})){if(typeof d.provider=="string"&&d.provider){let f=c(`services.${l}.instances.${u}.provider`,d.provider);f&&(d.provider=f.provider,d.implementation=f.implementation)}d.enabled===!0&&delete d.enabled}}console.log(`provider migrate:${a} 项迁移(反查表 ${s.size} 条)`);for(let l of i)console.log(l);if(o.length>0){console.warn("⚠️ 未命中反查表(人工处理,保留原值):");for(let l of o)console.warn(` ${l}`);process.exitCode=1}e.write?(await nt(t).writeIntegrationsConfig(n),console.log(`✅ 已写回 ${je.relative(process.cwd(),r)||r}`)):console.log("(dry-run:加 --write 落盘)")}import{spawn as Ac}from"child_process";import{existsSync as I,mkdirSync as $r,readFileSync as X,realpathSync as ae,readdirSync as Oc,rmSync as Pc,symlinkSync as Lc,unlinkSync as bo,writeFileSync as Ft}from"fs";import{createHash as Fc}from"crypto";import x from"path";import{fileURLToPath as _c}from"url";var Pe=".tbox-dev.local",mo="templates/template-agent-mall",Nc=["apps","server"],Vc=8e3;function Jc(e){let t=x.join(e,".real-env","config");return I(t)?Oc(t).filter(r=>I(x.join(t,r,"integrations.json"))).sort():[]}var go="'../../.tbox-dev.local/vite-override.mjs'",zc="TBOX_CONFIG_DIR";function Uc(){if(process.env.NODE_ENV==="test"&&process.env.TBOX_DEV_REPO_ROOT)return ae(process.env.TBOX_DEV_REPO_ROOT);let e=x.dirname(_c(import.meta.url));for(let t=0;t<10;t++){if(I(x.join(e,"pnpm-workspace.yaml")))return e;let r=x.dirname(e);if(r===e)break;e=r}throw new Error("未能定位仓库根(缺少 pnpm-workspace.yaml)——请在仓库内执行 tbox-app dev")}function Bc(e){let t=x.join(e,"package.json");if(!I(t))throw new Error(`目录缺 package.json:${e}`);try{let r=JSON.parse(X(t,"utf8"));if(typeof r.name!="string"||!r.name)throw new Error("name 缺失");return{name:r.name,version:String(r.version??"0.0.0")}}catch(r){throw new Error(`package.json 解析失败(${e}):${r.message}`)}}function Hc(e){let t=x.join(e,"tbox.module.json");if(!I(t))throw new Error(`目录缺 tbox.module.json:${e}(是否传了非模块目录?)`);let r=Ee(JSON.parse(X(t,"utf8")));if(!r.ok)throw new Error(`tbox.module.json 校验失败(${e}):
99
+ ${r.errors.join(`
100
+ `)}`);return r.value}function yo(e,t,r){let n=x.join(e,"src",t,"index.ts");if(!I(n))return!1;try{return new RegExp(`export\\s+const\\s+${r}\\b`).test(X(n,"utf8"))}catch{return!1}}function Wc(e){let t=x.join(e,"apps","client","vite.config.ts"),r=x.join(e,"apps","server","src","index.ts");if(!I(t))throw new Error(`模板缺 apps/client/vite.config.ts:${e}`);if(!I(r))throw new Error(`模板缺 apps/server/src/index.ts:${e}`);if(!X(t,"utf8").includes(go))throw new Error(`模板 vite.config.ts 缺 dev 能力探测标记("${go}")——模板过旧,请升级模板后重试`);let n=x.join(e,"apps","server","src","app.ts"),s=[X(r,"utf8"),...I(n)?[X(n,"utf8")]:[]].join(`
101
+ `);if((s.includes("registerAppConfig")||s.includes("loadChatConfig"))&&!s.includes(zc))throw new Error("模板 server 装配源(apps/server/src/index.ts|app.ts)缺 TBOX_CONFIG_DIR 锚点——模板过旧,请升级模板后重试");if(!I(x.join(e,"apps","server","src","modules.ts")))throw new Error(`模板缺 apps/server/src/modules.ts 装配文件:${e}`);if(!I(x.join(e,"apps","client","src","modules.ts")))throw new Error(`模板缺 apps/client/src/modules.ts 装配文件:${e}`)}function Kc(e){if((e==="wanda"||e==="joycity")&&!process.env.TBOX_API_KEY)throw new Error(`选择真实档 --real-env ${e} 但环境缺 TBOX_API_KEY —— 请先 source .real-env/env.sh 再运行`)}function ko(e){if(!Number.isInteger(e)||e<=0)return!1;try{return process.kill(e,0),!0}catch{return!1}}var Gc=new Set(["contracts-mall"]);function qc(e,t){return{id:e,pkg:`@tbox.cn/app-${e}`,version:"",dir:t,skeleton:!0,descriptor:{schemaVersion:1,name:e,version:"0.0.0",kind:"business",risk:{level:"low",writeBoundary:"source"},distribution:{defaultMode:"codegen"},contributes:{handlers:[],tools:[],cards:[],routes:[],pages:[],tabs:[],providers:{slots:[]},services:[],resources:[]},dependencies:{modules:[]},env:[]}}}function Xc(e,t,r){let n=new Map,s=[],i=c=>{let l=Bc(c),p=Hc(c);if(n.has(p.name))return n.get(p.name);let u={id:p.name,pkg:l.name,version:l.version,dir:c,descriptor:p};return n.set(p.name,u),u},o=(c,l,p)=>{if(n.has(l))return n.get(l);if(Gc.has(l)){let d=I(x.join(r,"packages",l))?ae(x.join(r,"packages",l)):x.join(t,"modules",l);if(!I(d)){if(!p)return null;throw new Error(`依赖契约包 "${l}" 真源缺席(模板 packages/ 与 modules/ 均无该目录)`)}let f=qc(l,d);return n.set(l,f),f}let u=x.join(t,"modules",l);if(!I(u)){if(!p)return null;throw new Error(`依赖模块 <${c.id}> 声明依赖 "${l}",但仓库无 <repoRoot>/modules/${l}——请确认目录名或先 clone 对应模块`)}return i(ae(u))},a=c=>{if(s.includes(c.id))throw new Error(`模块依赖环:${[...s.slice(s.indexOf(c.id)),c.id].join(" → ")}`);s=[...s,c.id];for(let l of c.descriptor.dependencies.modules)a(o(c,l.id,l.required)??c);s=s.slice(0,-1)};for(let c of e){if(!I(c))throw new Error(`--module 目录不存在:${c}(相对路径按仓库根解析)`);a(i(ae(c)))}return[...n.values()]}function Yc(e){return{id:e.id,pkg:e.pkg,mode:"local",hasServerEntry:yo(e.dir,"server","serverModule"),hasClientEntry:yo(e.dir,"client","clientModule"),hasCards:e.descriptor.contributes.cards.length>0}}function Zc(e,t,r){let n=t.map(Yc),s=[],i=[["server-modules",x.join(e,"apps","server","src","modules.ts"),"modules.server.ts"],["client-modules",x.join(e,"apps","client","src","modules.ts"),"modules.client.ts"]];for(let[o,a,c]of i){let l=X(a,"utf8"),p=l;try{let u=jt(l,o,n);p=u.content,s.push(...u.warnings)}catch(u){s.push(`[${o}] 锚点变换异常,原文兜底:${u.message}`)}Ft(x.join(r,c),p,"utf8")}return s}function Qc(e,t,r,n){let s={},i=(o,a)=>{s[o]=x.relative(t,a).split(x.sep).join("/")};for(let o of r)o.skeleton||(i(o.pkg,o.dir),i(`@app/${o.id}`,o.dir));i("@tbox.cn/app-sdk",x.join(e,"packages","app-sdk")),i("@tbox.cn/app-contracts",x.join(e,"packages","contracts"));for(let[o,a]of Object.entries(s))if(!I(x.resolve(t,a)))throw new Error(`redirect 目标缺失:${o} → ${a}`);return s}function ho(e,t,r){let n=[],s=0;for(let[i,o]of Object.entries(r)){let a=ae(x.resolve(x.join(e,Pe),o)),c=i.split("/"),l=x.join(e,"node_modules",c[0]),p=x.join(l,c[1]);if(!(()=>{if(!I(p))return!0;let d=ae(p);return d===a?!1:d.startsWith(x.join(t,"modules")+x.sep)||x.join(t,"modules")===d})()){s++;continue}if(I(p)){let d=ae(p);if(!d.startsWith(x.join(t,"modules")))throw new Error(`refuse 覆盖非 dev 管理的既有链接:${p} → ${d}(疑为 workspace/依赖原生链接;请人工确认)`)}$r(l,{recursive:!0}),Lc(x.relative(l,a),p),n.push(i)}return{planted:n,skipped:s}}function el(e){let t=JSON.parse(X(e,"utf8")),r=new Set;typeof t.provider=="string"&&t.provider&&r.add(t.provider);for(let n of Object.values(t.domains??{}))typeof n?.provider=="string"&&n.provider&&r.add(n.provider);for(let n of Object.values(t.services??{})){let s=typeof n.provider=="string"?n.provider:n.provider?.provider;typeof s=="string"&&s&&r.add(s)}return r}function tl(e,t,r,n,s=[]){if(n){for(let a of["packages/app-sdk","packages/contracts"])if(!I(x.join(e,a)))throw new Error(`--link-sdk 要求平台真源在场:<repoRoot>/${a}`)}let i={},o;for(let a of s){let c=x.isAbsolute(a)?a:x.resolve(e,a);if(!I(c))throw new Error(`独立配置档目录不存在:${c}(--config-dir 须为存在的目录)`);o=c}if(r){let a=r.includes("-")?r.slice(0,r.indexOf("-")):r,c=x.join(e,".real-env","config",a);if(!I(c))throw new Error(`档位目录不存在:${c}(可用:${Jc(e).join("/")})`);o||(o=c)}if(o){let a=x.join(o,"integrations.json");if(I(a)){let c=el(a),l=new Set;for(let u of t)for(let d of u.descriptor.contributes.providers.slots)l.add(d.provider);let p=[...c].filter(u=>!l.has(u));if(p.length>0)throw new Error(`一致性校验失败:provider ${p.join(", ")} 被 ${x.relative(e,o)||o} 档 integrations.json 引用但未随 dev 挂载——请追加 --module modules/provider-… 或更换 --config-dir / --real-env 档位`);i.TBOX_INTEGRATIONS_FILE=a}i.TBOX_CONFIG_DIR=o}return i}function rl(e,t,r,n,s){let i=JSON.stringify({templateDir:e,tier:s.tier??null,linkSdk:!!s.linkSdk,port:s.port??null,redirects:Object.fromEntries(Object.entries(t).sort(([o],[a])=>o<a?-1:1)),configs:Object.fromEntries(Object.entries(r).sort(([o],[a])=>o<a?-1:1)),modules:n.map(o=>`${o.id}@${o.version}`).sort()});return Fc("sha256").update(i).digest("hex").slice(0,16)}var nl=`// tbox-app dev 生成态构件(勿手改;regenerated by 'tbox-app dev')
102
+ import { register } from 'node:module';
103
+ register('./hook-core.mjs', import.meta.url);
104
+ `,sl=`// tbox-app dev 生成态构件(勿手改;regenerated by 'tbox-app dev')。
105
+ // resolve 钩子:① 相对 modules.js/modules.ts/modules → 物化装配 ② manifest 前缀重定向(mini exports)。
106
+ import { existsSync, readFileSync } from 'node:fs';
107
+ import { fileURLToPath, pathToFileURL } from 'node:url';
108
+ import path from 'node:path';
109
+
110
+ const HERE = path.dirname(fileURLToPath(import.meta.url));
111
+ let REDIRECTS = {};
112
+ try {
113
+ REDIRECTS = JSON.parse(readFileSync(path.join(HERE, 'dev-manifest.json'), 'utf8')).redirects ?? {};
114
+ } catch {}
115
+ const KEYS = Object.keys(REDIRECTS).sort((a, b) => b.length - a.length);
116
+
117
+ /** 按真身 package.json exports 解析子路径(铁律:exports 恒指 src/);无 exports → src 约定 */
118
+ function resolveSub(dir, sub) {
119
+ const pkgFile = path.join(dir, 'package.json');
120
+ if (existsSync(pkgFile)) {
121
+ try {
122
+ const pkg = JSON.parse(readFileSync(pkgFile, 'utf8'));
123
+ const exp = pkg.exports ? pkg.exports[sub === '' ? '.' : sub] : undefined;
124
+ const target =
125
+ typeof exp === 'string'
126
+ ? exp
127
+ : exp && typeof exp === 'object'
128
+ ? (exp.import ?? exp.default)
129
+ : sub === ''
130
+ ? pkg.main
131
+ : undefined;
132
+ if (typeof target === 'string') return pathToFileURL(path.resolve(dir, target)).href;
133
+ } catch {}
134
+ }
135
+ const guess = { '': 'src/index.ts', '/server': 'src/server/index.ts', '/client': 'src/client/index.ts' }[sub];
136
+ if (guess) {
137
+ const file = path.join(dir, guess);
138
+ if (existsSync(file)) return pathToFileURL(file).href;
1299
139
  }
1300
140
  return null;
1301
141
  }
1302
- function findModuleRegisterBody(src, objectName) {
1303
- const sf = parseSource(src);
1304
- let result = null;
1305
- function visit(node) {
1306
- if (result) return;
1307
- if (ts.isVariableStatement(node) && node.declarationList.declarations.some(
1308
- (d) => ts.isIdentifier(d.name) && d.name.text === objectName
1309
- )) {
1310
- const decl = node.declarationList.declarations.find(
1311
- (d) => ts.isIdentifier(d.name) && d.name.text === objectName
1312
- );
1313
- const methodBody = objectMethodBody(unwrapAs(decl.initializer), "register");
1314
- if (methodBody) {
1315
- result = methodBody;
1316
- return;
1317
- }
1318
- }
1319
- ts.forEachChild(node, visit);
1320
- }
1321
- visit(sf);
1322
- return result ?? findFunctionBody(src, "registerServer");
1323
- }
1324
- function hasTopLevelResolveService(body) {
1325
- let found = false;
1326
- function visit(node) {
1327
- if (found) return;
1328
- if (ts.isArrowFunction(node)) return;
1329
- if (ts.isCallExpression(node)) {
1330
- const expr = node.expression;
1331
- if (ts.isIdentifier(expr) && expr.text === "resolveService") {
1332
- found = true;
1333
- return;
1334
- }
1335
- const text = expr.getText();
1336
- if (text === "ctx.resolveService" || text.endsWith(".services.resolve")) {
1337
- found = true;
1338
- return;
1339
- }
1340
- }
1341
- ts.forEachChild(node, visit);
1342
- }
1343
- visit(body);
1344
- return found;
1345
- }
1346
- function hasCardsRegisterCall(body) {
1347
- let found = false;
1348
- function visit(node) {
1349
- if (found) return;
1350
- if (ts.isCallExpression(node)) {
1351
- const text = node.expression.getText();
1352
- if (text.endsWith(".cards.registerMap") || text.endsWith(".cards.register")) {
1353
- found = true;
1354
- return;
1355
- }
1356
- }
1357
- ts.forEachChild(node, visit);
1358
- }
1359
- visit(body);
1360
- return found;
1361
- }
1362
- function extractCardsRegisterArgs(src) {
1363
- const sf = parseSource(src);
1364
- const args = [];
1365
- function visit(node) {
1366
- if (ts.isCallExpression(node)) {
1367
- const exprText = node.expression.getText();
1368
- if (exprText.endsWith(".cards.register")) {
1369
- const arg = node.arguments[0];
1370
- if (arg) args.push(arg.getText().replace(/['"]/g, ""));
1371
- }
1372
- }
1373
- ts.forEachChild(node, visit);
1374
- }
1375
- visit(sf);
1376
- return args;
1377
- }
1378
- function extractRelativeImports(src) {
1379
- const sf = parseSource(src);
1380
- const map = {};
1381
- function visit(node) {
1382
- if (ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier)) {
1383
- const spec = node.moduleSpecifier.text;
1384
- if (spec.startsWith("./") || spec.startsWith("../")) {
1385
- const clause = node.importClause;
1386
- if (clause?.name) {
1387
- map[clause.name.text] = spec;
1388
- }
1389
- }
1390
- }
1391
- ts.forEachChild(node, visit);
1392
- }
1393
- visit(sf);
1394
- return map;
1395
- }
1396
-
1397
- // src/doctor/rules/register-pure.ts
1398
- async function registerPureRule(ctx) {
1399
- const issues = [];
1400
- const packagesDir = path13.join(ctx.appDir, "packages");
1401
- if (!existsSync9(packagesDir)) return issues;
1402
- for (const entry of await readdir5(packagesDir, { withFileTypes: true })) {
1403
- if (!entry.isDirectory()) continue;
1404
- const entryFile = path13.join(packagesDir, entry.name, "src/server/index.ts");
1405
- if (!existsSync9(entryFile)) continue;
1406
- const src = await readTextOrEmpty(entryFile);
1407
- const body = findModuleRegisterBody(src, "serverModule");
1408
- if (body && hasTopLevelResolveService(body)) {
1409
- issues.push({
1410
- rule: "register-pure",
1411
- level: "error",
1412
- message: `${entry.name}: \u6CE8\u518C\u5165\u53E3\uFF08serverModule.register\uFF09\u5185\u51FA\u73B0 resolveService\uFF08\u53EA\u5141\u8BB8\u5728 handle/\u4E8B\u4EF6\u56DE\u8C03\u5185\uFF09`
1413
- });
1414
- }
1415
- }
1416
- return issues;
1417
- }
1418
142
 
1419
- // src/doctor/module-cards.ts
1420
- import { existsSync as existsSync10 } from "fs";
1421
- import path14 from "path";
1422
- import ts2 from "typescript";
1423
- function unwrapAs2(expr) {
1424
- if (ts2.isAsExpression(expr) || ts2.isSatisfiesExpression(expr)) return expr.expression;
1425
- return expr;
1426
- }
1427
- function parseCardsObject(src, objectName) {
1428
- const sf = parseSource(src);
1429
- let result = null;
1430
- function mapFromObject(obj) {
1431
- const map = {};
1432
- for (const prop of obj.properties) {
1433
- if (!ts2.isPropertyAssignment(prop)) continue;
1434
- const name = prop.name;
1435
- const key = ts2.isIdentifier(name) ? name.text : ts2.isStringLiteral(name) ? name.text : "";
1436
- if (key) map[key] = prop.initializer;
1437
- }
1438
- return map;
1439
- }
1440
- for (const stmt of sf.statements) {
1441
- if (!ts2.isVariableStatement(stmt)) continue;
1442
- for (const d of stmt.declarationList.declarations) {
1443
- if (!ts2.isIdentifier(d.name) || !d.initializer) continue;
1444
- if (d.name.text === "cards") {
1445
- const init = unwrapAs2(d.initializer);
1446
- if (ts2.isObjectLiteralExpression(init)) {
1447
- result = mapFromObject(init);
1448
- return result;
1449
- }
1450
- }
1451
- if (objectName && d.name.text === objectName) {
1452
- const init = unwrapAs2(d.initializer);
1453
- if (ts2.isObjectLiteralExpression(init)) {
1454
- for (const prop of init.properties) {
1455
- if (ts2.isPropertyAssignment(prop) && ts2.isIdentifier(prop.name) && prop.name.text === "cards") {
1456
- const cardsInit = unwrapAs2(prop.initializer);
1457
- if (ts2.isObjectLiteralExpression(cardsInit)) {
1458
- result = mapFromObject(cardsInit);
1459
- return result;
1460
- }
1461
- }
1462
- }
1463
- }
1464
- }
1465
- }
1466
- }
1467
- return result;
1468
- }
1469
- function parseClientCardsMap(src) {
1470
- const map = parseCardsObject(src, "clientModule");
1471
- return map ? Object.keys(map) : [];
1472
- }
1473
- function extractMetaFields(src) {
1474
- const ct = src.match(/cardType:\s*['"]([^'"]+)['"]/);
1475
- const sv = src.match(/schemaVersion:\s*(\d+)/);
1476
- return {
1477
- cardType: ct?.[1],
1478
- schemaVersion: sv ? Number(sv[1]) : void 0
1479
- };
1480
- }
1481
- function resolveTsPath(base, rel) {
1482
- const candidates = [rel, `${rel}.ts`, `${rel}.tsx`, `${rel}/index.ts`, `${rel}/index.tsx`];
1483
- for (const c of candidates) {
1484
- if (existsSync10(path14.join(base, c))) return path14.join(base, c);
1485
- }
1486
- return null;
1487
- }
1488
- function resolveValueCardType(expr, imports, baseDir) {
1489
- const e = unwrapAs2(expr);
1490
- if (ts2.isObjectLiteralExpression(e)) {
1491
- const fields = extractMetaFields(e.getText());
1492
- return { cardType: fields.cardType, schemaVersion: fields.schemaVersion };
1493
- }
1494
- if (ts2.isStringLiteral(e)) {
1495
- return { cardType: e.text };
1496
- }
1497
- if (ts2.isIdentifier(e)) {
1498
- const rel = imports[e.text];
1499
- if (!rel) return null;
1500
- const resolved = resolveTsPath(baseDir, rel.replace(/^\.\//, ""));
1501
- if (!resolved) return null;
1502
- return { resolved };
143
+ export async function resolve(specifier, context, nextResolve) {
144
+ // 装配文件改指:tsx 可能先把 .js 改写为 .ts,两个形态都指向物化装配
145
+ if (/^\\.{1,2}\\/modules(?:\\.(?:js|ts))?$/.test(specifier)) {
146
+ return { url: pathToFileURL(path.join(HERE, 'modules.server.ts')).href, shortCircuit: true };
147
+ }
148
+ // manifest 前缀重定向(最少惊讶:种植链接为主通道,此处兜底 + linkSdk 单向)
149
+ const base = specifier.split('?')[0];
150
+ const hit = KEYS.find((k) => base === k || base.startsWith(k + '/'));
151
+ if (hit) {
152
+ const sub = base === hit ? '' : base.slice(hit.length);
153
+ const url = resolveSub(path.resolve(HERE, REDIRECTS[hit]), sub);
154
+ if (url) return { url, shortCircuit: true };
155
+ }
156
+ return nextResolve(specifier, context);
157
+ }
158
+ `,ol=`// tbox-app dev 生成态构件(勿手改;regenerated by 'tbox-app dev')。
159
+ // Vite 插件:client 装配改指物化构件 + manifest 前缀重定向 + fs 门面扩容。
160
+ import { existsSync, readFileSync } from 'node:fs';
161
+ import { fileURLToPath } from 'node:url';
162
+ import path from 'node:path';
163
+
164
+ const HERE = path.dirname(fileURLToPath(import.meta.url));
165
+ const MANIFEST = JSON.parse(readFileSync(path.join(HERE, 'dev-manifest.json'), 'utf8'));
166
+ const KEYS = Object.keys(MANIFEST.redirects ?? {}).sort((a, b) => b.length - a.length);
167
+
168
+ function resolveSub(dir, sub) {
169
+ const pkgFile = path.join(dir, 'package.json');
170
+ if (existsSync(pkgFile)) {
171
+ try {
172
+ const pkg = JSON.parse(readFileSync(pkgFile, 'utf8'));
173
+ const exp = pkg.exports ? pkg.exports[sub === '' ? '.' : sub] : undefined;
174
+ const target =
175
+ typeof exp === 'string'
176
+ ? exp
177
+ : exp && typeof exp === 'object'
178
+ ? (exp.import ?? exp.default)
179
+ : sub === ''
180
+ ? pkg.main
181
+ : undefined;
182
+ if (typeof target === 'string') return path.resolve(dir, target);
183
+ } catch {}
184
+ }
185
+ const guess = { '': 'src/index.ts', '/server': 'src/server/index.ts', '/client': 'src/client/index.ts' }[sub];
186
+ if (guess) {
187
+ const file = path.join(dir, guess);
188
+ if (existsSync(file)) return file;
1503
189
  }
1504
190
  return null;
1505
191
  }
1506
- async function resolveServerRegisteredCards(modDir) {
1507
- const entryFile = path14.join(modDir, "src/server/index.ts");
1508
- if (!existsSync10(entryFile)) return { cardTypes: [], schemaVersions: {}, keyMismatches: [], registerMissing: false };
1509
- const entrySrc = await readTextOrEmpty(entryFile);
1510
- const imports = extractRelativeImports(entrySrc);
1511
- const baseDir = path14.join(modDir, "src/server");
1512
- const cardTypes = [];
1513
- const schemaVersions = {};
1514
- const keyMismatches = [];
1515
- const cardsMap = parseCardsObject(entrySrc, "serverModule");
1516
- if (cardsMap) {
1517
- let registerMissing = false;
1518
- if (Object.keys(cardsMap).length > 0) {
1519
- const registerBody = findModuleRegisterBody(entrySrc, "serverModule");
1520
- registerMissing = !registerBody || !hasCardsRegisterCall(registerBody);
1521
- }
1522
- for (const [key, expr] of Object.entries(cardsMap)) {
1523
- const value = resolveValueCardType(expr, imports, baseDir);
1524
- if (!value) continue;
1525
- if (value.resolved) {
1526
- const metaSrc = await readTextOrEmpty(value.resolved);
1527
- const fields = extractMetaFields(metaSrc);
1528
- if (fields.cardType) {
1529
- cardTypes.push(fields.cardType);
1530
- if (fields.schemaVersion) schemaVersions[fields.cardType] = fields.schemaVersion;
1531
- if (key !== fields.cardType) keyMismatches.push({ key, cardType: fields.cardType });
1532
- }
1533
- } else if (value.cardType) {
1534
- cardTypes.push(value.cardType);
1535
- if (value.schemaVersion) schemaVersions[value.cardType] = value.schemaVersion;
1536
- if (key !== value.cardType) keyMismatches.push({ key, cardType: value.cardType });
1537
- }
1538
- }
1539
- return { cardTypes: [...new Set(cardTypes)], schemaVersions, keyMismatches, registerMissing };
1540
- }
1541
- for (const arg of extractCardsRegisterArgs(entrySrc)) {
1542
- if (arg.includes("cardType:")) {
1543
- const fields2 = extractMetaFields(arg);
1544
- if (fields2.cardType) {
1545
- cardTypes.push(fields2.cardType);
1546
- if (fields2.schemaVersion) schemaVersions[fields2.cardType] = fields2.schemaVersion;
1547
- }
1548
- continue;
1549
- }
1550
- if (/^[a-zA-Z0-9][\w-]*$/.test(arg) && !imports[arg]) {
1551
- cardTypes.push(arg);
1552
- continue;
1553
- }
1554
- const rel = imports[arg];
1555
- if (!rel) continue;
1556
- const resolved = resolveTsPath(baseDir, rel.replace(/^\.\//, ""));
1557
- if (!resolved) continue;
1558
- const metaSrc = await readTextOrEmpty(resolved);
1559
- const fields = extractMetaFields(metaSrc);
1560
- if (fields.cardType) {
1561
- cardTypes.push(fields.cardType);
1562
- if (fields.schemaVersion) schemaVersions[fields.cardType] = fields.schemaVersion;
1563
- }
1564
- }
1565
- return { cardTypes: [...new Set(cardTypes)], schemaVersions, keyMismatches, registerMissing: false };
1566
- }
1567
- function hasModuleObject(src, objectName) {
1568
- const sf = parseSource(src);
1569
- return sf.statements.some(
1570
- (stmt) => ts2.isVariableStatement(stmt) && stmt.declarationList.declarations.some(
1571
- (d) => ts2.isIdentifier(d.name) && d.name.text === objectName
1572
- )
1573
- );
1574
- }
1575
- async function resolveClientRegisterMissing(modDir) {
1576
- const entryFile = path14.join(modDir, "src/client/index.ts");
1577
- if (!existsSync10(entryFile)) return false;
1578
- const entrySrc = await readTextOrEmpty(entryFile);
1579
- if (!hasModuleObject(entrySrc, "clientModule")) return false;
1580
- const cardsMap = parseCardsObject(entrySrc, "clientModule");
1581
- if (!cardsMap || Object.keys(cardsMap).length === 0) return false;
1582
- const registerBody = findModuleRegisterBody(entrySrc, "clientModule");
1583
- return !registerBody || !hasCardsRegisterCall(registerBody);
1584
- }
1585
- async function collectModuleCards(ctx) {
1586
- const modules = await listLocalModules(ctx.appDir);
1587
- const result = [];
1588
- for (const mod of modules) {
1589
- const declared = {};
1590
- for (const c of mod.descriptor.contributes.cards ?? []) {
1591
- declared[c.cardType] = c.schemaVersion ?? 1;
1592
- }
1593
- const clientFile = path14.join(mod.dir, "src/client/index.ts");
1594
- const clientMap = existsSync10(clientFile) ? parseClientCardsMap(await readTextOrEmpty(clientFile)) : [];
1595
- const server = await resolveServerRegisteredCards(mod.dir);
1596
- result.push({
1597
- id: mod.id,
1598
- declared,
1599
- clientMap,
1600
- serverRegistered: server.cardTypes,
1601
- serverSchemaVersions: server.schemaVersions,
1602
- serverKeyMismatches: server.keyMismatches,
1603
- serverRegisterMissing: server.registerMissing,
1604
- clientRegisterMissing: await resolveClientRegisterMissing(mod.dir)
1605
- });
1606
- }
1607
- return result;
1608
- }
1609
-
1610
- // src/doctor/rules/card-type-consistent.ts
1611
- async function cardTypeConsistentRule(ctx) {
1612
- const issues = [];
1613
- for (const mod of await collectModuleCards(ctx)) {
1614
- const declared = Object.keys(mod.declared);
1615
- if (declared.length === 0 && mod.serverKeyMismatches.length === 0 && !mod.serverRegisterMissing && !mod.clientRegisterMissing) {
1616
- continue;
1617
- }
1618
- for (const mm of mod.serverKeyMismatches) {
1619
- issues.push({
1620
- rule: "card-type-consistent",
1621
- level: "error",
1622
- message: `${mod.id}: serverModule.cards key "${mm.key}" !== meta.cardType "${mm.cardType}"\uFF08key \u7EA6\u5B9A = cardType\uFF09`
1623
- });
1624
- }
1625
- if (mod.serverRegisterMissing) {
1626
- issues.push({
1627
- rule: "card-type-consistent",
1628
- level: "error",
1629
- message: `${mod.id}: serverModule \u58F0\u660E\u4E86 cards \u4F46 register \u5185\u672A\u8C03\u7528 ctx.cards.registerMap(cards)\uFF08\u6CE8\u518C\u52A8\u4F5C\u7F3A\u5931\uFF0C\u8FD0\u884C\u65F6\u7F3A\u5361\uFF09`
1630
- });
1631
- }
1632
- if (mod.clientRegisterMissing) {
1633
- issues.push({
1634
- rule: "card-type-consistent",
1635
- level: "error",
1636
- message: `${mod.id}: clientModule \u58F0\u660E\u4E86 cards \u4F46 register \u5185\u672A\u8C03\u7528 ctx.cards.registerMap(cards)\uFF08\u6CE8\u518C\u52A8\u4F5C\u7F3A\u5931\uFF0C\u8FD0\u884C\u65F6\u7F3A\u5361\uFF09`
1637
- });
1638
- }
1639
- const missingClient = declared.filter((t) => !mod.clientMap.includes(t));
1640
- if (missingClient.length > 0) {
1641
- issues.push({
1642
- rule: "card-type-consistent",
1643
- level: "error",
1644
- message: `${mod.id}: \u58F0\u660E\u5361\u7247 ${missingClient.join(",")} \u672A\u5728\u5BA2\u6237\u7AEF cards map \u5BFC\u51FA`
1645
- });
1646
- }
1647
- const missingServer = declared.filter((t) => !mod.serverRegistered.includes(t));
1648
- if (missingServer.length > 0) {
1649
- issues.push({
1650
- rule: "card-type-consistent",
1651
- level: "error",
1652
- message: `${mod.id}: \u58F0\u660E\u5361\u7247 ${missingServer.join(",")} \u672A\u5728\u670D\u52A1\u7AEF\u6CE8\u518C\uFF08serverModule.cards meta / cards.register\uFF09`
1653
- });
1654
- }
1655
- const extraClient = mod.clientMap.filter((t) => !declared.includes(t));
1656
- if (extraClient.length > 0) {
1657
- issues.push({
1658
- rule: "card-type-consistent",
1659
- level: "warning",
1660
- message: `${mod.id}: \u5BA2\u6237\u7AEF\u5BFC\u51FA\u672A\u58F0\u660E\u7684\u5361\u7247 ${extraClient.join(",")}`
1661
- });
1662
- }
1663
- const extraServer = mod.serverRegistered.filter((t) => !declared.includes(t));
1664
- if (extraServer.length > 0) {
1665
- issues.push({
1666
- rule: "card-type-consistent",
1667
- level: "warning",
1668
- message: `${mod.id}: \u670D\u52A1\u7AEF\u6CE8\u518C\u672A\u58F0\u660E\u7684\u5361\u7247 ${extraServer.join(",")}`
1669
- });
1670
- }
1671
- }
1672
- return issues;
1673
- }
1674
- async function clientCardCoverRule(ctx) {
1675
- const issues = [];
1676
- const mods = await collectModuleCards(ctx);
1677
- const registered = {};
1678
- for (const mod of mods) {
1679
- for (const t of mod.serverRegistered) {
1680
- if (t in registered) registered[t] += `,${mod.id}`;
1681
- else registered[t] = mod.id;
1682
- }
1683
- }
1684
- if (Object.keys(registered).length === 0) return issues;
1685
- const assembled = /* @__PURE__ */ new Set();
1686
- for (const mod of mods) {
1687
- for (const t of mod.clientMap) assembled.add(t);
1688
- }
1689
- for (const [cardType, moduleIds] of Object.entries(registered)) {
1690
- if (!assembled.has(cardType)) {
1691
- issues.push({
1692
- rule: "client-card-cover",
1693
- level: "error",
1694
- message: `\u670D\u52A1\u7AEF\u6CE8\u518C\u5361\u7247 ${cardType}\uFF08\u6A21\u5757 ${moduleIds}\uFF09\u672A\u51FA\u73B0\u5728\u5E94\u7528\u5BA2\u6237\u7AEF\u88C5\u914D\uFF08\u6F0F\u88C5\u914D\uFF09`
1695
- });
1696
- }
1697
- }
1698
- return issues;
1699
- }
1700
-
1701
- // src/doctor/rules/contract-version.ts
1702
- import { existsSync as existsSync11 } from "fs";
1703
- import { readdir as readdir6, stat as stat2 } from "fs/promises";
1704
- import path15 from "path";
1705
- async function contractVersionRule(ctx) {
1706
- const issues = [];
1707
- const packagesDir = path15.join(ctx.appDir, "packages");
1708
- if (!existsSync11(packagesDir)) return issues;
1709
- const installed = /* @__PURE__ */ new Map();
1710
- for (const entry of await readdir6(packagesDir, { withFileTypes: true })) {
1711
- if (!entry.isDirectory()) continue;
1712
- const pkg = readPkgJson(path15.join(packagesDir, entry.name));
1713
- if (pkg?.name && pkg.name.startsWith("@tbox.cn/app-contracts")) {
1714
- installed.set(pkg.name, pkg.version ?? "0.0.0");
1715
- }
1716
- }
1717
- const nmScope = path15.join(ctx.appDir, "node_modules", "@tbox.cn");
1718
- if (existsSync11(nmScope)) {
1719
- for (const entry of await readdir6(nmScope, { withFileTypes: true })) {
1720
- const full = path15.join(nmScope, entry.name);
1721
- let isDir = entry.isDirectory();
1722
- if (!isDir && entry.isSymbolicLink()) {
1723
- try {
1724
- isDir = (await stat2(full)).isDirectory();
1725
- } catch {
1726
- isDir = false;
1727
- }
1728
- }
1729
- if (!isDir) continue;
1730
- const pkg = readPkgJson(full);
1731
- if (pkg?.name && pkg.name.startsWith("@tbox.cn/app-contracts")) {
1732
- installed.set(pkg.name, pkg.version ?? "0.0.0");
1733
- }
1734
- }
1735
- }
1736
- for (const entry of await readdir6(packagesDir, { withFileTypes: true })) {
1737
- if (!entry.isDirectory()) continue;
1738
- const dir = path15.join(packagesDir, entry.name);
1739
- if (!existsSync11(path15.join(dir, "tbox.component.json"))) continue;
1740
- const pkg = readPkgJson(dir);
1741
- const allDeps = { ...pkg?.dependencies ?? {}, ...pkg?.peerDependencies ?? {} };
1742
- for (const [name, range] of Object.entries(allDeps)) {
1743
- if (!name.startsWith("@tbox.cn/app-contracts")) continue;
1744
- const installedVersion = installed.get(name);
1745
- if (!installedVersion) continue;
1746
- if (range === "*" || range.startsWith("workspace:") || range.startsWith("catalog:")) continue;
1747
- const cleanRange = range.replace(/^\^/, "");
1748
- if (cleanRange === installedVersion) continue;
1749
- const inter = intersectRanges2(range, installedVersion);
1750
- if (!inter) {
1751
- issues.push({
1752
- rule: "contract-version",
1753
- level: "error",
1754
- message: `${entry.name}: \u5951\u7EA6 ${name} \u8303\u56F4 ${range} \u4E0E\u5B9E\u88C5 ${installedVersion} \u65E0\u4EA4\u96C6`
1755
- });
1756
- }
1757
- }
1758
- }
1759
- return issues;
1760
- }
1761
-
1762
- // src/doctor/rules/module-deps-assembled.ts
1763
- import { existsSync as existsSync12 } from "fs";
1764
- import { readdir as readdir7 } from "fs/promises";
1765
- import path16 from "path";
1766
- async function moduleDepsAssembledRule(ctx) {
1767
- const issues = [];
1768
- const installedIds = /* @__PURE__ */ new Set();
1769
- const packagesDir = path16.join(ctx.appDir, "packages");
1770
- if (existsSync12(packagesDir)) {
1771
- for (const entry of await readdir7(packagesDir, { withFileTypes: true })) {
1772
- if (entry.isDirectory()) installedIds.add(entry.name);
1773
- }
1774
- }
1775
- for (const m of ctx.declared) installedIds.add(m.id);
1776
- const assembledIds = /* @__PURE__ */ new Set();
1777
- for (const file of ASSEMBLY_FILES) {
1778
- for (const id of ctx.actual[file.kind]?.moduleIds ?? []) assembledIds.add(id);
1779
- }
1780
- const depGraph = /* @__PURE__ */ new Map();
1781
- const localMods = await listLocalModules(ctx.appDir);
1782
- for (const mod of localMods) {
1783
- const deps = (mod.descriptor.dependencies?.modules ?? []).map((d) => d.id);
1784
- depGraph.set(mod.id, deps);
1785
- }
1786
- const WHITE = 0;
1787
- const GRAY = 1;
1788
- const BLACK = 2;
1789
- const color = /* @__PURE__ */ new Map();
1790
- const cycleStacks = [];
1791
- function dfs(id, stack) {
1792
- color.set(id, GRAY);
1793
- stack.push(id);
1794
- for (const dep of depGraph.get(id) ?? []) {
1795
- const c = color.get(dep) ?? WHITE;
1796
- if (c === GRAY) {
1797
- const idx = stack.indexOf(dep);
1798
- if (idx >= 0) cycleStacks.push([...stack.slice(idx), dep]);
1799
- } else if (c === WHITE) {
1800
- dfs(dep, stack);
1801
- }
1802
- }
1803
- stack.pop();
1804
- color.set(id, BLACK);
1805
- }
1806
- for (const id of depGraph.keys()) {
1807
- if ((color.get(id) ?? WHITE) === WHITE) dfs(id, []);
1808
- }
1809
- for (const cycle of cycleStacks) {
1810
- issues.push({
1811
- rule: "module-deps-assembled",
1812
- level: "error",
1813
- message: `dependencies.modules \u58F0\u660E\u5C42\u5B58\u5728\u5FAA\u73AF\u4F9D\u8D56\uFF1A${cycle.join(" \u2192 ")}`
1814
- });
1815
- }
1816
- for (const mod of localMods) {
1817
- for (const dep of mod.descriptor.dependencies?.modules ?? []) {
1818
- if (dep.required && !installedIds.has(dep.id)) {
1819
- issues.push({
1820
- rule: "module-deps-assembled",
1821
- level: "error",
1822
- message: `${mod.id}: \u4F9D\u8D56\u6A21\u5757 ${dep.id} \u672A\u5B89\u88C5\uFF08\u7F3A\u5931 packages/${dep.id} \u6216 npm \u6E05\u5355\uFF09`
1823
- });
1824
- continue;
1825
- }
1826
- const depIsBusiness = localMods.some((m) => m.id === dep.id);
1827
- if (depIsBusiness && dep.required && !assembledIds.has(dep.id)) {
1828
- issues.push({
1829
- rule: "module-deps-assembled",
1830
- level: "error",
1831
- message: `${mod.id}: \u4F9D\u8D56\u4E1A\u52A1\u6A21\u5757 ${dep.id} \u5DF2\u5B89\u88C5\u4F46\u672A\u88C5\u914D\uFF08modules.ts \u7F3A\u884C\uFF0C\u8FD0\u884C sync\uFF09`
1832
- });
1833
- }
1834
- }
1835
- }
1836
- return issues;
1837
- }
1838
-
1839
- // src/doctor/rules/singleton.ts
1840
- import { existsSync as existsSync13 } from "fs";
1841
- import { readdir as readdir8 } from "fs/promises";
1842
- import { createRequire } from "module";
1843
- import { realpathSync as realpathSync2 } from "fs";
1844
- import path17 from "path";
1845
- async function singletonRule(ctx) {
1846
- const issues = [];
1847
- const candidates = ["react", "zod", "@mastra/core"];
1848
- const dirs = ["apps/client", "apps/server", "packages/app-sdk", "packages/contracts"];
1849
- const packagesDir = path17.join(ctx.appDir, "packages");
1850
- if (existsSync13(packagesDir)) {
1851
- for (const entry of await readdir8(packagesDir, { withFileTypes: true })) {
1852
- if (entry.isDirectory() && existsSync13(path17.join(packagesDir, entry.name, "package.json"))) {
1853
- dirs.push(path17.join("packages", entry.name));
1854
- }
1855
- }
1856
- }
1857
- for (const name of candidates) {
1858
- const seen = /* @__PURE__ */ new Map();
1859
- for (const rel of dirs) {
1860
- const dir = path17.join(ctx.appDir, rel);
1861
- if (!existsSync13(path17.join(dir, "package.json"))) continue;
1862
- const resolved = resolveFrom(dir, name);
1863
- if (resolved) {
1864
- const list = seen.get(resolved) ?? [];
1865
- list.push(rel);
1866
- seen.set(resolved, list);
1867
- }
1868
- }
1869
- if (seen.size > 1) {
1870
- issues.push({
1871
- rule: "singleton",
1872
- level: "error",
1873
- message: `${name} \u89E3\u6790\u7ED3\u679C\u4E0D\u552F\u4E00\uFF1A${[...seen.entries()].map(([p, dirs2]) => `${p}(${dirs2.join(",")})`).join(" vs ")}`
1874
- });
1875
- }
1876
- }
1877
- return issues;
1878
- }
1879
- function resolveFrom(dir, name) {
1880
- try {
1881
- const req = createRequire(path17.join(dir, "__noop__.js"));
1882
- return realpathSync2(req.resolve(name));
1883
- } catch {
1884
- return null;
1885
- }
1886
- }
1887
-
1888
- // src/doctor/rules/sdk-internal-import.ts
1889
- import { existsSync as existsSync14 } from "fs";
1890
- import path18 from "path";
1891
- var SDK_PUBLIC_ENTRIES = /* @__PURE__ */ new Set([
1892
- "@tbox.cn/app-sdk",
1893
- "@tbox.cn/app-sdk/server",
1894
- "@tbox.cn/app-sdk/client",
1895
- "@tbox.cn/app-sdk/platform"
1896
- ]);
1897
- var FRAMEWORK_INTERNAL_PATTERNS = [
1898
- { re: /^@mastra\/core/, label: "@mastra/core*\uFF08\u6846\u67B6\u5185\u90E8\uFF0C\u6539\u7ECF @tbox.cn/app-sdk\uFF09" },
1899
- { re: /^@ag-ui\//, label: "@ag-ui/*\uFF08\u6846\u67B6\u5185\u90E8\uFF0C\u6539\u7ECF @tbox.cn/app-sdk\uFF09" },
1900
- { re: /^@ai-sdk\//, label: "@ai-sdk/*\uFF08\u6846\u67B6\u5185\u90E8\uFF09" },
1901
- { re: /^ai$/, label: "ai\uFF08\u6846\u67B6\u5185\u90E8\uFF09" },
1902
- { re: /^zustand$/, label: "zustand\uFF08\u6846\u67B6\u5185\u90E8\uFF09" }
1903
- ];
1904
- async function sdkInternalImportRule(ctx) {
1905
- const issues = [];
1906
- const roots = ["apps", "packages"];
1907
- for (const root of roots) {
1908
- const rootDir = path18.join(ctx.appDir, root);
1909
- if (!existsSync14(rootDir)) continue;
1910
- const files = [];
1911
- await walkFiles(rootDir, ctx.appDir, files);
1912
- for (const rel of files) {
1913
- if (rel.startsWith("packages/app-sdk/") || rel.startsWith("packages/contracts/")) continue;
1914
- const src = await readTextOrEmpty(path18.join(ctx.appDir, rel));
1915
- for (const spec of src.matchAll(/from\s+['"]([^'"]+)['"]/g)) {
1916
- const target = spec[1];
1917
- if (target.startsWith("@tbox.cn/app-sdk/") && !SDK_PUBLIC_ENTRIES.has(target)) {
1918
- issues.push({
1919
- rule: "sdk-internal-import",
1920
- level: "error",
1921
- message: `${rel}: \u4E0D\u5141\u8BB8 import SDK \u5185\u90E8\u8DEF\u5F84 ${target}`
1922
- });
1923
- } else if (target.startsWith("@tbox.cn/app-contracts/")) {
1924
- issues.push({
1925
- rule: "sdk-internal-import",
1926
- level: "error",
1927
- message: `${rel}: \u4E0D\u5141\u8BB8 import \u5951\u7EA6\u5B50\u8DEF\u5F84 ${target}\uFF08\u4EC5 @tbox.cn/app-contracts\uFF09`
1928
- });
1929
- } else if (isModuleFile(ctx.appDir, rel)) {
1930
- for (const pat of FRAMEWORK_INTERNAL_PATTERNS) {
1931
- if (pat.re.test(target)) {
1932
- issues.push({
1933
- rule: "sdk-internal-import",
1934
- level: "error",
1935
- message: `${rel}: \u6A21\u5757\u4E0D\u5141\u8BB8\u76F4\u63A5 import ${pat.label}`
1936
- });
1937
- }
1938
- }
1939
- }
1940
- }
1941
- }
1942
- }
1943
- return issues;
1944
- }
1945
- function isModuleFile(appDir, rel) {
1946
- const m = rel.match(/^packages\/([^/]+)\//);
1947
- if (!m) return false;
1948
- return existsSync14(path18.join(appDir, "packages", m[1], "tbox.component.json"));
1949
- }
1950
-
1951
- // src/doctor/rules/assembly-drift.ts
1952
- function assemblyDriftRule(ctx) {
1953
- const issues = [];
1954
- for (const file of ASSEMBLY_FILES) {
1955
- const declaredIds = new Set(
1956
- ctx.declared.filter((m) => contributesTo(file.kind, m)).map((m) => m.id)
1957
- );
1958
- const actualIds = new Set(ctx.actual[file.kind]?.moduleIds ?? []);
1959
- const missing = [...declaredIds].filter((id) => !actualIds.has(id));
1960
- const extra = [...actualIds].filter((id) => !declaredIds.has(id));
1961
- if (missing.length > 0) {
1962
- issues.push({
1963
- rule: "assembly-drift",
1964
- level: "warning",
1965
- message: `${file.path}: \u5DF2\u58F0\u660E\u672A\u88C5\u914D \u2192 ${missing.join(", ")}`
1966
- });
1967
- }
1968
- if (extra.length > 0) {
1969
- issues.push({
1970
- rule: "assembly-drift",
1971
- level: "warning",
1972
- message: `${file.path}: \u5DF2\u88C5\u914D\u672A\u58F0\u660E \u2192 ${extra.join(", ")}`
1973
- });
1974
- }
1975
- const broken = ctx.actual[file.kind]?.broken ?? [];
1976
- if (broken.length > 0) {
1977
- issues.push({
1978
- rule: "assembly-drift",
1979
- level: "warning",
1980
- message: `${file.path}: \u951A\u533A\u7834\u574F \u2192 ${broken.join(", ")}`
1981
- });
1982
- }
1983
- }
1984
- return issues;
1985
- }
1986
-
1987
- // src/doctor/rules/schema-version.ts
1988
- async function schemaVersionRule(ctx) {
1989
- const issues = [];
1990
- for (const mod of await collectModuleCards(ctx)) {
1991
- for (const [cardType, declaredSv] of Object.entries(mod.declared)) {
1992
- const serverSv = mod.serverSchemaVersions[cardType] ?? 1;
1993
- if (serverSv !== declaredSv) {
1994
- issues.push({
1995
- rule: "schema-version",
1996
- level: "error",
1997
- message: `${mod.id}: \u5361\u7247 ${cardType} contributes.schemaVersion=${declaredSv} \u4E0E meta schemaVersion=${serverSv} \u4E0D\u4E00\u81F4`
1998
- });
1999
- }
2000
- }
2001
- }
2002
- return issues;
2003
- }
2004
-
2005
- // src/doctor/rules/naked-write-route.ts
2006
- import { existsSync as existsSync15 } from "fs";
2007
- import path19 from "path";
2008
- var ALLOWED_POST_PREFIXES = ["/webhook", "/upload", "/demo", "/run"];
2009
- async function nakedWriteRouteRule(ctx) {
2010
- const issues = [];
2011
- for (const mod of await listLocalModules(ctx.appDir)) {
2012
- const serverDir = path19.join(mod.dir, "src/server");
2013
- if (!existsSync15(serverDir)) continue;
2014
- const files = [];
2015
- await walkFiles(serverDir, mod.dir, files);
2016
- for (const rel of files) {
2017
- const src = await readTextOrEmpty(path19.join(mod.dir, rel));
2018
- for (const m of src.matchAll(/\.(?:post|put|patch|delete)\(\s*['"]([^'"]+)['"]/g)) {
2019
- const route = m[1];
2020
- if (!route.startsWith("/")) continue;
2021
- const allowed = ALLOWED_POST_PREFIXES.some((p) => route.startsWith(p));
2022
- if (!allowed) {
2023
- issues.push({
2024
- rule: "naked-write-route",
2025
- level: "warning",
2026
- message: `${mod.id}/${rel}: \u88F8\u5199\u8DEF\u7531 ${m[0].replace("(", " (")} \u5EFA\u8BAE\u6539\u8D70 ctx.actions.registerAction\uFF08\u5168\u5C40\u52A8\u4F5C\u5206\u53D1\u5668\uFF09`
2027
- });
2028
- }
2029
- }
2030
- }
2031
- }
2032
- return issues;
2033
- }
2034
-
2035
- // src/doctor/rules/descriptor-schema.ts
2036
- import { existsSync as existsSync16 } from "fs";
2037
- import { readdir as readdir9 } from "fs/promises";
2038
- import path20 from "path";
2039
- async function descriptorSchemaRule(ctx) {
2040
- const issues = [];
2041
- const packagesDir = path20.join(ctx.appDir, "packages");
2042
- if (!existsSync16(packagesDir)) return issues;
2043
- for (const entry of await readdir9(packagesDir, { withFileTypes: true })) {
2044
- if (!entry.isDirectory()) continue;
2045
- const file = path20.join(packagesDir, entry.name, "tbox.component.json");
2046
- if (!existsSync16(file)) continue;
2047
- try {
2048
- const parsed = parseComponentDescriptor(JSON.parse(await readTextOrEmpty(file)));
2049
- if (!parsed.ok) {
2050
- issues.push({
2051
- rule: "descriptor-schema",
2052
- level: "error",
2053
- message: `${entry.name}/tbox.component.json: ${parsed.errors.join("; ")}`
2054
- });
2055
- }
2056
- } catch {
2057
- issues.push({
2058
- rule: "descriptor-schema",
2059
- level: "error",
2060
- message: `${entry.name}/tbox.component.json: JSON \u89E3\u6790\u5931\u8D25`
2061
- });
2062
- }
2063
- }
2064
- return issues;
2065
- }
2066
-
2067
- // src/doctor/rules/workspace-ref-resolved.ts
2068
- import { existsSync as existsSync17 } from "fs";
2069
- import { readdir as readdir10 } from "fs/promises";
2070
- import path21 from "path";
2071
- async function workspaceRefResolvedRule(ctx) {
2072
- const issues = [];
2073
- if (existsSync17(path21.join(ctx.appDir, "pnpm-workspace.template.yaml"))) return issues;
2074
- const memberIndex = /* @__PURE__ */ new Map();
2075
- for (const root of ["apps", "packages"]) {
2076
- const rootDir = path21.join(ctx.appDir, root);
2077
- if (!existsSync17(rootDir)) continue;
2078
- for (const entry of await readdir10(rootDir, { withFileTypes: true })) {
2079
- if (!entry.isDirectory()) continue;
2080
- const dir = path21.join(rootDir, entry.name);
2081
- const pkg = readPkgJson(dir);
2082
- if (pkg?.name) memberIndex.set(pkg.name, path21.join(root, entry.name));
2083
- }
2084
- }
2085
- if (memberIndex.size === 0) return issues;
2086
- for (const [name, rel] of memberIndex) {
2087
- const pkg = readPkgJson(path21.join(ctx.appDir, rel));
2088
- if (!pkg) continue;
2089
- for (const key of ["dependencies", "peerDependencies", "devDependencies"]) {
2090
- const deps = pkg[key];
2091
- if (!deps || typeof deps !== "object") continue;
2092
- for (const [depName, spec] of Object.entries(deps)) {
2093
- if (typeof spec !== "string" || !spec.startsWith("workspace:")) continue;
2094
- if (!memberIndex.has(depName)) {
2095
- issues.push({
2096
- rule: "workspace-ref-resolved",
2097
- level: "error",
2098
- message: `${rel}: workspace:* \u5F15\u7528 ${depName} \u672A\u547D\u4E2D\u4EFB\u4F55\u6210\u5458\uFF08packages/ \u6216 apps/\uFF09`
2099
- });
2100
- }
2101
- }
2102
- }
2103
- }
2104
- return issues;
2105
- }
2106
-
2107
- // src/doctor/rules/legacy-platform-files.ts
2108
- import { existsSync as existsSync18 } from "fs";
2109
- import path22 from "path";
2110
- var LEGACY_PATHS = [
2111
- "apps/server/src/auth",
2112
- "apps/server/src/agent",
2113
- "apps/server/src/gateway",
2114
- "apps/server/src/http",
2115
- "apps/server/src/plugins/integration.ts",
2116
- "apps/server/src/config/tts-config.ts",
2117
- "apps/server/src/config/model-providers.ts",
2118
- "apps/client/src/env",
2119
- "apps/client/src/bridge",
2120
- "apps/client/src/adapters",
2121
- "apps/client/src/services/http.ts",
2122
- "apps/client/src/services/auth-client.ts",
2123
- "apps/client/src/services/user-id.ts",
2124
- "apps/client/src/services/tbox-session.ts"
2125
- ];
2126
- function legacyPlatformFilesRule(ctx) {
2127
- const issues = [];
2128
- for (const rel of LEGACY_PATHS) {
2129
- if (existsSync18(path22.join(ctx.appDir, rel))) {
2130
- issues.push({
2131
- rule: "legacy-platform-files",
2132
- level: "error",
2133
- message: `${rel} \u4E3A\u65E7\u5F62\u6001\u5E73\u53F0\u6587\u4EF6\uFF08007\uFF1A\u5E73\u53F0\u80FD\u529B\u5DF2\u4E0B\u6C89 @tbox.cn/app-sdk\uFF09\u3002\u8BF7\u5220\u9664\u5E76\u6539\u7528 SDK API\uFF08server: createAuthRuntime/createPlatformApiRouter/createTtsProxy/createModelProvider/createPluginIntegration\uFF1Bclient: createAuthClient/createPlatformAdapters/createPlatformApi/configureRuntime\uFF09\uFF0C\u8BE6\u89C1 docs/proposals/007\u3002`
2134
- });
2135
- }
2136
- }
2137
- return issues;
2138
- }
2139
-
2140
- // src/doctor/rules/mall-deployment.ts
2141
- import { existsSync as existsSync19, readFileSync as readFileSync8 } from "fs";
2142
- import { readdir as readdir11 } from "fs/promises";
2143
- import { createRequire as createRequire2 } from "module";
2144
- import path23 from "path";
2145
- function resolveModuleRoot(ctx, pkg) {
2146
- try {
2147
- const req = createRequire2(path23.join(ctx.appDir, "package.json"));
2148
- const resolved = req.resolve(`${pkg}/package.json`);
2149
- return path23.dirname(resolved);
2150
- } catch {
2151
- return void 0;
2152
- }
2153
- }
2154
- function readServerSource2(moduleRoot) {
2155
- const candidates = [
2156
- path23.join(moduleRoot, "src/server/index.ts"),
2157
- path23.join(moduleRoot, "src/server/index.tsx")
2158
- ];
2159
- for (const c of candidates) {
2160
- if (existsSync19(c)) return readFileSync8(c, "utf8");
2161
- }
2162
- return "";
2163
- }
2164
- function extractFeatures(source) {
2165
- const match = source.match(/features\s*:\s*\[([^\]]*)\]/);
2166
- if (!match) return [];
2167
- return [...match[1].matchAll(/['"]([^'"]+)['"]/g)].map((m) => m[1]);
2168
- }
2169
- function extractIntentPatterns(source) {
2170
- const patterns = [];
2171
- for (const block of source.matchAll(/patterns\s*:\s*\[([^\]]*)\]/g)) {
2172
- for (const m of block[1].matchAll(/['"]([^'"]+)['"]/g)) {
2173
- patterns.push(m[1]);
2174
- }
2175
- }
2176
- return patterns;
2177
- }
2178
- function extractFeatureRefs(source) {
2179
- return [...source.matchAll(/isFeatureEnabled\s*\(\s*['"]([^'"]+)['"]\s*\)/g)].map((m) => m[1]);
2180
- }
2181
- async function deploymentConsistencyRule(ctx) {
2182
- const issues = [];
2183
- const deploymentDir = path23.join(ctx.appDir, "config/deployments");
2184
- if (!existsSync19(deploymentDir)) return issues;
2185
- const entries = await readdir11(deploymentDir);
2186
- const jsonFiles = entries.filter((e) => e.endsWith(".json"));
2187
- if (jsonFiles.length === 0) return issues;
2188
- for (const file of jsonFiles) {
2189
- let parsed;
2190
- try {
2191
- parsed = JSON.parse(readFileSync8(path23.join(deploymentDir, file), "utf8"));
2192
- } catch {
2193
- issues.push({ rule: "deployment-consistency", level: "error", message: `deployment \u914D\u7F6E\u975E\u6CD5 JSON\uFF1Aconfig/deployments/${file}` });
2194
- continue;
2195
- }
2196
- const components = parsed.deployment?.components ?? [];
2197
- const declaredPkgs = new Map(ctx.declared.map((d) => [d.componentId ?? d.id, d]));
2198
- for (const component of components) {
2199
- const componentId = component.componentId;
2200
- if (!componentId) {
2201
- issues.push({
2202
- rule: "deployment-consistency",
2203
- level: "error",
2204
- message: `config/deployments/${file}\uFF1Acomponents \u9879\u7F3A componentId`
2205
- });
2206
- continue;
2207
- }
2208
- const declared = declaredPkgs.get(componentId);
2209
- if (!declared) {
2210
- issues.push({
2211
- rule: "deployment-consistency",
2212
- level: component.optional ? "warning" : "error",
2213
- message: `config/deployments/${file}\uFF1AcomponentId "${componentId}" \u672A\u5B89\u88C5\u5BF9\u5E94\u6A21\u5757` + (component.optional ? "\uFF08optional \u7EC4\u4EF6\uFF0C\u964D\u7EA7 warning\uFF09" : "\uFF08\u5B89\u88C5\u5BF9\u5E94\u6A21\u5757\u6216\u6807\u8BB0 optional\uFF09")
2214
- });
2215
- continue;
2216
- }
2217
- const moduleRoot = resolveModuleRoot(ctx, declared.pkg);
2218
- if (moduleRoot && component.enabledFeatures?.length) {
2219
- const source = readServerSource2(moduleRoot);
2220
- const declaredFeatures = extractFeatures(source);
2221
- if (declaredFeatures.length > 0) {
2222
- for (const feature of component.enabledFeatures) {
2223
- if (!declaredFeatures.includes(feature)) {
2224
- issues.push({
2225
- rule: "deployment-consistency",
2226
- level: "error",
2227
- message: `config/deployments/${file}\uFF1Afeature "${feature}" \u4E0D\u5728 ${componentId} \u7684 declaration.features \u5185\uFF08\u5DF2\u58F0\u660E\uFF1A${declaredFeatures.join(", ") || "(\u65E0)"}\uFF09`
2228
- });
2229
- }
2230
- }
2231
- }
2232
- }
2233
- }
2234
- }
2235
- return issues;
2236
- }
2237
- async function intentOverlapRule(ctx) {
2238
- const issues = [];
2239
- const byPattern = /* @__PURE__ */ new Map();
2240
- for (const declared of ctx.declared) {
2241
- if (!declared.hasServerEntry) continue;
2242
- const root = resolveModuleRoot(ctx, declared.pkg);
2243
- if (!root) continue;
2244
- const patterns = extractIntentPatterns(readServerSource2(root));
2245
- for (const p of patterns) {
2246
- const list = byPattern.get(p) ?? [];
2247
- list.push(declared.id);
2248
- byPattern.set(p, list);
2249
- }
2250
- }
2251
- for (const [pattern, modules] of byPattern) {
2252
- if (modules.length > 1) {
2253
- issues.push({
2254
- rule: "intent-overlap",
2255
- level: "warning",
2256
- message: `\u610F\u56FE\u8BCD "${pattern}" \u5728\u591A\u4E2A\u6A21\u5757\u58F0\u660E\uFF08${modules.join(", ")}\uFF09\u2014\u2014\u5148\u6CE8\u518C\u5148\u547D\u4E2D`
2257
- });
2258
- }
2259
- }
2260
- return issues;
2261
- }
2262
- async function skillNameConflictRule(ctx) {
2263
- const issues = [];
2264
- const byName = /* @__PURE__ */ new Map();
2265
- for (const declared of ctx.declared) {
2266
- if (!declared.hasServerEntry) continue;
2267
- const root = resolveModuleRoot(ctx, declared.pkg);
2268
- if (!root) continue;
2269
- const skillsDir = path23.join(root, "skills");
2270
- if (!existsSync19(skillsDir)) continue;
2271
- try {
2272
- const names = (await readdir11(skillsDir, { withFileTypes: true })).filter((e) => e.isDirectory()).map((e) => e.name);
2273
- for (const name of names) {
2274
- const list = byName.get(name) ?? [];
2275
- list.push(declared.id);
2276
- byName.set(name, list);
2277
- }
2278
- } catch {
2279
- }
2280
- }
2281
- for (const [name, modules] of byName) {
2282
- if (modules.length > 1) {
2283
- issues.push({
2284
- rule: "skill-name-conflict",
2285
- level: "warning",
2286
- message: `skill "${name}" \u5728\u591A\u4E2A\u6A21\u5757\u58F0\u660E\uFF08${modules.join(", ")}\uFF09\u2014\u2014\u540E\u6CE8\u518C\u8986\u76D6`
2287
- });
2288
- }
2289
- }
2290
- return issues;
2291
- }
2292
- async function featureDeclarationRule(ctx) {
2293
- const issues = [];
2294
- for (const declared of ctx.declared) {
2295
- if (!declared.hasServerEntry) continue;
2296
- const root = resolveModuleRoot(ctx, declared.pkg);
2297
- if (!root) continue;
2298
- const source = readServerSource2(root);
2299
- if (!source) continue;
2300
- const declaredFeatures = extractFeatures(source);
2301
- const refs = extractFeatureRefs(source);
2302
- for (const ref of refs) {
2303
- if (declaredFeatures.length > 0 && !declaredFeatures.includes(ref)) {
2304
- issues.push({
2305
- rule: "feature-declaration",
2306
- level: "error",
2307
- message: `\u6A21\u5757 ${declared.id}\uFF1AisFeatureEnabled("${ref}") \u4E0D\u5728 declaration.features \u5185\uFF08\u5DF2\u58F0\u660E\uFF1A${declaredFeatures.join(", ") || "(\u65E0)"}\uFF09`
2308
- });
2309
- }
2310
- }
2311
- }
2312
- return issues;
2313
- }
2314
-
2315
- // src/doctor/index.ts
2316
- var DOCTOR_RULES = [
2317
- { name: "workspace-dag", run: workspaceDagRule },
2318
- { name: "register-pure", run: registerPureRule },
2319
- { name: "card-type-consistent", run: cardTypeConsistentRule },
2320
- { name: "client-card-cover", run: clientCardCoverRule },
2321
- { name: "contract-version", run: contractVersionRule },
2322
- { name: "module-deps-assembled", run: moduleDepsAssembledRule },
2323
- { name: "singleton", run: singletonRule },
2324
- { name: "sdk-internal-import", run: sdkInternalImportRule },
2325
- { name: "assembly-drift", run: assemblyDriftRule },
2326
- { name: "schema-version", run: schemaVersionRule },
2327
- { name: "naked-write-route", run: nakedWriteRouteRule },
2328
- { name: "descriptor-schema", run: descriptorSchemaRule },
2329
- { name: "workspace-ref-resolved", run: workspaceRefResolvedRule },
2330
- { name: "legacy-platform-files", run: legacyPlatformFilesRule },
2331
- { name: "deployment-consistency", run: deploymentConsistencyRule },
2332
- { name: "intent-overlap", run: intentOverlapRule },
2333
- { name: "skill-name-conflict", run: skillNameConflictRule },
2334
- { name: "feature-declaration", run: featureDeclarationRule }
2335
- ];
2336
- async function runDoctor(ctx, opts) {
2337
- const issues = [];
2338
- const only = opts?.only?.length ? new Set(opts.only) : null;
2339
- const skip = new Set(opts?.skip ?? []);
2340
- for (const rule of DOCTOR_RULES) {
2341
- if (only && !only.has(rule.name)) continue;
2342
- if (skip.has(rule.name)) continue;
2343
- try {
2344
- issues.push(...await rule.run(ctx));
2345
- } catch (err) {
2346
- issues.push({
2347
- rule: rule.name,
2348
- level: "error",
2349
- message: `\u89C4\u5219\u6267\u884C\u5F02\u5E38: ${err?.message ?? String(err)}`
2350
- });
2351
- }
2352
- }
2353
- return issues;
2354
- }
2355
192
 
2356
- // src/deps-resolve.ts
2357
- function isContractId(id, localIds) {
2358
- return !localIds.has(id) || id.startsWith("contracts-");
2359
- }
2360
- async function resolveDependencies(appDir, rootIds, declared, extraRoots = []) {
2361
- const mods = await listLocalModules(appDir);
2362
- const localIds = new Set(mods.map((m) => m.id));
2363
- const installedIds = new Set(declared?.map((m) => m.id) ?? []);
2364
- const pkgVersion = /* @__PURE__ */ new Map();
2365
- for (const mod of mods) {
2366
- pkgVersion.set(mod.id, mod.descriptor.version ?? "0.0.0");
2367
- }
2368
- const extraDeps = new Map(extraRoots.map((r) => [r.id, r.deps]));
2369
- const visited = /* @__PURE__ */ new Set();
2370
- const edges = [];
2371
- const queue = [...rootIds, ...extraRoots.map((r) => r.id)];
2372
- while (queue.length > 0) {
2373
- const id = queue.shift();
2374
- if (visited.has(id)) continue;
2375
- visited.add(id);
2376
- const mod = mods.find((m) => m.id === id);
2377
- const deps = extraDeps.get(id) ?? mod?.descriptor.dependencies?.modules ?? [];
2378
- for (const dep of deps) {
2379
- edges.push(dep);
2380
- if (!visited.has(dep.id) && (localIds.has(dep.id) || extraDeps.has(dep.id))) queue.push(dep.id);
2381
- }
2382
- }
2383
- const WHITE = 0;
2384
- const GRAY = 1;
2385
- const BLACK = 2;
2386
- const color = /* @__PURE__ */ new Map();
2387
- const cycles = [];
2388
- function dfs(id, stack) {
2389
- color.set(id, GRAY);
2390
- stack.push(id);
2391
- for (const dep of adjacency.get(id) ?? []) {
2392
- const c = color.get(dep) ?? WHITE;
2393
- if (c === GRAY) {
2394
- const idx = stack.indexOf(dep);
2395
- if (idx >= 0) cycles.push([...stack.slice(idx), dep]);
2396
- } else if (c === WHITE) {
2397
- dfs(dep, stack);
2398
- }
2399
- }
2400
- stack.pop();
2401
- color.set(id, BLACK);
2402
- }
2403
- const adjacency = /* @__PURE__ */ new Map();
2404
- const modById = new Map(mods.map((m) => [m.id, m]));
2405
- for (const id of visited) {
2406
- const mod = modById.get(id);
2407
- const deps = extraDeps.get(id) ?? mod?.descriptor.dependencies?.modules ?? [];
2408
- adjacency.set(id, deps.filter((d) => visited.has(d.id)).map((d) => d.id));
2409
- }
2410
- for (const id of visited) {
2411
- if ((color.get(id) ?? WHITE) === WHITE) dfs(id, []);
2412
- }
2413
- const toInstall = [...visited].filter((id) => !installedIds.has(id));
2414
- const inDegree = /* @__PURE__ */ new Map();
2415
- for (const id of toInstall) inDegree.set(id, 0);
2416
- const dependents = /* @__PURE__ */ new Map();
2417
- for (const id of toInstall) {
2418
- for (const dep of adjacency.get(id) ?? []) {
2419
- if (inDegree.has(dep)) {
2420
- inDegree.set(id, (inDegree.get(id) ?? 0) + 1);
2421
- const list = dependents.get(dep);
2422
- if (list) list.push(id);
2423
- else dependents.set(dep, [id]);
2424
- }
2425
- }
2426
- }
2427
- const ready = toInstall.filter((id) => (inDegree.get(id) ?? 0) === 0);
2428
- const order = [];
2429
- while (ready.length > 0) {
2430
- const id = ready.shift();
2431
- order.push(id);
2432
- for (const dependent of dependents.get(id) ?? []) {
2433
- const nd = (inDegree.get(dependent) ?? 0) - 1;
2434
- inDegree.set(dependent, nd);
2435
- if (nd === 0) ready.push(dependent);
2436
- }
2437
- }
2438
- if (order.length !== toInstall.length) {
2439
- }
2440
- const contracts = /* @__PURE__ */ new Set();
2441
- const business = /* @__PURE__ */ new Set();
2442
- const conflicts = [];
2443
- for (const edge of edges) {
2444
- const id = edge.id;
2445
- if (!installedIds.has(id) || !pkgVersion.has(id)) {
2446
- if (isContractId(id, localIds)) contracts.add(id);
2447
- else if (!isContractId(id, localIds)) business.add(id);
2448
- }
2449
- if (edge.range && pkgVersion.has(id)) {
2450
- const targetVersion = pkgVersion.get(id);
2451
- if (edge.range !== "*" && !edge.range.startsWith("workspace:")) {
2452
- const inter = intersectRanges2(edge.range, targetVersion);
2453
- if (!inter) {
2454
- conflicts.push(`${id}: \u4F9D\u8D56\u8303\u56F4 ${edge.range} \u4E0E\u76EE\u6807\u7248\u672C ${targetVersion} \u65E0\u4EA4\u96C6`);
2455
- }
2456
- }
2457
- }
2458
- }
193
+ export default function tboxDevOverride() {
2459
194
  return {
2460
- order,
2461
- contracts: [...contracts],
2462
- business: [...business],
2463
- cycles,
2464
- conflicts
2465
- };
2466
- }
2467
-
2468
- // src/commands/add.ts
2469
- function readSourceDescriptor(sourceDir) {
2470
- const file = path24.join(sourceDir, "tbox.component.json");
2471
- if (!existsSync20(file)) return null;
2472
- try {
2473
- const parsed = parseComponentDescriptor(JSON.parse(readFileSync9(file, "utf8")));
2474
- return parsed.ok ? parsed.value : null;
2475
- } catch {
2476
- return null;
2477
- }
2478
- }
2479
- function resolveDepSource(sourcePath, depId) {
2480
- if (!sourcePath) return null;
2481
- const dir = path24.dirname(sourcePath);
2482
- const siblingDir = path24.join(dir, depId);
2483
- if (existsSync20(path24.join(siblingDir, "package.json"))) return siblingDir;
2484
- try {
2485
- const tgz = readdirSync2(dir).find(
2486
- (f) => f.endsWith(".tgz") && f.includes(`-${depId}-`)
2487
- );
2488
- if (tgz) return path24.join(dir, tgz);
2489
- } catch {
2490
- }
2491
- return null;
2492
- }
2493
- function isInstalled(appDir, id) {
2494
- if (existsSync20(path24.join(appDir, "packages", id))) return true;
2495
- const manifest = readAppManifest(appDir);
2496
- return manifest?.npmModules.some((m) => m.id === id && m.mode === "sdk") ?? false;
2497
- }
2498
- async function addModule(opts) {
2499
- await addModuleWithDeps(opts, /* @__PURE__ */ new Set(), false);
2500
- }
2501
- async function addModuleWithDeps(opts, visited, asDependency) {
2502
- const tarballs = [];
2503
- try {
2504
- await addModuleWithDepsCore(opts, visited, asDependency, tarballs);
2505
- } finally {
2506
- for (const t of tarballs) t.dispose();
2507
- }
2508
- }
2509
- async function addModuleWithDepsCore(opts, visited, asDependency, tarballs) {
2510
- const appDir = process.cwd();
2511
- if (!readAppManifest(appDir)) throw new Error("\u5F53\u524D\u76EE\u5F55\u4E0D\u662F tbox-app \u5E94\u7528\uFF08\u7F3A .tbox/app.json\uFF09");
2512
- let id;
2513
- let sourceDescriptor = null;
2514
- let opened = null;
2515
- const isReg = !!opts.source && isRegistrySpec(opts.source);
2516
- if (opts.mode === "local") {
2517
- const short = opts.pkg;
2518
- id = short.startsWith("module-") ? short : `module-${short}`;
2519
- assertValidModuleId(id, `--mode local ${opts.pkg}`);
2520
- } else {
2521
- if (!opts.source) throw new Error(`--mode ${opts.mode} \u9700\u8981 --source <\u76EE\u5F55\u6216 tgz \u6216 registry \u5305>`);
2522
- const isRegSource = isReg && !existsSync20(path24.resolve(opts.source));
2523
- if (!isRegSource && !opts.source.endsWith(".tgz") && !LocalDirSource.isUsable(opts.source)) {
2524
- throw new Error(`\u6A21\u5757\u6E90\u65E0\u6548\uFF08\u7F3A package.json\uFF09: ${opts.source}`);
2525
- }
2526
- opened = openSource(opts.source, opts.registry);
2527
- if (opened.source instanceof TarballSource || opened.source instanceof RegistrySource) {
2528
- tarballs.push(opened.source);
2529
- }
2530
- const pkgOnly = opts.pkg.replace(/@[^/]+$/, "");
2531
- id = deriveModuleId(pkgOnly);
2532
- assertValidModuleId(id, `--mode ${opts.mode} ${opts.pkg}`);
2533
- sourceDescriptor = readSourceDescriptor(opened.sourceDir);
2534
- }
2535
- if (asDependency && isInstalled(appDir, id)) {
2536
- console.log(` \u26A0\uFE0F \u4F9D\u8D56 ${id} \u5DF2\u5B89\u88C5\uFF0C\u8DF3\u8FC7`);
2537
- return;
2538
- }
2539
- if (!asDependency && isInstalled(appDir, id)) {
2540
- const where = existsSync20(path24.join(appDir, "packages", id)) ? `packages/${id}` : "manifest \u5DF2\u767B\u8BB0\uFF08sdk \u5305\uFF0C\u65E0\u672C\u5730\u76EE\u5F55\uFF09";
2541
- throw new Error(`\u6A21\u5757 ${id} \u5DF2\u5B58\u5728\uFF08${where}\uFF09\u3002\u82E5\u8981\u91CD\u65B0\u5C55\u5F00\u8BF7\u5148 remove \u6216\u624B\u52A8\u5904\u7406\uFF1B\u88C5\u914D\u552F\u4E00\u8986\u76D6\u5165\u53E3\u4E3A sync --force`);
2542
- }
2543
- if (visited.has(id)) return;
2544
- visited.add(id);
2545
- if (!opts.noDeps) {
2546
- const ownDeps = sourceDescriptor?.dependencies?.modules ?? [];
2547
- const resolution = await resolveDependencies(appDir, [], void 0, [
2548
- { id, deps: ownDeps }
2549
- ]);
2550
- if (resolution.cycles.length > 0) {
2551
- throw new Error(
2552
- `\u4F9D\u8D56\u73AF\uFF1A${resolution.cycles.map((c) => c.join(" \u2192 ")).join("\uFF1B")}`
2553
- );
2554
- }
2555
- for (const c of resolution.conflicts) console.warn(` \u26A0\uFE0F \u4F9D\u8D56\u7248\u672C\u51B2\u7A81: ${c}`);
2556
- const installSeq = [
2557
- ...resolution.contracts,
2558
- ...resolution.order
2559
- ].filter((depId) => depId !== id && !isInstalled(appDir, depId));
2560
- for (const depId of [...new Set(installSeq)]) {
2561
- const depPkg = /^(module|scenario|contracts)-/.test(depId) ? `@tbox.cn/app-${depId}` : `@tbox.cn/${depId}`;
2562
- const depSource = isReg ? depPkg : resolveDepSource(opts.source, depId);
2563
- if (!depSource) {
2564
- console.warn(` \u26A0\uFE0F \u4F9D\u8D56 ${depId} \u672A\u5B89\u88C5\u4E14\u672A\u627E\u5230\u6E90\uFF08--source \u5144\u5F1F\u76EE\u5F55/tgz\uFF09\uFF0C\u53EF --no-deps \u8DF3\u8FC7`);
2565
- continue;
2566
- }
2567
- const depOpened = openSource(depSource, opts.registry);
2568
- if (depOpened.source instanceof TarballSource || depOpened.source instanceof RegistrySource) {
2569
- tarballs.push(depOpened.source);
2570
- }
2571
- const depIsBusiness = existsSync20(path24.join(depOpened.sourceDir, "tbox.component.json"));
2572
- await addModuleWithDeps(
2573
- { pkg: depPkg, mode: depIsBusiness ? "codegen" : "sdk", source: depSource, registry: opts.registry },
2574
- visited,
2575
- true
2576
- );
2577
- }
2578
- }
2579
- let entry;
2580
- if (opts.mode === "local") {
2581
- const short = opts.pkg;
2582
- const localId = short.startsWith("module-") ? short : `module-${short}`;
2583
- await createLocalSkeleton(appDir, short);
2584
- entry = { id: localId, package: `@app/${localId}`, version: "0.1.0", mode: "local" };
2585
- await updateAppsDependency(appDir, `@app/${localId}`, "add");
2586
- } else {
2587
- const sourceDir = opened.sourceDir;
2588
- const version = opened.source.resolveVersion();
2589
- if (opts.mode === "codegen") {
2590
- await expandCodegen(appDir, sourceDir, { id, package: `@app/${id}`, version, mode: "codegen", baseVersion: version });
2591
- await updateAppsDependency(appDir, `@app/${id}`, "add");
2592
- }
2593
- entry = {
2594
- id,
2595
- package: opts.pkg.replace(/@[^/]+$/, ""),
2596
- version,
2597
- mode: opts.mode,
2598
- ...opts.mode === "codegen" ? { baseVersion: version } : {}
2599
- };
2600
- }
2601
- const manifest = readAppManifest(appDir);
2602
- await writeAppManifest(appDir, upsertNpmModule(manifest, entry));
2603
- const sync = await syncAll(appDir);
2604
- if (sync.changedFiles.length > 0) {
2605
- console.log(` \u88C5\u914D\u66F4\u65B0: ${sync.changedFiles.join(", ")}`);
2606
- }
2607
- for (const w of sync.warnings) console.warn(` \u26A0\uFE0F ${w}`);
2608
- for (const c of sync.depConflicts) console.warn(` \u26A0\uFE0F \u4F9D\u8D56\u51B2\u7A81: ${c}`);
2609
- const actual = await collectActual(appDir);
2610
- const issues = await runDoctor({ appDir, declared: sync.declared, actual });
2611
- const errors = issues.filter((i) => i.level === "error");
2612
- if (errors.length > 0) {
2613
- console.error("\u274C doctor \u9519\u8BEF\uFF1A");
2614
- for (const e of errors) console.error(` - [${e.rule}] ${e.message}`);
2615
- process.exitCode = 1;
2616
- }
2617
- console.log(`\u2705 \u5DF2\u5B89\u88C5 ${entry.package}\uFF08mode=${opts.mode}${asDependency ? ", \u4F9D\u8D56" : ""}\uFF09`);
2618
- if (opts.mode !== "local") {
2619
- console.log(` \u4E0B\u4E00\u6B65: pnpm install && tbox-app module doctor`);
2620
- }
2621
- }
2622
-
2623
- // src/commands/remove.ts
2624
- async function removeModule(id, opts = {}) {
2625
- const appDir = process.cwd();
2626
- assertValidModuleId(id, "remove \u53C2\u6570");
2627
- const manifest = readAppManifest(appDir);
2628
- if (!manifest) throw new Error("\u5F53\u524D\u76EE\u5F55\u4E0D\u662F tbox-app \u5E94\u7528\uFF08\u7F3A .tbox/app.json\uFF09");
2629
- if (!opts.force) {
2630
- const blockers = [];
2631
- for (const mod of await listLocalModules(appDir)) {
2632
- if (mod.id === id) continue;
2633
- const dep = (mod.descriptor.dependencies?.modules ?? []).find((d) => d.id === id);
2634
- if (dep && dep.required) blockers.push(mod.id);
2635
- }
2636
- if (blockers.length > 0) {
2637
- throw new Error(`\u6A21\u5757 ${id} \u88AB\u4F9D\u8D56\uFF0C\u65E0\u6CD5\u79FB\u9664\uFF1A${blockers.join(", ")}\uFF08--force \u5FFD\u7565\uFF09`);
2638
- }
2639
- }
2640
- const entry = manifest.npmModules.find((m) => m.id === id);
2641
- const isNpmModule = Boolean(entry);
2642
- if (isNpmModule) {
2643
- await writeAppManifest(appDir, removeNpmModule(manifest, id));
2644
- }
2645
- const removedDir = await removePackageDir(appDir, id);
2646
- if (removedDir) console.log(` \u5DF2\u5220\u9664 packages/${id}`);
2647
- else if (entry?.mode === "sdk") console.log(` \u5DF2\u79FB\u9664 manifest \u767B\u8BB0\uFF08sdk \u5305\uFF0C\u65E0\u672C\u5730\u76EE\u5F55\uFF09`);
2648
- if (entry?.mode === "codegen" || entry?.mode === "local" || !isNpmModule) {
2649
- await updateAppsDependency(appDir, `@app/${id}`, "remove");
2650
- }
2651
- const sync = await syncAll(appDir);
2652
- if (sync.changedFiles.length > 0) {
2653
- console.log(` \u88C5\u914D\u66F4\u65B0: ${sync.changedFiles.join(", ")}`);
2654
- }
2655
- for (const w of sync.warnings) console.warn(` \u26A0\uFE0F ${w}`);
2656
- for (const c of sync.depConflicts) console.warn(` \u26A0\uFE0F \u4F9D\u8D56\u51B2\u7A81: ${c}`);
2657
- const actual = await collectActual(appDir);
2658
- const issues = await runDoctor({ appDir, declared: sync.declared, actual });
2659
- const errors = issues.filter((i) => i.level === "error");
2660
- if (errors.length > 0) {
2661
- console.error("\u274C doctor \u9519\u8BEF\uFF1A");
2662
- for (const e of errors) console.error(` - [${e.rule}] ${e.message}`);
2663
- process.exitCode = 1;
2664
- }
2665
- console.log(`\u2705 \u5DF2\u79FB\u9664 ${id}`);
2666
- }
2667
-
2668
- // src/commands/sync.ts
2669
- async function syncCommand(opts = {}) {
2670
- const appDir = process.cwd();
2671
- const sync = await syncAll(appDir, { force: opts.force ?? false });
2672
- if (sync.changedFiles.length === 0) {
2673
- console.log("\u88C5\u914D\u5DF2\u540C\u6B65\uFF0C\u65E0\u53D8\u66F4");
2674
- } else {
2675
- console.log(`\u5DF2\u66F4\u65B0\u88C5\u914D\u6587\u4EF6: ${sync.changedFiles.join(", ")}`);
2676
- }
2677
- for (const w of sync.warnings) console.warn(` \u26A0\uFE0F ${w}`);
2678
- for (const c of sync.depConflicts) console.warn(` \u26A0\uFE0F \u4F9D\u8D56\u51B2\u7A81: ${c}`);
2679
- const actual = await collectActual(appDir);
2680
- const issues = await runDoctor({ appDir, declared: sync.declared, actual });
2681
- const errors = issues.filter((i) => i.level === "error");
2682
- if (errors.length > 0) {
2683
- console.error("\u274C doctor \u9519\u8BEF\uFF1A");
2684
- for (const e of errors) console.error(` - [${e.rule}] ${e.message}`);
2685
- process.exitCode = 1;
2686
- } else {
2687
- console.log("\u2705 doctor \u65E0\u9519\u8BEF");
2688
- }
2689
- for (const w of issues.filter((i) => i.level === "warning")) {
2690
- console.warn(` \u26A0\uFE0F [${w.rule}] ${w.message}`);
2691
- }
2692
- }
2693
-
2694
- // src/commands/list.ts
2695
- async function listCommand() {
2696
- const appDir = process.cwd();
2697
- const manifest = readAppManifest(appDir);
2698
- const declared = await collectDeclared(appDir);
2699
- console.log(`\u6A21\u677F\u7248\u672C: ${manifest?.templateVersion ?? "\u672A\u77E5"}`);
2700
- if (declared.length === 0) {
2701
- console.log("\uFF08\u65E0\u5DF2\u88C5\u6A21\u5757\uFF09");
2702
- return;
2703
- }
2704
- for (const m of declared) {
2705
- console.log(` ${m.id.padEnd(28)} ${m.pkg} [${m.mode}]`);
2706
- }
2707
- const npmIds = new Set((manifest?.npmModules ?? []).map((m) => m.id));
2708
- const localOnly = (await listLocalModules(appDir)).filter((m) => !npmIds.has(m.id));
2709
- if (localOnly.length > 0) {
2710
- console.log(" \u672C\u5730\u6A21\u5757\uFF08\u672A\u5728 app.json \u767B\u8BB0\uFF09:");
2711
- for (const m of localOnly) console.log(` ${m.id}`);
2712
- }
2713
- }
2714
-
2715
- // src/commands/diff.ts
2716
- async function diffCommand(module) {
2717
- const appDir = process.cwd();
2718
- const declared = await collectDeclared(appDir);
2719
- const actual = await collectActual(appDir);
2720
- let dirty = false;
2721
- for (const file of ASSEMBLY_FILES) {
2722
- const kind = file.kind;
2723
- const declaredIds = declared.filter((m) => contributesTo(kind, m)).map((m) => m.id);
2724
- const actualIds = actual[kind]?.moduleIds ?? [];
2725
- let missing = declaredIds.filter((id) => !actualIds.includes(id));
2726
- let extra = actualIds.filter((id) => !declaredIds.includes(id));
2727
- if (module) {
2728
- missing = missing.filter((id) => id === module);
2729
- extra = extra.filter((id) => id === module);
2730
- }
2731
- const broken = actual[kind]?.broken ?? [];
2732
- const nonStandard = actual[kind]?.nonStandardLines ?? [];
2733
- const lines = [];
2734
- if (broken.length > 0) lines.push(`\u26A0\uFE0F \u951A\u533A\u7834\u574F: ${broken.join(", ")}`);
2735
- if (missing.length > 0) lines.push(` - \u5DF2\u58F0\u660E\u672A\u88C5\u914D: ${missing.join(", ")}`);
2736
- if (extra.length > 0) lines.push(` + \u5DF2\u88C5\u914D\u672A\u58F0\u660E: ${extra.join(", ")}`);
2737
- for (const ns of nonStandard) lines.push(` ? \u975E\u6807\u884C: ${ns}`);
2738
- if (lines.length > 0) {
2739
- dirty = true;
2740
- console.log(`${file.path}`);
2741
- for (const l of lines) console.log(` ${l}`);
2742
- }
2743
- }
2744
- if (!dirty) {
2745
- console.log("\u65E0\u6F02\u79FB\uFF1A\u58F0\u660E\u96C6\u5408\u4E0E\u5B9E\u9645\u88C5\u914D\u96C6\u5408\u4E00\u81F4");
2746
- }
2747
- }
2748
-
2749
- // src/commands/doctor.ts
2750
- async function doctorCommand(opts = {}) {
2751
- const appDir = process.cwd();
2752
- const declared = await collectDeclared(appDir);
2753
- const actual = await collectActual(appDir);
2754
- const issues = await runDoctor(
2755
- { appDir, declared, actual },
2756
- {
2757
- only: opts.only ? opts.only.split(",").map((s) => s.trim()).filter(Boolean) : void 0,
2758
- skip: opts.skip ? opts.skip.split(",").map((s) => s.trim()).filter(Boolean) : void 0
2759
- }
2760
- );
2761
- const errors = issues.filter((i) => i.level === "error");
2762
- const warnings = issues.filter((i) => i.level === "warning");
2763
- if (opts.json) {
2764
- console.log(JSON.stringify({ errors, warnings, rules: issues }, null, 2));
2765
- if (errors.length > 0) process.exitCode = 1;
2766
- return { issues };
2767
- }
2768
- if (issues.length === 0) {
2769
- console.log("\u2705 doctor \u5168\u7EFF\uFF08\u7ED3\u6784\u6821\u9A8C\u901A\u8FC7\uFF09");
2770
- } else {
2771
- for (const e of errors) console.error(`\u274C [${e.rule}] ${e.message}`);
2772
- for (const w of warnings) console.warn(`\u26A0\uFE0F [${w.rule}] ${w.message}`);
2773
- if (errors.length > 0) {
2774
- console.error(`doctor \u5931\u8D25: ${errors.length} \u4E2A\u9519\u8BEF`);
2775
- process.exitCode = 1;
2776
- } else {
2777
- console.log(`doctor \u901A\u8FC7\uFF08${warnings.length} \u4E2A\u544A\u8B66\uFF09`);
2778
- }
2779
- }
2780
- return { issues };
2781
- }
2782
-
2783
- // src/commands/update.ts
2784
- import { existsSync as existsSync21 } from "fs";
2785
- import { readdir as readdir12, readFile as readFile6, unlink, writeFile as writeFile7 } from "fs/promises";
2786
- import path25 from "path";
2787
-
2788
- // src/merge3.ts
2789
- import { diffArrays } from "diff";
2790
- function computeHunks(base, target) {
2791
- const changes = diffArrays(base, target);
2792
- const hunks = [];
2793
- let baseIdx = 0;
2794
- let pending = null;
2795
- for (const c of changes) {
2796
- if (c.removed) {
2797
- if (!pending) pending = { start: baseIdx, oldLines: [...c.value], newLines: [] };
2798
- else pending.oldLines.push(...c.value);
2799
- baseIdx += c.value.length;
2800
- } else if (c.added) {
2801
- if (!pending) pending = { start: baseIdx, oldLines: [], newLines: [...c.value] };
2802
- else pending.newLines.push(...c.value);
2803
- } else {
2804
- if (pending) {
2805
- hunks.push(pending);
2806
- pending = null;
2807
- }
2808
- baseIdx += c.value.length;
2809
- }
2810
- }
2811
- if (pending) hunks.push(pending);
2812
- return hunks;
2813
- }
2814
- function merge3(base, ours, theirs) {
2815
- const baseLines = base === "" ? [] : base.split("\n");
2816
- const oursLines = ours === "" ? [] : ours.split("\n");
2817
- const theirsLines = theirs === "" ? [] : theirs.split("\n");
2818
- const oursHunks = computeHunks(baseLines, oursLines);
2819
- const theirsHunks = computeHunks(baseLines, theirsLines);
2820
- const byStart = /* @__PURE__ */ new Map();
2821
- for (const h of oursHunks) byStart.set(h.start, { ours: h });
2822
- for (const h of theirsHunks) {
2823
- const existing = byStart.get(h.start);
2824
- if (existing) existing.theirs = h;
2825
- else byStart.set(h.start, { theirs: h });
2826
- }
2827
- const out = [];
2828
- const conflicts = [];
2829
- const consumed = /* @__PURE__ */ new Set();
2830
- const applyHunk = (o, t) => {
2831
- const oldLen = Math.max(o?.oldLines.length ?? 0, t?.oldLines.length ?? 0);
2832
- if (o && t) {
2833
- const same = JSON.stringify(o.oldLines) === JSON.stringify(t.oldLines) && JSON.stringify(o.newLines) === JSON.stringify(t.newLines);
2834
- if (same) {
2835
- out.push(...o.newLines);
2836
- } else {
2837
- conflicts.push([...o.newLines, "--", ...t.newLines].join("\n"));
2838
- out.push("<<<<<<< ours", ...o.newLines, "=======", ...t.newLines, ">>>>>>> theirs");
2839
- }
2840
- } else if (o) {
2841
- out.push(...o.newLines);
2842
- } else {
2843
- out.push(...t.newLines);
2844
- }
2845
- return oldLen;
2846
- };
2847
- let i = 0;
2848
- while (i < baseLines.length) {
2849
- const entry = byStart.get(i);
2850
- if (!entry || consumed.has(i)) {
2851
- out.push(baseLines[i]);
2852
- i++;
2853
- continue;
2854
- }
2855
- consumed.add(i);
2856
- const oldLen = applyHunk(entry.ours, entry.theirs);
2857
- if (oldLen > 0) i += oldLen;
2858
- }
2859
- const tail = byStart.get(baseLines.length);
2860
- if (tail && !consumed.has(baseLines.length)) {
2861
- applyHunk(tail.ours, tail.theirs);
2862
- }
2863
- return { merged: out.join("\n"), conflicts };
2864
- }
2865
-
2866
- // src/commands/update.ts
2867
- var EXCLUDED = /* @__PURE__ */ new Set(["node_modules", "dist", ".git", ".tbox"]);
2868
- function openSourceDir(source) {
2869
- if (source.endsWith(".tgz") && existsSync21(source)) {
2870
- const tarball = new TarballSource(source);
2871
- return { dir: tarball.getDir(), tarball };
2872
- }
2873
- return { dir: path25.resolve(source), tarball: null };
2874
- }
2875
- async function readPackageFiles(dir) {
2876
- const map = /* @__PURE__ */ new Map();
2877
- async function walk(rel) {
2878
- const full = path25.join(dir, rel);
2879
- const entries = await readdir12(full, { withFileTypes: true });
2880
- for (const entry of entries) {
2881
- if (EXCLUDED.has(entry.name)) continue;
2882
- const child = rel ? `${rel}/${entry.name}` : entry.name;
2883
- const childFull = path25.join(full, entry.name);
2884
- if (entry.isDirectory()) {
2885
- await walk(child);
2886
- } else {
2887
- try {
2888
- map.set(child, await readFile6(childFull, "utf8"));
2889
- } catch {
2890
- }
2891
- }
2892
- }
2893
- }
2894
- await walk("");
2895
- return map;
2896
- }
2897
- async function updateModule(opts) {
2898
- const appDir = process.cwd();
2899
- const manifest = readAppManifest(appDir);
2900
- if (!manifest) throw new Error("\u5F53\u524D\u76EE\u5F55\u4E0D\u662F tbox-app \u5E94\u7528\uFF08\u7F3A .tbox/app.json\uFF09");
2901
- const entry = manifest.npmModules.find((m) => m.id === opts.module);
2902
- if (!entry) throw new Error(`\u6A21\u5757 ${opts.module} \u672A\u5728 .tbox/app.json \u767B\u8BB0`);
2903
- if (entry.mode === "sdk") {
2904
- if (opts.dryRun) {
2905
- console.log(`[dry-run] pnpm update ${entry.package}`);
2906
- return;
2907
- }
2908
- const { execFileSync: execFileSync4 } = await import("child_process");
2909
- execFileSync4("pnpm", ["update", entry.package], { stdio: "inherit", cwd: appDir });
2910
- console.log(`\u2705 \u5DF2\u5347\u7EA7 ${entry.package}\uFF08sdk \u6A21\u5F0F\uFF09`);
2911
- return;
2912
- }
2913
- const pkgDir = path25.join(appDir, "packages", opts.module);
2914
- if (!existsSync21(pkgDir)) throw new Error(`\u7F3A\u5C11 packages/${opts.module}`);
2915
- const ours = await readPackageFiles(pkgDir);
2916
- const tarballs = [];
2917
- try {
2918
- let theirs;
2919
- let theirsVersion;
2920
- if (opts.source) {
2921
- const src = openSourceDir(opts.source);
2922
- if (src.tarball) tarballs.push(src.tarball);
2923
- theirs = await readPackageFiles(src.dir);
2924
- theirsVersion = src.tarball ? src.tarball.resolveVersion() : readPkgJson(src.dir)?.version ?? entry.version;
2925
- } else {
2926
- throw new Error("codegen \u5347\u7EA7\u9700\u8981 --source <\u65B0\u7248\u672C\u5730\u76EE\u5F55\u6216 tgz>\uFF08npm \u62C9\u53D6\u4E3A manual\uFF0C\u771F\u5B9E\u53D1\u5E03\u540E\u56DE\u843D\uFF09");
2927
- }
2928
- let base = /* @__PURE__ */ new Map();
2929
- if (opts.baseSource) {
2930
- const bsrc = openSourceDir(opts.baseSource);
2931
- if (bsrc.tarball) tarballs.push(bsrc.tarball);
2932
- base = await readPackageFiles(bsrc.dir);
2933
- }
2934
- const allKeys = /* @__PURE__ */ new Set([...base.keys(), ...ours.keys(), ...theirs.keys()]);
2935
- let mergedFiles = /* @__PURE__ */ new Map();
2936
- const conflictList = [];
2937
- for (const key of allKeys) {
2938
- const b = base.get(key) ?? "";
2939
- const o = ours.get(key) ?? "";
2940
- const t = theirs.get(key) ?? "";
2941
- if (opts.ours) {
2942
- if (o) mergedFiles.set(key, o);
2943
- continue;
2944
- }
2945
- if (opts.theirs) {
2946
- if (t) mergedFiles.set(key, t);
2947
- continue;
2948
- }
2949
- const result = merge3(b, o, t);
2950
- mergedFiles.set(key, result.merged);
2951
- if (result.conflicts.length > 0) {
2952
- conflictList.push(`${key}: ${result.conflicts.length} \u5904\u51B2\u7A81`);
2953
- }
2954
- }
2955
- if (opts.dryRun) {
2956
- console.log(`[dry-run] ${opts.module}: ${allKeys.size} \u6587\u4EF6\u5408\u5E76\uFF0C\u51B2\u7A81 ${conflictList.length} \u5904`);
2957
- for (const c of conflictList) console.log(` \u26A0\uFE0F ${c}`);
2958
- return;
2959
- }
2960
- for (const [key, content] of mergedFiles) {
2961
- const out = path25.join(pkgDir, key);
2962
- if (content === "" && existsSync21(out)) {
2963
- await unlink(out);
2964
- console.log(` \u{1F5D1} \u5DF2\u5220\u9664 ${key}\uFF08\u5347\u7EA7\u6E90\u5220\u9664\u8BE5\u6587\u4EF6\uFF09`);
2965
- continue;
2966
- }
2967
- await writeFile7(out, content, "utf8");
2968
- }
2969
- await writeAppManifest(appDir, updateNpmModule(manifest, opts.module, { baseVersion: theirsVersion, version: theirsVersion }));
2970
- const sync = await syncAll(appDir);
2971
- const actual = await collectActual(appDir);
2972
- const issues = await runDoctor({ appDir, declared: sync.declared, actual });
2973
- const errors = issues.filter((i) => i.level === "error");
2974
- if (errors.length > 0) {
2975
- console.error("\u274C doctor \u9519\u8BEF\uFF1A");
2976
- for (const e of errors) console.error(` - [${e.rule}] ${e.message}`);
2977
- process.exitCode = 1;
2978
- }
2979
- if (conflictList.length > 0) {
2980
- console.warn("\u26A0\uFE0F \u5347\u7EA7\u5B8C\u6210\uFF0C\u5B58\u5728\u51B2\u7A81\u6807\u8BB0\uFF08<<<<<<< ours / >>>>>>> theirs\uFF09\uFF0C\u8BF7\u4EBA\u5DE5/Agent \u89E3\u51B3\u540E\u8FD0\u884C doctor:");
2981
- for (const c of conflictList) console.warn(` - ${c}`);
2982
- } else {
2983
- console.log(`\u2705 \u5DF2\u5347\u7EA7 ${opts.module} \u2192 ${theirsVersion}`);
2984
- }
2985
- } finally {
2986
- for (const t of tarballs) t.dispose();
2987
- }
2988
- }
2989
-
2990
- // src/commands/publish.ts
2991
- import { existsSync as existsSync23, readFileSync as readFileSync11, mkdtempSync as mkdtempSync2, rmSync as rmSync2 } from "fs";
2992
- import { tmpdir as tmpdir2 } from "os";
2993
- import path27 from "path";
2994
-
2995
- // src/commands/pack.ts
2996
- import { existsSync as existsSync22, readFileSync as readFileSync10, readdirSync as readdirSync3 } from "fs";
2997
- import { execFileSync as execFileSync3 } from "child_process";
2998
- import path26 from "path";
2999
- function tarballFileName(name, version) {
3000
- const plain = name.replace(/^@/, "").replace("/", "-");
3001
- return `${plain}-${version}.tgz`;
3002
- }
3003
- function validatePublishableModule(moduleDir) {
3004
- if (!existsSync22(moduleDir) || !existsSync22(path26.join(moduleDir, "package.json"))) {
3005
- throw new Error(`\u6A21\u5757\u76EE\u5F55\u65E0\u6548\uFF08\u7F3A package.json\uFF09: ${moduleDir}`);
3006
- }
3007
- let pkg;
3008
- try {
3009
- pkg = JSON.parse(readFileSync10(path26.join(moduleDir, "package.json"), "utf8"));
3010
- } catch {
3011
- throw new Error(`package.json \u89E3\u6790\u5931\u8D25: ${path26.join(moduleDir, "package.json")}`);
3012
- }
3013
- const name = typeof pkg.name === "string" ? pkg.name : "";
3014
- if (!/^@tbox\.cn\//.test(name)) {
3015
- throw new Error(`\u53D1\u5E03\u5305\u540D\u5FC5\u987B\u4E3A @tbox.cn/* scope: ${name || "(\u7F3A\u5931)"}`);
3016
- }
3017
- const version = typeof pkg.version === "string" ? pkg.version : "";
3018
- if (!/^\d+\.\d+\.\d+/.test(version)) {
3019
- throw new Error(`\u7248\u672C\u53F7\u65E0\u6548\uFF08\u9700 semver x.y.z\uFF09: ${version || "(\u7F3A\u5931)"}`);
3020
- }
3021
- return { name, version };
3022
- }
3023
- function packModule(moduleDir, outDir) {
3024
- const { name, version } = validatePublishableModule(moduleDir);
3025
- const absOut = path26.resolve(outDir);
3026
- execFileSync3("pnpm", ["pack", "--pack-destination", absOut], { stdio: "inherit", cwd: moduleDir });
3027
- return path26.join(absOut, tarballFileName(name, version));
3028
- }
3029
- function detectKind(pkgDir, name) {
3030
- if (existsSync22(path26.join(pkgDir, "pnpm-workspace.template.yaml"))) return "template";
3031
- if (existsSync22(path26.join(pkgDir, "tbox.component.json"))) return "module";
3032
- if (/^@[^/]+\/app-contracts/.test(name)) return "contract";
3033
- return "generic";
3034
- }
3035
- function listAllFiles(dir) {
3036
- const out = [];
3037
- function walk(rel) {
3038
- const full = path26.join(dir, rel);
3039
- for (const entry of readdirSync3(full, { withFileTypes: true })) {
3040
- if (entry.name === "node_modules" || entry.name === ".git") continue;
3041
- const child = rel ? `${rel}/${entry.name}` : entry.name;
3042
- if (entry.isDirectory()) walk(child);
3043
- else out.push(child);
3044
- }
3045
- }
3046
- walk("");
3047
- return out;
3048
- }
3049
- function collectExportTargets(exports) {
3050
- const targets = [];
3051
- const push = (v) => {
3052
- if (typeof v === "string") targets.push(v);
195
+ name: 'tbox-dev-in-place',
196
+ enforce: 'pre',
197
+ resolveId(source, importer) {
198
+ // client 装配改指:apps/client 内相对引用 modules → 物化构件(兼容 tsx 的 .ts 改写)
199
+ if (
200
+ typeof importer === 'string' &&
201
+ importer.includes(\`\${path.sep}apps\${path.sep}client\${path.sep}\`) &&
202
+ /^\\.{1,2}\\/modules(?:\\.(?:js|ts))?$/.test(source)
203
+ ) {
204
+ return path.join(HERE, 'modules.client.ts');
205
+ }
206
+ const base = String(source).split('?')[0];
207
+ const hit = KEYS.find((k) => base === k || base.startsWith(k + '/'));
208
+ if (hit) {
209
+ const sub = base === hit ? '' : base.slice(hit.length);
210
+ return resolveSub(path.resolve(HERE, MANIFEST.redirects[hit]), sub) ?? null;
211
+ }
212
+ return null;
213
+ },
214
+ config() {
215
+ // dev 源解析域白名单收敛(安全:/@fs 经 0.0.0.0 监听面可达,禁止 repoRoot 全仓——
216
+ // 恒不含 .real-env/docs/dotfiles)。四源 = 模板根 + 模板家族目录 + 模块/平台真身 + 依赖库。
217
+ const allow = [
218
+ MANIFEST.templateDir,
219
+ path.join(MANIFEST.repoRoot, 'templates'),
220
+ path.join(MANIFEST.repoRoot, 'modules'),
221
+ path.join(MANIFEST.repoRoot, 'packages'),
222
+ path.join(MANIFEST.repoRoot, 'node_modules'),
223
+ ];
224
+ return {
225
+ server: { fs: { allow } },
226
+ // identity 单向代价(R-9b):redirects 键全部豁免预打包(含 sdk/contracts),冷启动略慢
227
+ optimizeDeps: { exclude: Object.keys(MANIFEST.redirects ?? {}) },
228
+ };
229
+ },
3053
230
  };
3054
- if (typeof exports === "string") push(exports);
3055
- else if (exports && typeof exports === "object") {
3056
- for (const v of Object.values(exports)) {
3057
- if (typeof v === "string") push(v);
3058
- else if (v && typeof v === "object") {
3059
- for (const t of Object.values(v)) push(t);
3060
- }
3061
- }
3062
- }
3063
- return targets;
3064
- }
3065
- function verifyPkgDir(pkgDir, kind) {
3066
- const issues = [];
3067
- const pkgFile = path26.join(pkgDir, "package.json");
3068
- let pkg;
3069
- try {
3070
- pkg = JSON.parse(readFileSync10(pkgFile, "utf8"));
3071
- } catch {
3072
- issues.push({ rule: "pack-manifest", level: "error", message: "\u4EA7\u7269\u7F3A\u5C11\u53EF\u89E3\u6790\u7684 package.json" });
3073
- return issues;
3074
- }
3075
- const kindOf = kind ?? detectKind(pkgDir, pkg.name ?? "");
3076
- const files = listAllFiles(pkgDir);
3077
- for (const key of ["dependencies", "peerDependencies", "devDependencies"]) {
3078
- const deps = pkg[key];
3079
- if (!deps || typeof deps !== "object") continue;
3080
- for (const [n, spec] of Object.entries(deps)) {
3081
- if (typeof spec === "string" && /^(workspace:|catalog:)/.test(spec)) {
3082
- issues.push({
3083
- rule: "pack-protocol-residue",
3084
- level: "error",
3085
- message: `${n}: \u53D1\u5E03\u6001\u6B8B\u7559 pnpm \u79C1\u6709\u534F\u8BAE "${spec}"\uFF08\u5E94\u5DF2\u5F52\u4E00\u5316\u4E3A\u5B9E\u88C5\u7248\u672C\uFF09`
3086
- });
3087
- }
3088
- }
3089
- }
3090
- if (pkg.exports !== void 0) {
3091
- for (const t of collectExportTargets(pkg.exports)) {
3092
- if (t.includes("/src/") || t === "src") {
3093
- issues.push({
3094
- rule: "pack-exports",
3095
- level: "error",
3096
- message: `exports \u6307\u5411\u5F00\u53D1\u6001\u6E90\u7801 "${t}"\uFF08\u53D1\u5E03\u6001\u5E94\u6307\u5411 dist/\uFF09`
3097
- });
3098
- } else if (t.startsWith("./") && !existsSync22(path26.join(pkgDir, t))) {
3099
- issues.push({
3100
- rule: "pack-exports",
3101
- level: "error",
3102
- message: `exports \u76EE\u6807\u7F3A\u5931\uFF08\u4EA7\u7269\u5185\u4E0D\u5B58\u5728\uFF09: ${t}`
3103
- });
3104
- }
3105
- }
3106
- }
3107
- const filesField = pkg.files;
3108
- if (Array.isArray(filesField) && filesField.length > 0) {
3109
- const allow = /* @__PURE__ */ new Set([...filesField.map((w) => String(w)), "package.json"]);
3110
- const bad = files.filter((f) => ![...allow].some((w) => f === w || f.startsWith(`${w}/`)));
3111
- if (bad.length > 0) {
3112
- issues.push({
3113
- rule: "pack-whitelist",
3114
- level: "error",
3115
- message: `\u4EA7\u7269\u542B\u767D\u540D\u5355\u5916\u6587\u4EF6: ${bad.slice(0, 5).join(", ")}${bad.length > 5 ? ` \u7B49 ${bad.length} \u4E2A` : ""}`
3116
- });
3117
- }
3118
- } else {
3119
- issues.push({
3120
- rule: "pack-whitelist",
3121
- level: "warning",
3122
- message: "\u672A\u58F0\u660E files \u767D\u540D\u5355\uFF0C\u4EA7\u7269\u8303\u56F4\u4E0D\u53EF\u63A7\uFF08\u5EFA\u8BAE\u58F0\u660E files \u9650\u5236\u6253\u5305\u5185\u5BB9\uFF09"
3123
- });
3124
- }
3125
- const leaked = files.filter((f) => /(^|\/)\.env$/.test(f) || /(^|\/)\.npmrc$/.test(f));
3126
- if (leaked.length > 0) {
3127
- issues.push({ rule: "pack-leak", level: "error", message: `\u4EA7\u7269\u542B\u654F\u611F\u6587\u4EF6: ${leaked.join(", ")}` });
3128
- }
3129
- if (kindOf === "module") {
3130
- for (const required of ["src", "dist", "tbox.component.json"]) {
3131
- if (!files.includes(required) && !existsSync22(path26.join(pkgDir, required))) {
3132
- issues.push({ rule: "pack-files", level: "error", message: `\u4E1A\u52A1\u6A21\u5757\u4EA7\u7269\u7F3A\u5C11 ${required}` });
3133
- }
3134
- }
3135
- } else if (kindOf === "template") {
3136
- for (const required of [
3137
- "pnpm-workspace.template.yaml",
3138
- "apps/server/package.json",
3139
- "apps/client/package.json",
3140
- "packages/module-weather/package.json",
3141
- ".tbox/app.json",
3142
- "tsconfig.base.json",
3143
- "scripts/verify-generated.mjs"
3144
- ]) {
3145
- if (!existsSync22(path26.join(pkgDir, required))) {
3146
- issues.push({ rule: "pack-files", level: "error", message: `\u6A21\u677F\u4EA7\u7269\u7F3A\u5C11 ${required}` });
3147
- }
3148
- }
3149
- for (const leaked2 of ["pnpm-workspace.yaml", "pnpm-lock.yaml"]) {
3150
- if (existsSync22(path26.join(pkgDir, leaked2))) {
3151
- issues.push({
3152
- rule: "pack-files",
3153
- level: "error",
3154
- message: `\u6A21\u677F\u4EA7\u7269\u6CC4\u6F0F ${leaked2}\uFF08\u5E94\u4EC5\u542B pnpm-workspace.template.yaml\uFF09`
3155
- });
3156
- }
3157
- }
3158
- } else if (kindOf === "contract") {
3159
- const dts = files.filter((f) => f.startsWith("dist/") && f.endsWith(".d.ts"));
3160
- if (dts.length === 0) {
3161
- issues.push({ rule: "pack-files", level: "error", message: "\u5951\u7EA6\u5305\u4EA7\u7269\u7F3A\u5C11 dist/*.d.ts" });
3162
- }
3163
- } else {
3164
- const bin = pkg.bin;
3165
- const binTargets = typeof bin === "string" ? [bin] : bin && typeof bin === "object" ? Object.values(bin) : [];
3166
- for (const target of binTargets) {
3167
- if (typeof target === "string" && !files.includes(target)) {
3168
- issues.push({ rule: "pack-files", level: "error", message: `bin \u6307\u5411\u6587\u4EF6\u7F3A\u5931: ${target}` });
3169
- }
3170
- }
3171
- }
3172
- const declaredDocs = Array.isArray(filesField) && filesField.length > 0;
3173
- for (const doc of ["LICENSE", "README.md"]) {
3174
- if (!files.includes(doc)) {
3175
- issues.push({
3176
- rule: "pack-docs",
3177
- level: "warning",
3178
- message: `\u4EA7\u7269\u7F3A\u5C11 ${doc}${declaredDocs ? "\uFF08files \u767D\u540D\u5355\u5DF2\u58F0\u660E\uFF1B\u53D1\u5E03\u5B8C\u5907\u6027 backlog\uFF09" : "\uFF08\u5EFA\u8BAE\u8865\u5145\u4EE5\u5B8C\u5907\u53D1\u5E03\u5143\u6570\u636E\uFF09"}`
3179
- });
3180
- }
3181
- }
3182
- return issues;
3183
- }
3184
- async function verifyTarball(tgzPath) {
3185
- const tarball = new TarballSource(tgzPath);
3186
- try {
3187
- const pkgDir = tarball.getDir();
3188
- const pkg = JSON.parse(await tarball.readFile("package.json"));
3189
- const kind = detectKind(pkgDir, pkg.name ?? "");
3190
- const issues = verifyPkgDir(pkgDir, kind);
3191
- if (pkg.name && pkg.version) {
3192
- const expect = tarballFileName(pkg.name, pkg.version);
3193
- if (path26.basename(tgzPath) !== expect) {
3194
- issues.push({
3195
- rule: "pack-filename",
3196
- level: "error",
3197
- message: `tgz \u6587\u4EF6\u540D ${path26.basename(tgzPath)} \u4E0E\u5305 ${pkg.name}@${pkg.version} \u671F\u671B ${expect} \u4E0D\u4E00\u81F4`
3198
- });
3199
- }
3200
- }
3201
- return issues;
3202
- } finally {
3203
- tarball.dispose();
3204
- }
3205
- }
3206
- function printIssues(issues) {
3207
- for (const i of issues) {
3208
- console.log(` ${i.level === "error" ? "\u274C" : "\u26A0\uFE0F"} [${i.rule}] ${i.message}`);
3209
- }
3210
- }
3211
- async function packCommand(opts) {
3212
- const moduleDir = path26.resolve(process.cwd(), opts.module);
3213
- const tgzPath = packModule(moduleDir, opts.out);
3214
- console.log(`\u2705 \u5DF2\u6253\u5305 ${path26.basename(tgzPath)} \u2192 ${path26.resolve(opts.out)}`);
3215
- if (opts.check) {
3216
- const issues = await verifyTarball(tgzPath);
3217
- printIssues(issues);
3218
- if (issues.some((i) => i.level === "error")) process.exitCode = 1;
3219
- }
3220
- }
3221
-
3222
- // src/commands/publish.ts
3223
- async function publishModule(opts) {
3224
- const cwd = process.cwd();
3225
- const moduleDir = path27.resolve(cwd, opts.module);
3226
- if (!existsSync23(moduleDir) || !existsSync23(path27.join(moduleDir, "package.json"))) {
3227
- throw new Error(`\u6A21\u5757\u76EE\u5F55\u65E0\u6548\uFF08\u7F3A package.json\uFF09: ${opts.module}`);
3228
- }
3229
- let tgzPath;
3230
- let workDir = null;
3231
- if (opts.tgz) {
3232
- tgzPath = path27.resolve(opts.tgz);
3233
- if (!existsSync23(tgzPath)) throw new Error(`tgz \u4EA7\u7269\u4E0D\u5B58\u5728: ${opts.tgz}`);
3234
- const tarball = new TarballSource(tgzPath);
3235
- try {
3236
- const tgzPkg = JSON.parse(await tarball.readFile("package.json"));
3237
- const modulePkg = JSON.parse(readFileSync11(path27.join(moduleDir, "package.json"), "utf8"));
3238
- if (tgzPkg.name !== modulePkg.name) {
3239
- throw new Error(
3240
- `--tgz \u5305\u540D ${tgzPkg.name ?? "(\u672A\u77E5)"} \u4E0E\u6A21\u5757 ${modulePkg.name ?? "(\u672A\u77E5)"} \u4E0D\u4E00\u81F4\uFF08\u8BF7\u786E\u8BA4 --tgz \u4E0E\u6A21\u5757\u5339\u914D\uFF09`
3241
- );
3242
- }
3243
- } finally {
3244
- tarball.dispose();
3245
- }
3246
- } else {
3247
- workDir = mkdtempSync2(path27.join(tmpdir2(), "tbox-publish-"));
3248
- tgzPath = packModule(moduleDir, workDir);
3249
- }
3250
- try {
3251
- const issues = await verifyTarball(tgzPath);
3252
- if (issues.length > 0) printIssues(issues);
3253
- const errors = issues.filter((i) => i.level === "error");
3254
- if (errors.length > 0) {
3255
- throw new Error(`\u4EA7\u7269\u95E8\u7981\u5931\u8D25\uFF08${errors.length} \u5904 error\uFF09\uFF0C\u5DF2\u4E2D\u6B62\u53D1\u5E03: ${path27.basename(tgzPath)}`);
3256
- }
3257
- if (!workDir) workDir = mkdtempSync2(path27.join(tmpdir2(), "tbox-publish-"));
3258
- const { execFileSync: execFileSync4 } = await import("child_process");
3259
- const registryArgs = opts.registry ? ["--registry", opts.registry] : [];
3260
- execFileSync4(
3261
- "pnpm",
3262
- ["publish", tgzPath, "--access", opts.access ?? "public", ...registryArgs, ...opts.dryRun ? ["--dry-run"] : []],
3263
- { stdio: "inherit", cwd: workDir }
3264
- );
3265
- console.log(
3266
- `\u2705 \u5DF2\u53D1\u5E03 ${path27.basename(tgzPath)}${opts.registry ? ` \u2192 ${opts.registry}` : ""}${opts.dryRun ? "\uFF08dry-run\uFF09" : ""}`
3267
- );
3268
- } finally {
3269
- if (workDir) {
3270
- try {
3271
- rmSync2(workDir, { recursive: true, force: true });
3272
- } catch {
3273
- }
3274
- }
3275
- }
3276
231
  }
3277
-
3278
- // src/index.ts
3279
- function createProgram() {
3280
- const program = new Command();
3281
- const pkgJson = createRequire3(import.meta.url)("../package.json");
3282
- program.name("tbox-app").description("tbox \u7EC4\u4EF6\u5316\u5E94\u7528 CLI\uFF1Acreate / module add|remove|list|sync|diff|doctor").version(pkgJson.version);
3283
- program.command("create <name>").description("\u4ECE\u6A21\u677F\u751F\u6210\u591A\u5305 workspace \u5E94\u7528\u9AA8\u67B6").option("--source <\u8DEF\u5F84>", "\u6A21\u677F\u6E90\uFF08\u672C\u5730\u76EE\u5F55 / \u6A21\u677F pack tgz / registry \u5305 @scope/pkg[@version]\uFF09").option("--local-deps <tgz\u76EE\u5F55>", "\u6CE8\u5165\u5E73\u53F0 overrides\uFF08file: \u672C\u5730\u53D1\u5E03\u6001 tgz\uFF0Cinstall \u79BB\u7EBF\u9A8C\u8BC1\u6001\uFF1B\u751F\u4EA7 B \u6863\u4E0D\u4F20\uFF09").option("--registry <url>", "\u62C9\u53D6 registry\uFF08registry \u6E90\u65F6\u751F\u6548\uFF1B\u7F3A\u7701\u81EA\u52A8\u89E3\u6790\u73AF\u5883\u955C\u50CF\uFF09").option("--force", "\u8986\u76D6\u5DF2\u5B58\u5728\u76EE\u5F55").action(async (name, opts) => {
3284
- try {
3285
- await createApp({ name, source: opts.source ?? "", force: opts.force, localDeps: opts.localDeps, registry: opts.registry });
3286
- } catch (err) {
3287
- console.error("\u274C", err.message);
3288
- process.exitCode = 1;
3289
- }
3290
- });
3291
- const moduleCmd = program.command("module").description("\u6A21\u5757\u7BA1\u7406\uFF08add/remove/list/sync/diff/doctor/update/publish/pack\uFF09");
3292
- moduleCmd.command("add <pkg>").description("\u5B89\u88C5/\u5C55\u5F00\u6A21\u5757\uFF08--mode local|codegen|sdk\uFF1B--source \u652F\u6301\u76EE\u5F55/tgz/registry \u5305\uFF09").option("--mode <local|codegen|sdk>", "\u5206\u53D1\u6A21\u5F0F", "codegen").option("--source <\u8DEF\u5F84>", "\u6A21\u5757\u6E90\uFF08\u672C\u5730\u76EE\u5F55/tgz/registry \u5305 @scope/pkg[@version]\uFF09").option("--registry <url>", "\u62C9\u53D6 registry\uFF08registry \u6E90\u65F6\u751F\u6548\uFF1B\u7F3A\u7701\u81EA\u52A8\u89E3\u6790\u73AF\u5883\u955C\u50CF\uFF09").option("--no-deps", "\u8DF3\u8FC7\u4F9D\u8D56\u81EA\u52A8\u89E3\u6790\uFF08P3 W3-B \u9003\u751F\u95E8\uFF09").action(async (pkg, opts) => {
3293
- try {
3294
- await addModule({ pkg, mode: opts.mode, source: opts.source, registry: opts.registry, noDeps: !opts.deps });
3295
- } catch (err) {
3296
- console.error("\u274C", err.message);
3297
- process.exitCode = 1;
3298
- }
3299
- });
3300
- moduleCmd.command("remove <id>").description("\u5378\u8F7D\u6A21\u5757\uFF08\u5220\u5305 + \u6E05\u7406\u88C5\u914D\uFF09").option("--force", "\u5FFD\u7565\u53CD\u5411\u4F9D\u8D56\u68C0\u67E5\u5F3A\u5236\u79FB\u9664").action(async (id, opts) => {
3301
- try {
3302
- await removeModule(id, { force: opts.force });
3303
- } catch (err) {
3304
- console.error("\u274C", err.message);
3305
- process.exitCode = 1;
3306
- }
3307
- });
3308
- moduleCmd.command("list").description("\u5217\u51FA\u5DF2\u88C5\u6A21\u5757").action(async () => {
3309
- try {
3310
- await listCommand();
3311
- } catch (err) {
3312
- console.error("\u274C", err.message);
3313
- process.exitCode = 1;
3314
- }
3315
- });
3316
- moduleCmd.command("sync").description("\u589E\u91CF\u88C5\u914D 4 \u6587\u4EF6\uFF08\u4E0D\u8986\u76D6\uFF1B\u7ED3\u6784\u7834\u574F\u964D\u7EA7\u4E3A\u544A\u8B66\uFF09").option("--force", "\u5168\u91CF\u8986\u76D6\u88C5\u914D\u6587\u4EF6\uFF08002 \xA76.5\uFF1A\u552F\u4E00\u8986\u76D6\u5165\u53E3\uFF09").action(async (opts) => {
3317
- try {
3318
- await syncCommand({ force: opts.force });
3319
- } catch (err) {
3320
- console.error("\u274C", err.message);
3321
- process.exitCode = 1;
3322
- }
3323
- });
3324
- moduleCmd.command("diff [module]").description("\u88C5\u914D\u6F02\u79FB\u53CC\u5411 diff\uFF08module \u53EF\u6307\u5B9A\uFF0C\u9ED8\u8BA4\u5168\u90E8\uFF09").action(async (module) => {
3325
- try {
3326
- await diffCommand(module);
3327
- } catch (err) {
3328
- console.error("\u274C", err.message);
3329
- process.exitCode = 1;
3330
- }
3331
- });
3332
- moduleCmd.command("doctor").description("\u7ED3\u6784\u6821\u9A8C\uFF08doctor \u5B8C\u6574\u7248 13 \u6761\u89C4\u5219\uFF09").option("--json", "\u53EA\u8F93\u51FA JSON \u7ED3\u679C").option("--only <\u89C4\u5219\u5217\u8868>", "\u53EA\u8FD0\u884C\u6307\u5B9A\u89C4\u5219\uFF08\u9017\u53F7\u5206\u9694\uFF09").option("--skip <\u89C4\u5219\u5217\u8868>", "\u8DF3\u8FC7\u6307\u5B9A\u89C4\u5219\uFF08\u9017\u53F7\u5206\u9694\uFF09").action(async (opts) => {
3333
- try {
3334
- await doctorCommand(opts);
3335
- } catch (err) {
3336
- console.error("\u274C", err.message);
3337
- process.exitCode = 1;
3338
- }
3339
- });
3340
- moduleCmd.command("update <module>").description("\u5347\u7EA7\u7EC4\u4EF6\uFF08codegen\u21923-way merge\uFF1Bsdk\u2192pnpm update\uFF09").option("--dry-run", "\u53EA\u6253\u5370\u5408\u5E76\u7ED3\u679C\u4E0D\u5199\u56DE").option("--ours", "\u51B2\u7A81\u5FEB\u901F\u53D6\u672C\u5730\u7248\u672C").option("--theirs", "\u51B2\u7A81\u5FEB\u901F\u53D6\u65B0\u7248\u7248\u672C").option("--source <\u8DEF\u5F84>", "\u65B0\u7248\u672C\u5730\u6E90\u76EE\u5F55\u6216 tgz").option("--base-source <\u8DEF\u5F84>", "base \u672C\u5730\u6E90\u76EE\u5F55\u6216 tgz").action(async (module, opts) => {
3341
- try {
3342
- await updateModule({ module, ...opts });
3343
- } catch (err) {
3344
- console.error("\u274C", err.message);
3345
- process.exitCode = 1;
3346
- }
3347
- });
3348
- moduleCmd.command("pack <module>").description("\u672C\u5730\u6253\u5305\u4E0D\u53D1\u5E03\uFF08\u6A21\u5757\u76EE\u5F55\u5982 modules/module-member\uFF1Bpnpm pack \u5185\u5EFA\u5F52\u4E00\u5316 \u2192 \u53D1\u5E03\u6001 tgz\uFF09").requiredOption("--out <dir>", "\u4EA7\u7269\u76EE\u5F55\uFF08pnpm \u81EA\u52A8\u521B\u5EFA\uFF0C\u540C\u540D\u8986\u76D6\u5199\uFF09").option("--check", "\u4EA7\u7269\u65AD\u8A00\uFF08\u534F\u8BAE\u6B8B\u7559 / exports / \u767D\u540D\u5355 / \u6CC4\u6F0F / \u6587\u4EF6\u4E00\u81F4\u6027\uFF09").action(async (module, opts) => {
3349
- try {
3350
- await packCommand({ module, out: opts.out, check: opts.check });
3351
- } catch (err) {
3352
- console.error("\u274C", err.message);
3353
- process.exitCode = 1;
3354
- }
3355
- });
3356
- moduleCmd.command("publish <module>").description("\u6A21\u5757\u664B\u5347 npm\uFF08\u6A21\u5757\u76EE\u5F55\u5982 modules/module-member\uFF1B\u6821\u9A8C + \u6253\u5305 + \u4EA7\u7269\u95E8\u7981 + pnpm publish <tgz>\uFF09").option("--tgz <path>", "\u590D\u7528\u5DF2\u9A8C\u8BC1\u4EA7\u7269\uFF08\u7F3A\u7701\u73B0\u573A pack \u5230\u4E34\u65F6\u76EE\u5F55\uFF09").option("--dry-run", "\u6821\u9A8C + \u95E8\u7981 + \u6253\u5370\uFF0C\u4E0D\u771F\u5B9E\u53D1\u5E03").option("--access <public|restricted>", "npm access", "public").option("--registry <url>", "\u53D1\u5E03 registry\uFF08\u7F3A\u7701 pnpm \u914D\u7F6E\u89E3\u6790\uFF1A@tbox.cn scope \u4F18\u5148\uFF09").action(async (module, opts) => {
3357
- try {
3358
- await publishModule({
3359
- module,
3360
- tgz: opts.tgz,
3361
- dryRun: opts.dryRun,
3362
- access: opts.access,
3363
- registry: opts.registry
3364
- });
3365
- } catch (err) {
3366
- console.error("\u274C", err.message);
3367
- process.exitCode = 1;
3368
- }
3369
- });
3370
- return program;
3371
- }
3372
- var isMain = typeof process.argv[1] === "string" && realpathSync3(fileURLToPath(import.meta.url)) === realpathSync3(process.argv[1]);
3373
- if (isMain) {
3374
- createProgram().parseAsync(process.argv);
3375
- }
3376
- export {
3377
- createProgram
3378
- };
232
+ `;async function xo(e){let t=Uc();if(e.prune){let m=e.template?x.resolve(t,e.template):x.resolve(t,mo);if(!I(m))throw new Error(`--template 目录不存在:${m}`);await al(ae(m),t);return}Kc(e.realEnv);let r=ae(e.template?x.resolve(t,e.template):x.resolve(t,mo));if(!I(x.join(r,".tbox","app.json")))throw new Error(`--template 目录缺 .tbox/app.json(不是应用/模板根?):${r}`);Wc(r);let n=x.join(r,Pe),s=x.join(n,"session.lock"),i=x.join(n,"dev-manifest.json"),o=(e.module??[]).map(m=>x.resolve(t,m)),a=Xc(o,t,r);if(a.length===0)throw new Error("未指定任何模块:请传 --module modules/<id>(至少一个);或使用 scripts/dev-mall-agent.sh 快速开始");let c=Qc(t,n,a,!!e.linkSdk),l=tl(t,a,e.realEnv,!!e.linkSdk,e.configDir??[]),p=rl(r,c,l,a,e),u={version:1,templateDir:r,repoRoot:t,tier:e.realEnv,linkSdk:!!e.linkSdk,redirects:c,modules:a.map(m=>({id:m.id,pkg:m.pkg,dir:x.relative(t,m.dir),kind:m.descriptor.kind})),configs:l};if(I(s)){let m=null;try{m=JSON.parse(X(s,"utf8"))}catch{}if(m&&ko(m.pid)&&m.pid!==process.pid){if(m.hash===p){console.log(`↩︎ 检测到同集合 dev 会话(pid=${m.pid})——attach 模式,直接拉起进程`),$r(n,{recursive:!0});let h=ho(r,t,c);console.log(`🔗 链接断言:planted=${h.planted.length} skipped=${h.skipped}`),await vo(r,l,e.port,s,c);return}throw new Error(`已有不同集合的 dev 会话运行中(pid=${m.pid},集合 ${m.hash} ≠ 本次 ${p})——并发会话会互相覆盖 .tbox-dev.local/ 构件。请先停掉对方或运行 'tbox-app dev --prune'`)}console.log("🧹 吸收残留死锁(pid 已不存在)")}$r(n,{recursive:!0});let d=Zc(r,a,n);for(let m of d)console.warn(`⚠️ ${m}`);let f=ho(r,t,c);console.log(`🔗 链接种植:planted=${f.planted.length} skipped=${f.skipped}`),Re(x.join(n,"package.json"),JSON.stringify({type:"module"},null,2)+`
233
+ `),Ft(x.join(n,"boot.mjs"),nl,"utf8"),Ft(x.join(n,"hook-core.mjs"),sl,"utf8"),Ft(x.join(n,"vite-override.mjs"),ol,"utf8"),Re(i,JSON.stringify(u,null,2)+`
234
+ `),Re(s,JSON.stringify({pid:process.pid,cmd:"tbox-app dev",hash:p,ts:new Date().toISOString()},null,2)+`
235
+ `);let g=e.port??Vc;console.log("─".repeat(64)),console.log(`📦 dev 模块集:${a.map(m=>m.id).join(", ")}`),Object.keys(l).length>0?(console.log(`🗂 integrations=${l.TBOX_INTEGRATIONS_FILE??"— (absent:登录将落内置换码器,缺 ALIPAY_* 凭证即 503)"}`),console.log(`🗂 config-dir=${l.TBOX_CONFIG_DIR??"—"}`)):console.log("🗂 configs:无注入(未指定 --real-env / --config-dir;裸跑形态)"),console.log(`🚀 dev ready → http://127.0.0.1:${g}`),console.log("ℹ️ dev 链接形态 ≠ codegen 发布态(L2≠L3):发布判据仍以 verify-cli 为准"),console.log("─".repeat(64)),await vo(r,l,e.port,s,c)}async function vo(e,t,r,n,s={}){let i=x.join(e,...Nc),a=JSON.parse(X(x.join(i,"package.json"),"utf8")).scripts?.dev??"",c=a.split(/\s+/);if(c[0]!=="tsx"||c[1]!=="watch"||c.length<3)throw new Error(`apps/server scripts.dev 形态不受支持(需 "tsx watch … <entry>"):"${a}"——请升级模板`);let l=c[c.length-1],p=x.join(e,Pe,"boot.mjs"),u=[...new Set(Object.values(s).map(h=>x.resolve(x.join(e,Pe),h)))],d=["--watch-path=./src",`--watch-path=${x.join(e,Pe)}`,...u.map(h=>`--watch-path=${h}`)],f={...process.env,...t,...r!==void 0?{PORT:String(r)}:{}},g=Ac("node",[...d,"--watch","--import","tsx","--import",p,l],{cwd:i,env:f,stdio:"inherit",detached:!0});_t=g.pid,il();let m=await new Promise(h=>{let v=!1,S=$=>{v||(v=!0,_t=void 0,h($))};g.on("exit",($,R)=>S($??(R?130:1))),g.on("error",$=>{console.error("❌ dev 子进程启动失败:",$),S(1)})});try{bo(n)}catch{}process.exitCode=m??1}var _t,wo=!1;function il(){if(wo)return;wo=!0;let e=t=>{if(_t)try{process.kill(-_t,t)}catch{}};process.on("SIGINT",()=>e("SIGINT")),process.on("SIGTERM",()=>e("SIGTERM"))}async function al(e,t){let r=x.join(e,Pe),n=x.join(r,"dev-manifest.json"),s=x.join(r,"session.lock");if(!I(r)){console.log("✨ 构件区不存在,无需清理");return}if(I(s))try{let o=JSON.parse(X(s,"utf8"));if(o.pid!==process.pid&&ko(o.pid))throw new Error(`dev 会话仍在运行(pid=${o.pid})——请先 Ctrl+C 停止再 --prune`)}catch(o){if(o.message.startsWith("dev 会话"))throw o}let i=0;if(I(n))try{let o=JSON.parse(X(n,"utf8"));for(let a of Object.keys(o.redirects??{})){let c=a.split("/"),l=x.join(e,"node_modules",c[0],c[1]);if(!I(l))continue;let p=ae(l);[x.join(t,"modules"),x.join(t,"packages")].some(d=>p===d||p.startsWith(d+x.sep))&&(bo(l),i++)}}catch{}Pc(r,{recursive:!0,force:!0}),console.log(`✨ prune 完成:构件区已清、种植链接回收 ${i} 条(模板:${x.relative(t,e)})`)}import{readFileSync as cl}from"fs";var Nt=[{type:"agent",description:"通用 AI Agent 应用(Base 模板)",templatePackage:"@tbox.cn/app-template-agent",documentFile:"agent.md"},{type:"agent-mall",description:"商圈 AI Agent 应用(Mall 模板)",templatePackage:"@tbox.cn/app-template-agent-mall",documentFile:"agent-mall.md"},{type:"agent-support",description:"客服支持 AI Agent 应用(Support 模板)",templatePackage:"@tbox.cn/app-template-agent-support",documentFile:"agent-support.md"}];function ll(e){return cl(new URL(`../starter/${e}`,import.meta.url),"utf8")}function pl(e){let t=Nt.map(({type:r,description:n,templatePackage:s})=>({type:r,description:n,templatePackage:s}));return e?JSON.stringify({starters:t},null,2)+`
236
+ `:["可用 starter 类型:",...t.map(r=>` ${r.type.padEnd(11)} ${r.description}(${r.templatePackage})`),"",`查询文档: tbox-app starter --type <${Nt.map(r=>r.type).join("|")}>`].join(`
237
+ `)+`
238
+ `}function So(e){if(e.type&&e.list)throw new Error("--type 与 --list 互斥");if(e.json&&!e.list)throw new Error("--json 仅配合 --list 使用");if(!e.type||e.list){process.stdout.write(pl(!!e.json));return}let t=Nt.find(r=>r.type===e.type);if(!t)throw new Error(`未知 starter 类型: ${e.type}(可用:${Nt.map(r=>r.type).join("、")};使用 --list 查看)`);process.stdout.write(ll(t.documentFile))}import{createHash as fl}from"crypto";import{readdir as ml,readFile as gl,stat as yl}from"fs/promises";import{join as Le,relative as hl,basename as Do}from"path";import{parse as vl}from"yaml";import{z as et}from"zod";import{z as y}from"zod";var jr=y.object({version:y.literal(1),name:y.string().min(1),description:y.string().default(""),defaultTurnTimeoutMs:y.number().int().min(5e3).default(15e4),driver:y.enum(["agui","support"]).optional()}).strict(),Er=y.object({version:y.literal(1),description:y.string().default(""),standards:y.array(y.string().min(1)).min(1)}).strict(),$o=y.union([y.string().min(1),y.array(y.string().min(1)).min(1)]),Eo=y.union([$o,y.object({any_of:y.array($o).min(1)}).strict()]),ul=Eo;function jo(e){let t=r=>r==="none"||r==="direct-out"?[]:[r];return typeof e=="string"?t(e):Array.isArray(e)?e:e.any_of.flatMap(r=>typeof r=="string"?t(r):r)}var dl=y.object({expect:Eo,optional:y.array(y.string().min(1)).default([]),forbid:y.array(y.string().min(1)).default([]),pre:y.array(y.string().min(1)).default([]),mode:y.enum(["strict","soft"]).default("strict")}).strict().refine(e=>e.expect!=="direct-out"||e.pre.length===0,{message:"direct-out pre 互斥(direct-out 断言零模型调用,pre 断言走 preTool 轨)"}).refine(e=>e.optional.every(t=>!jo(e.expect).includes(t)),{message:"optional 与 expect 重叠(optional 豁免会使 expect 永不满足)"}).refine(e=>e.forbid.every(t=>!jo(e.expect).includes(t)),{message:"forbid expect 重叠(契约自相矛盾,恒 fail)"}),Rr=y.object({id:y.string().regex(/^[A-Za-z0-9][A-Za-z0-9-]{2,50}$/),priority:y.enum(["P0","P1","P2"]),tags:y.array(y.string().min(1)).default([]),skip:y.string().min(1).optional(),turns:y.array(y.object({text:y.string().trim().min(1,"不能为空白"),newSession:y.boolean().default(!1)}).strict()).min(1),tools:dl,cards:y.object({expect:ul}).strict().optional(),assertions:y.array(y.string().trim().min(1,"不能为空白")).min(1),judge_hints:y.array(y.string().min(1)).default([])}).strict(),Yg=y.object({suite:jr,rubric:Er,cases:y.array(Rr).min(1)}).strict(),Qe=y.object({version:y.literal(1),runId:y.string(),startedAt:y.string(),finishedAt:y.string(),dataset:y.object({name:y.string(),hash:y.string(),caseCount:y.number().int()}),rubricHash:y.string(),judgeModel:y.string().nullable(),instance:y.object({baseUrl:y.string(),pid:y.number().nullable(),startedAt:y.string().nullable(),journalRoot:y.string().nullable(),degraded:y.boolean()}),devUserPrefix:y.string(),dimensions:y.record(y.string(),y.object({pass:y.number(),fail:y.number(),na:y.number()})),cases:y.array(y.object({id:y.string(),status:y.enum(["pass","fail","inconclusive","skipped"]),skipReason:y.string().optional(),dims:y.record(y.string(),y.enum(["pass","fail","warning","na"])),failures:y.array(y.string()).default([])})),slices:y.record(y.string(),y.record(y.string(),y.object({pass:y.number(),total:y.number()}))),ops:y.object({durationMs:y.number(),turnCount:y.number(),infraErrors:y.number(),aborted:y.boolean(),llmTokens:y.number().nullable(),judgeFailures:y.number().int().default(0)})}).strict(),Ro=y.object({version:y.literal(1),pid:y.number().int(),port:y.number().int(),baseUrl:y.string(),journalRoot:y.string(),startedAt:y.string(),detached:y.boolean()}).strict();var wl=et.object({tools:et.array(et.string().min(1)).min(1),cards:et.array(et.string().min(1)).min(1)}).strict(),bl=/^[a-zA-Z][a-zA-Z0-9_-]*$/,kl=new Set(["none","direct-out"]);function Dr(e){return typeof e=="string"?[e]:Array.isArray(e)?e:e.any_of.flatMap(Dr)}function xl(e){return[...Dr(e.expect),...e.optional,...e.forbid,...e.pre]}function To(e,t){return e<t?-1:e>t?1:0}var Sl=(e,t)=>To(e.name,t.name);function Co(e){return fl("sha256").update(e.join(`
239
+ \0
240
+ `)).digest("hex")}async function Vt(e,t,r){let n;try{n=await gl(e,"utf-8")}catch(o){throw new Error(`读取 ${r} 失败(${e}): ${o.message}`)}let s;try{s=vl(n)}catch(o){throw new Error(`${r} YAML 语法错误(${e}): ${o.message}`)}let i=t.safeParse(s);if(!i.success){let o=i.error.issues.map(a=>` - ${a.path.join(".")||"<root>"}: ${a.message}`).join(`
241
+ `);throw new Error(`${r} 契约校验失败(${e}):
242
+ ${o}`)}return i.data}async function $l(e){let t=[],r=async n=>{let s;try{s=await ml(n,{withFileTypes:!0})}catch{return}for(let i of s.sort(Sl)){let o=Le(n,i.name);i.isDirectory()?await r(o):i.isFile()&&(i.name.endsWith(".yaml")||i.name.endsWith(".yml"))&&t.push(o)}};return await r(e),t.sort()}async function Jt(e){let t=Le(e,"suite.yaml"),r=Le(e,"rubric.yaml"),n=await Vt(t,jr,"suite.yaml"),s=await Vt(r,Er,"rubric.yaml"),i=await $l(Le(e,"cases"));if(i.length===0)throw new Error(`数据集为空:${Le(e,"cases")} 下未发现任何 .yaml 用例`);let o=[],a=[],c=new Map;for(let v of i){let S=hl(e,v);try{let $=await Vt(v,Rr,"case");Do(v).replace(/\.ya?ml$/,"")!==$.id&&a.push(` - ${S}: 文件名与用例 id 不符(文件 ${Do(v)},id ${$.id})`),o.push($),c.set($.id,S)}catch($){a.push(` - ${S}: ${$.message.split(`
243
+ `).slice(1).join(";")||$.message}`)}}let l=new Map;for(let v of o)l.set(v.id,(l.get(v.id)??0)+1);let p=[...l.entries()].filter(([,v])=>v>1);if(p.length>0&&a.push(` - 用例 id 重复: ${p.map(([v,S])=>`${v}×${S}`).join(", ")}`),n.driver==="support")for(let v of o)v.turns.some(S=>S.newSession)&&a.push(` - ${c.get(v.id)??`cases/${v.id}.yaml`}: driver=support 暂不支持 newSession(support 会话绑定连接)`);let u=null,d=Le(e,"tools-manifest.yaml"),f=!1;try{f=(await yl(d)).isFile()}catch{f=!1}if(f){let v=await Vt(d,wl,"tools-manifest.yaml");u={tools:new Set(v.tools),cards:new Set(v.cards)};for(let S of o){let $=c.get(S.id)??`cases/${S.id}.yaml`;for(let R of xl(S.tools))kl.has(R)||(bl.test(R)?u.tools.has(R)||a.push(` - ${$}: 工具引用 '${R}' 不在词表(tools-manifest.yaml)——检查拼写或更新词表`):a.push(` - ${$}: 工具引用 '${R}' 非法形态(须 ^[a-zA-Z][a-zA-Z0-9_-]*$)——疑似期望文案粘连进 DSL`));if(S.cards)for(let R of Dr(S.cards.expect))u.cards.has(R)||a.push(` - ${$}: 卡片引用 '${R}' 不在词表(tools-manifest.yaml cards 节)`)}}if(a.length>0)throw new Error(`数据集校验失败(${e},${a.length} 处):
244
+ ${a.join(`
245
+ `)}`);let g=Co([JSON.stringify(s)]),m=[...o].sort((v,S)=>To(v.id,S.id)),h=Co([JSON.stringify(n),JSON.stringify(s),...m.map(v=>JSON.stringify(v))]);return{dir:e,suite:n,rubric:s,cases:o,datasetHash:h,rubricHash:g,manifest:u}}function Mo(e){let t={},r={},n={},s={},i=0,o=0;for(let a of e.cases){a.skip&&(t[a.skip]=(t[a.skip]??0)+1),r[a.priority]=(r[a.priority]??0)+1;let c=a.tags[2]??"<none>";n[c]=(n[c]??0)+1;let l=1;for(let p of a.turns)p.newSession&&(l+=1);s[String(l)]=(s[String(l)]??0)+1,a.turns.length>1&&(i+=1),a.tools.mode==="soft"&&(o+=1)}return{total:e.cases.length,evaluated:e.cases.filter(a=>!a.skip).length,skippedByReason:t,byPriority:r,byCaseType:n,sessionHistogram:s,multiTurnCases:i,softModeCases:o}}import{randomBytes as tp}from"crypto";import{mkdir as rp,readFile as si}from"fs/promises";import{join as Or}from"path";import{readdir as Io,readFile as Po}from"fs/promises";import{join as Ao}from"path";var W=class extends Error{constructor(t){super(t),this.name="InfraError"}},Oo=400,jl=1e4,El=1500,Rl=300;async function Cr(e,t,r){let n=r??fetch,s;try{s=await n(`${e}/api/auth/dev-login`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({devUser:t})})}catch(o){throw new W(`dev-login 请求失败(${e}): ${o.message}`)}if(!s.ok)throw new W(`dev-login 失败 HTTP ${s.status}(devUser=${t};prod 档会 403——检查实例启动语义)`);let i=await s.json().catch(()=>{});if(!i?.token)throw new W("dev-login 响应缺 token");return i.token}function Dl(e){if(e==null)return;let t=typeof e=="string"?e:JSON.stringify(e);return t.length>Oo?`${t.slice(0,Oo)}…`:t}function Cl(e,t){let r=e.type;if(!t.started){if(r==="RUN_FINISHED"||r==="RUN_ERROR")return;t.started=!0}let n=e.toolCallId;switch(r){case"TEXT_MESSAGE_CONTENT":t.reply+=e.delta??"";break;case"TOOL_CALL_START":{let s=e.toolCallName;s==="__tbox_pretool"?t.preTools.set(n??`pre-${t.preTools.size}`,{}):t.toolCalls.set(n??`tool-${t.toolCalls.size}`,{name:s,argsBuf:""});break}case"TOOL_CALL_ARGS":{if(n===void 0)break;let s=t.toolCalls.get(n);if(s){s.argsBuf=(s.argsBuf??"")+(e.delta??"");break}let i=t.preTools.get(n);i&&(i.argsRaw=(i.argsRaw??"")+(e.delta??""));break}case"TOOL_CALL_RESULT":{if(n===void 0)break;let s=t.toolCalls.get(n);if(s){e.isError===!0&&(s.isError=!0),s.resultSummary=Dl(e.content);break}let i=t.preTools.get(n);i&&(i.resultRaw=typeof e.content=="string"?e.content:JSON.stringify(e.content??""));break}case"CUSTOM":if(e.name==="tbox:card"){let s=e.value;s?.cardType&&t.cards.push({cardType:s.cardType,status:s.status??"ready"})}break;case"RUN_FINISHED":t.runStatus="finished";break;case"RUN_ERROR":t.runStatus="error";break;case"SUPPORT_ERROR":t.runStatus="error";break;default:break}}function Tl(e,t,r,n,s){let i=[];for(let a of e.toolCalls.values()){let c;if(a.argsBuf)try{c=JSON.parse(a.argsBuf)}catch{c=void 0}i.push({name:a.name,...c!==void 0?{args:c}:{},...a.isError?{isError:!0}:{},...a.resultSummary!==void 0?{resultSummary:a.resultSummary}:{}})}let o=[];for(let a of e.preTools.values()){let c=a.toolName,l;try{a.argsRaw&&(c=JSON.parse(a.argsRaw).toolName??c)}catch{}try{a.resultRaw&&(l=JSON.parse(a.resultRaw).status)}catch{}c&&o.push({name:c,outcome:l==="error"?"error":l==="done"?"ok":l??"ok"})}return{index:t,conversationId:r,userText:n,reply:e.reply,toolCalls:i,preTools:o,cards:e.cards,runStatus:e.runStatus,durationMs:s}}function Ml(e){let t=e.WebSocketCtor??globalThis.WebSocket;if(typeof t!="function")throw new W("当前 Node 缺少全局 WebSocket:评测驱动需要 Node ≥22(仓库基准 Node 24)");return t}async function Lo(e,t,r,n,s){let i=await Cr(e.baseUrl,r,e.fetchFn),o=`${e.baseUrl.replace(/^http/,"ws")}/ws`,a=Ml(e),c=await new Promise((f,g)=>{let m,h=setTimeout(()=>g(new W(`WS 连接超时(${o})`)),jl);try{m=new a(o)}catch(R){clearTimeout(h),g(new W(`WS 创建失败: ${R.message}`));return}let v=()=>{$(),f(m)},S=()=>{$(),g(new W(`WS 连接失败(${o})`))},$=()=>{clearTimeout(h),m.removeEventListener("open",v),m.removeEventListener("error",S)};m.addEventListener("open",v),m.addEventListener("error",S)}),l=[],p=()=>{},u=f=>{p(`WS 传输层故障(${f.type})`)};c.addEventListener("error",u),c.addEventListener("close",u);let d=e.driver??"agui";try{let f;if(await new Promise((v,S)=>{let $=A=>{let N=A.data;try{let P=JSON.parse(N);if(P.type==="HELLO_ACK"){let w=P.supportSession;typeof w?.publicConversationId=="string"&&(f=w.publicConversationId)}["RUN_STARTED","HELLO_ACK","TEXT_MESSAGE_START"].includes(P.type)&&(R(),v())}catch{}},R=()=>{clearTimeout(F),c.removeEventListener("message",$)},F=setTimeout(()=>{R(),v()},El);c.addEventListener("message",$);try{c.send(JSON.stringify({type:"HELLO",token:i}))}catch(A){R(),S(new W(`WS send 失败(连接已关闭?): ${A.message}`))}}),d==="support"&&f===void 0)throw new W("support 驱动未取得服务端会话 id(HELLO_ACK 缺席或无 supportSession.publicConversationId)");let g=1,m=d==="support"?f:`evalconv-${n}-${t.id}-s${g}`,h=!1;for(let v=0;v<t.turns.length;v++){let S=t.turns[v];if(v>0&&S.newSession){if(d==="support")throw new W(`support 驱动暂不支持 newSession(用例 ${t.id})——会话绑定在连接上,跨会话需重连握手`);g+=1,m=`evalconv-${n}-${t.id}-s${g}`}let $={reply:"",toolCalls:new Map,preTools:new Map,cards:[],runStatus:"timeout",started:!h},R=Date.now();await new Promise((F,A)=>{p=M=>{w(),A(new W(M))};let P=M=>{try{let b=JSON.parse(M.data);Cl(b,$),$.runStatus!=="timeout"&&(w(),F())}catch{}},w=()=>{clearTimeout(D),c.removeEventListener("message",P),p=()=>{}},D=setTimeout(()=>{w(),F()},s);c.addEventListener("message",P);try{c.send(JSON.stringify({type:"SEND_MESSAGE",content:S.text,conversationId:m}))}catch(M){w(),A(new W(`WS send 失败(连接已关闭?): ${M.message}`))}}),l.push(Tl($,v,m,S.text,Date.now()-R)),h=$.runStatus==="timeout",d==="support"&&v<t.turns.length-1&&await new Promise(F=>setTimeout(F,Rl))}}finally{p=()=>{};try{c.removeEventListener("error",u),c.removeEventListener("close",u),c.close()}catch{}}return{caseId:t.id,devUser:r,turns:l}}async function Il(e,t){let r;try{r=await Io(e)}catch{return}for(let n of r){let s;try{s=await Io(Ao(e,n))}catch{continue}for(let i of s){let o=Ao(e,n,i,"conversations",t,"journal.jsonl");try{return await Po(o,"utf-8"),o}catch{continue}}}}async function Fo(e,t){try{let r={},n=[...new Set(e.turns.map(s=>s.conversationId))];for(let s of n){let i=await Il(t,s);if(!i)continue;let o=await Po(i,"utf-8");for(let a of o.split(`
246
+ `)){if(!a.trim())continue;let c;try{c=JSON.parse(a)}catch{continue}if(c.type==="intent_route"&&c.data?.continued===!1&&(r.intentDirectOut=!0),c.type==="run_end"){let l=c.data?.llm;l&&(r.inputTokens=(r.inputTokens??0)+(l.inputTokens??0),r.outputTokens=(r.outputTokens??0)+(l.outputTokens??0))}}}return{...e,...Object.keys(r).length>0?{journal:r}:{}}}catch(r){return console.warn(`[eval] journal 富化跳过: ${r.message}`),e}}var Al="__tbox_",Ol="agent-ui-cards";function zt(e){return!e.startsWith(Al)&&!e.startsWith(Ol)}function No(e,t,r="agui"){let n=r==="support"?{dim:"tools",status:"na",detail:["support 驱动:TOOL_CALL_* 不出边界,tools 维不判定"]}:_l(e.tools,t);return[Pl(t),n,Vl(e,t)]}function Pl(e){let t=e.turns.filter(r=>r.runStatus!=="finished");return t.length===0?{dim:"run",status:"pass",detail:[]}:{dim:"run",status:"fail",detail:t.map(r=>`第 ${r.index+1} 轮 runStatus=${r.runStatus}`)}}function Vo(e){return typeof e=="string"?e:Array.isArray(e)?`[${e.join(" + ")}]`:`any_of(${e.any_of.map(Vo).join(" | ")})`}function Ll(e){let t=new Set;for(let r of e.turns)for(let n of r.toolCalls)zt(n.name)&&t.add(n.name);return t}function Fl(e){let t=new Set;for(let r of e.turns)for(let n of r.preTools)n.outcome!=="skip"&&t.add(n.name);return t}function Jo(e,t,r){return typeof e=="string"?_o(e,t,r):Array.isArray(e)?e.every(n=>_o(n,t,r)):e.any_of.some(n=>Jo(n,t,r))}function _o(e,t,r){return e==="none"||e==="direct-out"?t.size===0:t.has(e)||r.has(e)}function zo(e){return typeof e=="string"?e==="none"||e==="direct-out"?[]:[e]:Array.isArray(e)?e:e.any_of.flatMap(t=>zo(t))}function _l(e,t){let r=Ll(t),n=Fl(t),s=new Set(e.optional),i=new Set([...r].filter(f=>!s.has(f))),o=new Set([...r,...n]),a=[],c=!1,l=zo(e.expect).filter(f=>!r.has(f)&&n.has(f));Jo(e.expect,i,o)?l.length>0&&a.push(`note: expect 经 preTool 预执行命中 [${l.sort().join(", ")}](模型侧未显式调用)`):(c=!0,a.push(`期望 ${Vo(e.expect)},实际业务调用 [${[...r].sort().join(", ")||"无"}] / preTool [${[...n].sort().join(", ")||"无"}]`)),e.expect==="direct-out"&&i.size===0&&t.journal?.intentDirectOut!==!0&&a.push("degraded: journal 缺席或无 intent_route 佐证,direct-out 退化为 none 判定");let u=e.forbid.filter(f=>i.has(f));u.length>0&&(c=!0,a.push(`禁用工具被调用: [${u.join(", ")}]`));let d=e.pre.filter(f=>!n.has(f));return d.length>0&&(c=!0,a.push(`期望 preTool [${d.join(", ")}] 未命中,实际 preTool [${[...n].sort().join(", ")||"无"}]`)),c?{dim:"tools",status:e.mode==="soft"?"warning":"fail",detail:a}:{dim:"tools",status:"pass",detail:a}}function Nl(e){let t=new Set;for(let r of e.turns)for(let n of r.cards)n.status!=="error"&&t.add(n.cardType);return t}function Vl(e,t){if(!e.cards)return{dim:"cards",status:"na",detail:[]};let r=e.cards.expect,n=typeof r=="string"?[r]:Array.isArray(r)?r:r.any_of.flatMap(o=>typeof o=="string"?[o]:o),s=Nl(t);return(Array.isArray(r)||typeof r=="string"?n.every(o=>s.has(o)):n.some(o=>s.has(o)))?{dim:"cards",status:"pass",detail:[]}:{dim:"cards",status:"fail",detail:[`期望卡片 [${n.join(", ")}] 未齐,实际出卡 [${[...s].sort().join(", ")||"无"}]`]}}import{mkdir as Uo,writeFile as Ut}from"fs/promises";import{join as Fe}from"path";function Bo(e){let t={},r={priority:{},primary_category:{},case_type:{}},n=[],s=0,i=(c,l)=>{let p=t[c]??{pass:0,fail:0,na:0};l==="pass"||l==="warning"?p.pass+=1:l==="fail"?p.fail+=1:p.na+=1,t[c]=p},o=(c,l,p)=>{let u=r[c][l]??{pass:0,total:0};u.total+=1,p&&(u.pass+=1),r[c][l]=u};for(let c of e.results){if(c.spec.skip){n.push({id:c.spec.id,status:"skipped",skipReason:c.spec.skip,dims:{},failures:[]});continue}s+=c.trace?.turns.length??0;let l={},p=[];for(let g of c.dims)l[g.dim]=g.status,i(g.dim,g.status),g.status==="fail"&&p.push(`${g.dim}: ${g.detail.join(";")}`);let u=c.textStatus;l.text=u==="pass"?"pass":u==="fail"?"fail":"na",u==="pass"?i("text","pass"):u==="fail"?(i("text","fail"),p.push(...c.textFailures&&c.textFailures.length>0?c.textFailures:["text: LLM 判定失败(无明细)"])):i("text","na");let d;c.notExecuted?(d="inconclusive",p.push("未执行:infra-aborted 熔断中止")):p.length>0?d="fail":u==="inconclusive"?(d="inconclusive",p.push("text: 全部断言 unclear(judge 无法判定)")):d="pass",n.push({id:c.spec.id,status:d,dims:l,failures:p});let f=d==="pass";o("priority",c.spec.priority,f),o("primary_category",c.spec.tags[0]??"<none>",f),o("case_type",c.spec.tags[2]??"<none>",f)}let a={version:1,runId:e.runId,startedAt:e.startedAt,finishedAt:e.finishedAt,dataset:{name:e.datasetName,hash:e.datasetHash,caseCount:e.results.length},rubricHash:e.rubricHash,judgeModel:e.judgeModel,instance:e.instance,devUserPrefix:e.devUserPrefix,dimensions:t,cases:n.sort((c,l)=>c.id<l.id?-1:c.id>l.id?1:0),slices:r,ops:{durationMs:new Date(e.finishedAt).getTime()-new Date(e.startedAt).getTime(),turnCount:s,infraErrors:e.ops.infraErrors,aborted:e.ops.aborted,llmTokens:e.ops.llmTokens,judgeFailures:e.ops.judgeFailures}};return Qe.parse(a)}function Ho(e,t){let r=new Map(t.cases.map(i=>[i.id,i.status])),n=new Map(e.cases.map(i=>[i.id,i.status])),s={newFailed:[],fixed:[],flipped:[]};for(let[i,o]of n){let a=r.get(i);a===void 0||a===o||(a!=="fail"&&o==="fail"?s.newFailed.push(i):a==="fail"&&o!=="fail"?s.fixed.push(i):s.flipped.push({id:i,from:a,to:o}))}return s}async function Wo(e,t,r,n){await Uo(Fe(e,"traces"),{recursive:!0}),await Ut(Fe(e,"report.json"),JSON.stringify(t,null,2),"utf-8"),await Ut(Fe(e,"report.md"),zl(t),"utf-8");for(let[s,i]of r)await Ut(Fe(e,"traces",`${s}.json`),JSON.stringify(i,null,2),"utf-8");if(n.size>0){await Uo(Fe(e,"judge"),{recursive:!0});for(let[s,i]of n)await Ut(Fe(e,"judge",`${s}.md`),i,"utf-8")}}function Jl(e){return e.sort((t,r)=>r[1].total-t[1].total).map(([t,r])=>`${t} ${r.pass}/${r.total}`).join(" · ")}function zl(e,t){let r=c=>e.cases.filter(l=>l.status===c),n=r("fail"),s=r("inconclusive"),i=r("skipped"),o=[];o.push(`# 评测报告 ${e.runId}`,""),o.push(`- 数据集: ${e.dataset.name}(${e.dataset.caseCount} 例,hash ${e.dataset.hash.slice(0,12)})`),o.push(`- 实例: ${e.instance.baseUrl}${e.instance.degraded?"(degraded:journal 不可达)":""}`),o.push(`- judge: ${e.judgeModel??"未启用"} rubric: ${e.rubricHash.slice(0,12)}`);let a=["pass","fail","inconclusive","skipped"].map(c=>`${c}=${r(c).length}`).join(" ");o.push(`- 结果: ${a} 用时 ${(e.ops.durationMs/1e3).toFixed(0)}s turns=${e.ops.turnCount}${e.ops.aborted?" ⚠️ infra-aborted(熔断)":""}${e.ops.judgeFailures>0?` ⚠️ judge 失败 ${e.ops.judgeFailures} 例(text 维降级,需人工复核)`:""}`,""),o.push("## 维度","");for(let[c,l]of Object.entries(e.dimensions))o.push(`- ${c}: pass=${l.pass} fail=${l.fail} na=${l.na}`);o.push("","## 切片","");for(let[c,l]of Object.entries(e.slices))o.push(`- ${c}: ${Jl(Object.entries(l))}`);if(t&&(o.push("","## 基线对比",""),o.push(`- 新失败: ${t.newFailed.length>0?t.newFailed.join(", "):"无"}`),o.push(`- 已修复: ${t.fixed.length>0?t.fixed.join(", "):"无"}`),t.flipped.length>0&&o.push(`- 翻转: ${t.flipped.map(c=>`${c.id} ${c.from}→${c.to}`).join(", ")}`)),n.length>0){o.push("","## 失败用例","");for(let c of n){o.push(`### ${c.id}(${Object.entries(c.dims).filter(([,l])=>l==="fail").map(([l])=>l).join("+")||"?"})`);for(let l of c.failures)o.push(`- ${l}`);o.push("")}}if(s.length>0){o.push("## Inconclusive(judge 全 unclear,不计失败)","");for(let c of s)o.push(`- ${c.id}`);o.push("")}if(i.length>0){o.push("## Skipped","");for(let c of i)o.push(`- ${c.id}(${c.skipReason})`);o.push("")}return o.join(`
247
+ `)}function Ko(e){return{userText:e.userText,reply:e.reply.length>4e3?`${e.reply.slice(0,4e3)}…`:e.reply,tools:e.toolCalls.filter(t=>zt(t.name)).map(t=>({name:t.name,...t.resultSummary!==void 0?{result:t.resultSummary}:{}})),framework:e.toolCalls.filter(t=>!zt(t.name)).map(t=>t.name),preTools:e.preTools.map(t=>`${t.name}(${t.outcome})`),cards:e.cards.map(t=>({cardType:t.cardType,status:t.status})),runStatus:e.runStatus}}import{z as _e}from"zod";function Go(e){let t=e.EVAL_JUDGE_BASE_URL??e.APP_AI_BASE_URL,r=e.EVAL_JUDGE_API_KEY??e.APP_AI_API_KEY,n=e.EVAL_JUDGE_MODEL??e.APP_AI_MODEL_NAME;if(!t||!r||!n)throw new Error(`评测 judge 配置缺失(需 EVAL_JUDGE_BASE_URL/API_KEY/MODEL,或回退 APP_AI_BASE_URL/API_KEY/MODEL_NAME;缺: ${[!t&&"BASE_URL",!r&&"API_KEY",!n&&"MODEL"].filter(Boolean).join(", ")})`);return{baseUrl:t.replace(/\/$/,""),apiKey:r,model:n}}function Ul(e){let t=e.match(/```(?:json)?\s*([\s\S]*?)\s*```/);return t?t[1]:e}async function qo(e,t,r,n,s){let i=s?.fetchFn??fetch,o=[{role:"system",content:t},{role:"user",content:r}],a="",c=3;for(let l=0;l<c;l++){l>0&&await new Promise(f=>setTimeout(f,800*l));let p=l===0?r:`${r}
248
+
249
+ (上次输出无法解析为 JSON,请只输出 JSON,不要任何其他文字)`,u;try{u=await i(`${e.baseUrl}/chat/completions`,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${e.apiKey}`,"accept-encoding":"identity"},body:JSON.stringify({model:e.model,temperature:0,messages:l===0?o:[o[0],{role:"user",content:p}]})})}catch(f){throw new Error(`judge LLM 请求失败: ${f.message}`)}if(!u.ok){let f=await u.text().catch(()=>"");throw new Error(`judge LLM HTTP ${u.status}: ${f.slice(0,300)}`)}if(a=(await u.json().catch(()=>{}))?.choices?.[0]?.message?.content??"",!!a.trim())try{return{parsed:n.parse(JSON.parse(Ul(a))),raw:a}}catch{}}throw new Error(`judge 输出无法解析为 JSON(${c} 次尝试): ${a.slice(0,300)||"<空响应>"}`)}var Bl=_e.object({assertions:_e.array(_e.object({text:_e.string().min(1),verdict:_e.enum(["pass","fail","unclear"]),reason:_e.string().min(1)})).min(1)});function Hl(e,t,r){let n=["你是智能体行为评测裁判。依据评审标准,对每条断言独立判定 pass/fail/unclear。","判定基于证据中的实际回复与工具出参(判「如实」而非「应然」)。","证据轨别:tools 是模型主动调用;preTools 是系统预执行(非模型决策);__tbox_ 前缀是框架伪工具。判定「模型是否调用/误路由」类断言时只依据 tools。","断言按语义满足判定,不逐字匹配文案;断言与评审标准冲突、或断言前提在当前对话不成立时(如要求重试建议但重试已成功),以评审标准和实际行为为准。","证据不足或断言在当前对话中无法验证时判 unclear,不要猜测。",'只输出 JSON:{"assertions":[{"text":"断言原文","verdict":"pass|fail|unclear","reason":"判定理由"}]}',"","评审标准:",...r.standards.map((o,a)=>/^\d+[.、.]\s*/.test(o)?o:`${a+1}. ${o}`)].join(`
250
+ `),s=t.turns.map((o,a)=>({turn:a+1,...Ko(o)})),i=[`用例 ${e.id}(priority=${e.priority})`,`证据:${JSON.stringify(s)}`,"断言:",...e.assertions.map((o,a)=>`${a+1}. ${o}`)];return e.judge_hints.length>0&&i.push("评审备注(附加上下文,优先级低于评审标准):",...e.judge_hints.map(o=>`- ${o}`)),i.push("逐条输出判定 JSON。"),{system:n,user:i.join(`
251
+ `)}}function Xo(e){let t=e.some(n=>n.verdict==="fail"),r=e.some(n=>n.verdict==="pass");return t?{status:"fail",failures:e.filter(n=>n.verdict==="fail").map(n=>`${n.text} —— ${n.reason}`)}:r?{status:"pass",failures:[]}:{status:"inconclusive",failures:[]}}async function Yo(e,t,r,n,s){let{system:i,user:o}=Hl(r,n,t),{parsed:a,raw:c}=await qo(e,i,o,Bl,s),l=new Map(a.assertions.map(u=>[u.text.trim(),u]));return{verdicts:r.assertions.map(u=>l.get(u.trim())??{text:u,verdict:"unclear",reason:"judge 输出缺失该断言"}),raw:c}}import{spawn as Zo,spawnSync as Wl}from"child_process";import{existsSync as ei,openSync as Kl,readFileSync as Gl}from"fs";import{rm as ql,writeFile as Xl}from"fs/promises";import{join as be}from"path";var Mr="apps/server",Tr="dist/index.js",Ir=".tbox-server.json",Qo=".tbox-server.log",ti=60,Yl=1e3;function Ar(e){return ei(be(e,Mr,"package.json"))?e:null}function tt(e){let t;try{t=Gl(be(e,Ir),"utf-8")}catch{return null}try{return Ro.parse(JSON.parse(t))}catch{return null}}async function Zl(e,t){await Xl(be(e,Ir),JSON.stringify(t,null,2),"utf-8")}async function Ht(e){await ql(be(e,Ir),{force:!0})}function Ql(e){return`${e.replace(/\/$/,"")}/api/health`}async function Bt(e,t){let r=t?.attempts??ti,n=t?.intervalMs??Yl;for(let s=0;s<r;s++){try{if((await fetch(Ql(e))).ok)return!0}catch{}await new Promise(i=>setTimeout(i,n))}return!1}async function ri(e){if(!Ar(e.appDir))throw new Error(`当前目录不是应用根(缺 ${Mr}/package.json): ${e.appDir}`);let t=be(e.appDir,Mr),r=be(t,Tr);if(e.build){let l=Wl("pnpm",["--filter","@app/server","build"],{cwd:e.appDir,stdio:"inherit"});if(l.status!==0)throw new Error(`构建失败(pnpm --filter @app/server build,exit=${l.status})`)}if(!ei(r))throw new Error("应用未构建:先 --build,或使用 `tbox-app eval run --base-url` 评测外部实例");let n=tt(e.appDir);if(n&&await Bt(n.baseUrl,{attempts:2,intervalMs:500}))throw new Error(`已在运行 ${n.baseUrl}(pid=${n.pid});tbox-app stop 后再启动`);await Ht(e.appDir);let s=e.port??8e3,i=be(e.appDir,".eval-journal"),o={version:1,pid:0,port:s,baseUrl:`http://127.0.0.1:${s}`,journalRoot:i,startedAt:new Date().toISOString(),detached:e.detached},a;if(e.detached){let l=Kl(be(e.appDir,Qo),"a");a=Zo("node",[Tr],{cwd:t,env:{...process.env,PORT:String(s),TBOX_LOCAL_LOG:"on",TBOX_LOCAL_LOG_DIR:i,TBOX_APP_ID:"eval"},stdio:["ignore",l,l],detached:!0}),a.unref()}else a=Zo("node",[Tr],{cwd:t,env:{...process.env,PORT:String(s),TBOX_LOCAL_LOG:"on",TBOX_LOCAL_LOG_DIR:i,TBOX_APP_ID:"eval"},stdio:"inherit"});if(o.pid=a.pid??0,!await Bt(o.baseUrl)){try{e.detached?process.kill(-o.pid,"SIGKILL"):a.kill("SIGKILL")}catch{}throw new Error(`server ${ti}s 未就绪(${o.baseUrl};日志 ${Qo})`)}if(!(()=>{try{return process.kill(o.pid,0),!0}catch{return!1}})()||!e.detached&&a.exitCode!==null)throw new Error(`端口 ${s} 已被其他进程占用(health 来自占用者);--port 换端口后重试`);return await Zl(e.appDir,o),{child:a,state:o}}async function ni(e,t){await ep(t.pid,t.detached,void 0),await Ht(e)}async function ep(e,t,r){let n=()=>{try{return process.kill(e,0),!0}catch{return!1}};if(!n())return;if(t)try{process.kill(-e,"SIGTERM")}catch{}else{r?.kill("SIGTERM");try{process.kill(e,"SIGTERM")}catch{}}let s=Date.now()+2e3;for(;n()&&Date.now()<s;)await new Promise(i=>setTimeout(i,100));if(n())try{t?process.kill(-e,"SIGKILL"):process.kill(e,"SIGKILL")}catch{}}var oi=5;function pe(e,t){e||console.log(`[eval] ${t}`)}function np(e,t){if(t.length===0)return e;let r=new Map;for(let s of t){let i=s.indexOf("=");if(i<=0)throw new Error(`--filter 形状非法: "${s}"(应为 key=value)`);let o=s.slice(0,i),a=s.slice(i+1);if(!["priority","tag","id","skip"].includes(o))throw new Error(`--filter 未知键: "${o}"(支持 priority/tag/id/skip)`);let c=r.get(o)??[];c.push({key:o,value:a}),r.set(o,c)}let n=(s,i,o)=>{switch(i){case"priority":return s.priority===o;case"id":return s.id===o;case"tag":return s.tags.includes(o);case"skip":return s.skip===o;default:return!1}};return e.filter(s=>[...r.values()].every(i=>i.some(o=>n(s,o.key,o.value))))}async function ii(e){if(e.max!==void 0&&(!Number.isInteger(e.max)||e.max<1))throw new Error("--max 需为 ≥1 的整数");if(!Number.isInteger(e.concurrency)||e.concurrency<1)throw new Error("--concurrency 需为 ≥1 的整数");let t=await Jt(e.dataset),r=np(t.cases,e.filters);if(e.rerunFailedDir){let w=await si(Or(e.rerunFailedDir,"report.json"),"utf-8"),D=Qe.parse(JSON.parse(w)),M=new Set(D.cases.filter(b=>b.status==="fail"||b.status==="inconclusive").map(b=>b.id));r=r.filter(b=>M.has(b.id)),r.length===0&&pe(e.quiet,"--rerun-failed: 上次无 fail/inconclusive 用例,空跑")}e.max!==void 0&&(r=r.slice(0,e.max));let n,s=null,i=null,o=e.journalRoot??null;if(e.baseUrl)n=e.baseUrl.replace(/\/$/,"");else{let w=tt(e.cwd);if(!w)throw new Error("未发现可用实例:先 `tbox-app start -d`(状态文件 .tbox-server.json)或传 --base-url <url>");if(!await Bt(w.baseUrl,{attempts:3,intervalMs:500}))throw new Error(`状态文件实例不可达(${w.baseUrl}):实例可能已退出,重新 start -d 或传 --base-url`);n=w.baseUrl,s=w.pid,i=w.startedAt,o=o??w.journalRoot}let a=e.judge?Go(process.env):null,c=tp(4).toString("hex"),l=`eval-${c}`,p=e.outDir??Or(e.cwd,".eval","runs",`${op()}-${c}`),u=new Date().toISOString();pe(e.quiet,`runId=${c} dataset=${t.suite.name} cases=${r.length} concurrency=${e.concurrency} judge=${a?a.model:"off"}`),pe(e.quiet,`instance=${n}${o?` journal=${o}`:"(journal 缺席,direct-out 降级)"}`);let d=[];for(let w of r)w.skip&&d.push({spec:w,dims:[]});let f=new Map,g=new Map,m=0,h=0,v=!1,S=0,$=r.filter(w=>!w.skip),R=[...$],F=async()=>{for(;;){if(v)return;let w=R.shift();if(!w)return;let D=`eval-${c}-${w.id}`,M,b;try{M=await Lo({baseUrl:n,driver:t.suite.driver??"agui",...e.deps},w,D,c,t.suite.defaultTurnTimeoutMs),o&&(M=await Fo(M,o)),f.set(w.id,M)}catch(j){if(j instanceof W){m+=1,h+=1,h>=oi&&(v=!0),d.push({spec:w,dims:[{dim:"run",status:"fail",detail:[j.message]}]}),pe(e.quiet,`✗ ${w.id} infra: ${j.message}`),v&&pe(e.quiet,`熔断(连续 ${oi} 个 infra 错误),中止余量 ${R.length} 例`);continue}throw j}h=0;let k=No(w,M,t.suite.driver??"agui");if(a)try{let{verdicts:j,raw:ee}=await Yo(a,t.rubric,w,M,e.deps);g.set(w.id,ee),b=Xo(j)}catch(j){S+=1,b={status:"inconclusive",failures:[`judge 调用失败: ${j.message}`]},pe(e.quiet,`⚠ ${w.id} judge 失败: ${j.message}`)}d.push({spec:w,trace:M,dims:k,textStatus:b?.status,textFailures:b?.failures});let O=k.filter(j=>j.status==="fail").map(j=>j.dim);pe(e.quiet,`${O.length>0||b?.status==="fail"?"✗":"✓"} ${w.id}${O.length>0?` (${O.join(",")}${b?.status==="fail"?",text":""})`:""}`)}};await Promise.all(Array.from({length:Math.max(1,Math.min(e.concurrency,16))},F));for(let w of $)d.some(D=>D.spec.id===w.id)||d.push({spec:w,dims:[],notExecuted:!0});let A=[...f.values()].reduce((w,D)=>w+(D.journal?.inputTokens??0)+(D.journal?.outputTokens??0),0);await sp(n,$.map(w=>`eval-${c}-${w.id}`),e.deps?.fetchFn,e.quiet);let N=Bo({runId:c,startedAt:u,finishedAt:new Date().toISOString(),datasetName:t.suite.name,datasetHash:t.datasetHash,rubricHash:t.rubricHash,judgeModel:a?.model??null,instance:{baseUrl:n,pid:s,startedAt:i,journalRoot:o,degraded:!o},devUserPrefix:l,results:d,ops:{infraErrors:m,aborted:v,llmTokens:A>0?A:null,judgeFailures:S}}),P;if(e.baselinePath){let w=await si(e.baselinePath,"utf-8"),D=Qe.parse(JSON.parse(w));P=Ho(N,D)}return await rp(p,{recursive:!0}),await Wo(p,N,f,g),console.log(`报告: ${Or(p,"report.md")}`),process.env.EVAL_REPORT==="json"&&console.log(JSON.stringify(N)),P&&pe(e.quiet,`基线对比: 新失败=${P.newFailed.length} 已修复=${P.fixed.length} 翻转=${P.flipped.length}`),{exitCode:v?2:0,runDir:p,report:N}}async function sp(e,t,r,n){let s=r??fetch;for(let i of t)try{let o=await Cr(e,i,s),a=await s(`${e}/api/memory`,{headers:{authorization:`Bearer ${o}`}});if(!a.ok)continue;let l=((await a.json().catch(()=>{}))?.records??[]).map(p=>p.id).filter(p=>typeof p=="string");for(let p of l)await s(`${e}/api/memory/${encodeURIComponent(p)}`,{method:"DELETE",headers:{authorization:`Bearer ${o}`}}).catch(()=>{});l.length>0&&pe(n??!1,`KB 清理 ${i}: ${l.length} 条`)}catch(o){console.warn(`[eval] KB 清理跳过 ${i}: ${o.message}`)}}function op(){let e=new Date,t=(r,n=2)=>String(r).padStart(n,"0");return`${e.getFullYear()}${t(e.getMonth()+1)}${t(e.getDate())}-${t(e.getHours())}${t(e.getMinutes())}${t(e.getSeconds())}`}async function ai(e){let t=await Jt(e.dataset),r=Mo(t);console.log(`✅ 数据集校验通过: ${t.suite.name}(${t.dir})`),console.log(` datasetHash: ${t.datasetHash.slice(0,12)} rubricHash: ${t.rubricHash.slice(0,12)}`),console.log(` 词表: ${t.manifest?`tools=${t.manifest.tools.size} cards=${t.manifest.cards.size}(tools-manifest.yaml)`:"未提供(跳过词表校验)"}`),console.log(` 用例: ${r.total}(参评 ${r.evaluated}) 多轮: ${r.multiTurnCases} soft 模式: ${r.softModeCases}`);let n=Object.entries(r.skippedByReason);n.length>0&&console.log(` skip: ${n.map(([a,c])=>`${a}=${c}`).join(" ")}`);let s=Object.entries(r.byPriority).sort(([a],[c])=>a.localeCompare(c));console.log(` priority: ${s.map(([a,c])=>`${a}=${c}`).join(" ")}`);let i=Object.entries(r.sessionHistogram).sort(([a],[c])=>Number(a)-Number(c));console.log(` 会话数分布: ${i.map(([a,c])=>`${a}会话=${c}`).join(" ")}`);let o=Object.entries(r.byCaseType).sort(([a],[c])=>a.localeCompare(c));console.log(` case_type: ${o.map(([a,c])=>`${a}=${c}`).join(" ")}`)}async function ci(e){let t=await ii({...e,cwd:process.cwd()});process.exitCode=t.exitCode}async function li(e){let t=process.cwd();if(!Ar(t))throw new Error("当前目录不是应用根(缺 apps/server/package.json);请在生成应用目录执行");let r=await ri({appDir:t,port:e.port,build:e.build,detached:e.detached===!0});if(e.detached){console.log(`✅ server 运行中 ${r.state.baseUrl}(pid=${r.state.pid},日志 .tbox-server.log,journal .eval-journal/)`),console.log(" 停止: tbox-app stop 评测: tbox-app eval run --dataset <dir>");return}let n=r.child,s=i=>{n.kill(i)};process.on("SIGINT",()=>s("SIGINT")),process.on("SIGTERM",()=>s("SIGTERM")),console.log(`✅ server 运行中 ${r.state.baseUrl}(前台,Ctrl+C 停止)`),await new Promise(i=>{n.on("exit",()=>i())}),await Ht(t),console.log("server 已退出")}import{spawnSync as ip}from"child_process";import{existsSync as ap}from"fs";import{join as cp}from"path";async function pi(){let e=process.cwd(),t=tt(e);if(t){if(t.detached)await ni(e,t);else{console.log(`server 运行中(前台 pid=${t.pid},${t.baseUrl})——在其终端 Ctrl+C 停止`);return}console.log(`✅ 已停止(pid=${t.pid})`);return}let r=cp(e,"scripts","stop.sh");if(ap(r)){let n=ip("bash",["scripts/stop.sh"],{stdio:"inherit"});if(n.status!==0)throw new Error(`scripts/stop.sh 退出码 ${n.status}`);return}console.log("未发现运行中的 server(无 .tbox-server.json)")}function dp(){let e=new lp,t=pp(import.meta.url)("../package.json");e.name("tbox-app").description("tbox 组件化应用 CLI:starter / create / module add|remove|list|sync|diff|doctor / start|stop|eval").version(t.version),e.command("create <target>").description("从模板生成多包 workspace 应用骨架").option("--name <package-name>","生成应用 package.json.name(当前目录初始化时建议显式指定)").option("--source <路径>","模板源(本地目录 / 模板 pack tgz / registry 包 @scope/pkg[@version])").option("--local-deps <tgz目录>","注入平台 overrides(file: 本地发布态 tgz,install 离线验证态;生产 B 档不传)").option("--config-dir <目录>","合并应用配置目录进应用 config/(同名覆盖/异名保留;可重复传,后者覆盖前者同名文件——独立完整配置档单目录注入为主流用法;通用能力,与模板/registry 源正交)",(o,a)=>[...a,o],[]).option("--env-file <路径>","环境变量文件(结合 --config-dir 使用:拷入应用 config/env.sh——启动脚本 source 的档位环境;镜像部署链 build-app-image 使用)",o=>o).option("--registry <url>","拉取 registry(registry 源时生效;缺省自动解析环境镜像)").option("--force","覆盖已存在目录").action(async(o,a)=>{try{await cn({name:o,appName:a.name,source:a.source??"",force:a.force,localDeps:a.localDeps,configDir:a.configDir,envFile:a.envFile,registry:a.registry})}catch(c){console.error("❌",c.message),process.exitCode=1}}),e.command("starter").description("查询 starter 文档与可用类型").option("--type <agent|agent-mall|agent-support>","starter 类型").option("--list","列出可用 starter 类型").option("--json","以 JSON 输出(仅配合 --list)").action(o=>{try{So(o)}catch(a){console.error("❌",a.message),process.exitCode=1}});let r=e.command("module").description("模块管理(add/remove/list/sync/diff/doctor/update/publish/pack)");r.command("add <pkg>").description("安装/展开模块(--mode local|codegen|sdk;--source 支持目录/tgz/registry 包)").option("--mode <local|codegen|sdk>","分发模式","codegen").option("--source <路径>","模块源(本地目录/tgz/registry 包 @scope/pkg[@version])").option("--registry <url>","拉取 registry(registry 源时生效;缺省自动解析环境镜像)").option("--no-deps","跳过依赖自动解析(P3 W3-B 逃生门)").action(async(o,a)=>{try{await _s({pkg:o,mode:a.mode,source:a.source,registry:a.registry,noDeps:!a.deps})}catch(c){console.error("❌",c.message),process.exitCode=1}}),r.command("remove <id>").description("卸载模块(删包 + 清理装配)").option("--force","忽略反向依赖检查强制移除").action(async(o,a)=>{try{await Vs(o,{force:a.force})}catch(c){console.error("❌",c.message),process.exitCode=1}}),r.command("list").description("列出已装模块").action(async()=>{try{await zs()}catch(o){console.error("❌",o.message),process.exitCode=1}}),r.command("sync").description("增量装配 4 文件(不覆盖;结构破坏降级为告警)").option("--force","全量覆盖装配文件(002 §6.5:唯一覆盖入口)").action(async o=>{try{await Js({force:o.force})}catch(a){console.error("❌",a.message),process.exitCode=1}}),r.command("diff [module]").description("装配漂移双向 diff(module 可指定,默认全部)").action(async o=>{try{await Us(o)}catch(a){console.error("❌",a.message),process.exitCode=1}}),r.command("doctor").description("结构校验(doctor 完整版 20 条规则)").option("--json","只输出 JSON 结果").option("--only <规则列表>","只运行指定规则(逗号分隔)").option("--skip <规则列表>","跳过指定规则(逗号分隔)").action(async o=>{try{await Bs(o)}catch(a){console.error("❌",a.message),process.exitCode=1}}),r.command("update <module>").description("升级组件(codegen→3-way merge;sdk→pnpm update)").option("--dry-run","只打印合并结果不写回").option("--ours","冲突快速取本地版本").option("--theirs","冲突快速取新版版本").option("--source <路径>","新版本地源目录或 tgz").option("--base-source <路径>","base 本地源目录或 tgz").action(async(o,a)=>{try{await qs({module:o,...a})}catch(c){console.error("❌",c.message),process.exitCode=1}}),r.command("pack <module>").description("本地打包不发布(模块目录如 modules/module-member;pnpm pack 内建归一化 → 发布态 tgz)").requiredOption("--out <dir>","产物目录(pnpm 自动创建,同名覆盖写)").option("--check","产物断言(协议残留 / exports / 白名单 / 泄漏 / 文件一致性)").action(async(o,a)=>{try{await Qs({module:o,out:a.out,check:a.check})}catch(c){console.error("❌",c.message),process.exitCode=1}}),r.command("publish <module>").description("模块晋升 npm(模块目录如 modules/module-member;校验 + 打包 + 产物门禁 + pnpm publish <tgz>)").option("--tgz <path>","复用已验证产物(缺省现场 pack 到临时目录)").option("--dry-run","校验 + 门禁 + 打印,不真实发布").option("--access <public|restricted>","npm access","public").option("--registry <url>","发布 registry(缺省 pnpm 配置解析:@tbox.cn scope 优先)").action(async(o,a)=>{try{await ro({module:o,tgz:a.tgz,dryRun:a.dryRun,access:a.access,registry:a.registry})}catch(c){console.error("❌",c.message),process.exitCode=1}});let n=e.command("miniapp-container").description("小程序容器管理(init)");e.command("dev").description("原地快速开发调试(真源直挂:装配物化 + 包身份种植 + 配置面注入;改源码即热重启)").option("--template <dir>","模板/应用根目录(相对仓库根;缺省 mall 模板)").option("--module <dir>","仓库模块目录,可多次传入(依赖闭包自动展开)",(o,a)=>[...a,o],[]).option("--real-env <tier>","real-env 档位(wanda|joycity|mock):config 重定向 + provider 纳入 + env 防呆").option("--config-dir <目录>","独立完整配置档:三路配置(integrations/deployments/config dir)独占注入该目录,不与档位主档叠加(可重复传,后者覆盖前者)。常用 .real-env/config/wanda-intranet 切内网端点",(o,a)=>[...a,o],[]).option("--link-sdk","平台真源在场断言(重定向总表恒含 app-sdk/contracts 兜底;本旗标为显式 identity 强化)").option("--port <n>","服务端口(缺省 8000)",o=>Number(o)).option("--prune","清理构件区与种植链接后退出").action(async o=>{try{await xo(o)}catch(a){console.error("❌",a.message),process.exitCode=1}});let s=e.command("provider").description("Provider 绑定管理(bind/unbind/use/unuse/migrate——写 config/integrations.json)");s.command("bind <service> <provider> [implementation]").description("绑定服务槽:三参精确钉版(parking.query joycity joycity-c-parking@1);两参派生(implementation 经 catalog 唯一供给推导)").option("--instance <mallId>","per-instance 覆盖位键(mall 场景 = mallId);缺省写槽级默认位").action(async(o,a,c,l)=>{try{await io(o,a,c,{instance:l.instance})}catch(p){console.error("❌",p.message),process.exitCode=1}}),s.command("use <provider>").description("设置默认厂商:root(应用全局)或 --domain(域级);implementation 恒派生,激活仍需槽 instances 在场").option("--domain <domain>","写域级默认(domains.<域前缀>.provider);缺省写应用全局 root.provider").action(async(o,a)=>{try{await ao(o,{domain:a.domain})}catch(c){console.error("❌",c.message),process.exitCode=1}}),s.command("unuse").description("清除默认厂商:root 或 --domain(空域对象/空 domains 一并清除)").option("--domain <domain>","仅清除该域级默认;缺省清除应用全局 root.provider").action(async o=>{try{await co({domain:o.domain})}catch(a){console.error("❌",a.message),process.exitCode=1}}),s.command("unbind <service>").description("解绑服务槽(--instance 删实例覆盖位;缺省槽与实例集删除)").option("--instance <mallId>","仅删除该实例覆盖位(回落槽级默认)").action(async(o,a)=>{try{await lo(o,{instance:a.instance})}catch(c){console.error("❌",c.message),process.exitCode=1}}),s.command("migrate").description("integrations v2 一次性迁移:旧单串 providerId → 双字段(缺省 dry-run,--write 落盘)").option("--write","落盘(缺省 dry-run 仅报告)").action(async o=>{try{await fo({write:o.write})}catch(a){console.error("❌",a.message),process.exitCode=1}}),n.command("init [platform]").description("初始化小程序容器到 apps/(缺省 alipay;--source 支持目录/tgz/registry 包)").option("--source <source>","容器源:目录 / tgz / registry 包(缺省 @tbox.cn/app-miniapp-container-<platform>)").option("--registry <registry>","拉取 registry(registry 源时生效;缺省自动解析环境镜像)").option("--force","已存在时删除重展开").action(async(o,a)=>{try{await no({platform:o,source:a.source,registry:a.registry,force:a.force})}catch(c){console.error("❌",c.message),process.exitCode=1}}),e.command("start").description("启动本应用 server(编排语义;前台或 -d 后台;不设 NODE_ENV=production)").option("-d","后台运行(日志 .tbox-server.log,状态 .tbox-server.json)").option("--port <n>","端口(缺省 8000)","8000").option("--build","先构建 apps/server(tsup)").action(async o=>{try{await li({detached:o.d,port:Number(o.port),build:o.build})}catch(a){console.error("❌",a.message),process.exitCode=1}}),e.command("stop").description("停止本应用 server(状态文件优先,scripts/stop.sh 兜底;幂等)").action(async()=>{try{await pi()}catch(o){console.error("❌",o.message),process.exitCode=1}});let i=e.command("eval").description("智能体行为评测(validate 契约校验 | run 评测编排)");return i.command("validate").description("校验评测数据集契约(零网络零进程;迁移 QA 与 nightly 候选)").requiredOption("--dataset <dir>","数据集目录(含 suite.yaml/rubric.yaml/cases/)").action(async o=>{try{await ai({dataset:o.dataset})}catch(a){console.error("❌",a.message),process.exitCode=1}}),i.command("run").description("执行评测(纯客户端;实例经 --base-url 或 .tbox-server.json 发现)").requiredOption("--dataset <dir>","数据集目录(必传——显式优于聪明)").option("--base-url <url>","外部实例地址(缺省读 <cwd>/.tbox-server.json)").option("--filter <k=v>","过滤用例(priority=P0 / tag=memory / id=<id>;可重复,AND)",(o,a)=>[...a,o],[]).option("--concurrency <n>","并发用例数(缺省 2;wanda 沙箱限速未知时保守起步)","2").option("--no-judge","跳过 text 维 LLM 判定(纯路由快速回路)").option("--baseline <report.json>","基线报告对比(新失败/已修复/翻转)").option("--rerun-failed <runDir>","只重跑上次 run 中 fail+inconclusive 的用例").option("--journal-root <dir>","显式 journal 根(--base-url 模式的 ingest 供给;缺席则降级)").option("--out <dir>","报告目录覆盖(缺省 <cwd>/.eval/runs/<ts>-<runId>)").option("--max <n>","截断用例数(调试用)").option("--quiet","静默进度输出").action(async o=>{try{await ci({dataset:o.dataset,baseUrl:o.baseUrl,filters:o.filter,concurrency:Number(o.concurrency),judge:o.judge,baselinePath:o.baseline,rerunFailedDir:o.rerunFailed,journalRoot:o.journalRoot,outDir:o.out,max:o.max?Number(o.max):void 0,quiet:o.quiet??!1})}catch(a){console.error("❌",a.message),process.exitCode=1}}),e}var fp=typeof process.argv[1]=="string"&&ui(up(import.meta.url))===ui(process.argv[1]);fp&&dp().parseAsync(process.argv);export{dp as createProgram};