@tbox.cn/app-cli 0.4.2 → 0.5.0
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/README.md +1 -1
- package/dist/{chunk-SGF4P5YN.js → chunk-D4Z67K2V.js} +1 -1
- package/dist/chunk-IA5ZAM5W.js +16 -0
- package/dist/{contracts-resolver-UQGGTGAO.js → contracts-resolver-ABE4US6D.js} +1 -1
- package/dist/declared-TVLLELHR.js +2 -0
- package/dist/index.js +86 -65
- package/package.json +3 -3
- package/starter/agent-mall.md +15 -12
- package/starter/agent.md +9 -9
- package/dist/chunk-GY4SXKTS.js +0 -16
- package/dist/declared-4SBML2FU.js +0 -2
- package/starter/agent-support.md +0 -38
package/dist/index.js
CHANGED
|
@@ -1,21 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{a as
|
|
3
|
-
`),c=!1,l=!1,p=!1,u=null,d=0;for(let f=0;f<a.length;f++){let
|
|
4
|
-
`)),d}catch(s){return console.warn(` ⚠️ 携带锁 specifiers 对齐失败(降级跳过——frozen install 如失败请手动 pnpm install): ${s.message}`),-1}}function
|
|
5
|
-
`)){let s=n.replace(/\r$/,"");if(!s.trim()||s.trimStart().startsWith("#"))continue;if(!/^ /.test(s)){r=/^catalog\s*:/.test(s);continue}if(!r)continue;let
|
|
6
|
-
# create --local-deps 注入:平台包经 file: 本地发布态 tgz 消费(005 v0.4.0 验证态;生产 B 档移除本块走 registry)
|
|
7
|
-
overrides:
|
|
8
|
-
`+i.join(`
|
|
9
|
-
`)+`
|
|
10
|
-
`}function Oi(e){let t=pt(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 un(e){let t=0;for(let r of pt(e,{withFileTypes:!0}))r.name===".DS_Store"||r.name==="node_modules"||(r.isDirectory()?t+=un(T.join(e,r.name)):t+=1);return t}function Pi(e){if(!Y(e)||!er(e).isDirectory())throw new Error(`--config-dir 不是目录: ${e}`);if(un(e)===0)throw new Error(`--config-dir 目录为空(无任何文件可合并): ${e}`)}var Li=/^(?:@[a-z0-9._-]+\/)?[a-z0-9][a-z0-9._-]*$/;function Fi(e){let t=T.join(e,"scripts");if(!Y(t))return;function r(n){for(let s of pt(n,{withFileTypes:!0})){let i=T.join(n,s.name);s.isDirectory()?r(i):s.isFile()&&$i(i,493)}}r(t)}function _i(e){if(!Li.test(e))throw new Error(`应用包名无效: ${e}(需为 npm 包名;使用 --name 指定)`)}function Ni(e){return e===T.resolve(process.cwd())}function Vi(e,t){let r=T.relative(e,t);return r!==""&&r!==".."&&!r.startsWith(`..${T.sep}`)&&!T.isAbsolute(r)}function Ji(e,t){if(!Y(e))return;let r=er(e),n=lt(e),s=Y(t)?lt(t):T.resolve(t);if(n===s||r.isDirectory()&&Vi(n,s))throw new Error(`模板源与目标目录重合或目标位于模板源内,无法初始化: ${e}`)}async function dn(e){let t=T.resolve(process.cwd(),e.name),r=Y(t),n=Ni(t);if(r&&!er(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&&_i(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(Ji(a,t),c&&e.localDeps)throw new Error("registry 源(--source @scope/pkg)与 --local-deps 互斥:registry 安装无需/不允许本地依赖注入");e.localDeps&&Oi(T.resolve(e.localDeps));for(let d of i)Pi(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 Qt(u,t,{recursive:!0,force:!0,filter:S=>{let $=T.basename(S);return!(Ci.has($)||Di.has($))}}),Fi(t);let f=T.join(t,Ti);Y(f)&&await ln(f,T.join(t,pn));let g=T.join(t,Mi),m=Y(g);if(m){await ln(g,T.join(t,Ii));let S=tn(t);S>0&&console.log(` 🔒 携带锁 specifiers 已对齐 registry 归一态(${S} 项 catalog: → 字面量)`)}if(e.localDeps){let S=Ai(T.resolve(e.localDeps),t);await ji(T.join(t,pn),S,"utf8"),m&&De(t)}for(let S of i)await Qt(T.resolve(S),T.join(t,"config"),{recursive:!0,force:!0,filter:$=>T.basename($)!==".DS_Store"&&T.basename($)!=="node_modules"});e.envFile&&await Qt(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 Ei(h,"utf8"));S.name=s,await Ri(h,JSON.stringify(S,null,2)+`
|
|
11
|
-
`,"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 wr,readdirSync as cc}from"fs";import Q from"path";import{copyFile as Zi,mkdir as kt,rm as xn,writeFile as ne}from"fs/promises";import W from"path";import{existsSync as ze,readFileSync as mt}from"fs";import{readFile as dt,readdir as Bi,writeFile as Wi}from"fs/promises";import _ from"path";import V from"typescript";var zi=/^@tbox\.cn\/app-(?:sdk|toolkit|contracts(?:-|$))/,Ui=new Set(["@tbox.cn/app-contracts-mall"]);function ut(e){return zi.test(e)}function Je(e){return Ui.has(e)}var mn=["dependencies","optionalDependencies","peerDependencies","devDependencies"],Hi=new Set(["name","version","private","exports"]),gt=/\.(?:ts|tsx|mts|cts|js|jsx|mjs|cjs)$/,Ki=new Set(["node_modules","dist",".git",".tbox"]),Gi=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';
|
|
2
|
+
import{a as Me,b as en,c as Ie,d as tn,e as rn,f as nn,g as sn,h as on,i as an,j as cn,k as ln,l as pn,m as un,n as dn,o as fn,p as He,q as dt,r as ar,s as W,t as ge}from"./chunk-IA5ZAM5W.js";import{d as ut}from"./chunk-D4Z67K2V.js";import{Command as Xp}from"commander";import{createRequire as Zp}from"module";import{fileURLToPath as Qp}from"url";import{realpathSync as Bi}from"fs";import{existsSync as X,readdirSync as ot,readFileSync as Oc,realpathSync as Ut,statSync as Ar,chmodSync as Ac}from"fs";import{cp as Wt,readFile as Ir,rename as co,writeFile as lo}from"fs/promises";import T from"path";function B(e){return sn(e)}async function oe(e,t){nn(e,t)}function mn(e,t){return on(e,t)}function gn(e,t){return an(e,t)}function hn(e,t){return cn(e,t)}function yn(e,t,r){return ln(e,t,r)}function je(e,t){pn(e,t)}import{existsSync as ft,readFileSync as cr,writeFileSync as Wi}from"fs";import{execFileSync as Ui}from"child_process";import mt from"path";function he(e){if(!ft(mt.join(e,"pnpm-lock.yaml")))return!1;try{return Ui("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}}function vn(e){let t=mt.join(e,"pnpm-lock.yaml"),r=mt.join(e,"pnpm-workspace.yaml"),n=mt.join(e,"package.json");if(!ft(t)||!ft(r)||!ft(n))return 0;try{let s=JSON.parse(cr(n,"utf8")),o=new Map;for(let f of[s.dependencies,s.devDependencies,s.optionalDependencies])for(let[m,h]of Object.entries(f??{}))h!=="catalog:"&&!h.startsWith("workspace:")&&!h.startsWith("file:")&&o.set(m,h);if(o.size===0)return 0;let i=Hi(cr(r,"utf8")),a=cr(t,"utf8").split(`
|
|
3
|
+
`),c=!1,l=!1,p=!1,u=null,d=0;for(let f=0;f<a.length;f++){let m=a[f];if(!m.trim()||m.startsWith("#"))continue;if(!/^ /.test(m)){c=/^importers:/.test(m),l=!1,p=!1,u=null;continue}if(!c)continue;if(/^ {2}\S/.test(m)){l=/^ {2}\.:\s*$/.test(m),p=!1,u=null;continue}if(!l)continue;if(/^ {4}\S/.test(m)){p=/^( {4})(dependencies|devDependencies|optionalDependencies):\s*$/.test(m),u=null;continue}if(!p)continue;let h=m.match(/^ {6}['"]?([^'":\s]+)['"]?:\s*$/);if(h){u=h[1];continue}if(m.match(/^ {8}specifier:\s*['"]?catalog:['"]?\s*$/)&&u){if(o.has(u)){let g=i.get(u);g!==void 0&&(a[f]=m.replace(/^( {8}specifier:\s*)['"]?catalog:['"]?(\s*)$/,`$1${g}$2`),d++)}u=null}}return d>0&&Wi(t,a.join(`
|
|
4
|
+
`)),d}catch(s){return console.warn(` ⚠️ 携带锁 specifiers 对齐失败(降级跳过——frozen install 如失败请手动 pnpm install): ${s.message}`),-1}}function Hi(e){let t=new Map,r=!1;for(let n of e.split(`
|
|
5
|
+
`)){let s=n.replace(/\r$/,"");if(!s.trim()||s.trimStart().startsWith("#"))continue;if(!/^ /.test(s)){r=/^catalog\s*:/.test(s);continue}if(!r)continue;let o=s.match(/^\s+["']?([^"':#\s]+)["']?\s*:\s*(.+?)\s*(?:#.*)?$/);o&&t.set(o[1],o[2].replace(/^['"]|['"]$/g,""))}return t}import{existsSync as ht,readFileSync as bn,mkdtempSync as xn,rmSync as Sn}from"fs";import{execFileSync as Gi}from"child_process";import{tmpdir as yt}from"os";import{readFile as qi,readdir as Yi,stat as vu}from"fs/promises";import q from"path";import{execFileSync as gt}from"child_process";import wn from"path";var Ke=null;function lr(e){if(e?.startsWith("@tbox.cn/"))try{let r=gt("pnpm",["config","get","@tbox.cn:registry"],{encoding:"utf8"}).trim().split(/\r?\n/).pop()??"";if(r)return r}catch{}if(Ke)return Ke;try{Ke=gt("pnpm",["config","get","registry"],{encoding:"utf8"}).trim().split(/\r?\n/).pop()??"https://registry.npmjs.org/"}catch{Ke="https://registry.npmjs.org/"}return Ke}function Oe(e){return/^@[^/]+\/[^@/]+(@.+)?$/.test(e)}function Ki(e){return e.replace(/@[^/]+$/,"")}function kn(e,t,r){let n=r??lr(Ki(e)),s=["view",e,"dist.tarball"];r&&s.push("--registry",r);let i=gt("pnpm",s,{encoding:"utf8"}).trim().split(/\r?\n/).pop()??"";if(!i)throw new Error(`registry 拉取失败:${e} 无 dist.tarball(registry=${n})`);let a=i.split("/").pop()??`${e.replace(/^@/,"").replace(/\//g,"-").split("@")[0]}.tgz`,c=wn.join(wn.resolve(t),a);return gt("curl",["-fsSL","-o",c,i],{stdio:"ignore"}),c}var Xi=new Set(["node_modules","dist","dist-ssr",".git",".tbox"]);async function $n(e,t,r){let n=await Yi(e,{withFileTypes:!0});for(let s of n){let o=q.join(e,s.name),i=q.relative(t,o).split(q.sep).join("/");if(s.isDirectory()){if(Xi.has(s.name))continue;await $n(o,t,r)}else s.isFile()&&r.push(i)}}var ie=class{constructor(t){this.dir=t;this.name=q.basename(q.resolve(t))}dir;name;getDir(){return this.dir}async listFiles(){let t=[];return await $n(this.dir,this.dir,t),t.sort()}async readFile(t){return qi(q.join(this.dir,t),"utf8")}static isUsable(t){return ht(q.join(t,"package.json"))}resolveVersion(){return pr(this.dir).version}};function pr(e){let t;try{t=bn(q.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 ee=class{constructor(t,r){this.spec=t;this.registry=r;this.name=t,this.packDir=xn(q.join(yt(),"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??lr(t);console.log(`📦 从 registry 拉取 ${this.spec}(${r})`);let n=kn(this.spec,this.packDir,this.registry);this.inner=new z(n);let s,o="";try{let i=JSON.parse(bn(q.join(this.inner.getDir(),"package.json"),"utf8"));s=i.name,o=i.version??""}catch{}return this.resolvedName=s??this.spec,this.resolvedVersion=o,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(yt())&&Sn(this.packDir,{recursive:!0,force:!0})}catch{}}};function ae(e,t){if(e.endsWith(".tgz")&&ht(e)){let r=new z(e);return{sourceDir:r.getDir(),source:r}}if(!ht(e)&&Oe(e)){let r=new ee(e,t);return{sourceDir:r.getDir(),source:r}}return{sourceDir:e,source:new ie(e)}}var z=class{name;inner;tmpRoot;constructor(t){let r=q.resolve(t);this.name=q.basename(r,".tgz"),this.tmpRoot=xn(q.join(yt(),"tbox-tgz-")),Gi("tar",["-xzf",r,"-C",this.tmpRoot],{stdio:"ignore"});let n=q.join(this.tmpRoot,"package");this.inner=new ie(ht(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(yt())&&Sn(this.tmpRoot,{recursive:!0,force:!0})}catch{}}};import{existsSync as Zi}from"fs";import jn from"path";var Qi=/^@tbox\.cn\/app-/,ea=/^@tbox\.cn\/app-(?:sdk|toolkit|contracts(?:-|$))/,ta=new Set(["@tbox.cn/app-contracts-mall"]);function vt(e){return ea.test(e)}function Ge(e){return ta.has(e)}function wt(e,t){if(!Qi.test(t))return null;let r=t.replace(/^@tbox\.cn\/app-/,""),n=jn.join(e,r);return Zi(jn.join(n,"tbox.module.json"))?n:null}import{existsSync as xe,readFileSync as Mr,readdirSync as Rc}from"fs";import ne from"path";import{copyFile as pa,mkdir as Ct,rm as Pn,writeFile as ce}from"fs/promises";import H from"path";import{existsSync as qe,readFileSync as xt}from"fs";import{readFile as kt,readdir as ra,writeFile as na}from"fs/promises";import N from"path";import J from"typescript";var Rn=["dependencies","optionalDependencies","peerDependencies","devDependencies"],sa=new Set(["name","version","private","exports"]),St=/\.(?:ts|tsx|mts|cts|js|jsx|mjs|cjs)$/,oa=new Set(["node_modules","dist",".git",".tbox"]),ia=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';
|
|
12
6
|
`,"src/runtime.ts":`export * from '@tbox.cn/app-contracts-mall';
|
|
13
7
|
`,"src/server/index.ts":`export * from '@tbox.cn/app-contracts-mall/server';
|
|
14
|
-
`}}]]);function
|
|
15
|
-
`)){let c=a.replace(/\r$/,"");if(/^\S/.test(c)){
|
|
16
|
-
`,"utf8");for(let[c,l]of Object.entries(r.files)){let p=
|
|
17
|
-
`}catch{continue}else p=
|
|
18
|
-
`,"utf8")}var
|
|
8
|
+
`}}]]);function $t(e){return`@app/${e}`}function Dn(e,t){return Ge(e)||t?"codegen":"sdk"}function Ee(e){return{id:e.id,publicName:e.package,localName:$t(e.id),version:e.version}}function aa(e){let t=new Map;for(let r of e?.npmModules??[]){if(r.mode!=="codegen")continue;let n=Ee(r);t.set(n.publicName,n)}return t}function Cn(e,t){let r=new Map(e);return r.set(t.publicName,t),r}function Re(e){return aa(B(e))}function jt(e){try{return JSON.parse(xt(N.join(e,"package.json"),"utf8")).tboxCodegenShim===!0}catch{return!1}}function ca(e,t,r){let n=[...t].sort((s,o)=>{let i=r==="local"?s.publicName:s.localName;return(r==="local"?o.publicName:o.localName).length-i.length});for(let s of n){let o=r==="local"?s.publicName:s.localName,i=r==="local"?s.localName:s.publicName;if(e===o)return i;if(e.startsWith(`${o}/`))return`${i}${e.slice(o.length)}`}return e}function Ye(e,t,r,n="local"){let s=[...r];if(s.length===0)return e;let o=t.endsWith(".tsx")?J.ScriptKind.TSX:t.endsWith(".jsx")?J.ScriptKind.JSX:t.endsWith(".json")?J.ScriptKind.JSON:J.ScriptKind.TS,i=J.createSourceFile(t,e,J.ScriptTarget.Latest,!0,o),a=[];function c(u){let d=ca(u.text,s,n);if(d===u.text)return;let f=e.slice(u.getStart(i),u.end),m=f[0];(m==="'"||m==='"'||m==="`")&&f.at(-1)===m&&a.push({start:u.getStart(i),end:u.end,value:`${m}${d}${m}`})}function l(u){if(J.isImportDeclaration(u)&&J.isStringLiteralLike(u.moduleSpecifier))c(u.moduleSpecifier);else if(J.isExportDeclaration(u)&&u.moduleSpecifier&&J.isStringLiteralLike(u.moduleSpecifier))c(u.moduleSpecifier);else if(J.isImportTypeNode(u)&&J.isLiteralTypeNode(u.argument)&&J.isStringLiteralLike(u.argument.literal))c(u.argument.literal);else if(J.isCallExpression(u)&&u.arguments.length>0&&J.isStringLiteralLike(u.arguments[0])){let d=u.expression.kind===J.SyntaxKind.ImportKeyword,f=J.isIdentifier(u.expression)&&u.expression.text==="loadModuleSkills";(d||f)&&c(u.arguments[0])}else if(J.isPropertyAssignment(u)&&J.isArrayLiteralExpression(u.initializer)){let d=u.name.getText(i).replace(/["']/g,"");if(d==="external"||d==="noExternal")for(let f of u.initializer.elements)J.isStringLiteralLike(f)&&c(f)}J.forEachChild(u,l)}l(i);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 Tn(e,t,r,n){let s=[...t];for(let o of Rn){let i=e[o];if(!i||typeof i!="object"||Array.isArray(i))continue;let a={...i};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))vt(c)?a[c]="catalog:":a[c]==="catalog:"&&n?.has(c)&&(a[c]=n.get(c));e[o]=a}}function la(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 ur(e){return typeof e=="string"?la(e):Array.isArray(e)?e.map(ur):!e||typeof e!="object"?e:Object.fromEntries(Object.entries(e).map(([t,r])=>[t,ur(r)]))}function Mn(e){if(!e.exports||typeof e.exports!="object")return;let t=ur(e.exports);"./package.json"in t||(t["./package.json"]="./package.json"),e.exports=t}function Ae(e,t,r,n){let s=JSON.parse(JSON.stringify(e));return s.name=$t(t.id),s.private=!0,Tn(s,r,"local",n),Mn(s),s}function te(e){let t=/^\s+["']?([^"':#\s]+)["']?\s*:\s*(.+?)\s*(?:#.*)?$/,r=N.resolve(e);for(;;){let n=N.join(r,"pnpm-workspace.yaml");if(qe(n)){let o=new Map,i=!1;for(let a of xt(n,"utf8").split(`
|
|
9
|
+
`)){let c=a.replace(/\r$/,"");if(/^\S/.test(c)){i=/^catalog\s*:/.test(c);continue}if(!i)continue;let l=c.match(t);l&&o.set(l[1],l[2].replace(/^['"]|['"]$/g,""))}return o.size>0?o:void 0}let s=N.dirname(r);if(s===r)return;r=s}}function En(e,t){return JSON.stringify(e)===JSON.stringify(t)}function In(e,t,r,n={}){let s={},o=new Set([...Object.keys(e),...Object.keys(t),...Object.keys(r)]);for(let i of o){if(sa.has(i)){let a=n.ours?t[i]:r[i];a!==void 0&&(s[i]=a);continue}if(n.ours){t[i]!==void 0&&(s[i]=t[i]);continue}if(n.theirs){r[i]!==void 0&&(s[i]=r[i]);continue}En(t[i],e[i])?r[i]!==void 0&&(s[i]=r[i]):En(r[i],e[i])?t[i]!==void 0&&(s[i]=t[i]):r[i]!==void 0?s[i]=r[i]:t[i]!==void 0&&(s[i]=t[i])}return s}function Et(e){if(!qe(N.join(e,"package.json")))throw new Error(`codegen 源无效(缺 package.json): ${e}`);let t;try{t=JSON.parse(xt(N.join(e,"package.json"),"utf8")).name}catch{throw new Error(`codegen 源 package.json 无法解析: ${e}`)}if(t&&Ge(t)&&!qe(N.join(e,"src","index.ts")))throw new Error(`codegen 源缺少 src/index.ts:请使用包含源码载荷的契约包版本(${e})`)}function Rt(e,t){let r=[...t];return new Map([...e].map(([n,s])=>[n,St.test(n)?Ye(s,n,r):s]))}async function On(e,t){let r=ia.get(t.publicName);if(!r)return!1;let n=N.join(e,"packages",t.id),{mkdir:s,rm:o,writeFile:i}=await import("fs/promises");await o(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 i(N.join(n,"package.json"),JSON.stringify(a,null,2)+`
|
|
10
|
+
`,"utf8");for(let[c,l]of Object.entries(r.files)){let p=N.join(n,c);await s(N.dirname(p),{recursive:!0}),await i(p,l,"utf8")}return!0}async function bt(e,t){if(qe(e))for(let r of await ra(e,{withFileTypes:!0})){if(oa.has(r.name))continue;let n=N.join(e,r.name);r.isDirectory()?await bt(n,t):r.isFile()&&(St.test(r.name)||r.name==="package.json")&&t.push(n)}}async function Dt(e,t,r="local"){let n=[...t],s=te(e),o=[];await bt(N.join(e,"packages"),o);let i=[];for(let a of o){let c=N.relative(e,a).split(N.sep).join("/"),l=await kt(a,"utf8"),p=l;if(N.basename(a)==="package.json")try{let u=JSON.parse(l);Tn(u,n,r,s),p=JSON.stringify(u,null,2)+`
|
|
11
|
+
`}catch{continue}else p=Ye(l,c,n,r);p!==l&&(await na(a,p,"utf8"),i.push(c))}return i}async function An(e){let t=Re(e),r=[],n=[];await bt(N.join(e,"apps"),n),await bt(N.join(e,"packages"),n);for(let s of t.values()){let o=N.join(e,"packages",s.id);if(!qe(o))r.push(`packages/${s.id}: codegen 包缺失`);else{let i=N.join(o,"package.json");try{JSON.parse(await kt(i,"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 o=N.relative(e,s).split(N.sep).join("/");if(N.basename(s)==="package.json"){try{let a=JSON.parse(await kt(s,"utf8"));for(let c of Rn){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(`${o}: codegen 依赖未归一为 ${u.localName}: workspace:*`)}}catch{}continue}let i=await kt(s,"utf8");Ye(i,o,t.values())!==i&&r.push(`${o}: 源码仍引用 codegen 发布包名`)}return[...new Set(r)]}function Xe(e){return JSON.parse(xt(N.join(e,"package.json"),"utf8"))}function Ln(e){return e.replace(/^@tbox\.cn\/app-/,"").replace(/^@[^/]+\//,"")}async function _n(e,t,r,n=[]){let s=new ie(t),o=H.join(e,"packages",r.id);await Pn(o,{recursive:!0,force:!0}),await Ct(o,{recursive:!0});let i=Xe(t),a=typeof i.name=="string"?i.name:r.package,c=Ee({id:r.id,package:a,version:r.version}),l=Cn(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(o,u);if(await Ct(H.dirname(d),{recursive:!0}),!St.test(u)){await pa(H.join(t,u),d);continue}let f=await s.readFile(u);await ce(d,Ye(f,u,l.values()))}let p=Ae(i,r,l.values(),te(t));await ce(H.join(o,"package.json"),JSON.stringify(p,null,2)+`
|
|
12
|
+
`,"utf8")}var ua=e=>`import type { ServerContext, ServerModule } from '@tbox.cn/app-sdk/server';
|
|
19
13
|
|
|
20
14
|
/**
|
|
21
15
|
* ${e} 本地模块(local 模式,app 自有包)注册入口。
|
|
@@ -34,11 +28,11 @@ export const serverModule = {
|
|
|
34
28
|
// _ctx.services.register('member', member);
|
|
35
29
|
},
|
|
36
30
|
} satisfies ServerModule;
|
|
37
|
-
`,
|
|
31
|
+
`,da=`/**
|
|
38
32
|
* Service 层(业务专属实现,直接 import 或经 ctx.services 注册供跨模块消费)。
|
|
39
33
|
*/
|
|
40
34
|
export {};
|
|
41
|
-
`,
|
|
35
|
+
`,fa=`/**
|
|
42
36
|
* Handler 定义(001 §3.2)。
|
|
43
37
|
*/
|
|
44
38
|
import type { HandlerDefinition } from '@tbox.cn/app-sdk/server';
|
|
@@ -47,7 +41,7 @@ export const exampleHandler: HandlerDefinition = {
|
|
|
47
41
|
id: 'example',
|
|
48
42
|
handle: async () => ({ cards: [], text: '本地模块示例' }),
|
|
49
43
|
};
|
|
50
|
-
`,
|
|
44
|
+
`,ma=`/**
|
|
51
45
|
* 客户端入口:导出 clientModule(CLI sync 装配读取;与 serverModule 逐行对称)。
|
|
52
46
|
* cards map 的 key 约定 = cardType;无卡片模块写空 map。
|
|
53
47
|
*/
|
|
@@ -62,13 +56,13 @@ export const clientModule = {
|
|
|
62
56
|
// _ctx.cards.registerMap(cards);
|
|
63
57
|
},
|
|
64
58
|
} satisfies ClientModule;
|
|
65
|
-
`,
|
|
59
|
+
`,ga=`/**
|
|
66
60
|
* 本地模块主入口(package.json exports "." 指向)。
|
|
67
61
|
* 消费方通常走 ./server / ./client 子路径;主入口聚合导出便于直接引用。
|
|
68
62
|
*/
|
|
69
63
|
export * from './server';
|
|
70
64
|
export * from './client';
|
|
71
|
-
`,
|
|
65
|
+
`,ha=`{
|
|
72
66
|
"extends": "../../tsconfig.base.json",
|
|
73
67
|
"compilerOptions": {
|
|
74
68
|
"jsx": "react-jsx",
|
|
@@ -76,35 +70,51 @@ export * from './client';
|
|
|
76
70
|
},
|
|
77
71
|
"include": ["src"]
|
|
78
72
|
}
|
|
79
|
-
`;async function
|
|
80
|
-
`,"utf8"),await
|
|
81
|
-
`,"utf8"),await
|
|
82
|
-
`,"utf8")}return n}async function
|
|
83
|
-
`));f!==p.block&&(c=
|
|
84
|
-
`));return w!==
|
|
85
|
-
`));return
|
|
73
|
+
`;async function Fn(e,t){let r=t.startsWith("module-")?t:`module-${t}`,n=H.join(e,"packages",r);await Pn(n,{recursive:!0,force:!0}),await Ct(H.join(n,"src/server"),{recursive:!0}),await Ct(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:"}},o={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 ce(H.join(n,"package.json"),JSON.stringify(s,null,2)+`
|
|
74
|
+
`,"utf8"),await ce(H.join(n,"tbox.module.json"),JSON.stringify(o,null,2)+`
|
|
75
|
+
`,"utf8"),await ce(H.join(n,"tsconfig.json"),ha,"utf8"),await ce(H.join(n,"src/server/index.ts"),ua(r),"utf8"),await ce(H.join(n,"src/server/service.ts"),da,"utf8"),await ce(H.join(n,"src/server/handler.ts"),fa,"utf8"),await ce(H.join(n,"src/client/index.ts"),ma,"utf8"),await ce(H.join(n,"src/index.ts"),ga,"utf8"),r}import{existsSync as Nn}from"fs";import{readFileSync as ya}from"fs";import{writeFile as va}from"fs/promises";import Jn from"path";var wa=["apps/server/package.json","apps/client/package.json"];async function Ze(e,t,r){let n=!1;for(let s of wa){let o=Jn.join(e,s);if(!Nn(o))continue;let i=JSON.parse(ya(o,"utf8")),a=i.dependencies??={};r==="add"&&!a[t]?(a[t]="workspace:*",n=!0):r==="remove"&&a[t]&&(delete a[t],n=!0),n&&await va(o,JSON.stringify(i,null,2)+`
|
|
76
|
+
`,"utf8")}return n}async function zn(e,t){let r=Jn.join(e,"packages",t);if(!Nn(r))return!1;let{rm:n}=await import("fs/promises");return await n(r,{recursive:!0,force:!0}),!0}import{existsSync as yr,readFileSync as Ea}from"fs";import{readFile as Ra,writeFile as Da}from"fs/promises";import vr from"path";var le=[{path:"apps/server/src/modules.ts",kind:"server-modules"},{path:"apps/client/src/modules.ts",kind:"client-modules"}];function ka(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 Tt(e,t){return ka(t)}function Ot(e,t){return At[e].contributes(t)}function Mt(e){return e.replace(/^@[^/]+\//,"").replace(/\/(server|client)$/,"").replace(/\/$/,"")}var At={"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}},gr=/^import\s*\{\s*([\w$]+)\s+as\s+([\w$]+)\s*\}\s*from\s*['"]([^'"]+)['"];\s*$/,hr=/^\]\s*(?:as\s+const)?;\s*$/;function It(e,t){let r=`// @tbox:${t}-begin`,n=`// @tbox:${t}-end`,s=e.indexOf(r);if(s===-1)return null;let o=e.indexOf(n,s);if(o===-1)return null;let i=s+r.length;return{block:e.slice(i,o),start:i,end:o}}function dr(e,t,r){return e.slice(0,t.start)+r+e.slice(t.end)}function Bn(e,t){let r=At[t],n=[],s=[],o=[],i=It(e,"imports");if(i)for(let p of i.block.split(/\r?\n/)){let u=p.match(gr);u?n.push(Mt(u[3])):p.trim()&&s.push(`imports: ${p.trim()}`)}else o.push("imports");let a=It(e,r.mapMarker);if(a){let p=a.block.split(/\r?\n/),u=p.findIndex(m=>m.trim().startsWith(r.header)),d=[...p].reverse().findIndex(m=>hr.test(m.trim())),f=d===-1?-1:p.length-1-d;if(u===-1||f<=u)o.push(r.mapMarker);else for(let m of p.slice(u+1,f))!m.match(r.entryPattern)&&m.trim()&&s.push(`${r.mapMarker}: ${m.trim()}`)}else o.push(r.mapMarker);let c=new Map;if(i)for(let p of i.block.split(/\r?\n/)){let u=p.match(gr);u&&c.set(u[2],Mt(u[3]))}let l=[];if(a){let p=a.block.split(/\r?\n/),u=p.findIndex(m=>m.trim().startsWith(r.header)),d=[...p].reverse().findIndex(m=>hr.test(m.trim())),f=d===-1?-1:p.length-1-d;if(u!==-1&&f>u)for(let m of p.slice(u+1,f)){let h=m.match(r.entryPattern);if(h){let v=c.get(h[1]);v&&l.push(v)}}}return{moduleIds:[...new Set([...n,...l])],nonStandardLines:s,broken:o}}function Pt(e,t,r,n=!1){let s=At[t],o=[],i=r.filter(s.contributes),a=new Map(i.map(w=>[Tt(t,w.id),w])),c=e,l=!1,p=It(c,"imports");if(!p)return o.push(`[${t}] imports 锚区缺失/破坏,跳过`),{content:c,changed:l,warnings:o,moduleIds:[]};let u=p.block.split(/\r?\n/),d=[];for(let w of u){let O=w.match(gr);if(!O){if(w.trim()==="")continue;n||o.push(`[${t}] imports 锚区非标行保留: ${w.trim()}`),n||d.push(w);continue}let L=O[2],S=O[3],$=a.get(L);$&&Mt($.pkg)===Mt(S)?d.push(w):($&&d.push(Vn(t,L,$.pkg)),l=!0)}for(let w of i){let O=Tt(t,w.id);d.some(L=>L.includes(`as ${O} } from`))||(d.push(Vn(t,O,w.pkg)),l=!0)}let f=fr(d.join(`
|
|
77
|
+
`));f!==p.block&&(c=dr(c,p,f),l=!0);let m=It(c,s.mapMarker);if(!m)return o.push(`[${t}] ${s.mapMarker} 锚区缺失/破坏,跳过`),{content:c,changed:l,warnings:o,moduleIds:[]};let h=m.block.split(/\r?\n/),v=h.findIndex(w=>w.trim().startsWith(s.header)),g=[...h].reverse().findIndex(w=>hr.test(w.trim())),b=g===-1?-1:h.length-1-g;if(v===-1||b<=v){if(n){let w=fr([s.header,...i.map(O=>mr(t,Tt(t,O.id))),s.footer].join(`
|
|
78
|
+
`));return w!==m.block&&(c=dr(c,m,w),l=!0),{content:c,changed:l,warnings:o,moduleIds:i.map(O=>O.id)}}return o.push(`[${t}] ${s.mapMarker} 锚区结构不符合预期,降级为告警不覆盖`),{content:c,changed:l,warnings:o,moduleIds:[]}}let x=h.slice(0,v),j=h.slice(b+1),D=h.slice(v+1,b),R=[];for(let w of D){let O=w.match(s.entryPattern);if(!O){if(w.trim()==="")continue;n||o.push(`[${t}] ${s.mapMarker} 锚区非标行保留: ${w.trim()}`),n||R.push(w);continue}let L=O[1];a.has(L)?R.push(mr(t,L)):l=!0}for(let w of i){let O=Tt(t,w.id);R.some(L=>L.includes(O))||(R.push(mr(t,O)),l=!0)}let A=h[b],E=fr([...x,s.header,...R,A,...j].join(`
|
|
79
|
+
`));return E!==m.block&&(c=dr(c,m,E),l=!0),{content:c,changed:l,warnings:o,moduleIds:i.map(w=>w.id)}}function fr(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`
|
|
86
80
|
`+t.join(`
|
|
87
81
|
`)+`
|
|
88
|
-
`}function
|
|
89
|
-
`,"utf8"),{merged:o,conflicts:a}}function Mn(e,t){let r=In(e),n=In(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 In(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 fa(e){try{return da(e,"utf8")}catch{return}}async function me(e,t={}){let r=await ue(e),n=[],s=[],i=[];for(let c of se){let l=cr.join(e,c.path);if(!ar(l)){n.push(`缺少装配文件 ${c.path}`);continue}let p=await ga(l,"utf8"),u=Rt(p,c.kind,r,t.force??!1);n.push(...u.warnings.map(d=>`${c.path}: ${d}`)),u.changed&&(await ya(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=cr.join(e,"packages",c.id,"package.json");if(!ar(l))continue;let p={};try{p=JSON.parse(ma(l,"utf8")).dependencies??{}}catch{continue}for(let u of o){if(!ar(cr.join(e,u)))continue;let d=await An(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 ha}from"fs";import{readFile as va}from"fs/promises";import wa from"path";async function G(e){let t={};for(let r of se){let n=wa.join(e,r.path);if(!ha(n)){t[r.kind]={moduleIds:[],nonStandardLines:[],broken:["missing-file"]};continue}let s=await va(n,"utf8"),i=Tn(s,r.kind);t[r.kind]={moduleIds:i.moduleIds,nonStandardLines:i.nonStandardLines,broken:i.broken}}return t}import{existsSync as Pn}from"fs";import{readdir as $a}from"fs/promises";import lr from"path";import{readFileSync as ba}from"fs";import{readFile as ka,readdir as xa}from"fs/promises";import Dt from"path";async function $e(e,t,r){let n=await xa(e,{withFileTypes:!0});for(let s of n){if(s.name==="node_modules"||s.name==="dist"||s.name===".git")continue;let i=Dt.join(e,s.name);s.isDirectory()?await $e(i,t,r):s.isFile()&&/\.(ts|tsx|mjs|js)$/.test(s.name)&&r.push(Dt.relative(t,i).split(Dt.sep).join("/"))}}async function L(e){try{return await ka(e,"utf8")}catch{return""}}function Sa(e){try{return JSON.parse(ba(e,"utf8"))}catch{return null}}function Z(e){return Sa(Dt.join(e,"package.json"))}function Ct(e,t){let r=On(e),n=On(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 On(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 Ln(e){let t=[],r=lr.join(e.appDir,"packages");if(!Pn(r))return t;for(let n of await $a(r,{withFileTypes:!0})){if(!n.isDirectory())continue;let s=lr.join(r,n.name);if(!Pn(lr.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 Jn}from"fs";import{readdir as Ea}from"fs/promises";import zn from"path";import C from"typescript";function oe(e){return C.createSourceFile("entry.ts",e,C.ScriptTarget.Latest,!0,C.ScriptKind.TS)}function Fn(e){return e&&(C.isAsExpression(e)||C.isSatisfiesExpression(e)?e.expression:e)}function ja(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=Fn(o.initializer);if(a&&C.isArrowFunction(a)&&a.body&&C.isBlock(a.body)){n=a.body;return}let c=_n(a,"register");if(c){n=c;return}}C.forEachChild(i,s)}}return s(r),n}function _n(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 He(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=_n(Fn(o.initializer),"register");if(a){n=a;return}}C.forEachChild(i,s)}}return s(r),n??ja(e,"registerServer")}function Nn(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 pr(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 Vn(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 ur(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 Un(e){let t=[],r=zn.join(e.appDir,"packages");if(!Jn(r))return t;for(let n of await Ea(r,{withFileTypes:!0})){if(!n.isDirectory())continue;let s=zn.join(r,n.name,"src/server/index.ts");if(!Jn(s))continue;let i=await L(s),o=He(i,"serverModule");o&&Nn(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 Mt(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 Ra(e){let t=Mt(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 Wn(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 Hn(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=Wn(r,s.replace(/^\.\//,""));return i?{resolved:i}:null}return null}async function Da(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=ur(r),s=ye.join(e,"src/server"),i=[],o={},a=[],c=Mt(r,"serverModule");if(c){let l=!1;if(Object.keys(c).length>0){let p=He(r,"serverModule");l=!p||!pr(p)}for(let[p,u]of Object.entries(c)){let d=Hn(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 Vn(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=Wn(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 Ca(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 Ta(e){let t=ye.join(e,"src/client/index.ts");if(!Ge(t))return!1;let r=await L(t);if(!Ca(r,"clientModule"))return!1;let n=Mt(r,"clientModule");if(!n||Object.keys(n).length===0)return!1;let s=He(r,"clientModule");return!s||!pr(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)?Ra(await L(i)):[],a=await Da(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 Ta(n.dir)})}return r}function Tt(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?Tt(e.operand):E.isArrayLiteralExpression(e)?e.elements.every(t=>E.isOmittedExpression(t)||Tt(t)):E.isObjectLiteralExpression(e)?e.properties.every(t=>E.isPropertyAssignment(t)&&Tt(t.initializer)):!1}function Bn(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":Tt(r)?"ok":"non-literal":"non-literal"}return"missing"}function Ma(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 Kn(e){let t=ye.join(e,"src/server/index.ts");if(!Ge(t))return[];let r=await L(t),n=ur(r),s=ye.join(e,"src/server"),i=Mt(r,"serverModule");if(!i)return[];let o=[];for(let[a,c]of Object.entries(i)){let l=Hn(c,n,s);if(l){if(l.resolved){let p=await L(l.resolved),u=Ke(p).cardType??a,d=Ma(p);o.push({cardType:u,status:d?Bn(d):"missing",source:l.resolved})}else if(l.cardType){let p=ge(c),u=E.isObjectLiteralExpression(p)?Bn(p):"missing";o.push({cardType:l.cardType,status:u,source:"inline"})}}}return o}async function Gn(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 qn(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 dr}from"fs";import{readdir as fr,stat as Ia}from"fs/promises";import Me from"path";async function Xn(e){let t=[],r=Me.join(e.appDir,"packages");if(!dr(r))return t;let n=new Map;for(let i of await fr(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(dr(s))for(let i of await fr(s,{withFileTypes:!0})){let o=Me.join(s,i.name),a=i.isDirectory();if(!a&&i.isSymbolicLink())try{a=(await Ia(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 fr(r,{withFileTypes:!0})){if(!i.isDirectory())continue;let o=Me.join(r,i.name);if(!dr(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;Ct(p,u)||t.push({rule:"contract-version",level:"error",message:`${i.name}: 契约 ${l} 范围 ${p} 与实装 ${u} 无交集`})}}return t}import{existsSync as Aa}from"fs";import{readdir as Oa}from"fs/promises";import Pa from"path";async function Yn(e){let t=[],r=new Set,n=Pa.join(e.appDir,"packages");if(Aa(n))for(let f of await Oa(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 mr}from"fs";import{readdir as La}from"fs/promises";import{createRequire as Fa}from"module";import{realpathSync as _a}from"fs";import Ie from"path";async function Zn(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(mr(s))for(let i of await La(s,{withFileTypes:!0}))i.isDirectory()&&mr(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(!mr(Ie.join(c,"package.json")))continue;let l=Na(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 Na(e,t){try{let r=Fa(Ie.join(e,"__noop__.js"));return _a(r.resolve(t))}catch{return null}}import{existsSync as Qn}from"fs";import gr from"path";var Va=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"]),Ja=[{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 es(e){let t=[],r=["apps","packages"];for(let n of r){let s=gr.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(gr.join(e.appDir,o));for(let c of a.matchAll(/from\s+['"]([^'"]+)['"]/g)){let l=c[1];if(l.startsWith("@tbox.cn/app-sdk/")&&!Va.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(za(e.appDir,o))for(let p of Ja)p.re.test(l)&&t.push({rule:"sdk-internal-import",level:"error",message:`${o}: 模块不允许直接 import ${p.label}`})}}}return t}function za(e,t){let r=t.match(/^packages\/([^/]+)\//);return r?Qn(gr.join(e,"packages",r[1],"tbox.module.json")):!1}function ts(e){let t=[];for(let r of se){let n=new Set(e.declared.filter(c=>jt(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 rs(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 Ua}from"fs";import ns from"path";var Ba=["/webhook","/upload","/demo","/run"];async function ss(e){let t=[];for(let r of await z(e.appDir)){let n=ns.join(r.dir,"src/server");if(!Ua(n))continue;let s=[];await $e(n,r.dir,s);for(let i of s){let o=await L(ns.join(r.dir,i));for(let a of o.matchAll(/\.(?:post|put|patch|delete)\(\s*['"]([^'"]+)['"]/g)){let c=a[1];if(!c.startsWith("/"))continue;Ba.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 os}from"fs";import{readdir as Wa}from"fs/promises";import is from"path";async function as(e){let t=[],r=is.join(e.appDir,"packages");if(!os(r))return t;for(let n of await Wa(r,{withFileTypes:!0})){if(!n.isDirectory())continue;let s=is.join(r,n.name,"tbox.module.json");if(os(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 cs}from"fs";import{readdir as Ha}from"fs/promises";import Xe from"path";async function ls(e){let t=[];if(cs(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(cs(s))for(let i of await Ha(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 Ka}from"fs";import Ga from"path";var qa=["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 ps(e){let t=[];for(let r of qa)Ka(Ga.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 us,readFileSync as Xa}from"fs";import{readdir as Ya}from"fs/promises";import{createRequire as Za}from"module";import Ye from"path";function ds(e,t){try{let n=Za(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(us(r))return Xa(r,"utf8");return""}function ec(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 fs(e){let t=[],r=new Map;for(let n of e.declared){if(!n.hasServerEntry)continue;let s=ds(e,n.pkg);if(!s)continue;let i=ec(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 ms(e){let t=[],r=new Map;for(let n of e.declared){if(!n.hasServerEntry)continue;let s=ds(e,n.pkg);if(!s)continue;let i=Ye.join(s,"skills");if(us(i))try{let o=(await Ya(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 gs(e){let t=[];for(let r of await z(e.appDir))for(let n of await Kn(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 ys}from"fs";import{readdir as hs}from"fs/promises";import It from"path";async function vs(e){let t=[],r=It.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=It.join(r,n.name,"src");if(!ys(s))continue;let i=[];await ws(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 ws(e,t,r){for(let n of await hs(e,{withFileTypes:!0})){let s=It.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=It.relative(t,s);i.split(`
|
|
90
|
-
`).forEach((c,l)=>{/console\.(log|warn|error|info|debug)\s*\(/.test(c)&&r.push(`${
|
|
91
|
-
`).forEach((c,l)=>{
|
|
92
|
-
`).forEach((c,l)=>{/ctx\.auth\.(getContext|setContext|requireIdentity|isAnonymous)\s*\(/.test(c)&&r.push(`${
|
|
93
|
-
`),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 Ns(e,t=""){let r=[];for(let n of await Fs(e,{withFileTypes:!0})){let s=hr.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 Ns(s,i));continue}/\.tsx?$/.test(n.name)&&(/\.test\.tsx?$/.test(n.name)||r.push({rel:i,text:await L(s)}))}return r}var ac=[{name:"workspace-dag",run:Ln},{name:"register-pure",run:Un},{name:"card-type-consistent",run:Gn},{name:"client-card-cover",run:qn},{name:"contract-version",run:Xn},{name:"module-deps-assembled",run:Yn},{name:"singleton",run:Zn},{name:"sdk-internal-import",run:es},{name:"assembly-drift",run:ts},{name:"schema-version",run:rs},{name:"naked-write-route",run:ss},{name:"descriptor-schema",run:as},{name:"workspace-ref-resolved",run:ls},{name:"legacy-platform-files",run:ps},{name:"intent-overlap",run:fs},{name:"skill-name-conflict",run:ms},{name:"sample-data-valid",run:gs},{name:"module-console-logger",run:vs},{name:"module-mall-scope",run:xs},{name:"integration-services",run:Rs},{name:"codegen-ref-resolved",run:Ds},{name:"module-auth-context",run:Ms},{name:"card-face-labels",run:Ps},{name:"module-internal-tool",run:_s}];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 ac)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 Vs(e,t){return!t.has(e)||e.startsWith("contracts-")}async function Js(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))&&(Vs(k,i)?w.add(k):Vs(k,i)||D.add(k)),b.range&&a.has(k)){let O=a.get(k);b.range!=="*"&&!b.range.startsWith("workspace:")&&(Ct(b.range,O)||M.push(`${k}: 依赖范围 ${b.range} 与目标版本 ${O} 无交集`))}}return{order:P,contracts:[...w],business:[...D],cycles:h,conflicts:M}}function lc(e){let t=Q.join(e,"tbox.module.json");if(!ve(t))return null;try{let r=Ee(JSON.parse(wr(t,"utf8")));return r.ok?r.value:null}catch{return null}}function pc(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=cc(r).find(i=>i.endsWith(".tgz")&&i.includes(`-${t}-`));if(s)return Q.join(r,s)}catch{}return null}function vr(e,t){let r=Q.join(e,"packages",t);return ve(r)&&!ht(r)?!0:B(e)?.npmModules.some(s=>s.id===t&&s.mode==="sdk")??!1}async function zs(e){await Us(e,new Set,!1)}async function Us(e,t,r){let n=[];try{await uc(e,t,r,n)}finally{for(let s of n)s.dispose()}}async function uc(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=Sn(v),ke(i,`--mode ${e.mode} ${e.pkg}`),o=lc(a.sourceDir)}if(r&&vr(s,i)){console.log(` ⚠️ 依赖 ${i} 已安装,跳过`);return}if(!r&&vr(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 Js(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&&!vr(s,$));for(let $ of[...new Set(S)]){let R=/^(module|scenario|contracts|provider)-/.test($)?`@tbox.cn/app-${$}`:`@tbox.cn/${$}`,F=c?R:pc(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=gn(R,N);await Us({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 jn(s,h),l={id:v,package:`@app/${v}`,version:"0.1.0",mode:"local"},await We(s,`@app/${v}`,"add")}else{let h=a.sourceDir,v=a.source.resolveVersion();e.mode==="codegen"&&(vt(h),await $n(s,h,{id:i,package:e.pkg.replace(/@[^/]+$/,""),version:v,mode:"codegen",baseVersion:v},Se(s).values()),await We(s,yt(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,Yr(p,l)),l.mode==="codegen"){let h=await bt(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?", 依赖":""})`),dc(s,l.id),e.mode!=="local"&&console.log(" 下一步: pnpm install && tbox-app module doctor")}function dc(e,t){try{let r=Q.join(e,"packages",t,"package.json");if(!ve(r))return;let s=JSON.parse(wr(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=wr(o,"utf8").match(/['"]?@tbox\.cn\/app-contracts-mall['"]?\s*:\s*['"]([^'"]+)['"]/)?.[1]?.replace(/[\^~]/,"");if(!l)return;fc(i,l)>0&&console.warn(` ⚠️ 版本配套:${t} 依赖 contracts-mall ${i}+,当前应用 catalog 为 ${l}——请升级模板或应用 catalog(015 W1)`)}catch{}}function fc(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 mc from"path";async function Bs(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,Zr(n,e));let o=mc.join(r,"packages",e),c=ht(o)&&(s?.mode==="sdk"||!s);(c?!1:await Dn(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 bn(r,xe(s)):!1;p&&console.log(" 已恢复 contracts-mall SDK shim(@app/contracts-mall)"),!p&&!c&&(s?.mode==="codegen"||s?.mode==="local"||!i)&&await We(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 Ws(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 Hs(){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 Ks(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=>jt(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 Gs(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 kr}from"fs";import{readdir as yc,readFile as Zs,unlink as hc,writeFile as vc}from"fs/promises";import Ae from"path";import{diffArrays as gc}from"diff";function qs(e,t){let r=gc(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 Xs(e,t,r){let n=e===""?[]:e.split(`
|
|
82
|
+
`}function Vn(e,t,r){let n=At[e];return`import { ${n.importExport} as ${t} } from '${r}/${n.importSuffix}';`}function mr(e,t){return` ${t},`}import{writeFile as ba}from"fs/promises";import xa from"path";import{readFileSync as $a}from"fs";function Sa(e){try{return JSON.parse(ja(e)??"{}")}catch{return null}}async function Hn(e,t,r){let n=xa.join(e,t),s=Sa(n)??{},o=s.dependencies??{},i={},a=[];for(let[c,l]of Object.entries(r)){if(vt(c)||l==="workspace:*"||l.startsWith("catalog:"))continue;let p=o[c];if(!p){o[c]=l,i[c]=l;continue}if(p===l)continue;if(p==="catalog:"){let d=te(e)?.get(c);d&&!Wn(d,l)&&a.push(`${c}: 应用 catalog ${d} 与模块要求 ${l} 无交集`);continue}let u=Wn(p,l);u?u!==p&&(o[c]=u,i[c]=u):a.push(`${c}: 已有 ${p},模块要求 ${l}`)}return s.dependencies=o,await ba(n,JSON.stringify(s,null,2)+`
|
|
83
|
+
`,"utf8"),{merged:i,conflicts:a}}function Wn(e,t){let r=Un(e),n=Un(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,o=s(r.lower,n.lower)>=0?r.lower:n.lower,i=s(r.upper,n.upper)<=0?r.upper:n.upper;return s(o,i)>=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 Un(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 ja(e){try{return $a(e,"utf8")}catch{return}}async function ye(e,t={}){let r=await ge(e),n=[],s=[],o=[];for(let c of le){let l=vr.join(e,c.path);if(!yr(l)){n.push(`缺少装配文件 ${c.path}`);continue}let p=await Ra(l,"utf8"),u=Pt(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 i=["apps/server/package.json","apps/client/package.json"];for(let c of r){let l=vr.join(e,"packages",c.id,"package.json");if(!yr(l))continue;let p={};try{p=JSON.parse(Ea(l,"utf8")).dependencies??{}}catch{continue}for(let u of i){if(!yr(vr.join(e,u)))continue;let d=await Hn(e,u,p);o.push(...d.conflicts.map(f=>`${c.id} → ${u}: ${f}`))}}let a=he(e);return{declared:r,changedFiles:s,warnings:n,depConflicts:o,lockfileRefreshed:a}}import{existsSync as Ca}from"fs";import{readFile as Ta}from"fs/promises";import Ma from"path";async function Y(e){let t={};for(let r of le){let n=Ma.join(e,r.path);if(!Ca(n)){t[r.kind]={moduleIds:[],nonStandardLines:[],broken:["missing-file"]};continue}let s=await Ta(n,"utf8"),o=Bn(s,r.kind);t[r.kind]={moduleIds:o.moduleIds,nonStandardLines:o.nonStandardLines,broken:o.broken}}return t}import{existsSync as Gn}from"fs";import{readdir as La}from"fs/promises";import wr from"path";import{readFileSync as Ia}from"fs";import{readFile as Oa,readdir as Aa}from"fs/promises";import Lt from"path";async function De(e,t,r){let n=await Aa(e,{withFileTypes:!0});for(let s of n){if(s.name==="node_modules"||s.name==="dist"||s.name===".git")continue;let o=Lt.join(e,s.name);s.isDirectory()?await De(o,t,r):s.isFile()&&/\.(ts|tsx|mjs|js)$/.test(s.name)&&r.push(Lt.relative(t,o).split(Lt.sep).join("/"))}}async function F(e){try{return await Oa(e,"utf8")}catch{return""}}function Pa(e){try{return JSON.parse(Ia(e,"utf8"))}catch{return null}}function re(e){return Pa(Lt.join(e,"package.json"))}function _t(e,t){let r=Kn(e),n=Kn(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,o=s(r.lower,n.lower)>=0?r.lower:n.lower,i=s(r.upper,n.upper)<=0?r.upper:n.upper;return s(o,i)>=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 Kn(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 qn(e){let t=[],r=wr.join(e.appDir,"packages");if(!Gn(r))return t;for(let n of await La(r,{withFileTypes:!0})){if(!n.isDirectory())continue;let s=wr.join(r,n.name);if(!Gn(wr.join(s,"tbox.module.json")))continue;let o=/^provider-/.test(n.name),i=re(s),a={...i?.dependencies??{},...i?.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)`}),o&&(/^@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 es}from"fs";import{readdir as Fa}from"fs/promises";import ts from"path";import P from"typescript";function pe(e){return P.createSourceFile("entry.ts",e,P.ScriptTarget.Latest,!0,P.ScriptKind.TS)}function Yn(e){return e&&(P.isAsExpression(e)||P.isSatisfiesExpression(e)?e.expression:e)}function _a(e,t){let r=pe(e),n=null;function s(o){if(!n){if(P.isFunctionDeclaration(o)&&o.name?.text===t&&o.body){n=o.body;return}if(P.isVariableStatement(o)&&o.declarationList.declarations.some(i=>P.isIdentifier(i.name)&&i.name.text===t)){let i=o.declarationList.declarations.find(l=>P.isIdentifier(l.name)&&l.name.text===t),a=Yn(i.initializer);if(a&&P.isArrowFunction(a)&&a.body&&P.isBlock(a.body)){n=a.body;return}let c=Xn(a,"register");if(c){n=c;return}}P.forEachChild(o,s)}}return s(r),n}function Xn(e,t){if(!e||!P.isObjectLiteralExpression(e))return null;for(let r of e.properties){if(P.isMethodDeclaration(r)&&r.name.getText()===t&&r.body)return r.body;if(P.isPropertyAssignment(r)&&r.name.getText()===t&&r.initializer){let n=r.initializer;if(P.isArrowFunction(n)&&n.body&&P.isBlock(n.body)||P.isFunctionExpression(n)&&n.body)return n.body}}return null}function Qe(e,t){let r=pe(e),n=null;function s(o){if(!n){if(P.isVariableStatement(o)&&o.declarationList.declarations.some(i=>P.isIdentifier(i.name)&&i.name.text===t)){let i=o.declarationList.declarations.find(c=>P.isIdentifier(c.name)&&c.name.text===t),a=Xn(Yn(i.initializer),"register");if(a){n=a;return}}P.forEachChild(o,s)}}return s(r),n??_a(e,"registerServer")}function Zn(e){let t=!1;function r(n){if(!t&&!P.isArrowFunction(n)){if(P.isCallExpression(n)){let s=n.expression;if(P.isIdentifier(s)&&s.text==="resolveService"){t=!0;return}let o=s.getText();if(o==="ctx.resolveService"||o.endsWith(".services.resolve")){t=!0;return}}P.forEachChild(n,r)}}return r(e),t}function kr(e){let t=!1;function r(n){if(!t){if(P.isCallExpression(n)){let s=n.expression.getText();if(s.endsWith(".cards.registerMap")||s.endsWith(".cards.register")){t=!0;return}}P.forEachChild(n,r)}}return r(e),t}function Qn(e){let t=pe(e),r=[];function n(s){if(P.isCallExpression(s)&&s.expression.getText().endsWith(".cards.register")){let i=s.arguments[0];i&&r.push(i.getText().replace(/['"]/g,""))}P.forEachChild(s,n)}return n(t),r}function br(e){let t=pe(e),r={};function n(s){if(P.isImportDeclaration(s)&&P.isStringLiteral(s.moduleSpecifier)){let o=s.moduleSpecifier.text;if(o.startsWith("./")||o.startsWith("../")){let i=s.importClause;i?.name&&(r[i.name.text]=o)}}P.forEachChild(s,n)}return n(t),r}async function rs(e){let t=[],r=ts.join(e.appDir,"packages");if(!es(r))return t;for(let n of await Fa(r,{withFileTypes:!0})){if(!n.isDirectory())continue;let s=ts.join(r,n.name,"src/server/index.ts");if(!es(s))continue;let o=await F(s),i=Qe(o,"serverModule");i&&Zn(i)&&t.push({rule:"register-pure",level:"error",message:`${n.name}: 注册入口(serverModule.register)内出现 resolveService(只允许在 handle/事件回调内)`})}return t}import{existsSync as tt}from"fs";import we from"path";import M from"typescript";function ve(e){return M.isAsExpression(e)||M.isSatisfiesExpression(e)?e.expression:e}function Nt(e,t){let r=pe(e),n=null;function s(o){let i={};for(let a of o.properties){if(!M.isPropertyAssignment(a))continue;let c=a.name,l=M.isIdentifier(c)||M.isStringLiteral(c)?c.text:"";l&&(i[l]=a.initializer)}return i}for(let o of r.statements)if(M.isVariableStatement(o)){for(let i of o.declarationList.declarations)if(!(!M.isIdentifier(i.name)||!i.initializer)){if(i.name.text==="cards"){let a=ve(i.initializer);if(M.isObjectLiteralExpression(a))return n=s(a),n}if(t&&i.name.text===t){let a=ve(i.initializer);if(M.isObjectLiteralExpression(a)){for(let c of a.properties)if(M.isPropertyAssignment(c)&&M.isIdentifier(c.name)&&c.name.text==="cards"){let l=ve(c.initializer);if(M.isObjectLiteralExpression(l))return n=s(l),n}}}}}return n}function Na(e){let t=Nt(e,"clientModule");return t?Object.keys(t):[]}function et(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 ss(e,t){let r=[t,`${t}.ts`,`${t}.tsx`,`${t}/index.ts`,`${t}/index.tsx`];for(let n of r)if(tt(we.join(e,n)))return we.join(e,n);return null}function os(e,t,r){let n=ve(e);if(M.isObjectLiteralExpression(n)){let s=et(n.getText());return{cardType:s.cardType,schemaVersion:s.schemaVersion}}if(M.isStringLiteral(n))return{cardType:n.text};if(M.isIdentifier(n)){let s=t[n.text];if(!s)return null;let o=ss(r,s.replace(/^\.\//,""));return o?{resolved:o}:null}return null}async function Ja(e){let t=we.join(e,"src/server/index.ts");if(!tt(t))return{cardTypes:[],schemaVersions:{},keyMismatches:[],registerMissing:!1};let r=await F(t),n=br(r),s=we.join(e,"src/server"),o=[],i={},a=[],c=Nt(r,"serverModule");if(c){let l=!1;if(Object.keys(c).length>0){let p=Qe(r,"serverModule");l=!p||!kr(p)}for(let[p,u]of Object.entries(c)){let d=os(u,n,s);if(d)if(d.resolved){let f=await F(d.resolved),m=et(f);m.cardType&&(o.push(m.cardType),m.schemaVersion&&(i[m.cardType]=m.schemaVersion),p!==m.cardType&&a.push({key:p,cardType:m.cardType}))}else d.cardType&&(o.push(d.cardType),d.schemaVersion&&(i[d.cardType]=d.schemaVersion),p!==d.cardType&&a.push({key:p,cardType:d.cardType}))}return{cardTypes:[...new Set(o)],schemaVersions:i,keyMismatches:a,registerMissing:l}}for(let l of Qn(r)){if(l.includes("cardType:")){let m=et(l);m.cardType&&(o.push(m.cardType),m.schemaVersion&&(i[m.cardType]=m.schemaVersion));continue}if(/^[a-zA-Z0-9][\w-]*$/.test(l)&&!n[l]){o.push(l);continue}let p=n[l];if(!p)continue;let u=ss(s,p.replace(/^\.\//,""));if(!u)continue;let d=await F(u),f=et(d);f.cardType&&(o.push(f.cardType),f.schemaVersion&&(i[f.cardType]=f.schemaVersion))}return{cardTypes:[...new Set(o)],schemaVersions:i,keyMismatches:a,registerMissing:!1}}function za(e,t){return pe(e).statements.some(n=>M.isVariableStatement(n)&&n.declarationList.declarations.some(s=>M.isIdentifier(s.name)&&s.name.text===t))}async function Va(e){let t=we.join(e,"src/client/index.ts");if(!tt(t))return!1;let r=await F(t);if(!za(r,"clientModule"))return!1;let n=Nt(r,"clientModule");if(!n||Object.keys(n).length===0)return!1;let s=Qe(r,"clientModule");return!s||!kr(s)}async function rt(e){let t=await W(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 o=we.join(n.dir,"src/client/index.ts"),i=tt(o)?Na(await F(o)):[],a=await Ja(n.dir);r.push({id:n.id,declared:s,clientMap:i,serverRegistered:a.cardTypes,serverSchemaVersions:a.schemaVersions,serverKeyMismatches:a.keyMismatches,serverRegisterMissing:a.registerMissing,clientRegisterMissing:await Va(n.dir)})}return r}function Ft(e){return M.isStringLiteral(e)||M.isNumericLiteral(e)||e.kind===M.SyntaxKind.NullKeyword||e.kind===M.SyntaxKind.TrueKeyword||e.kind===M.SyntaxKind.FalseKeyword?!0:M.isPrefixUnaryExpression(e)&&e.operator===M.SyntaxKind.MinusToken?Ft(e.operand):M.isArrayLiteralExpression(e)?e.elements.every(t=>M.isOmittedExpression(t)||Ft(t)):M.isObjectLiteralExpression(e)?e.properties.every(t=>M.isPropertyAssignment(t)&&Ft(t.initializer)):!1}function ns(e){for(let t of e.properties)if(M.isPropertyAssignment(t)&&M.isIdentifier(t.name)&&t.name.text==="sampleData"){let r=ve(t.initializer);return M.isObjectLiteralExpression(r)?r.properties.length===0?"empty":Ft(r)?"ok":"non-literal":"non-literal"}return"missing"}function Ba(e){let t=pe(e),r={};for(let n of t.statements){if(M.isVariableStatement(n)){for(let s of n.declarationList.declarations)if(M.isIdentifier(s.name)&&s.initializer){let o=ve(s.initializer);M.isObjectLiteralExpression(o)&&(r[s.name.text]=o)}}if(M.isExportAssignment(n)&&!n.isExportEquals){let s=ve(n.expression);if(M.isObjectLiteralExpression(s))return s;if(M.isIdentifier(s)&&r[s.text])return r[s.text]}}return null}async function is(e){let t=we.join(e,"src/server/index.ts");if(!tt(t))return[];let r=await F(t),n=br(r),s=we.join(e,"src/server"),o=Nt(r,"serverModule");if(!o)return[];let i=[];for(let[a,c]of Object.entries(o)){let l=os(c,n,s);if(l){if(l.resolved){let p=await F(l.resolved),u=et(p).cardType??a,d=Ba(p);i.push({cardType:u,status:d?ns(d):"missing",source:l.resolved})}else if(l.cardType){let p=ve(c),u=M.isObjectLiteralExpression(p)?ns(p):"missing";i.push({cardType:l.cardType,status:u,source:"inline"})}}}return i}async function as(e){let t=[];for(let r of await rt(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 o=n.filter(c=>!r.serverRegistered.includes(c));o.length>0&&t.push({rule:"card-type-consistent",level:"error",message:`${r.id}: 声明卡片 ${o.join(",")} 未在服务端注册(serverModule.cards meta / cards.register)`});let i=r.clientMap.filter(c=>!n.includes(c));i.length>0&&t.push({rule:"card-type-consistent",level:"warning",message:`${r.id}: 客户端导出未声明的卡片 ${i.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 cs(e){let t=[],r=await rt(e),n={};for(let o of r)for(let i of o.serverRegistered)i in n?n[i]+=`,${o.id}`:n[i]=o.id;if(Object.keys(n).length===0)return t;let s=new Set;for(let o of r)for(let i of o.clientMap)s.add(i);for(let[o,i]of Object.entries(n))s.has(o)||t.push({rule:"client-card-cover",level:"error",message:`服务端注册卡片 ${o}(模块 ${i})未出现在应用客户端装配(漏装配)`});return t}import{existsSync as xr}from"fs";import{readdir as Sr,stat as Wa}from"fs/promises";import Pe from"path";async function ls(e){let t=[],r=Pe.join(e.appDir,"packages");if(!xr(r))return t;let n=new Map;for(let o of await Sr(r,{withFileTypes:!0})){if(!o.isDirectory())continue;let i=re(Pe.join(r,o.name));i?.name&&i.name.startsWith("@tbox.cn/app-contracts")&&n.set(i.name,i.version??"0.0.0")}let s=Pe.join(e.appDir,"node_modules","@tbox.cn");if(xr(s))for(let o of await Sr(s,{withFileTypes:!0})){let i=Pe.join(s,o.name),a=o.isDirectory();if(!a&&o.isSymbolicLink())try{a=(await Wa(i)).isDirectory()}catch{a=!1}if(!a)continue;let c=re(i);c?.name&&c.name.startsWith("@tbox.cn/app-contracts")&&n.set(c.name,c.version??"0.0.0")}for(let o of await Sr(r,{withFileTypes:!0})){if(!o.isDirectory())continue;let i=Pe.join(r,o.name);if(!xr(Pe.join(i,"tbox.module.json")))continue;let a=re(i),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;_t(p,u)||t.push({rule:"contract-version",level:"error",message:`${o.name}: 契约 ${l} 范围 ${p} 与实装 ${u} 无交集`})}}return t}import{existsSync as Ua}from"fs";import{readdir as Ha}from"fs/promises";import Ka from"path";async function ps(e){let t=[],r=new Set,n=Ka.join(e.appDir,"packages");if(Ua(n))for(let f of await Ha(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 le)for(let m of e.actual[f.kind]?.moduleIds??[])s.add(m);let o=new Map,i=await W(e.appDir);for(let f of i){let m=(f.descriptor.dependencies?.modules??[]).map(h=>h.id);o.set(f.id,m)}let a=0,c=1,l=2,p=new Map,u=[];function d(f,m){p.set(f,c),m.push(f);for(let h of o.get(f)??[]){let v=p.get(h)??a;if(v===c){let g=m.indexOf(h);g>=0&&u.push([...m.slice(g),h])}else v===a&&d(h,m)}m.pop(),p.set(f,l)}for(let f of o.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 i)for(let m of f.descriptor.dependencies?.modules??[]){if(m.required&&!r.has(m.id)){t.push({rule:"module-deps-assembled",level:"error",message:`${f.id}: 依赖模块 ${m.id} 未安装(缺失 packages/${m.id} 或 npm 清单)`});continue}i.some(v=>v.id===m.id)&&m.required&&!s.has(m.id)&&t.push({rule:"module-deps-assembled",level:"error",message:`${f.id}: 依赖业务模块 ${m.id} 已安装但未装配(modules.ts 缺行,运行 sync)`})}return t}import{existsSync as $r}from"fs";import{readdir as Ga}from"fs/promises";import{createRequire as qa}from"module";import{realpathSync as Ya}from"fs";import Le from"path";async function us(e){let t=[],r=["react","zod","@mastra/core"],n=["apps/client","apps/server","packages/app-sdk","packages/contracts"],s=Le.join(e.appDir,"packages");if($r(s))for(let o of await Ga(s,{withFileTypes:!0}))o.isDirectory()&&$r(Le.join(s,o.name,"package.json"))&&n.push(Le.join("packages",o.name));for(let o of r){let i=new Map;for(let a of n){let c=Le.join(e.appDir,a);if(!$r(Le.join(c,"package.json")))continue;let l=Xa(c,o);if(l){let p=i.get(l)??[];p.push(a),i.set(l,p)}}i.size>1&&t.push({rule:"singleton",level:"error",message:`${o} 解析结果不唯一:${[...i.entries()].map(([a,c])=>`${a}(${c.join(",")})`).join(" vs ")}`})}return t}function Xa(e,t){try{let r=qa(Le.join(e,"__noop__.js"));return Ya(r.resolve(t))}catch{return null}}import{existsSync as ds}from"fs";import jr from"path";var Za=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"]),Qa=[{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 fs(e){let t=[],r=["apps","packages"];for(let n of r){let s=jr.join(e.appDir,n);if(!ds(s))continue;let o=[];await De(s,e.appDir,o);for(let i of o){if(i.startsWith("packages/app-sdk/")||i.startsWith("packages/contracts/"))continue;let a=await F(jr.join(e.appDir,i));for(let c of a.matchAll(/from\s+['"]([^'"]+)['"]/g)){let l=c[1];if(l.startsWith("@tbox.cn/app-sdk/")&&!Za.has(l))t.push({rule:"sdk-internal-import",level:"error",message:`${i}: 不允许 import SDK 内部路径 ${l}`});else if(l.startsWith("@tbox.cn/app-contracts/"))t.push({rule:"sdk-internal-import",level:"error",message:`${i}: 不允许 import 契约子路径 ${l}(仅 @tbox.cn/app-contracts)`});else if(ec(e.appDir,i))for(let p of Qa)p.re.test(l)&&t.push({rule:"sdk-internal-import",level:"error",message:`${i}: 模块不允许直接 import ${p.label}`})}}}return t}function ec(e,t){let r=t.match(/^packages\/([^/]+)\//);return r?ds(jr.join(e,"packages",r[1],"tbox.module.json")):!1}function ms(e){let t=[];for(let r of le){let n=new Set(e.declared.filter(c=>Ot(r.kind,c)).map(c=>c.id)),s=new Set(e.actual[r.kind]?.moduleIds??[]),o=[...n].filter(c=>!s.has(c)),i=[...s].filter(c=>!n.has(c));o.length>0&&t.push({rule:"assembly-drift",level:"warning",message:`${r.path}: 已声明未装配 → ${o.join(", ")}`}),i.length>0&&t.push({rule:"assembly-drift",level:"warning",message:`${r.path}: 已装配未声明 → ${i.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 gs(e){let t=[];for(let r of await rt(e))for(let[n,s]of Object.entries(r.declared)){let o=r.serverSchemaVersions[n]??1;o!==s&&t.push({rule:"schema-version",level:"error",message:`${r.id}: 卡片 ${n} contributes.schemaVersion=${s} 与 meta schemaVersion=${o} 不一致`})}return t}import{existsSync as tc}from"fs";import hs from"path";var rc=["/webhook","/upload","/demo","/run"];async function ys(e){let t=[];for(let r of await W(e.appDir)){let n=hs.join(r.dir,"src/server");if(!tc(n))continue;let s=[];await De(n,r.dir,s);for(let o of s){let i=await F(hs.join(r.dir,o));for(let a of i.matchAll(/\.(?:post|put|patch|delete)\(\s*['"]([^'"]+)['"]/g)){let c=a[1];if(!c.startsWith("/"))continue;rc.some(p=>c.startsWith(p))||t.push({rule:"naked-write-route",level:"warning",message:`${r.id}/${o}: 裸写路由 ${a[0].replace("("," (")} 建议改走 ctx.actions.registerAction(全局动作分发器)`})}}}return t}import{existsSync as vs}from"fs";import{readdir as nc}from"fs/promises";import ws from"path";async function ks(e){let t=[],r=ws.join(e.appDir,"packages");if(!vs(r))return t;for(let n of await nc(r,{withFileTypes:!0})){if(!n.isDirectory())continue;let s=ws.join(r,n.name,"tbox.module.json");if(vs(s))try{let o=Me(JSON.parse(await F(s)));o.ok||t.push({rule:"descriptor-schema",level:"error",message:`${n.name}/tbox.module.json: ${o.errors.join("; ")}`})}catch{t.push({rule:"descriptor-schema",level:"error",message:`${n.name}/tbox.module.json: JSON 解析失败`})}}return t}import{existsSync as bs}from"fs";import{readdir as sc}from"fs/promises";import nt from"path";async function xs(e){let t=[];if(bs(nt.join(e.appDir,"pnpm-workspace.template.yaml")))return t;let r=new Map;for(let n of["apps","packages"]){let s=nt.join(e.appDir,n);if(bs(s))for(let o of await sc(s,{withFileTypes:!0})){if(!o.isDirectory())continue;let i=nt.join(s,o.name),a=re(i);a?.name&&r.set(a.name,nt.join(n,o.name))}}if(r.size===0)return t;for(let[n,s]of r){let o=re(nt.join(e.appDir,s));if(o)for(let i of["dependencies","peerDependencies","devDependencies"]){let a=o[i];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 oc}from"fs";import ic from"path";var ac=["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 Ss(e){let t=[];for(let r of ac)oc(ic.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 $s,readFileSync as cc}from"fs";import{readdir as lc}from"fs/promises";import{createRequire as pc}from"module";import st from"path";function js(e,t){try{let n=pc(st.join(e.appDir,"package.json")).resolve(`${t}/package.json`);return st.dirname(n)}catch{return}}function uc(e){let t=[st.join(e,"src/server/index.ts"),st.join(e,"src/server/index.tsx")];for(let r of t)if($s(r))return cc(r,"utf8");return""}function dc(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 Es(e){let t=[],r=new Map;for(let n of e.declared){if(!n.hasServerEntry)continue;let s=js(e,n.pkg);if(!s)continue;let o=dc(uc(s));for(let i of o){let a=r.get(i)??[];a.push(n.id),r.set(i,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 Rs(e){let t=[],r=new Map;for(let n of e.declared){if(!n.hasServerEntry)continue;let s=js(e,n.pkg);if(!s)continue;let o=st.join(s,"skills");if($s(o))try{let i=(await lc(o,{withFileTypes:!0})).filter(a=>a.isDirectory()).map(a=>a.name);for(let a of i){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 Ds(e){let t=[];for(let r of await W(e.appDir))for(let n of await is(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 Cs}from"fs";import{readdir as Ts}from"fs/promises";import Jt from"path";async function Ms(e){let t=[],r=Jt.join(e.appDir,"packages");if(!Cs(r))return t;for(let n of await Ts(r,{withFileTypes:!0})){if(!n.isDirectory()||n.name.startsWith("contracts-"))continue;let s=Jt.join(r,n.name,"src");if(!Cs(s))continue;let o=[];await Is(s,s,o),o.length>0&&t.push({rule:"module-console-logger",level:"warning",message:`${n.name}: 源码含裸 console 调用 ${o.length} 处(${o.slice(0,3).join("、")}${o.length>3?"…":""})——建议改用 ctx.logger(见 docs/reference/modules.md 模块日志规范)`})}return t}async function Is(e,t,r){for(let n of await Ts(e,{withFileTypes:!0})){let s=Jt.join(e,n.name);if(n.isDirectory()){if(n.name==="__tests__"||n.name==="node_modules")continue;await Is(s,t,r);continue}if(!/\.tsx?$/.test(n.name)||/\.test\.tsx?$/.test(n.name))continue;let o=await F(s),i=Jt.relative(t,s);o.split(`
|
|
84
|
+
`).forEach((c,l)=>{/console\.(log|warn|error|info|debug)\s*\(/.test(c)&&r.push(`${i}:${l+1}`)})}}import{existsSync as Os}from"fs";import{readdir as As}from"fs/promises";import zt from"path";async function Ps(e){let t=[],r=zt.join(e.appDir,"packages");if(!Os(r))return t;for(let n of await As(r,{withFileTypes:!0})){if(!n.isDirectory()||n.name.startsWith("contracts-"))continue;let s=zt.join(r,n.name,"src");if(!Os(s))continue;let o=[];await Ls(s,s,o),o.length>0&&t.push({rule:"module-mall-scope",level:"warning",message:`${n.name}: 源码含手解 attributes.mall ${o.length} 处(${o.slice(0,3).join("、")}${o.length>3?"…":""})——改用 parseMallScope/extractMallScope/withMallScopeFromQuery(见 docs/reference/modules.md 读取入口族)`})}return t}var fc=/\.attributes\??\.?\bmall\b|\.attributes\??\.?\[\s*['"]mall['"]\s*\]/,mc=/as\s*\{[^}]*\bmall\b/,gc=/^\s*(\*|\/\/|\/\*)/;async function Ls(e,t,r){for(let n of await As(e,{withFileTypes:!0})){let s=zt.join(e,n.name);if(n.isDirectory()){if(n.name==="__tests__"||n.name==="node_modules")continue;await Ls(s,t,r);continue}if(!/\.tsx?$/.test(n.name)||/\.test\.tsx?$/.test(n.name))continue;let o=await F(s),i=zt.relative(t,s);o.split(`
|
|
85
|
+
`).forEach((c,l)=>{gc.test(c)||(fc.test(c)||mc.test(c))&&r.push(`${i}:${l+1}`)})}}import{existsSync as _s,readFileSync as Fs}from"fs";import Ns from"path";function hc(e,t=process.env,r="<config>"){let n=en(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 Js(e){let t=Ns.join(e.appDir,"config","deployments","development.json");if(_s(t))try{let h=JSON.parse(Fs(t,"utf8"));if(h&&typeof h=="object"&&"malls"in h)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=Ns.join(e.appDir,"config","integrations.json");if(!_s(r))return[];let n=await ut(e.appDir);if(!n.strictValidation){let h=n.version===null;return[{rule:"integration-services",level:h?"warning":"error",message:h?"integrations: contracts 未安装——pnpm install 后重试(安装后启用严格校验与求值诊断)":`integrations: contracts 严格校验面不可用(${n.version},需 ≥0.9)——${n.notes[0]??"升级应用 @tbox.cn/app-contracts 依赖后可用"}`}]}let{resolveEffectiveService:s,buildSupplyLookup:o,moduleDomainOf:i,normalizeInstances:a,RESOURCE_TYPE_IDS:c}=n.module,l;try{l=JSON.parse(hc(Fs(r,"utf8"),process.env,r))}catch(h){return[{rule:"integration-services",level:"error",message:`config/integrations.json 非法 JSON:${h.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=un(e.appDir),d=o(dn(e.appDir)),f=rn(e.appDir,u,l,process.env);return tn({contracts:{resolveEffectiveService:s,buildSupplyLookup:o,moduleDomainOf:i,normalizeInstances:a,RESOURCE_TYPE_IDS:c},integrations:l,supplies:d,...f}).map(h=>({rule:"integration-services",level:h.severity,message:h.message,...h.code!==void 0?{code:h.code}:{},...h.service!==void 0?{service:h.service}:{},...h.module!==void 0?{module:h.module}:{},...h.instance!==void 0?{instance:h.instance}:{}}))}async function zs(e){return(await An(e.appDir)).map(r=>({rule:"codegen-ref-resolved",level:"error",message:r}))}import{existsSync as Vs}from"fs";import{readdir as Bs}from"fs/promises";import Vt from"path";async function Ws(e){let t=[],r=Vt.join(e.appDir,"packages");if(!Vs(r))return t;for(let n of await Bs(r,{withFileTypes:!0})){if(!n.isDirectory()||n.name.startsWith("contracts-"))continue;let s=Vt.join(r,n.name,"src");if(!Vs(s))continue;let o=[];await Us(s,s,o),o.length>0&&t.push({rule:"module-auth-context",level:"error",message:`${n.name}: 源码读 ctx.auth 全局单例 ${o.length} 处(${o.slice(0,3).join("、")}${o.length>3?"…":""})——ctx.auth 已于 app-sdk 0.11.0 拆除,改用显式通道(handle 第四参 reqCtx / getToolRequestContext,见 docs/reference/modules.md)`})}return t}async function Us(e,t,r){for(let n of await Bs(e,{withFileTypes:!0})){let s=Vt.join(e,n.name);if(n.isDirectory()){if(n.name==="__tests__"||n.name==="node_modules")continue;await Us(s,t,r);continue}if(!/\.tsx?$/.test(n.name)||/\.test\.tsx?$/.test(n.name))continue;let o=await F(s),i=Vt.relative(t,s);o.split(`
|
|
86
|
+
`).forEach((c,l)=>{/ctx\.auth\.(getContext|setContext|requireIdentity|isAnonymous)\s*\(/.test(c)&&r.push(`${i}:${l+1}`)})}}import Er from"path";import ke from"typescript";var Hs="presentation",Ks="labelsEmbedded",yc=new Set([".ts",".tsx"]);function vc(e){let t;try{t=pe(e)}catch{return[]}let r=[],n=s=>{if(ke.isPropertyAssignment(s)&&(ke.isIdentifier(s.name)||ke.isStringLiteral(s.name))&&s.name.text===Hs&&ke.isObjectLiteralExpression(s.initializer)){let o=s.initializer.properties.some(c=>c.name!==void 0&&ke.isIdentifier(c.name)&&c.name.text==="variant"),i=s.initializer.properties.some(c=>c.name!==void 0&&ke.isIdentifier(c.name)&&c.name.text===Ks),a=s.initializer.properties.some(ke.isSpreadAssignment);o&&!i&&!a&&r.push(t.getLineAndCharacterOfPosition(s.getStart(t)).line+1)}ke.forEachChild(s,n)};return n(t),r}async function Gs(e){let t=[];for(let r of await W(e.appDir)){let n=Er.join(r.dir,"src"),s=[];await De(n,n,s);for(let o of s){if(!yc.has(Er.extname(o)))continue;let i=await F(Er.join(n,o));if(i.includes(Hs))for(let a of vc(i))t.push({rule:"card-face-labels",level:"error",message:`${r.id}: src/${o}:${String(a)} 的 presentation 缺 ${Ks}——卡面图自带卡名与「我的积分」字样,缺省时前端会再叠一层导致文字重叠;卡图确实无字请显式写 false`})}}return t}import{existsSync as qs}from"fs";import{readdir as Ys}from"fs/promises";import Rr from"path";async function Xs(e){let t=[],r=Rr.join(e.appDir,"packages");if(!qs(r))return t;for(let n of await Ys(r,{withFileTypes:!0})){if(!n.isDirectory()||n.name.startsWith("contracts-"))continue;let s=Rr.join(r,n.name,"src");if(!qs(s))continue;let o=await Zs(s);if(o.length===0)continue;let i=o.map(u=>u.text).join(`
|
|
87
|
+
`),a=new Set;for(let u of i.matchAll(/internalTool\s*\(\s*(\w+)\s*\)/g))a.add(u[1]);let c=[...i.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],m=d.index??0,h=u+1<c.length?c[u+1].index??m+4e3:m+4e3,g=i.slice(m,Math.min(h,m+8e3)).includes("模型勿调用"),b=a.has(f);g&&!b?l.push(f):b&&!g&&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 Zs(e,t=""){let r=[];for(let n of await Ys(e,{withFileTypes:!0})){let s=Rr.join(e,n.name),o=t?`${t}/${n.name}`:n.name;if(n.isDirectory()){if(n.name==="__tests__"||n.name==="node_modules")continue;r.push(...await Zs(s,o));continue}/\.tsx?$/.test(n.name)&&(/\.test\.tsx?$/.test(n.name)||r.push({rel:o,text:await F(s)}))}return r}import{existsSync as _e,readFileSync as be}from"fs";import{createRequire as wc}from"module";import{realpathSync as Qs}from"fs";import U from"path";var kc=/^@tbox\.cn\/app-theme-(.+)$/;function eo(e,t){try{let r=wc(U.join(e,"__noop__.js"));return Qs(r.resolve(t))}catch{return null}}function to(e){let t=U.join(e,"apps","client","package.json");if(!_e(t))return[];let r;try{r=JSON.parse(be(t,"utf-8"))}catch{return[]}let n=[...Object.keys(r.dependencies??{}),...Object.keys(r.devDependencies??{})],s=[];for(let o of n){let i=kc.exec(o);if(!i)continue;let a=eo(U.join(e,"apps","client"),`${o}/package.json`);a&&s.push({pkg:o,id:i[1],dir:U.dirname(a)})}return s}function ro(e){return e.replace(/\/\*[\s\S]*?\*\//g,"")}function bc(e){let t=new Set,r=[],n=(s,o)=>{if(o>5||t.has(s)||!_e(s))return;t.add(s);let i=ro(be(s,"utf-8"));r.push(i);for(let a of i.matchAll(/@import\s+['"]([^'"]+)['"]/g))a[1].startsWith(".")&&n(U.resolve(U.dirname(s),a[1]),o+1)};return n(e,0),{text:r.join(`
|
|
88
|
+
`),files:[...t]}}function xc(e){let t=U.join(e,"package.json");if(!_e(t))return null;try{let n=JSON.parse(be(t,"utf-8")).exports?.["."],s=typeof n=="string"?n:n&&typeof n=="object"?n.import??n.default:void 0;return typeof s!="string"?null:U.join(e,s)}catch{return null}}function Dr(e){let t=eo(U.join(e,"apps","client"),"@app/agent-ui-kit/package.json");if(!t)return null;try{let n=Qs(e);if(!t.startsWith(n+U.sep))return null}catch{return null}let r=U.dirname(t);try{let s=JSON.parse(be(t,"utf-8")).exports?.["./theme/contract"],o=typeof s=="string"?U.join(r,s):U.join(r,"src","theme","contract.json"),i=JSON.parse(be(o,"utf-8"));return new Set([...i.platform,...i.domains,...i.assets])}catch{return null}}function Cr(e,t,r){let n=[],s=xc(e);if(!s||!_e(s))return n.push("exports['.'] 未指向存在的 css 入口"),n;let{text:o,files:i}=bc(s);if(new RegExp(`html\\s*\\[\\s*data-theme\\s*=\\s*['"]${t}['"]\\s*\\]\\s*\\{`).test(o)||n.push(`缺 html[data-theme='${t}'] 锚(主题覆写须挂主题段,不得裸写)`),/^:root\s*[{,]/m.test(o)&&n.push(`含裸 :root 选择器(污染 kit 默认层;覆写须限定在 html[data-theme='${t}'] 段)`),r){let c=new Set;for(let p of o.matchAll(/(--tbox-[a-z0-9-]+)\s*:/g))c.add(p[1]);let l=[...c].filter(p=>!r.has(p));l.length>0&&n.push(`覆写变量超出契约封闭清单 → ${l.join(", ")}(清单真源 = kit ./theme/contract)`)}for(let c of i){let l=ro(be(c,"utf-8"));for(let p of l.matchAll(/url\(\s*['"]?(\.\/[^'")]+)['"]?\s*\)/g)){let u=U.resolve(U.dirname(c),p[1]);_e(u)||n.push(`${U.relative(e,c)} 中 url('${p[1]}') 指向不存在的文件`)}}return n}function Sc(e,t){let r=Dr(e);return Cr(t.dir,t.id,r).map(n=>({rule:"theme-package-structure",level:"error",message:`${t.pkg}: ${n}`}))}function $c(e){try{return JSON.parse(be(U.join(e.dir,"package.json"),"utf-8")).exports?.["./brand.js"]!=null}catch{return!1}}function jc(e,t){let n=["apps/client/src/main.tsx","apps/client/src/App.tsx"].map(l=>U.join(e,l)).filter(l=>_e(l)).map(l=>be(l,"utf-8")).join(`
|
|
89
|
+
`),s=t.pkg.replace(/[/.]/g,l=>`\\${l}`),o=new RegExp(`(?:import\\s+|from\\s+)['"]${s}(?:/[^'"]*)?['"]`).test(n),i=new RegExp(`applyTheme\\(\\s*['"]${t.id}['"]\\s*\\)`).test(n),c=!$c(t)||new RegExp(`(?:import\\s+|from\\s+)['"]${s}/brand\\.js['"]`).test(n);if(!o||!i||!c){let l=[o?null:"css import",i?null:`applyTheme('${t.id}')`,c?null:"brand.js import"].filter(Boolean).join(" + ");return[{rule:"theme-import",level:"warning",message:`${t.pkg}: 已安装未接线(client 入口缺 ${l};接线三行见包 README)`}]}return[]}function no(e){return to(e.appDir).flatMap(t=>Sc(e.appDir,t))}function so(e){return to(e.appDir).flatMap(t=>jc(e.appDir,t))}var Ec=[{name:"workspace-dag",run:qn},{name:"register-pure",run:rs},{name:"card-type-consistent",run:as},{name:"client-card-cover",run:cs},{name:"contract-version",run:ls},{name:"module-deps-assembled",run:ps},{name:"singleton",run:us},{name:"sdk-internal-import",run:fs},{name:"assembly-drift",run:ms},{name:"schema-version",run:gs},{name:"naked-write-route",run:ys},{name:"descriptor-schema",run:ks},{name:"workspace-ref-resolved",run:xs},{name:"legacy-platform-files",run:Ss},{name:"intent-overlap",run:Es},{name:"skill-name-conflict",run:Rs},{name:"sample-data-valid",run:Ds},{name:"module-console-logger",run:Ms},{name:"module-mall-scope",run:Ps},{name:"integration-services",run:Js},{name:"codegen-ref-resolved",run:zs},{name:"module-auth-context",run:Ws},{name:"card-face-labels",run:Gs},{name:"module-internal-tool",run:Xs},{name:"theme-package-structure",run:no},{name:"theme-import",run:so}];async function Q(e,t){let r=[],n=t?.only?.length?new Set(t.only):null,s=new Set(t?.skip??[]);for(let o of Ec)if(!(n&&!n.has(o.name))&&!s.has(o.name))try{r.push(...await o.run(e))}catch(i){r.push({rule:o.name,level:"error",message:`规则执行异常: ${i?.message??String(i)}`})}return r}function oo(e,t){return!t.has(e)||e.startsWith("contracts-")}async function io(e,t,r,n=[]){let s=await W(e),o=new Set(s.map(S=>S.id)),i=new Set(r?.map(S=>S.id)??[]),a=new Map;for(let S of s)a.set(S.id,S.descriptor.version??"0.0.0");let c=new Map(n.map(S=>[S.id,S.deps])),l=new Set,p=[],u=[...t,...n.map(S=>S.id)];for(;u.length>0;){let S=u.shift();if(l.has(S))continue;l.add(S);let $=s.find(C=>C.id===S),_=c.get(S)??$?.descriptor.dependencies?.modules??[];for(let C of _)p.push(C),!l.has(C.id)&&(o.has(C.id)||c.has(C.id))&&u.push(C.id)}let d=0,f=1,m=2,h=new Map,v=[];function g(S,$){h.set(S,f),$.push(S);for(let _ of b.get(S)??[]){let C=h.get(_)??d;if(C===f){let se=$.indexOf(_);se>=0&&v.push([...$.slice(se),_])}else C===d&&g(_,$)}$.pop(),h.set(S,m)}let b=new Map,x=new Map(s.map(S=>[S.id,S]));for(let S of l){let $=x.get(S),_=c.get(S)??$?.descriptor.dependencies?.modules??[];b.set(S,_.filter(C=>l.has(C.id)).map(C=>C.id))}for(let S of l)(h.get(S)??d)===d&&g(S,[]);let j=[...l].filter(S=>!i.has(S)),D=new Map;for(let S of j)D.set(S,0);let R=new Map;for(let S of j)for(let $ of b.get(S)??[])if(D.has($)){D.set(S,(D.get(S)??0)+1);let _=R.get($);_?_.push(S):R.set($,[S])}let A=j.filter(S=>(D.get(S)??0)===0),E=[];for(;A.length>0;){let S=A.shift();E.push(S);for(let $ of R.get(S)??[]){let _=(D.get($)??0)-1;D.set($,_),_===0&&A.push($)}}E.length,j.length;let w=new Set,O=new Set,L=[];for(let S of p){let $=S.id;if((!i.has($)||!a.has($))&&(oo($,o)?w.add($):oo($,o)||O.add($)),S.range&&a.has($)){let _=a.get($);S.range!=="*"&&!S.range.startsWith("workspace:")&&(_t(S.range,_)||L.push(`${$}: 依赖范围 ${S.range} 与目标版本 ${_} 无交集`))}}return{order:E,contracts:[...w],business:[...O],cycles:v,conflicts:L}}function Dc(e){let t=ne.join(e,"tbox.module.json");if(!xe(t))return null;try{let r=Me(JSON.parse(Mr(t,"utf8")));return r.ok?r.value:null}catch{return null}}function Cc(e,t){if(!e)return null;let r=ne.dirname(e),n=ne.join(r,t);if(xe(ne.join(n,"package.json")))return n;try{let s=Rc(r).find(o=>o.endsWith(".tgz")&&o.includes(`-${t}-`));if(s)return ne.join(r,s)}catch{}return null}function Tr(e,t){let r=ne.join(e,"packages",t);return xe(r)&&!jt(r)?!0:B(e)?.npmModules.some(s=>s.id===t&&s.mode==="sdk")??!1}async function Bt(e){await ao(e,new Set,!1)}async function ao(e,t,r){let n=[];try{await Tc(e,t,r,n)}finally{for(let s of n)s.dispose()}}async function Tc(e,t,r,n){let s=process.cwd();if(!B(s))throw new Error("当前目录不是 tbox-app 应用(缺 .tbox/app.json)");if(/^@tbox\.cn\/app-theme-/.test(e.pkg)||/\/app-theme-/.test(e.pkg))throw new Error(`主题包不属模块面(${e.pkg})——换/装主题请用 tbox-app theme use <id>${e.source?` --source ${e.source}`:""}`);let o,i=null,a=null,c=!!e.source&&Oe(e.source);if(e.mode==="local"){let v=e.pkg;o=v.startsWith("module-")?v:`module-${v}`,je(o,`--mode local ${e.pkg}`)}else{if(!e.source)throw new Error(`--mode ${e.mode} 需要 --source <目录或 tgz 或 registry 包>`);if(!(c&&!xe(ne.resolve(e.source)))&&!e.source.endsWith(".tgz")&&!ie.isUsable(e.source))throw new Error(`模块源无效(缺 package.json): ${e.source}`);a=ae(e.source,e.registry),(a.source instanceof z||a.source instanceof ee)&&n.push(a.source);let g=e.pkg.replace(/@[^/]+$/,"");o=Ln(g),je(o,`--mode ${e.mode} ${e.pkg}`),i=Dc(a.sourceDir)}if(r&&Tr(s,o)){console.log(` ⚠️ 依赖 ${o} 已安装,跳过`);return}if(!r&&Tr(s,o)){let v=xe(ne.join(s,"packages",o))?`packages/${o}`:"manifest 已登记(sdk 包,无本地目录)";throw new Error(`模块 ${o} 已存在(${v})。若要重新展开请先 remove 或手动处理;装配唯一覆盖入口为 sync --force`)}if(t.has(o))return;if(t.add(o),!e.noDeps){let v=i?.dependencies?.modules??[],g=await io(s,[],void 0,[{id:o,deps:v}]);if(g.cycles.length>0)throw new Error(`依赖环:${g.cycles.map(x=>x.join(" → ")).join(";")}`);for(let x of g.conflicts)console.warn(` ⚠️ 依赖版本冲突: ${x}`);let b=[...g.contracts,...g.order].filter(x=>x!==o&&!Tr(s,x));for(let x of[...new Set(b)]){let j=/^(module|scenario|contracts|provider)-/.test(x)?`@tbox.cn/app-${x}`:`@tbox.cn/${x}`,D=c?j:Cc(e.source,x);if(!D){console.warn(` ⚠️ 依赖 ${x} 未安装且未找到源(--source 兄弟目录/tgz),可 --no-deps 跳过`);continue}let R=ae(D,e.registry);(R.source instanceof z||R.source instanceof ee)&&n.push(R.source);let A=xe(ne.join(R.sourceDir,"tbox.module.json")),E=Dn(j,A);await ao({pkg:j,mode:E,source:D,registry:e.registry},t,!0)}}let l;if(e.mode==="local"){let v=e.pkg,g=v.startsWith("module-")?v:`module-${v}`;await Fn(s,v),l={id:g,package:`@app/${g}`,version:"0.1.0",mode:"local"},await Ze(s,`@app/${g}`,"add")}else{let v=a.sourceDir,g=a.source.resolveVersion();e.mode==="codegen"&&(Et(v),await _n(s,v,{id:o,package:e.pkg.replace(/@[^/]+$/,""),version:g,mode:"codegen",baseVersion:g},Re(s).values()),await Ze(s,$t(o),"add")),l={id:o,package:e.pkg.replace(/@[^/]+$/,""),version:g,mode:e.mode,...e.mode==="codegen"?{baseVersion:g}:{}}}let p=B(s);if(await oe(s,mn(p,l)),l.mode==="codegen"){let v=await Dt(s,Re(s).values());v.length>0&&console.log(` codegen 引用同步: ${v.join(", ")}`)}let u=await ye(s);u.changedFiles.length>0&&console.log(` 装配更新: ${u.changedFiles.join(", ")}`);for(let v of u.warnings)console.warn(` ⚠️ ${v}`);for(let v of u.depConflicts)console.warn(` ⚠️ 依赖冲突: ${v}`);let d=await Y(s),m=(await Q({appDir:s,declared:u.declared,actual:d},{skip:["integration-services"]})).filter(v=>v.level==="error");if(m.length>0){console.error("❌ doctor 错误:");for(let v of m)console.error(` - [${v.rule}] ${v.message}`);process.exitCode=1}let h=await He(s);h.generated?console.log(` ⚡ 已生成 mock 槽绑定(config/integrations.json:${h.services.join(", ")})`):h.reason==="in-place"&&console.log(" ℹ config/integrations.json 已在场,跳过 mock 绑定生成"),console.log(`✅ 已安装 ${l.package}(mode=${e.mode}${r?", 依赖":""})`),Mc(s,l.id),e.mode!=="local"&&console.log(" 下一步: pnpm install && tbox-app module doctor")}function Mc(e,t){try{let r=ne.join(e,"packages",t,"package.json");if(!xe(r))return;let s=JSON.parse(Mr(r,"utf8")).dependencies?.["@tbox.cn/app-contracts-mall"],o=typeof s=="string"?s.replace(/[\^~]/,""):void 0;if(!o)return;let i=ne.join(e,"pnpm-workspace.yaml");if(!xe(i))return;let l=Mr(i,"utf8").match(/['"]?@tbox\.cn\/app-contracts-mall['"]?\s*:\s*['"]([^'"]+)['"]/)?.[1]?.replace(/[\^~]/,"");if(!l)return;Ic(o,l)>0&&console.warn(` ⚠️ 版本配套:${t} 依赖 contracts-mall ${o}+,当前应用 catalog 为 ${l}——请升级模板或应用 catalog(015 W1)`)}catch{}}function Ic(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))}var Pc=new Set(["node_modules","dist","dist-ssr",".git",".DS_Store"]),Lc=new Set(["pnpm-lock.yaml"]),_c="pnpm-workspace.template.yaml",Or="pnpm-workspace.yaml",Fc="pnpm-lock.template.yaml",Nc="pnpm-lock.yaml";function Jc(e,t){let r=T.join(t,Or),n=X(r)?Oc(r,"utf8"):"",s=ot(e).filter(c=>/^tbox\.cn-app-(sdk|toolkit|contracts|theme-[a-z0-9-]+)-.+\.tgz$/.test(c)).filter(c=>{let l=c.match(/^tbox\.cn-app-(theme-[a-z0-9-]+)-.+\.tgz$/);return l?new RegExp(`@tbox\\.cn/app-${l[1]}(?![a-z0-9-])`).test(n):!0}).sort();if(s.length===0)return null;let o=Ut(t),i=new Set,a=[];for(let c of s){let l=c.replace(/^tbox\.cn-app-/,"").replace(/\.tgz$/,""),p=l.lastIndexOf("-"),u="@tbox.cn/app-"+l.slice(0,p);if(i.has(u))throw new Error(`--local-deps 目录含同一平台包多版本(overrides 重复 key 歧义,须单版本目录): ${u}`);i.add(u);let d=T.relative(o,Ut(T.join(e,c)));a.push(` ${JSON.stringify(u)}: ${JSON.stringify(`file:${d}`)}`)}return a}function zc(e,t){let r=e.split(`
|
|
90
|
+
`),n=r.findIndex(o=>/^overrides:[ \t]*$/.test(o));if(n===-1)return`${e.trimEnd()}
|
|
91
|
+
|
|
92
|
+
# create --local-deps 注入:平台包经 file: 本地发布态 tgz 消费(005 v0.4.0 验证态;生产 B 档移除本块走 registry)
|
|
93
|
+
overrides:
|
|
94
|
+
${t.join(`
|
|
95
|
+
`)}
|
|
96
|
+
`;let s=n+1;for(;s<r.length&&/^[ \t]+\S/.test(r[s]);)s++;return r.splice(s,0,...t),r.join(`
|
|
97
|
+
`)}function Vc(e){let t=ot(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$/,""),o=s.lastIndexOf("-"),i="@tbox.cn/app-"+s.slice(0,o);if(r.has(i))throw new Error(`--local-deps 目录含同一平台包多版本(overrides 重复 key 歧义,须单版本目录): ${i}`);r.add(i)}}function uo(e){let t=0;for(let r of ot(e,{withFileTypes:!0}))r.name===".DS_Store"||r.name==="node_modules"||(r.isDirectory()?t+=uo(T.join(e,r.name)):t+=1);return t}function po(e){if(!X(e)||!Ar(e).isDirectory())throw new Error(`--config-dir 不是目录: ${e}`);if(uo(e)===0)throw new Error(`--config-dir 目录为空(无任何文件可合并): ${e}`)}var Bc=/^(?:@[a-z0-9._-]+\/)?[a-z0-9][a-z0-9._-]*$/;function Wc(e){let t=T.join(e,"scripts");if(!X(t))return;function r(n){for(let s of ot(n,{withFileTypes:!0})){let o=T.join(n,s.name);s.isDirectory()?r(o):s.isFile()&&Ac(o,493)}}r(t)}function Uc(e){if(!Bc.test(e))throw new Error(`应用包名无效: ${e}(需为 npm 包名;使用 --name 指定)`)}function Hc(e){return e===T.resolve(process.cwd())}function Kc(e,t){let r=T.relative(e,t);return r!==""&&r!==".."&&!r.startsWith(`..${T.sep}`)&&!T.isAbsolute(r)}function Gc(e,t){if(!X(e))return;let r=Ar(e),n=Ut(e),s=X(t)?Ut(t):T.resolve(t);if(n===s||r.isDirectory()&&Kc(n,s))throw new Error(`模板源与目标目录重合或目标位于模板源内,无法初始化: ${e}`)}async function fo(e){let t=T.resolve(process.cwd(),e.name),r=X(t),n=Hc(t);if(r&&!Ar(t).isDirectory())throw new Error(`目标不是目录: ${e.name}`);if(r&&!n&&!e.force)throw new Error(`目录已存在: ${e.name}(加 --force 覆盖)`);if(n&&X(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&&Uc(s);let o=Array.isArray(e.configDir)?e.configDir:e.configDir?[e.configDir]:[],i=e.source,a=T.resolve(i),c=!X(a)&&Oe(i),l=a.endsWith(".tgz");if(!c&&!l&&!ie.isUsable(a))throw new Error(`模板源无效(缺 package.json): ${a}`);if(!c&&l&&!X(a))throw new Error(`模板源无效(tgz 不存在): ${a}`);if(Gc(a,t),c&&e.localDeps)throw new Error("registry 源(--source @scope/pkg)与 --local-deps 互斥:registry 安装无需/不允许本地依赖注入");e.localDeps&&Vc(T.resolve(e.localDeps));for(let d of o)po(T.resolve(d));let p=ae(c?i:a,e.registry),u=p.sourceDir;try{let d=B(u);if(!d)throw new Error(`模板结构异常: ${a} 的 .tbox/app.json 缺失、损坏或缺少 templateVersion(出生版本)字段`);await Wt(u,t,{recursive:!0,force:!0,filter:b=>{let x=T.basename(b);return!(Lc.has(x)||Pc.has(x))}}),Wc(t);let f=T.join(t,_c);X(f)&&await co(f,T.join(t,Or));let m=T.join(t,Fc),h=X(m);if(h){await co(m,T.join(t,Nc));let b=vn(t);b>0&&console.log(` 🔒 携带锁 specifiers 已对齐 registry 归一态(${b} 项 catalog: → 字面量)`)}if(e.localDeps){let b=Jc(T.resolve(e.localDeps),t),x=T.join(t,Or),j=await Ir(x,"utf8");await lo(x,zc(j,b),"utf8"),h&&he(t)}for(let b of o)await Wt(T.resolve(b),T.join(t,"config"),{recursive:!0,force:!0,filter:x=>T.basename(x)!==".DS_Store"&&T.basename(x)!=="node_modules"});e.envFile&&await Wt(T.resolve(e.envFile),T.join(t,"config","env.sh"),{force:!0});let v=T.join(t,"package.json");if(X(v)){let b=JSON.parse(await Ir(v,"utf8"));b.name=s,await lo(v,JSON.stringify(b,null,2)+`
|
|
98
|
+
`,"utf8")}let g={templateVersion:d.templateVersion,npmModules:d.npmModules??[]};if(await oe(t,g),console.log(`✅ 已创建应用 ${s}`),console.log(c?` ⚡ 已从 registry 模板包 ${p.source.name} 展开(发布态)`:l?` ⚡ 已从模板包 ${T.basename(a)} 展开(发布态)`:" ⚡ 已从模板目录展开(本地开发源)"),e.localDeps?(console.log(` ⚡ 已注入平台 overrides(--local-deps: ${e.localDeps},install 离线消费本地发布态产物)`),h&&console.log(" 🔒 携带锁已增量刷新为 file: 形态(仅平台三包换形态,间接依赖保钉)——B 档移除 overrides 块后 plain install 回 registry 形态、frozen 恢复可用")):h&&console.log(" 🔒 依赖已由 pnpm-lock.yaml 冻结(直接+间接全精确)——请随应用提交,安装可 --frozen-lockfile"),o.length>0&&console.log(` ⚡ 已合并应用配置(--config-dir: ${o.join(" + ")} → config/)`),e.profile){let b=T.join(t,"profiles"),x=T.join(b,`${e.profile}.json`);if(!X(x)){let E=X(b)?ot(b).filter(w=>w.endsWith(".json")).map(w=>w.replace(/\.json$/,"")).join(", "):"";throw new Error(`画像不存在: ${e.profile}(可用: ${E||"模板无 profiles/ 目录"})`)}let j=JSON.parse(await Ir(x,"utf8"));if(j.configOverlay){let E=T.join(t,j.configOverlay);po(E),await Wt(E,T.join(t,"config"),{recursive:!0,force:!0,filter:w=>T.basename(w)!==".DS_Store"&&T.basename(w)!=="node_modules"}),console.log(` ⚡ 已叠加画像配置(profiles/${e.profile}/config → config/)`)}let D=T.resolve(u,"..","..","modules"),R=E=>wt(D,E)??E,A=process.cwd();process.chdir(t);try{for(let E of j.modules??[])await Bt({pkg:E,mode:"codegen",source:R(E),...e.registry?{registry:e.registry}:{}})}finally{process.chdir(A)}console.log(` ⚡ 已按画像加装 ${j.modules?.length??0} 模块(profile: ${e.profile})`)}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 z&&p.source.dispose()}}import qc from"path";async function mo(e,t={}){let r=process.cwd();je(e,"remove 参数");let n=B(r);if(!n)throw new Error("当前目录不是 tbox-app 应用(缺 .tbox/app.json)");if(!t.force){let h=[];for(let v of await W(r)){if(v.id===e)continue;let g=(v.descriptor.dependencies?.modules??[]).find(b=>b.id===e);g&&g.required&&h.push(v.id)}if(h.length>0)throw new Error(`模块 ${e} 被依赖,无法移除:${h.join(", ")}(--force 忽略)`)}let s=n.npmModules.find(h=>h.id===e),o=!!s;o&&await oe(r,gn(n,e));let i=qc.join(r,"packages",e),c=jt(i)&&(s?.mode==="sdk"||!s);(c?!1:await zn(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"&&Ge(s.package)?await On(r,Ee(s)):!1;p&&console.log(" 已恢复 contracts-mall SDK shim(@app/contracts-mall)"),!p&&!c&&(s?.mode==="codegen"||s?.mode==="local"||!o)&&await Ze(r,`@app/${e}`,"remove");let u=await ye(r);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 Y(r),m=(await Q({appDir:r,declared:u.declared,actual:d})).filter(h=>h.level==="error");if(m.length>0){console.error("❌ doctor 错误:");for(let h of m)console.error(` - [${h.rule}] ${h.message}`);process.exitCode=1}console.log(c&&!o?`✅ ${e} 未登记,保留 SDK shim`:`✅ 已移除 ${e}`)}import{existsSync as yo,readFileSync as Fe,writeFileSync as ho}from"fs";import ue from"path";var Ht=/^[a-z0-9]+(-[a-z0-9]+)*$/,it=/^@tbox\.cn\/app-theme-([a-z0-9-]+)$/,Yc=/^import\s+['"](@tbox\.cn\/app-theme-[a-z0-9-]+)\/index\.css['"];?\s*$/gm,Xc=/^import\s+['"](@tbox\.cn\/app-theme-[a-z0-9-]+)\/brand\.js['"];?\s*$/gm,Zc=/^import\s*\{\s*applyTheme\s*\}\s*from\s*['"](?:@app\/agent-ui-kit|@tbox\.cn\/app-agent-ui-kit)\/theme['"];?\s*$/m,Qc=/^applyTheme\(\s*['"]([^'"]+)['"]\s*\);?\s*$/gm;function Ce(e){let t=[...e.matchAll(Yc)];if(t.length===0)throw new Error("无主题接线点(apps/client/src/main.tsx 无主题 css import)——support/h5 模板或未接线形态不适用 --theme / theme use");if(t.length>1||new Set(t.map(a=>a[1])).size>1)throw new Error(`主题接线不唯一(${t.map(a=>a[1]).join(", ")})——换主题 = 替换语义,请先手工归一到单主题`);let r=t[0][1],n=it.exec(r);if(!n)throw new Error(`主题包名越界:${r}(须 ^@tbox.cn/app-theme-<id>$)`);let s=n[1],o=[...e.matchAll(Xc)];if(o.length>1||o[0]&&o[0][1]!==r)throw new Error("brand.js 接线与主题包不一致(替换语义要求单主题三行自洽)");let i=[...e.matchAll(Qc)];if(i.length!==1)throw new Error(`applyTheme 调用不唯一(${i.length} 处)——接线三行须恰一处`);if(i[0][1]!==s)throw new Error(`applyTheme('${i[0][1]}') 与接线包后缀 '${s}' 不一致——请先修正接线三行`);if(!Zc.test(e))throw new Error("缺 applyTheme import 行(@app/agent-ui-kit/theme 或 @tbox.cn/app-agent-ui-kit/theme)——接线三行不完整");return{factoryPkg:r,factoryId:s,hasBrandJs:o.length===1}}function go(e,t){Ce(e);let r=/^import\s+['"]@tbox\.cn\/app-theme-[a-z0-9-]+\/brand\.js['"];?\s*$/m.test(e),n=[];for(let s of e.split(`
|
|
99
|
+
`)){let o=s.trim();if(/^import\s+['"]@tbox\.cn\/app-theme-[a-z0-9-]+\/index\.css['"];?$/.test(o)){n.push(`import '${t.pkg}/index.css';`),t.hasBrandJs&&!r&&n.push(`import '${t.pkg}/brand.js';`);continue}if(/^import\s+['"]@tbox\.cn\/app-theme-[a-z0-9-]+\/brand\.js['"];?$/.test(o)){t.hasBrandJs&&n.push(`import '${t.pkg}/brand.js';`);continue}if(/^applyTheme\(/.test(o)){n.push(`applyTheme('${t.id}');`);continue}n.push(s)}return n.join(`
|
|
100
|
+
`)}async function vo(e,t={}){let r=process.cwd();if(!B(r))throw new Error("当前目录不是 tbox-app 应用(缺 .tbox/app.json)");if(!Ht.test(e))throw new Error(`非法主题 id "${e}"——仅允许小写字母/数字/中划线(@tbox.cn/app-theme-<id> 包名约定)`);let n=ue.join(r,"apps","client"),s=ue.join(n,"src","main.tsx");if(!yo(s))throw new Error(`缺接线入口文件:${ue.relative(r,s)}`);let o=Fe(s,"utf8"),i=Ce(o),a=`@tbox.cn/app-theme-${e}`,c=ue.join(n,"package.json"),l=JSON.parse(Fe(c,"utf8")),p=i.factoryId===e&&i.factoryPkg===a,u=!!(l.dependencies?.[a]??l.devDependencies?.[a]);if(p&&u){console.log(`✅ 主题已是 ${e}(接线与依赖未改动)`);return}let d=t.source??a,f=ae(d,t.registry);try{let m=f.source.getDir(),h;try{h=JSON.parse(Fe(ue.join(m,"package.json"),"utf8")).name}catch{}if(h!==a)throw new Error(`源包名与目标主题不一致:${h??"(缺 name)"} ≠ ${a}——请核对 --source 指向`);let v=Cr(m,e,Dr(r));if(v.length>0)throw new Error(`主题包结构校验未过(${d}):
|
|
101
|
+
- ${v.join(`
|
|
102
|
+
- `)}`);let b=JSON.parse(Fe(ue.join(m,"package.json"),"utf8")).exports??{};if(b["."]===void 0&&b["./index.css"]===void 0)throw new Error(`主题包缺 css 入口 exports('.' 或 './index.css'):${h}`);let x=b["./brand.js"]!==void 0,j=p?o:go(o,{pkg:a,id:e,hasBrandJs:x});if(!p&&Ce(j).factoryId!==e)throw new Error("接线手术产物自校验未过(规范形漂移)——未写盘,请反馈");let D="";for(let A of["dependencies","devDependencies"]){let E=l[A];if(E)for(let w of Object.keys(E))it.test(w)&&w!==a&&(delete E[w],D=w)}let R;if(f.source instanceof ee)R=f.source.resolveVersion()||"latest";else{let A=f.source instanceof z?ue.resolve(String(t.source)):f.source.getDir();R=`file:${ue.relative(n,A)}`}l.dependencies={...l.dependencies??{},[a]:R},p||ho(s,j,"utf8"),ho(c,JSON.stringify(l,null,2)+`
|
|
103
|
+
`,"utf8"),he(r),console.log(p?`🔧 依赖补写:${a}(spec: ${R})——接线已就位,未改动`:`🎨 主题接线:${i.factoryPkg} → ${a}(spec: ${R})`),D&&console.log(` 依赖交换:-${D} +${a}`),console.log("下一步: pnpm install && tbox-app module doctor(㉖ 接线复查)")}finally{(f.source instanceof z||f.source instanceof ee)&&f.source.dispose()}}async function wo(e={}){let t=process.cwd();if(!B(t))throw new Error("当前目录不是 tbox-app 应用(缺 .tbox/app.json)");let r=ue.join(t,"apps","client","package.json"),n=JSON.parse(Fe(r,"utf8")),s=[];for(let c of["dependencies","devDependencies"])for(let[l,p]of Object.entries(n[c]??{}))it.test(l)&&s.push({pkg:l,spec:p});let o=ue.join(t,"apps","client","src","main.tsx"),i=null;if(yo(o))try{i=Ce(Fe(o,"utf8")).factoryId}catch{i=null}let a=s.map(({pkg:c,spec:l})=>{let p=it.exec(c)?.[1]??"";return{pkg:c,id:p,spec:l,wired:i===p}});if(e.json){console.log(JSON.stringify({themes:a},null,2));return}if(a.length===0){console.log("(无主题包——裸 kit 视觉态;换主题:tbox-app theme use <id>)");return}for(let c of a)console.log(`${c.wired?"●":"○"} ${c.pkg}@${c.spec}${c.wired?"(接线生效)":"(已安装未接线)"}`)}async function ko(e={}){let t=process.cwd(),r=await ye(t,{force:e.force??!1});r.changedFiles.length===0?console.log("装配已同步,无变更"):console.log(`已更新装配文件: ${r.changedFiles.join(", ")}`);for(let i of r.warnings)console.warn(` ⚠️ ${i}`);for(let i of r.depConflicts)console.warn(` ⚠️ 依赖冲突: ${i}`);let n=await Y(t),s=await Q({appDir:t,declared:r.declared,actual:n}),o=s.filter(i=>i.level==="error");if(o.length>0){console.error("❌ doctor 错误:");for(let i of o)console.error(` - [${i.rule}] ${i.message}`);process.exitCode=1}else console.log("✅ doctor 无错误");for(let i of s.filter(a=>a.level==="warning"))console.warn(` ⚠️ [${i.rule}] ${i.message}`)}async function bo(){let e=process.cwd(),t=B(e),r=await ge(e);if(console.log(`模板版本: ${t?.templateVersion??"未知"}`),r.length===0){console.log("(无已装模块)");return}for(let o of r)console.log(` ${o.id.padEnd(28)} ${o.pkg} [${o.mode}]`);let n=new Set((t?.npmModules??[]).map(o=>o.id)),s=(await W(e)).filter(o=>!n.has(o.id));if(s.length>0){console.log(" 本地模块(未在 app.json 登记):");for(let o of s)console.log(` ${o.id}`)}}async function xo(e){let t=process.cwd(),r=await ge(t),n=await Y(t),s=!1;for(let o of le){let i=o.kind,a=r.filter(m=>Ot(i,m)).map(m=>m.id),c=n[i]?.moduleIds??[],l=a.filter(m=>!c.includes(m)),p=c.filter(m=>!a.includes(m));e&&(l=l.filter(m=>m===e),p=p.filter(m=>m===e));let u=n[i]?.broken??[],d=n[i]?.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 m of d)f.push(` ? 非标行: ${m}`);if(f.length>0){s=!0,console.log(`${o.path}`);for(let m of f)console.log(` ${m}`)}}s||console.log("无漂移:声明集合与实际装配集合一致")}async function So(e={}){let t=process.cwd(),r=await ge(t),n=await Y(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}),o=s.filter(a=>a.level==="error"),i=s.filter(a=>a.level==="warning");if(e.json)return console.log(JSON.stringify({errors:o,warnings:i,rules:s},null,2)),o.length>0&&(process.exitCode=1),{issues:s};if(s.length===0)console.log("✅ doctor 全绿(结构校验通过)");else{for(let a of o)console.error(`❌ [${a.rule}] ${a.message}`);for(let a of i)console.warn(`⚠️ [${a.rule}] ${a.message}`);o.length>0?(console.error(`doctor 失败: ${o.length} 个错误`),process.exitCode=1):console.log(`doctor 通过(${i.length} 个告警)`)}return{issues:s}}import{existsSync as Lr}from"fs";import{readdir as tl,readFile as Ro,unlink as rl,writeFile as nl}from"fs/promises";import Ne from"path";import{diffArrays as el}from"diff";function $o(e,t){let r=el(e,t),n=[],s=0,o=null;for(let i of r)i.removed?(o?o.oldLines.push(...i.value):o={start:s,oldLines:[...i.value],newLines:[]},s+=i.value.length):i.added?o?o.newLines.push(...i.value):o={start:s,oldLines:[],newLines:[...i.value]}:(o&&(n.push(o),o=null),s+=i.value.length);return o&&n.push(o),n}function jo(e,t,r){let n=e===""?[]:e.split(`
|
|
94
104
|
`),s=t===""?[]:t.split(`
|
|
95
|
-
`),
|
|
96
|
-
`),o
|
|
97
|
-
`)),l.push("<<<<<<< ours",...
|
|
98
|
-
`),conflicts:p}}var
|
|
99
|
-
`)}else
|
|
100
|
-
`,"utf8");let u=
|
|
101
|
-
`),console.log(" ⚡ 已创建 config/integrations.json 空骨架(无 local 供给可派生 mock 绑定)"))}function
|
|
105
|
+
`),o=r===""?[]:r.split(`
|
|
106
|
+
`),i=$o(n,s),a=$o(n,o),c=new Map;for(let h of i)c.set(h.start,{ours:h});for(let h of a){let v=c.get(h.start);v?v.theirs=h:c.set(h.start,{theirs:h})}let l=[],p=[],u=new Set,d=(h,v)=>{let g=Math.max(h?.oldLines.length??0,v?.oldLines.length??0);return h&&v?JSON.stringify(h.oldLines)===JSON.stringify(v.oldLines)&&JSON.stringify(h.newLines)===JSON.stringify(v.newLines)?l.push(...h.newLines):(p.push([...h.newLines,"--",...v.newLines].join(`
|
|
107
|
+
`)),l.push("<<<<<<< ours",...h.newLines,"=======",...v.newLines,">>>>>>> theirs")):h?l.push(...h.newLines):l.push(...v.newLines),g},f=0;for(;f<n.length;){let h=c.get(f);if(!h||u.has(f)){l.push(n[f]),f++;continue}u.add(f);let v=d(h.ours,h.theirs);v>0&&(f+=v)}let m=c.get(n.length);return m&&!u.has(n.length)&&d(m.ours,m.theirs),{merged:l.join(`
|
|
108
|
+
`),conflicts:p}}var sl=new Set(["node_modules","dist",".git",".tbox"]);function Eo(e){if(e.endsWith(".tgz")&&Lr(e)){let t=new z(e);return{dir:t.getDir(),tarball:t}}return{dir:Ne.resolve(e),tarball:null}}async function Pr(e){let t=new Map;async function r(n){let s=Ne.join(e,n),o=await tl(s,{withFileTypes:!0});for(let i of o){if(sl.has(i.name))continue;let a=n?`${n}/${i.name}`:i.name,c=Ne.join(s,i.name);if(i.isDirectory())await r(a);else try{t.set(a,await Ro(c,"utf8"))}catch{}}}return await r(""),t}async function Do(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=Ne.join(t,"packages",e.module);if(!Lr(s))throw new Error(`缺少 packages/${e.module}`);let o=await Pr(s),i=[];try{let a,c,l;if(e.source){let $=Eo(e.source);$.tarball&&i.push($.tarball),n.mode==="codegen"&&Et($.dir),l=$.dir,a=await Pr($.dir),c=$.tarball?$.tarball.resolveVersion():re($.dir)?.version??n.version}else throw new Error("codegen 升级需要 --source <新版本地目录或 tgz>(npm 拉取为 manual,真实发布后回落)");let p=Re(t),u=n.mode==="codegen"?Ee(n):null,d=u?new Map([...p,[u.publicName,u]]):p,f=Xe(l),m=Xe(s),h=te(l),v=n.mode==="codegen"&&f?Ae(f,n,d.values(),h):f,g=new Map,b=null;if(e.baseSource){let $=Eo(e.baseSource);$.tarball&&i.push($.tarball),b=$.dir,g=await Pr($.dir)}let x=n.mode==="codegen"?Rt(o,d.values()):o,j=n.mode==="codegen"?Rt(g,d.values()):g,D=n.mode==="codegen"?Rt(a,d.values()):a,R=new Set([...j.keys(),...x.keys(),...D.keys()]);R.delete("package.json");let A=new Map,E=[];for(let $ of R){let _=j.get($)??"",C=x.get($)??"",se=D.get($)??"";if(e.ours){C&&A.set($,C);continue}if(e.theirs){se&&A.set($,se);continue}let ir=jo(_,C,se);A.set($,ir.merged),ir.conflicts.length>0&&E.push(`${$}: ${ir.conflicts.length} 处冲突`)}if(n.mode==="codegen"&&v){let $={};try{$=JSON.parse(g.get("package.json")??"{}")}catch{$={}}let _=Ae($,n,d.values(),b?te(b):void 0),C=Ae(m,n,d.values(),te(t)),se=In(_,C,v,{ours:e.ours,theirs:e.theirs});A.set("package.json",JSON.stringify(se,null,2)+`
|
|
109
|
+
`)}else A.set("package.json",await Ro(Ne.join(s,"package.json"),"utf8"));if(e.dryRun){console.log(`[dry-run] ${e.module}: ${R.size} 文件合并,冲突 ${E.length} 处`);for(let $ of E)console.log(` ⚠️ ${$}`);return}for(let[$,_]of A){let C=Ne.join(s,$);if(_===""&&Lr(C)){await rl(C),console.log(` 🗑 已删除 ${$}(升级源删除该文件)`);continue}await nl(C,_,"utf8")}await oe(t,yn(r,e.module,{baseVersion:c,version:c})),n.mode==="codegen"&&await Dt(t,Re(t).values());let w=await ye(t),O=await Y(t),S=(await Q({appDir:t,declared:w.declared,actual:O})).filter($=>$.level==="error");if(S.length>0){console.error("❌ doctor 错误:");for(let $ of S)console.error(` - [${$.rule}] ${$.message}`);process.exitCode=1}if(E.length>0){console.warn("⚠️ 升级完成,存在冲突标记(<<<<<<< ours / >>>>>>> theirs),请人工/Agent 解决后运行 doctor:");for(let $ of E)console.warn(` - ${$}`)}else console.log(`✅ 已升级 ${e.module} → ${c}`)}finally{for(let a of i)a.dispose()}}import{existsSync as Jr,readFileSync as ul,mkdtempSync as Oo,rmSync as dl}from"fs";import{tmpdir as Ao}from"os";import fe from"path";import{existsSync as de,readFileSync as Co,readdirSync as ol}from"fs";import{execFileSync as il}from"child_process";import V from"path";function To(e,t){return`${e.replace(/^@/,"").replace("/","-")}-${t}.tgz`}function al(e){if(!de(e)||!de(V.join(e,"package.json")))throw new Error(`模块目录无效(缺 package.json): ${e}`);let t;try{t=JSON.parse(Co(V.join(e,"package.json"),"utf8"))}catch{throw new Error(`package.json 解析失败: ${V.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 _r(e,t){let{name:r,version:n}=al(e),s=V.resolve(t);return il("pnpm",["pack","--pack-destination",s],{stdio:"inherit",cwd:e}),V.join(s,To(r,n))}function Mo(e,t){return de(V.join(e,"pnpm-workspace.template.yaml"))?"template":de(V.join(e,"tbox.module.json"))?"module":/^@[^/]+\/app-contracts(?:-|$)/.test(t)?"contract":"generic"}function cl(e){let t=[];function r(n){let s=V.join(e,n);for(let o of ol(s,{withFileTypes:!0})){if(o.name==="node_modules"||o.name===".git")continue;let i=n?`${n}/${o.name}`:o.name;o.isDirectory()?r(i):t.push(i)}}return r(""),t}function ll(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 pl(e,t){let r=[],n=V.join(e,"package.json"),s;try{s=JSON.parse(Co(n,"utf8"))}catch{return r.push({rule:"pack-manifest",level:"error",message:"产物缺少可解析的 package.json"}),r}let o=t??Mo(e,s.name??""),i=cl(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 ll(s.exports))p.includes("/src/")||p==="src"?r.push({rule:"pack-exports",level:"error",message:`exports 指向开发态源码 "${p}"(发布态应指向 dist/)`}):p.startsWith("./")&&!de(V.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=i.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=i.filter(p=>/(^|\/)\.env$/.test(p)||/(^|\/)\.npmrc$/.test(p));if(c.length>0&&r.push({rule:"pack-leak",level:"error",message:`产物含敏感文件: ${c.join(", ")}`}),o==="module")for(let p of["src","dist","tbox.module.json"])!i.includes(p)&&!de(V.join(e,p))&&r.push({rule:"pack-files",level:"error",message:`业务模块产物缺少 ${p}`});else if(o==="template"){for(let p of["package.json","pnpm-workspace.template.yaml","apps/server/package.json","apps/client/package.json",".tbox/app.json"])de(V.join(e,p))||r.push({rule:"pack-files",level:"error",message:`模板产物缺少 ${p}`});for(let p of["pnpm-workspace.yaml","pnpm-lock.yaml"])de(V.join(e,p))&&r.push({rule:"pack-files",level:"error",message:`模板产物泄漏 ${p}(应仅含 pnpm-workspace.template.yaml / pnpm-lock.template.yaml)`});de(V.join(e,"pnpm-lock.template.yaml"))||r.push({rule:"pack-files",level:"warning",message:"模板产物未携带 pnpm-lock.template.yaml(间接依赖未冻结——平台包发布后跑 sync-template-lockfile 生成)"})}else if(o==="contract")i.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"&&!de(V.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"&&!i.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"])i.includes(p)||r.push({rule:"pack-docs",level:"warning",message:`产物缺少 ${p}${l?"(files 白名单已声明;发布完备性 backlog)":"(建议补充以完备发布元数据)"}`});return r}async function Fr(e){let t=new z(e);try{let r=t.getDir(),n=JSON.parse(await t.readFile("package.json")),s=Mo(r,n.name??""),o=pl(r,s);if(n.name&&n.version){let i=To(n.name,n.version);V.basename(e)!==i&&o.push({rule:"pack-filename",level:"error",message:`tgz 文件名 ${V.basename(e)} 与包 ${n.name}@${n.version} 期望 ${i} 不一致`})}return o}finally{t.dispose()}}function Nr(e){for(let t of e)console.log(` ${t.level==="error"?"❌":"⚠️"} [${t.rule}] ${t.message}`)}async function Io(e){let t=V.resolve(process.cwd(),e.module),r=_r(t,e.out);if(console.log(`✅ 已打包 ${V.basename(r)} → ${V.resolve(e.out)}`),e.check){let n=await Fr(r);Nr(n),n.some(s=>s.level==="error")&&(process.exitCode=1)}}async function Po(e){let t=process.cwd(),r=fe.resolve(t,e.module);if(!Jr(r)||!Jr(fe.join(r,"package.json")))throw new Error(`模块目录无效(缺 package.json): ${e.module}`);let n=(()=>{try{return JSON.parse(ul(fe.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,o=null;if(e.tgz){if(s=fe.resolve(e.tgz),!Jr(s))throw new Error(`tgz 产物不存在: ${e.tgz}`);let i=new z(s);try{let a=JSON.parse(await i.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 产物 ${fe.basename(s)} 标记 private:true——防误发布门禁拦截`)}finally{i.dispose()}}else o=Oo(fe.join(Ao(),"tbox-publish-")),s=_r(r,o);try{let i=await Fr(s);i.length>0&&Nr(i);let a=i.filter(p=>p.level==="error");if(a.length>0)throw new Error(`产物门禁失败(${a.length} 处 error),已中止发布: ${fe.basename(s)}`);o||(o=Oo(fe.join(Ao(),"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:o}),console.log(`✅ 已发布 ${fe.basename(s)}${e.registry?` → ${e.registry}`:""}${e.dryRun?"(dry-run)":""}`)}finally{if(o)try{dl(o,{recursive:!0,force:!0})}catch{}}}import{existsSync as Kt}from"fs";import{cp as fl,readFile as ml,rm as gl,writeFile as hl}from"fs/promises";import Je from"path";function yl(e){return Kt(Je.join(e,"mini.project.json"))&&Kt(Je.join(e,"app.json"))&&Kt(Je.join(e,"pages"))}async function Lo(e){let t=e.platform??"alipay";je(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}`,o;if(e.source)o=ae(e.source,e.registry);else try{o=ae(s,e.registry)}catch(i){throw new Error(`未找到 ${s}(registry 拉取失败:${i.message})。离线场景用 --source <容器目录或 tgz>`)}try{let i=o.sourceDir;if(!yl(i))throw new Error(`源不是小程序容器工程(缺 mini.project.json/app.json/pages): ${e.source??s}`);let a=Je.join(r,"apps",`miniapp-container-${t}`);if(Kt(a)){if(!e.force)throw new Error(`容器已存在: apps/miniapp-container-${t}(--force 删除重展开)`);await gl(a,{recursive:!0,force:!0})}await fl(i,a,{recursive:!0,force:!0,filter:d=>{let f=Je.basename(d);return!["node_modules","dist",".git",".DS_Store",".tbox"].includes(f)}});let c=Je.join(a,"package.json"),l=JSON.parse(await ml(c,"utf8")),p=typeof l.name=="string"?l.name:s;l.name=`@app/miniapp-container-${t}`,l.private=!0,await hl(c,JSON.stringify(l,null,2)+`
|
|
110
|
+
`,"utf8");let u=pr(i);await oe(r,hn(n,{id:t,package:u.name??s,version:o.source.resolveVersion()||u.version,dir:`apps/miniapp-container-${t}`})),he(r),console.log(`✅ 已初始化容器 @app/miniapp-container-${t}(v${o.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{(o.source instanceof z||o.source instanceof ee)&&o.source.dispose()}}import{existsSync as _o,readFileSync as vl}from"fs";import at from"path";function zr(e){return at.join(e,"config","integrations.json")}async function Fo(e){if(_o(zr(e)))return;let t=await He(e);t.generated?console.log(` ⚡ 已生成 mock 槽绑定(config/integrations.json:${t.services.join(", ")})`):(Ie(zr(e),JSON.stringify({services:{}},null,2)+`
|
|
111
|
+
`),console.log(" ⚡ 已创建 config/integrations.json 空骨架(无 local 供给可派生 mock 绑定)"))}function Gt(e){let t=zr(e);if(!_o(t))throw new Error("config/integrations.json 不存在(provider bind 需已初始化的槽位配置面——create 产物或手动创建)");let r=fn(e);if(r===null){try{JSON.parse(vl(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 Se(e,t){await dt(e).writeIntegrationsConfig(t)}async function qt(e){let{collectDeclared:t}=await import("./declared-TVLLELHR.js"),r=await t(e),n=await Y(e),s=await Q({appDir:e,declared:r,actual:n},{only:["integration-services"]}),o=s.filter(i=>i.level==="error");for(let i of o)console.error(` ❌ [${i.rule}] ${i.message}`);for(let i of s.filter(a=>a.level==="warning"))console.warn(` ⚠️ [${i.rule}] ${i.message}`);o.length>0&&(process.exitCode=1)}async function Yt(e){let t=await ut(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 No(e,t,r,n={}){let s=at.resolve(n.appDir??process.cwd());await Yt(s),await Fo(s);let o=Gt(s);if(n.instance){let i=o.services[e]??{instances:{}};i.instances??={};let a={...i.instances[n.instance],provider:t};r?a.implementation=r:delete a.implementation,i.instances[n.instance]=a,o.services[e]=i,await Se(s,o),console.log(r?`✅ 已绑定 ${e}@${n.instance} → ${t}/${r}(实例覆盖位)`:`✅ 已绑定 ${e}@${n.instance} → ${t}(实例覆盖位——implementation 派生:catalog 唯一供给)`)}else{let i=o.services[e];i?(i.provider=t,r?i.implementation=r:delete i.implementation):o.services[e]=r?{provider:t,implementation:r}:{provider:t},await Se(s,o),console.log(r?`✅ 已绑定 ${e} → ${t}/${r}${i?"(实例集保留)":"(新槽——instances 缺席 = 通配激活,立即生效;限定实例集请补 instances 显式键)"}`:`✅ 已绑定 ${e} → ${t}(implementation 派生:catalog 唯一供给)${i?"(实例集保留)":"(新槽——instances 缺席 = 通配激活,立即生效;限定实例集请补 instances 显式键)"}`)}await qt(s)}async function Jo(e,t={}){let r=at.resolve(t.appDir??process.cwd());await Yt(r),await Fo(r);let n=Gt(r);if(t.domain)n.domains??={},n.domains[t.domain]={...n.domains[t.domain],provider:e},await Se(r,n),console.log(`✅ 域默认厂商 ${t.domain} → ${e}(该域全部槽生效——槽级显式绑定除外)`);else{n.provider=e,await Se(r,n),console.log(`✅ 应用默认厂商 → ${e}(全部未显式绑定槽生效——instances 缺席槽即通配激活)`);let s=Object.entries(n.domains??{}).filter(([,o])=>typeof o.provider=="string"&&o.provider!==e).map(([o])=>o);s.length>0&&console.log(` ℹ 域级覆盖仍优先生效:${s.join(", ")}`)}await qt(r)}async function zo(e={}){let t=at.resolve(e.appDir??process.cwd());await Yt(t);let r=Gt(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 Se(t,r),console.log(`✅ 已清除域默认厂商 ${e.domain}(该域槽回落槽级/root 继承)`)}else{if(r.provider===void 0)throw new Error("root 无默认厂商(无可删项——provider use 设置)");delete r.provider,await Se(t,r),console.log("✅ 已清除应用默认厂商(未显式绑定槽将 binding-unresolved——doctor 即时反馈)")}await qt(t)}async function Vo(e,t={}){let r=at.resolve(t.appDir??process.cwd());await Yt(r);let n=Gt(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 Se(r,n),console.log(`✅ 已解绑 ${e}@${t.instance}(实例覆盖删除——回落槽级默认)`)}else delete n.services[e],await Se(r,n),console.log(`✅ 已解绑 ${e}(槽与实例集删除——模块走降级链)`);await qt(r)}import{existsSync as Bo,readFileSync as Wo}from"fs";import Te from"path";async function wl(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 W(e))for(let o of s.descriptor.contributes.providers?.slots??[])r(o);let n=Te.join(e,".tbox","app.json");if(Bo(n))try{let s=JSON.parse(Wo(n,"utf8"));for(let o of s.npmModules??[]){if(typeof o.id!="string"||typeof o.package!="string")continue;let i=ar(Te.join(e,"packages",o.id))??ar(Te.join(kl(e),...o.package.split("/")));for(let a of i?.contributes.providers?.slots??[])r(a)}}catch{}return t}function kl(e){return Te.join(e,"node_modules")}function bl(e,t){return t.get(e)}async function Uo(e={}){let t=Te.resolve(e.appDir??process.cwd()),r=Te.join(t,"config","integrations.json");if(!Bo(r))throw new Error(`config/integrations.json 不存在:${r}`);let n=JSON.parse(Wo(r,"utf8"));if(!n.services||typeof n.services!="object")throw new Error("integrations.json 缺 services");let s=await wl(t),o=[],i=[],a=0,c=(l,p)=>{let u=bl(p,s);return u?(o.push(` ${l}: '${p}' → provider='${u.provider}', implementation='${u.implementation}'`),a++):i.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,o.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 o)console.log(l);if(i.length>0){console.warn("⚠️ 未命中反查表(人工处理,保留原值):");for(let l of i)console.warn(` ${l}`);process.exitCode=1}e.write?(await dt(t).writeIntegrationsConfig(n),console.log(`✅ 已写回 ${Te.relative(process.cwd(),r)||r}`)):console.log("(dry-run:加 --write 落盘)")}import{spawn as xl}from"child_process";import{existsSync as I,mkdirSync as Xt,readFileSync as K,realpathSync as Z,readdirSync as Vr,rmSync as Sl,symlinkSync as $l,unlinkSync as Zo,writeFileSync as ze}from"fs";import{createHash as jl}from"crypto";import k from"path";import{fileURLToPath as El}from"url";var Ve=".tbox-dev.local",Ho="templates/template-agent",Rl=["apps","server"],Dl=8e3;function Cl(e){let t=k.join(e,".real-env","config");return I(t)?Vr(t).filter(r=>I(k.join(t,r,"integrations.json"))).sort():[]}function Br(e){return e.includes("-")?e.slice(0,e.indexOf("-")):e}var Ko="'../../.tbox-dev.local/vite-override.mjs'",Tl="TBOX_CONFIG_DIR";function Ml(){if(process.env.NODE_ENV==="test"&&process.env.TBOX_DEV_REPO_ROOT)return Z(process.env.TBOX_DEV_REPO_ROOT);let e=k.dirname(El(import.meta.url));for(let t=0;t<10;t++){if(I(k.join(e,"pnpm-workspace.yaml")))return e;let r=k.dirname(e);if(r===e)break;e=r}throw new Error("未能定位仓库根(缺少 pnpm-workspace.yaml)——请在仓库内执行 tbox-app dev")}function Il(e){let t=k.join(e,"package.json");if(!I(t))throw new Error(`目录缺 package.json:${e}`);try{let r=JSON.parse(K(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 Ol(e){let t=k.join(e,"tbox.module.json");if(!I(t))throw new Error(`目录缺 tbox.module.json:${e}(是否传了非模块目录?)`);let r=Me(JSON.parse(K(t,"utf8")));if(!r.ok)throw new Error(`tbox.module.json 校验失败(${e}):
|
|
102
112
|
${r.errors.join(`
|
|
103
|
-
`)}`);return r.value}function
|
|
104
|
-
`);if((s.includes("registerAppConfig")||s.includes("loadChatConfig"))&&!s.includes(
|
|
113
|
+
`)}`);return r.value}function Go(e,t,r){let n=k.join(e,"src",t,"index.ts");if(!I(n))return!1;try{return new RegExp(`export\\s+const\\s+${r}\\b`).test(K(n,"utf8"))}catch{return!1}}function Al(e){let t=k.join(e,"apps","client","vite.config.ts"),r=k.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(!K(t,"utf8").includes(Ko))throw new Error(`模板 vite.config.ts 缺 dev 能力探测标记("${Ko}")——模板过旧,请升级模板后重试`);let n=k.join(e,"apps","server","src","app.ts"),s=[K(r,"utf8"),...I(n)?[K(n,"utf8")]:[]].join(`
|
|
114
|
+
`);if((s.includes("registerAppConfig")||s.includes("loadChatConfig"))&&!s.includes(Tl))throw new Error("模板 server 装配源(apps/server/src/index.ts|app.ts)缺 TBOX_CONFIG_DIR 锚点——模板过旧,请升级模板后重试");if(!I(k.join(e,"apps","server","src","modules.ts")))throw new Error(`模板缺 apps/server/src/modules.ts 装配文件:${e}`);if(!I(k.join(e,"apps","client","src","modules.ts")))throw new Error(`模板缺 apps/client/src/modules.ts 装配文件:${e}`)}function Pl(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 Qo(e){if(!Number.isInteger(e)||e<=0)return!1;try{return process.kill(e,0),!0}catch{return!1}}var Ll=new Set(["contracts-mall"]);function _l(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 Fl(e,t,r){let n=new Map,s=[],o=c=>{let l=Il(c),p=Ol(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},i=(c,l,p)=>{if(n.has(l))return n.get(l);if(Ll.has(l)){let d=I(k.join(r,"packages",l))?Z(k.join(r,"packages",l)):k.join(t,"modules",l);if(!I(d)){if(!p)return null;throw new Error(`依赖契约包 "${l}" 真源缺席(模板 packages/ 与 modules/ 均无该目录)`)}let f=_l(l,d);return n.set(l,f),f}let u=k.join(t,"modules",l);if(!I(u)){if(!p)return null;throw new Error(`依赖模块 <${c.id}> 声明依赖 "${l}",但仓库无 <repoRoot>/modules/${l}——请确认目录名或先 clone 对应模块`)}return o(Z(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(i(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(o(Z(c)))}return[...n.values()]}function Nl(e){return{id:e.id,pkg:e.pkg,mode:"local",hasServerEntry:Go(e.dir,"server","serverModule"),hasClientEntry:Go(e.dir,"client","clientModule"),hasCards:e.descriptor.contributes.cards.length>0}}function Jl(e,t,r){let n=t.map(Nl),s=[],o=[["server-modules",k.join(e,"apps","server","src","modules.ts"),"modules.server.ts"],["client-modules",k.join(e,"apps","client","src","modules.ts"),"modules.client.ts"]];for(let[i,a,c]of o){let l=K(a,"utf8"),p=l;try{let u=Pt(l,i,n);p=u.content,s.push(...u.warnings)}catch(u){s.push(`[${i}] 锚点变换异常,原文兜底:${u.message}`)}ze(k.join(r,c),p,"utf8")}return s}function zl(e,t,r,n){let s={},o=(a,c)=>{s[a]=k.relative(t,c).split(k.sep).join("/")};for(let a of r)a.skeleton||(o(a.pkg,a.dir),o(`@app/${a.id}`,a.dir));o("@tbox.cn/app-sdk",k.join(e,"packages","app-sdk")),o("@tbox.cn/app-contracts",k.join(e,"packages","contracts"));let i=k.join(e,"packages","agent-ui-kit");I(i)&&(o("@tbox.cn/app-agent-ui-kit",i),o("@app/agent-ui-kit",i));for(let[a,c]of Object.entries(s))if(!I(k.resolve(t,c)))throw new Error(`redirect 目标缺失:${a} → ${c}`);return s}function qo(e,t,r){let n=[],s=0;for(let[o,i]of Object.entries(r)){let a=Z(k.resolve(k.join(e,Ve),i)),c=o.split("/"),l=k.join(e,"node_modules",c[0]),p=k.join(l,c[1]);if(!(()=>{if(!I(p))return!0;let d=Z(p);return d===a?!1:d.startsWith(k.join(t,"modules")+k.sep)||k.join(t,"modules")===d})()){s++;continue}if(I(p)){let d=Z(p);if(!d.startsWith(k.join(t,"modules")))throw new Error(`refuse 覆盖非 dev 管理的既有链接:${p} → ${d}(疑为 workspace/依赖原生链接;请人工确认)`)}Xt(l,{recursive:!0}),$l(k.relative(l,a),p),n.push(o)}return{planted:n,skipped:s}}function Vl(e){let t=JSON.parse(K(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 Bl(e,t,r,n,s=[]){if(n){for(let a of["packages/app-sdk","packages/contracts"])if(!I(k.join(e,a)))throw new Error(`--link-sdk 要求平台真源在场:<repoRoot>/${a}`)}let o={},i;for(let a of s){let c=k.isAbsolute(a)?a:k.resolve(e,a);if(!I(c))throw new Error(`独立配置档目录不存在:${c}(--config-dir 须为存在的目录)`);i=c}if(r){let a=Br(r),c=k.join(e,".real-env","config",a);if(!I(c))throw new Error(`档位目录不存在:${c}(可用:${Cl(e).join("/")})`);i||(i=c)}if(i){let a=k.join(i,"integrations.json");if(I(a)){let c=Vl(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(", ")} 被 ${k.relative(e,i)||i} 档 integrations.json 引用但未随 dev 挂载——请确认 modules/provider-<vendor> 真源在场(--real-env 自动纳入缺席时已 warn)或追加 --module modules/provider-… / 更换 --config-dir / --real-env 档位`);o.TBOX_INTEGRATIONS_FILE=a}o.TBOX_CONFIG_DIR=i}return o}function Wl(e,t,r,n,s,o){let i=JSON.stringify({templateDir:e,tier:s.tier??null,linkSdk:!!s.linkSdk,port:s.port??null,theme:o,redirects:Object.fromEntries(Object.entries(t).sort(([a],[c])=>a<c?-1:1)),configs:Object.fromEntries(Object.entries(r).sort(([a],[c])=>a<c?-1:1)),modules:n.map(a=>`${a.id}@${a.version}`).sort()});return jl("sha256").update(i).digest("hex").slice(0,16)}var Ul=`// tbox-app dev 生成态构件(勿手改;regenerated by 'tbox-app dev')
|
|
105
115
|
import { register } from 'node:module';
|
|
106
116
|
register('./hook-core.mjs', import.meta.url);
|
|
107
|
-
`,
|
|
117
|
+
`,Hl=`// tbox-app dev 生成态构件(勿手改;regenerated by 'tbox-app dev')。
|
|
108
118
|
// resolve 钩子:① 相对 modules.js/modules.ts/modules → 物化装配 ② manifest 前缀重定向(mini exports)。
|
|
109
119
|
import { existsSync, readFileSync } from 'node:fs';
|
|
110
120
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
@@ -158,7 +168,7 @@ export async function resolve(specifier, context, nextResolve) {
|
|
|
158
168
|
}
|
|
159
169
|
return nextResolve(specifier, context);
|
|
160
170
|
}
|
|
161
|
-
`,
|
|
171
|
+
`,Kl=`// tbox-app dev 生成态构件(勿手改;regenerated by 'tbox-app dev')。
|
|
162
172
|
// Vite 插件:client 装配改指物化构件 + manifest 前缀重定向 + fs 门面扩容。
|
|
163
173
|
import { existsSync, readFileSync } from 'node:fs';
|
|
164
174
|
import { fileURLToPath } from 'node:url';
|
|
@@ -166,14 +176,20 @@ import path from 'node:path';
|
|
|
166
176
|
|
|
167
177
|
const HERE = path.dirname(fileURLToPath(import.meta.url));
|
|
168
178
|
const MANIFEST = JSON.parse(readFileSync(path.join(HERE, 'dev-manifest.json'), 'utf8'));
|
|
169
|
-
|
|
179
|
+
// 主题轴:嵌套 redirects 合并(仅 client 侧;server hook-core 不合并——server 永不 import 主题包)
|
|
180
|
+
const REDIRECTS = { ...(MANIFEST.redirects ?? {}), ...(MANIFEST.theme?.redirects ?? {}) };
|
|
181
|
+
const KEYS = Object.keys(REDIRECTS).sort((a, b) => b.length - a.length);
|
|
170
182
|
|
|
171
183
|
function resolveSub(dir, sub) {
|
|
172
184
|
const pkgFile = path.join(dir, 'package.json');
|
|
173
185
|
if (existsSync(pkgFile)) {
|
|
174
186
|
try {
|
|
175
187
|
const pkg = JSON.parse(readFileSync(pkgFile, 'utf8'));
|
|
176
|
-
|
|
188
|
+
// exports 键归一:specifier 子路径 '/x' ↔ exports 键 './x'(主题 css/brand 子路径依赖此映射;
|
|
189
|
+
// 模块 '/server' 先命中原键再回落归一键——严格可加,既有解析次序不变)
|
|
190
|
+
const exp = pkg.exports
|
|
191
|
+
? pkg.exports[sub === '' ? '.' : sub] ?? pkg.exports['.' + sub]
|
|
192
|
+
: undefined;
|
|
177
193
|
const target =
|
|
178
194
|
typeof exp === 'string'
|
|
179
195
|
? exp
|
|
@@ -210,45 +226,50 @@ export default function tboxDevOverride() {
|
|
|
210
226
|
const hit = KEYS.find((k) => base === k || base.startsWith(k + '/'));
|
|
211
227
|
if (hit) {
|
|
212
228
|
const sub = base === hit ? '' : base.slice(hit.length);
|
|
213
|
-
return resolveSub(path.resolve(HERE,
|
|
229
|
+
return resolveSub(path.resolve(HERE, REDIRECTS[hit]), sub) ?? null;
|
|
214
230
|
}
|
|
215
231
|
return null;
|
|
216
232
|
},
|
|
217
233
|
config() {
|
|
218
234
|
// dev 源解析域白名单收敛(安全:/@fs 经 0.0.0.0 监听面可达,禁止 repoRoot 全仓——
|
|
219
|
-
// 恒不含 .real-env/docs/dotfiles
|
|
235
|
+
// 恒不含 .real-env/docs/dotfiles)。六源 = 模板根 + 模板家族目录 + 模块/平台真身 +
|
|
236
|
+
// 依赖库 + 主题素材(F1 修复:css url() 大资产不经 JS import-analysis,/@fs 被
|
|
237
|
+
// fs.allow 拒——allow 列表必须与装配轴同步扩容;dev --theme redirect 目标同域)。
|
|
220
238
|
const allow = [
|
|
221
239
|
MANIFEST.templateDir,
|
|
222
240
|
path.join(MANIFEST.repoRoot, 'templates'),
|
|
223
241
|
path.join(MANIFEST.repoRoot, 'modules'),
|
|
224
242
|
path.join(MANIFEST.repoRoot, 'packages'),
|
|
225
243
|
path.join(MANIFEST.repoRoot, 'node_modules'),
|
|
244
|
+
path.join(MANIFEST.repoRoot, 'themes'),
|
|
226
245
|
];
|
|
227
246
|
return {
|
|
228
247
|
server: { fs: { allow } },
|
|
229
|
-
// identity 单向代价(R-9b):redirects
|
|
230
|
-
optimizeDeps: { exclude: Object.keys(
|
|
248
|
+
// identity 单向代价(R-9b):redirects 键(含主题轴合并键)全部豁免预打包,冷启动略慢
|
|
249
|
+
optimizeDeps: { exclude: Object.keys(REDIRECTS) },
|
|
231
250
|
};
|
|
232
251
|
},
|
|
233
252
|
};
|
|
234
253
|
}
|
|
235
|
-
`;async function
|
|
236
|
-
`),
|
|
237
|
-
`),
|
|
238
|
-
|
|
239
|
-
|
|
254
|
+
`;async function ei(e){let t=Ml();if(e.prune){let g=e.template?k.resolve(t,e.template):k.resolve(t,Ho);if(!I(g))throw new Error(`--template 目录不存在:${g}`);await ql(Z(g),t);return}Pl(e.realEnv);let r=Z(e.template?k.resolve(t,e.template):k.resolve(t,Ho));if(!I(k.join(r,".tbox","app.json")))throw new Error(`--template 目录缺 .tbox/app.json(不是应用/模板根?):${r}`);Al(r);let n=[];if(e.profile){let g=k.join(r,"profiles"),b=k.join(g,`${e.profile}.json`);if(!I(b)){let D=I(g)?Vr(g).filter(R=>R.endsWith(".json")).map(R=>R.replace(/\.json$/,"")).join(", "):"";throw new Error(`画像不存在: ${e.profile}(可用: ${D||"模板无 profiles/ 目录"})`)}let x=JSON.parse(K(b,"utf8")),j=k.join(t,"modules");for(let D of x.modules??[]){let R=wt(j,D);if(!R)throw new Error(`画像模块 ${D} 无 in-repo 真源(<repoRoot>/modules/${D.replace(/^@tbox\.cn\/app-/,"")} 缺 tbox.module.json)——dev 为真源直挂形态,registry 兜底不适用`);n.push(Z(R))}}let s=k.join(r,Ve),o=k.join(s,"session.lock"),i=k.join(s,"dev-manifest.json"),a=(e.module??[]).map(g=>k.resolve(t,g));if(e.realEnv){let g=k.join(t,"modules",`provider-${Br(e.realEnv)}`);I(k.join(g,"tbox.module.json"))?a.unshift(Z(g)):console.warn(`⚠️ 档位 ${e.realEnv} 无 provider 真源(modules/provider-${Br(e.realEnv)})——裸跑继续,N⊆H 校验把关`)}let c;if(e.theme){if(!Ht.test(e.theme))throw new Error(`非法主题 id "${e.theme}"——仅允许小写字母/数字/中划线(themes/<id> 目录约定)`);let g=k.join(t,"themes"),b=k.join(g,`theme-${e.theme}`);if(!I(k.join(b,"package.json"))){let A=I(g)?Vr(g).filter(E=>E.startsWith("theme-")&&I(k.join(g,E,"package.json"))).map(E=>E.slice(6)).join(", "):"";throw new Error(`主题不存在:themes/theme-${e.theme}(可用 id: ${A||"无"})`)}let x=JSON.parse(K(k.join(b,"package.json"),"utf8"));if(x.name!==`@tbox.cn/app-theme-${e.theme}`)throw new Error(`主题包名与目录不一致:themes/theme-${e.theme} name=${x.name??"(缺)"}(期望 @tbox.cn/app-theme-${e.theme})`);let j=x.exports??{};if(j["."]===void 0&&j["./index.css"]===void 0)throw new Error(`主题包缺 css 入口 exports('.' 或 './index.css'):themes/theme-${e.theme}`);let D=K(k.join(r,"apps","client","src","main.tsx"),"utf8"),R=Ce(D);if(R.hasBrandJs&&j["./brand.js"]===void 0)throw new Error(`出厂接线含 brand.js 行,而 themes/${e.theme} exports 无 './brand.js'——换线将残留出厂标题(混合态),拒`);if(R.factoryId===e.theme)console.log(`🎨 theme=${e.theme}(= 出厂接线,no-op)`);else{let A=Z(b),E=k.relative(s,A).split(k.sep).join("/");c={id:e.theme,redirects:{[R.factoryPkg]:E,"@tbox.cn/app-agent-ui-kit/theme":"theme","@app/agent-ui-kit/theme":"theme"}}}}let l=Fl([...n,...a],t,r);if(l.length===0)throw new Error("未指定任何模块:请传 --module modules/<id> 或 --profile <name>(至少一个)");let p=zl(t,s,l,!!e.linkSdk),u=Bl(t,l,e.realEnv,!!e.linkSdk,e.configDir??[]),d=Wl(r,p,u,l,e,c?.id??null),f={version:1,templateDir:r,repoRoot:t,tier:e.realEnv,profile:e.profile,...c?{theme:c}:{},linkSdk:!!e.linkSdk,redirects:p,modules:l.map(g=>({id:g.id,pkg:g.pkg,dir:k.relative(t,g.dir),kind:g.descriptor.kind})),configs:u};if(I(k.join(r,"pnpm-lock.yaml"))&&!I(k.join(r,"pnpm-workspace.template.yaml"))){let g=["⚠️ 目标为生成应用(dev-on-generated-app):跨仓 dev 属未保障形态,React/库可能双物理副本(dedupe 部分兜底)"];try{let b=JSON.parse(K(k.join(r,"apps","client","node_modules","react","package.json"),"utf-8")).version,x=te(t)?.get("react");b&&x&&b!==x&&g.push(`版本漂移 app=${b} vs 仓 catalog=${x}`)}catch{}console.warn(g.join(";"))}if(I(o)){let g=null;try{g=JSON.parse(K(o,"utf8"))}catch{}if(g&&Qo(g.pid)&&g.pid!==process.pid){if(g.hash===d){console.log(`↩︎ 检测到同集合 dev 会话(pid=${g.pid})——attach 模式,直接拉起进程`),Xt(s,{recursive:!0});let b=qo(r,t,p);console.log(`🔗 链接断言:planted=${b.planted.length} skipped=${b.skipped}`),await Yo(r,u,e.port,o,p);return}throw new Error(`已有不同集合的 dev 会话运行中(pid=${g.pid},集合 ${g.hash} ≠ 本次 ${d})——并发会话会互相覆盖 .tbox-dev.local/ 构件。请先停掉对方或运行 'tbox-app dev --prune'`)}console.log("🧹 吸收残留死锁(pid 已不存在)")}Xt(s,{recursive:!0});let m=Jl(r,l,s);for(let g of m)console.warn(`⚠️ ${g}`);let h=qo(r,t,p);if(console.log(`🔗 链接种植:planted=${h.planted.length} skipped=${h.skipped}`),Ie(k.join(s,"package.json"),JSON.stringify({type:"module"},null,2)+`
|
|
255
|
+
`),ze(k.join(s,"boot.mjs"),Ul,"utf8"),ze(k.join(s,"hook-core.mjs"),Hl,"utf8"),ze(k.join(s,"vite-override.mjs"),Kl,"utf8"),c){let g=k.join(s,"theme");Xt(g,{recursive:!0}),ze(k.join(g,"index.ts"),["// tbox-app dev 生成态构件(勿手改;regenerated by 'tbox-app dev')","// applyTheme 拦截 shim:本地导出恒胜 star 导出(主题轴数据面——变换在生成期完成)","import { applyTheme as __kit } from '@tbox.cn/app-agent-ui-kit';","export { currentTheme } from '@tbox.cn/app-agent-ui-kit';",`export function applyTheme(_id: string): void { __kit('${c.id}'); }`,""].join(`
|
|
256
|
+
`),"utf8"),ze(k.join(g,"package.json"),JSON.stringify({name:"@tbox-dev/theme-shim",type:"module",exports:{".":"./index.ts"}},null,2)+`
|
|
257
|
+
`,"utf8")}Ie(i,JSON.stringify(f,null,2)+`
|
|
258
|
+
`),Ie(o,JSON.stringify({pid:process.pid,cmd:"tbox-app dev",hash:d,ts:new Date().toISOString()},null,2)+`
|
|
259
|
+
`);let v=e.port??Dl;console.log("─".repeat(64)),console.log(`📦 dev 模块集:${l.map(g=>g.id).join(", ")}`),console.log(`🖼 profile=${e.profile??"—"}`),e.theme&&console.log(`🎨 theme=${c?e.theme:`${e.theme}(出厂同款)`}`),Object.keys(u).length>0?(console.log(`🗂 integrations=${u.TBOX_INTEGRATIONS_FILE??"— (absent:登录将落内置换码器,缺 ALIPAY_* 凭证即 503)"}`),console.log(`🗂 config-dir=${u.TBOX_CONFIG_DIR??"—"}`)):console.log("🗂 configs:无注入(未指定 --real-env / --config-dir;裸跑形态)"),console.log(`🚀 dev ready → http://127.0.0.1:${v}`),console.log("ℹ️ dev 链接形态 ≠ codegen 发布态(L2≠L3):发布判据仍以 verify-cli 为准"),console.log("─".repeat(64)),await Yo(r,u,e.port,o,p)}async function Yo(e,t,r,n,s={}){let o=k.join(e,...Rl),a=JSON.parse(K(k.join(o,"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=k.join(e,Ve,"boot.mjs"),u=[...new Set(Object.values(s).map(v=>k.resolve(k.join(e,Ve),v)))],d=["--watch-path=./src",`--watch-path=${k.join(e,Ve)}`,...u.map(v=>`--watch-path=${v}`)],f={...process.env,...t,...r!==void 0?{PORT:String(r)}:{}},m=xl("node",[...d,"--watch","--import","tsx","--import",p,l],{cwd:o,env:f,stdio:"inherit",detached:!0});Zt=m.pid,Gl();let h=await new Promise(v=>{let g=!1,b=x=>{g||(g=!0,Zt=void 0,v(x))};m.on("exit",(x,j)=>b(x??(j?130:1))),m.on("error",x=>{console.error("❌ dev 子进程启动失败:",x),b(1)})});try{Zo(n)}catch{}process.exitCode=h??1}var Zt,Xo=!1;function Gl(){if(Xo)return;Xo=!0;let e=t=>{if(Zt)try{process.kill(-Zt,t)}catch{}};process.on("SIGINT",()=>e("SIGINT")),process.on("SIGTERM",()=>e("SIGTERM"))}async function ql(e,t){let r=k.join(e,Ve),n=k.join(r,"dev-manifest.json"),s=k.join(r,"session.lock");if(!I(r)){console.log("✨ 构件区不存在,无需清理");return}if(I(s))try{let i=JSON.parse(K(s,"utf8"));if(i.pid!==process.pid&&Qo(i.pid))throw new Error(`dev 会话仍在运行(pid=${i.pid})——请先 Ctrl+C 停止再 --prune`)}catch(i){if(i.message.startsWith("dev 会话"))throw i}let o=0;if(I(n))try{let i=JSON.parse(K(n,"utf8"));for(let a of Object.keys(i.redirects??{})){let c=a.split("/"),l=k.join(e,"node_modules",c[0],c[1]);if(!I(l))continue;let p=Z(l);[k.join(t,"modules"),k.join(t,"packages")].some(d=>p===d||p.startsWith(d+k.sep))&&(Zo(l),o++)}}catch{}Sl(r,{recursive:!0,force:!0}),console.log(`✨ prune 完成:构件区已清、种植链接回收 ${o} 条(模板:${k.relative(t,e)})`)}import{readFileSync as Yl}from"fs";var Qt=[{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",profile:"mall",documentFile:"agent-mall.md"}];function Xl(e){return Yl(new URL(`../starter/${e}`,import.meta.url),"utf8")}function Zl(e){let t=Qt.map(({type:r,description:n,templatePackage:s})=>({type:r,description:n,templatePackage:s}));return e?JSON.stringify({starters:t},null,2)+`
|
|
260
|
+
`:["可用 starter 类型:",...t.map(r=>` ${r.type.padEnd(11)} ${r.description}(${r.templatePackage})`),"",`查询文档: tbox-app starter --type <${Qt.map(r=>r.type).join("|")}>`].join(`
|
|
240
261
|
`)+`
|
|
241
|
-
`}function
|
|
262
|
+
`}function ti(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(Zl(!!e.json));return}let t=Qt.find(r=>r.type===e.type);if(!t)throw new Error(`未知 starter 类型: ${e.type}(可用:${Qt.map(r=>r.type).join("、")};使用 --list 查看)`);process.stdout.write(Xl(t.documentFile))}import{createHash as tp}from"crypto";import{readdir as rp,readFile as np,stat as sp}from"fs/promises";import{join as Be,relative as op,basename as ii}from"path";import{parse as ip}from"yaml";import{z as lt}from"zod";import{z as y}from"zod";var Wr=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(),Ur=y.object({version:y.literal(1),description:y.string().default(""),standards:y.array(y.string().min(1)).min(1)}).strict(),ri=y.union([y.string().min(1),y.array(y.string().min(1)).min(1)]),si=y.union([ri,y.object({any_of:y.array(ri).min(1)}).strict()]),Ql=si;function ni(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 ep=y.object({expect:si,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=>!ni(e.expect).includes(t)),{message:"optional 与 expect 重叠(optional 豁免会使 expect 永不满足)"}).refine(e=>e.forbid.every(t=>!ni(e.expect).includes(t)),{message:"forbid 与 expect 重叠(契约自相矛盾,恒 fail)"}),Hr=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:ep,cards:y.object({expect:Ql}).strict().optional(),assertions:y.array(y.string().trim().min(1,"不能为空白")).min(1),judge_hints:y.array(y.string().min(1)).default([])}).strict(),cy=y.object({suite:Wr,rubric:Ur,cases:y.array(Hr).min(1)}).strict(),ct=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(),oi=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 ap=lt.object({tools:lt.array(lt.string().min(1)).min(1),cards:lt.array(lt.string().min(1)).min(1)}).strict(),cp=/^[a-zA-Z][a-zA-Z0-9_-]*$/,lp=new Set(["none","direct-out"]);function Kr(e){return typeof e=="string"?[e]:Array.isArray(e)?e:e.any_of.flatMap(Kr)}function pp(e){return[...Kr(e.expect),...e.optional,...e.forbid,...e.pre]}function ci(e,t){return e<t?-1:e>t?1:0}var up=(e,t)=>ci(e.name,t.name);function ai(e){return tp("sha256").update(e.join(`
|
|
242
263
|
\0
|
|
243
|
-
`)).digest("hex")}async function
|
|
264
|
+
`)).digest("hex")}async function er(e,t,r){let n;try{n=await np(e,"utf-8")}catch(i){throw new Error(`读取 ${r} 失败(${e}): ${i.message}`)}let s;try{s=ip(n)}catch(i){throw new Error(`${r} YAML 语法错误(${e}): ${i.message}`)}let o=t.safeParse(s);if(!o.success){let i=o.error.issues.map(a=>` - ${a.path.join(".")||"<root>"}: ${a.message}`).join(`
|
|
244
265
|
`);throw new Error(`${r} 契约校验失败(${e}):
|
|
245
|
-
${
|
|
246
|
-
`).slice(1).join(";")
|
|
266
|
+
${i}`)}return o.data}async function dp(e){let t=[],r=async n=>{let s;try{s=await rp(n,{withFileTypes:!0})}catch{return}for(let o of s.sort(up)){let i=Be(n,o.name);o.isDirectory()?await r(i):o.isFile()&&(o.name.endsWith(".yaml")||o.name.endsWith(".yml"))&&t.push(i)}};return await r(e),t.sort()}async function tr(e){let t=Be(e,"suite.yaml"),r=Be(e,"rubric.yaml"),n=await er(t,Wr,"suite.yaml"),s=await er(r,Ur,"rubric.yaml"),o=await dp(Be(e,"cases"));if(o.length===0)throw new Error(`数据集为空:${Be(e,"cases")} 下未发现任何 .yaml 用例`);let i=[],a=[],c=new Map;for(let g of o){let b=op(e,g);try{let x=await er(g,Hr,"case");ii(g).replace(/\.ya?ml$/,"")!==x.id&&a.push(` - ${b}: 文件名与用例 id 不符(文件 ${ii(g)},id ${x.id})`),i.push(x),c.set(x.id,b)}catch(x){a.push(` - ${b}: ${x.message.split(`
|
|
267
|
+
`).slice(1).join(";")||x.message}`)}}let l=new Map;for(let g of i)l.set(g.id,(l.get(g.id)??0)+1);let p=[...l.entries()].filter(([,g])=>g>1);if(p.length>0&&a.push(` - 用例 id 重复: ${p.map(([g,b])=>`${g}×${b}`).join(", ")}`),n.driver==="support")for(let g of i)g.turns.some(b=>b.newSession)&&a.push(` - ${c.get(g.id)??`cases/${g.id}.yaml`}: driver=support 暂不支持 newSession(support 会话绑定连接)`);let u=null,d=Be(e,"tools-manifest.yaml"),f=!1;try{f=(await sp(d)).isFile()}catch{f=!1}if(f){let g=await er(d,ap,"tools-manifest.yaml");u={tools:new Set(g.tools),cards:new Set(g.cards)};for(let b of i){let x=c.get(b.id)??`cases/${b.id}.yaml`;for(let j of pp(b.tools))lp.has(j)||(cp.test(j)?u.tools.has(j)||a.push(` - ${x}: 工具引用 '${j}' 不在词表(tools-manifest.yaml)——检查拼写或更新词表`):a.push(` - ${x}: 工具引用 '${j}' 非法形态(须 ^[a-zA-Z][a-zA-Z0-9_-]*$)——疑似期望文案粘连进 DSL`));if(b.cards)for(let j of Kr(b.cards.expect))u.cards.has(j)||a.push(` - ${x}: 卡片引用 '${j}' 不在词表(tools-manifest.yaml cards 节)`)}}if(a.length>0)throw new Error(`数据集校验失败(${e},${a.length} 处):
|
|
247
268
|
${a.join(`
|
|
248
|
-
`)}`);let
|
|
249
|
-
`)){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
|
|
250
|
-
`)}function
|
|
269
|
+
`)}`);let m=ai([JSON.stringify(s)]),h=[...i].sort((g,b)=>ci(g.id,b.id)),v=ai([JSON.stringify(n),JSON.stringify(s),...h.map(g=>JSON.stringify(g))]);return{dir:e,suite:n,rubric:s,cases:i,datasetHash:v,rubricHash:m,manifest:u}}function li(e){let t={},r={},n={},s={},o=0,i=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&&(o+=1),a.tools.mode==="soft"&&(i+=1)}return{total:e.cases.length,evaluated:e.cases.filter(a=>!a.skip).length,skippedByReason:t,byPriority:r,byCaseType:n,sessionHistogram:s,multiTurnCases:o,softModeCases:i}}import{randomBytes as Bp}from"crypto";import{mkdir as Wp,readFile as Li}from"fs/promises";import{join as Qr}from"path";import{readdir as pi,readFile as fi}from"fs/promises";import{join as ui}from"path";var G=class extends Error{constructor(t){super(t),this.name="InfraError"}},di=400,fp=1e4,mp=1500,gp=300;async function Gr(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(i){throw new G(`dev-login 请求失败(${e}): ${i.message}`)}if(!s.ok)throw new G(`dev-login 失败 HTTP ${s.status}(devUser=${t};prod 档会 403——检查实例启动语义)`);let o=await s.json().catch(()=>{});if(!o?.token)throw new G("dev-login 响应缺 token");return o.token}function hp(e){if(e==null)return;let t=typeof e=="string"?e:JSON.stringify(e);return t.length>di?`${t.slice(0,di)}…`:t}function yp(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 o=t.preTools.get(n);o&&(o.argsRaw=(o.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=hp(e.content);break}let o=t.preTools.get(n);o&&(o.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 vp(e,t,r,n,s){let o=[];for(let a of e.toolCalls.values()){let c;if(a.argsBuf)try{c=JSON.parse(a.argsBuf)}catch{c=void 0}o.push({name:a.name,...c!==void 0?{args:c}:{},...a.isError?{isError:!0}:{},...a.resultSummary!==void 0?{resultSummary:a.resultSummary}:{}})}let i=[];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&&i.push({name:c,outcome:l==="error"?"error":l==="done"?"ok":l??"ok"})}return{index:t,conversationId:r,userText:n,reply:e.reply,toolCalls:o,preTools:i,cards:e.cards,runStatus:e.runStatus,durationMs:s}}function wp(e){let t=e.WebSocketCtor??globalThis.WebSocket;if(typeof t!="function")throw new G("当前 Node 缺少全局 WebSocket:评测驱动需要 Node ≥22(仓库基准 Node 24)");return t}async function mi(e,t,r,n,s){let o=await Gr(e.baseUrl,r,e.fetchFn),i=`${e.baseUrl.replace(/^http/,"ws")}/ws`,a=wp(e),c=await new Promise((f,m)=>{let h,v=setTimeout(()=>m(new G(`WS 连接超时(${i})`)),fp);try{h=new a(i)}catch(j){clearTimeout(v),m(new G(`WS 创建失败: ${j.message}`));return}let g=()=>{x(),f(h)},b=()=>{x(),m(new G(`WS 连接失败(${i})`))},x=()=>{clearTimeout(v),h.removeEventListener("open",g),h.removeEventListener("error",b)};h.addEventListener("open",g),h.addEventListener("error",b)}),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((g,b)=>{let x=R=>{let A=R.data;try{let E=JSON.parse(A);if(E.type==="HELLO_ACK"){let w=E.supportSession;typeof w?.publicConversationId=="string"&&(f=w.publicConversationId)}["RUN_STARTED","HELLO_ACK","TEXT_MESSAGE_START"].includes(E.type)&&(j(),g())}catch{}},j=()=>{clearTimeout(D),c.removeEventListener("message",x)},D=setTimeout(()=>{j(),g()},mp);c.addEventListener("message",x);try{c.send(JSON.stringify({type:"HELLO",token:o}))}catch(R){j(),b(new G(`WS send 失败(连接已关闭?): ${R.message}`))}}),d==="support"&&f===void 0)throw new G("support 驱动未取得服务端会话 id(HELLO_ACK 缺席或无 supportSession.publicConversationId)");let m=1,h=d==="support"?f:`evalconv-${n}-${t.id}-s${m}`,v=!1;for(let g=0;g<t.turns.length;g++){let b=t.turns[g];if(g>0&&b.newSession){if(d==="support")throw new G(`support 驱动暂不支持 newSession(用例 ${t.id})——会话绑定在连接上,跨会话需重连握手`);m+=1,h=`evalconv-${n}-${t.id}-s${m}`}let x={reply:"",toolCalls:new Map,preTools:new Map,cards:[],runStatus:"timeout",started:!v},j=Date.now();await new Promise((D,R)=>{p=L=>{w(),R(new G(L))};let E=L=>{try{let S=JSON.parse(L.data);yp(S,x),x.runStatus!=="timeout"&&(w(),D())}catch{}},w=()=>{clearTimeout(O),c.removeEventListener("message",E),p=()=>{}},O=setTimeout(()=>{w(),D()},s);c.addEventListener("message",E);try{c.send(JSON.stringify({type:"SEND_MESSAGE",content:b.text,conversationId:h}))}catch(L){w(),R(new G(`WS send 失败(连接已关闭?): ${L.message}`))}}),l.push(vp(x,g,h,b.text,Date.now()-j)),v=x.runStatus==="timeout",d==="support"&&g<t.turns.length-1&&await new Promise(D=>setTimeout(D,gp))}}finally{p=()=>{};try{c.removeEventListener("error",u),c.removeEventListener("close",u),c.close()}catch{}}return{caseId:t.id,devUser:r,turns:l}}async function kp(e,t){let r;try{r=await pi(e)}catch{return}for(let n of r){let s;try{s=await pi(ui(e,n))}catch{continue}for(let o of s){let i=ui(e,n,o,"conversations",t,"journal.jsonl");try{return await fi(i,"utf-8"),i}catch{continue}}}}async function gi(e,t){try{let r={},n=[...new Set(e.turns.map(s=>s.conversationId))];for(let s of n){let o=await kp(t,s);if(!o)continue;let i=await fi(o,"utf-8");for(let a of i.split(`
|
|
270
|
+
`)){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 bp="__tbox_",xp="agent-ui-cards";function rr(e){return!e.startsWith(bp)&&!e.startsWith(xp)}function yi(e,t,r="agui"){let n=r==="support"?{dim:"tools",status:"na",detail:["support 驱动:TOOL_CALL_* 不出边界,tools 维不判定"]}:Ep(e.tools,t);return[Sp(t),n,Dp(e,t)]}function Sp(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 vi(e){return typeof e=="string"?e:Array.isArray(e)?`[${e.join(" + ")}]`:`any_of(${e.any_of.map(vi).join(" | ")})`}function $p(e){let t=new Set;for(let r of e.turns)for(let n of r.toolCalls)rr(n.name)&&t.add(n.name);return t}function jp(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 wi(e,t,r){return typeof e=="string"?hi(e,t,r):Array.isArray(e)?e.every(n=>hi(n,t,r)):e.any_of.some(n=>wi(n,t,r))}function hi(e,t,r){return e==="none"||e==="direct-out"?t.size===0:t.has(e)||r.has(e)}function ki(e){return typeof e=="string"?e==="none"||e==="direct-out"?[]:[e]:Array.isArray(e)?e:e.any_of.flatMap(t=>ki(t))}function Ep(e,t){let r=$p(t),n=jp(t),s=new Set(e.optional),o=new Set([...r].filter(f=>!s.has(f))),i=new Set([...r,...n]),a=[],c=!1,l=ki(e.expect).filter(f=>!r.has(f)&&n.has(f));wi(e.expect,o,i)?l.length>0&&a.push(`note: expect 经 preTool 预执行命中 [${l.sort().join(", ")}](模型侧未显式调用)`):(c=!0,a.push(`期望 ${vi(e.expect)},实际业务调用 [${[...r].sort().join(", ")||"无"}] / preTool [${[...n].sort().join(", ")||"无"}]`)),e.expect==="direct-out"&&o.size===0&&t.journal?.intentDirectOut!==!0&&a.push("degraded: journal 缺席或无 intent_route 佐证,direct-out 退化为 none 判定");let u=e.forbid.filter(f=>o.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 Rp(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 Dp(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(i=>typeof i=="string"?[i]:i),s=Rp(t);return(Array.isArray(r)||typeof r=="string"?n.every(i=>s.has(i)):n.some(i=>s.has(i)))?{dim:"cards",status:"pass",detail:[]}:{dim:"cards",status:"fail",detail:[`期望卡片 [${n.join(", ")}] 未齐,实际出卡 [${[...s].sort().join(", ")||"无"}]`]}}import{mkdir as bi,writeFile as nr}from"fs/promises";import{join as We}from"path";function xi(e){let t={},r={priority:{},primary_category:{},case_type:{}},n=[],s=0,o=(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},i=(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 m of c.dims)l[m.dim]=m.status,o(m.dim,m.status),m.status==="fail"&&p.push(`${m.dim}: ${m.detail.join(";")}`);let u=c.textStatus;l.text=u==="pass"?"pass":u==="fail"?"fail":"na",u==="pass"?o("text","pass"):u==="fail"?(o("text","fail"),p.push(...c.textFailures&&c.textFailures.length>0?c.textFailures:["text: LLM 判定失败(无明细)"])):o("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";i("priority",c.spec.priority,f),i("primary_category",c.spec.tags[0]??"<none>",f),i("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 ct.parse(a)}function Si(e,t){let r=new Map(t.cases.map(o=>[o.id,o.status])),n=new Map(e.cases.map(o=>[o.id,o.status])),s={newFailed:[],fixed:[],flipped:[]};for(let[o,i]of n){let a=r.get(o);a===void 0||a===i||(a!=="fail"&&i==="fail"?s.newFailed.push(o):a==="fail"&&i!=="fail"?s.fixed.push(o):s.flipped.push({id:o,from:a,to:i}))}return s}async function $i(e,t,r,n){await bi(We(e,"traces"),{recursive:!0}),await nr(We(e,"report.json"),JSON.stringify(t,null,2),"utf-8"),await nr(We(e,"report.md"),Tp(t),"utf-8");for(let[s,o]of r)await nr(We(e,"traces",`${s}.json`),JSON.stringify(o,null,2),"utf-8");if(n.size>0){await bi(We(e,"judge"),{recursive:!0});for(let[s,o]of n)await nr(We(e,"judge",`${s}.md`),o,"utf-8")}}function Cp(e){return e.sort((t,r)=>r[1].total-t[1].total).map(([t,r])=>`${t} ${r.pass}/${r.total}`).join(" · ")}function Tp(e,t){let r=c=>e.cases.filter(l=>l.status===c),n=r("fail"),s=r("inconclusive"),o=r("skipped"),i=[];i.push(`# 评测报告 ${e.runId}`,""),i.push(`- 数据集: ${e.dataset.name}(${e.dataset.caseCount} 例,hash ${e.dataset.hash.slice(0,12)})`),i.push(`- 实例: ${e.instance.baseUrl}${e.instance.degraded?"(degraded:journal 不可达)":""}`),i.push(`- judge: ${e.judgeModel??"未启用"} rubric: ${e.rubricHash.slice(0,12)}`);let a=["pass","fail","inconclusive","skipped"].map(c=>`${c}=${r(c).length}`).join(" ");i.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 维降级,需人工复核)`:""}`,""),i.push("## 维度","");for(let[c,l]of Object.entries(e.dimensions))i.push(`- ${c}: pass=${l.pass} fail=${l.fail} na=${l.na}`);i.push("","## 切片","");for(let[c,l]of Object.entries(e.slices))i.push(`- ${c}: ${Cp(Object.entries(l))}`);if(t&&(i.push("","## 基线对比",""),i.push(`- 新失败: ${t.newFailed.length>0?t.newFailed.join(", "):"无"}`),i.push(`- 已修复: ${t.fixed.length>0?t.fixed.join(", "):"无"}`),t.flipped.length>0&&i.push(`- 翻转: ${t.flipped.map(c=>`${c.id} ${c.from}→${c.to}`).join(", ")}`)),n.length>0){i.push("","## 失败用例","");for(let c of n){i.push(`### ${c.id}(${Object.entries(c.dims).filter(([,l])=>l==="fail").map(([l])=>l).join("+")||"?"})`);for(let l of c.failures)i.push(`- ${l}`);i.push("")}}if(s.length>0){i.push("## Inconclusive(judge 全 unclear,不计失败)","");for(let c of s)i.push(`- ${c.id}`);i.push("")}if(o.length>0){i.push("## Skipped","");for(let c of o)i.push(`- ${c.id}(${c.skipReason})`);i.push("")}return i.join(`
|
|
271
|
+
`)}function ji(e){return{userText:e.userText,reply:e.reply.length>4e3?`${e.reply.slice(0,4e3)}…`:e.reply,tools:e.toolCalls.filter(t=>rr(t.name)).map(t=>({name:t.name,...t.resultSummary!==void 0?{result:t.resultSummary}:{}})),framework:e.toolCalls.filter(t=>!rr(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 Ue}from"zod";function Ei(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 Mp(e){let t=e.match(/```(?:json)?\s*([\s\S]*?)\s*```/);return t?t[1]:e}async function Ri(e,t,r,n,s){let o=s?.fetchFn??fetch,i=[{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}
|
|
251
272
|
|
|
252
|
-
(上次输出无法解析为 JSON,请只输出 JSON,不要任何其他文字)`,u;try{u=await
|
|
253
|
-
`),s=t.turns.map((
|
|
254
|
-
`)}}function ei(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 ti(e,t,r,n,s){let{system:i,user:o}=ql(r,n,t),{parsed:a,raw:c}=await Qo(e,i,o,Gl,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 ri,spawnSync as Xl}from"child_process";import{existsSync as si,openSync as Yl,readFileSync as Zl}from"fs";import{rm as Ql,writeFile as ep}from"fs/promises";import{join as be}from"path";var Or="apps/server",Ar="dist/index.js",Pr=".tbox-server.json",ni=".tbox-server.log",oi=60,tp=1e3;function Lr(e){return si(be(e,Or,"package.json"))?e:null}function tt(e){let t;try{t=Zl(be(e,Pr),"utf-8")}catch{return null}try{return Mo.parse(JSON.parse(t))}catch{return null}}async function rp(e,t){await ep(be(e,Pr),JSON.stringify(t,null,2),"utf-8")}async function Kt(e){await Ql(be(e,Pr),{force:!0})}function np(e){return`${e.replace(/\/$/,"")}/api/health`}async function Ht(e,t){let r=t?.attempts??oi,n=t?.intervalMs??tp;for(let s=0;s<r;s++){try{if((await fetch(np(e))).ok)return!0}catch{}await new Promise(i=>setTimeout(i,n))}return!1}async function ii(e){if(!Lr(e.appDir))throw new Error(`当前目录不是应用根(缺 ${Or}/package.json): ${e.appDir}`);let t=be(e.appDir,Or),r=be(t,Ar);if(e.build){let l=Xl("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(!si(r))throw new Error("应用未构建:先 --build,或使用 `tbox-app eval run --base-url` 评测外部实例");let n=tt(e.appDir);if(n&&await Ht(n.baseUrl,{attempts:2,intervalMs:500}))throw new Error(`已在运行 ${n.baseUrl}(pid=${n.pid});tbox-app stop 后再启动`);await Kt(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=Yl(be(e.appDir,ni),"a");a=ri("node",[Ar],{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=ri("node",[Ar],{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 Ht(o.baseUrl)){try{e.detached?process.kill(-o.pid,"SIGKILL"):a.kill("SIGKILL")}catch{}throw new Error(`server ${oi}s 未就绪(${o.baseUrl};日志 ${ni})`)}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 rp(e.appDir,o),{child:a,state:o}}async function ai(e,t){await sp(t.pid,t.detached,void 0),await Kt(e)}async function sp(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 li=5;function pe(e,t){e||console.log(`[eval] ${t}`)}function ap(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 pi(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 Ut(e.dataset),r=ap(t.cases,e.filters);if(e.rerunFailedDir){let w=await ci(Fr(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 Ht(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?Zo(process.env):null,c=op(4).toString("hex"),l=`eval-${c}`,p=e.outDir??Fr(e.cwd,".eval","runs",`${lp()}-${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 Vo({baseUrl:n,driver:t.suite.driver??"agui",...e.deps},w,D,c,t.suite.defaultTurnTimeoutMs),o&&(M=await Jo(M,o)),f.set(w.id,M)}catch(j){if(j instanceof H){m+=1,h+=1,h>=li&&(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,`熔断(连续 ${li} 个 infra 错误),中止余量 ${R.length} 例`);continue}throw j}h=0;let k=Uo(w,M,t.suite.driver??"agui");if(a)try{let{verdicts:j,raw:ee}=await ti(a,t.rubric,w,M,e.deps);g.set(w.id,ee),b=ei(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 cp(n,$.map(w=>`eval-${c}-${w.id}`),e.deps?.fetchFn,e.quiet);let N=Go({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 ci(e.baselinePath,"utf-8"),D=Qe.parse(JSON.parse(w));P=qo(N,D)}return await ip(p,{recursive:!0}),await Xo(p,N,f,g),console.log(`报告: ${Fr(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 cp(e,t,r,n){let s=r??fetch;for(let i of t)try{let o=await Ir(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 lp(){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 ui(e){let t=await Ut(e.dataset),r=Po(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 di(e){let t=await pi({...e,cwd:process.cwd()});process.exitCode=t.exitCode}async function fi(e){let t=process.cwd();if(!Lr(t))throw new Error("当前目录不是应用根(缺 apps/server/package.json);请在生成应用目录执行");let r=await ii({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 Kt(t),console.log("server 已退出")}import{spawnSync as pp}from"child_process";import{existsSync as up}from"fs";import{join as dp}from"path";async function mi(){let e=process.cwd(),t=tt(e);if(t){if(t.detached)await ai(e,t);else{console.log(`server 运行中(前台 pid=${t.pid},${t.baseUrl})——在其终端 Ctrl+C 停止`);return}console.log(`✅ 已停止(pid=${t.pid})`);return}let r=dp(e,"scripts","stop.sh");if(up(r)){let n=pp("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 yp(){let e=new fp,t=mp(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 dn({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{Ro(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 zs({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 Bs(o,{force:a.force})}catch(c){console.error("❌",c.message),process.exitCode=1}}),r.command("list").description("列出已装模块").action(async()=>{try{await Hs()}catch(o){console.error("❌",o.message),process.exitCode=1}}),r.command("sync").description("增量装配 4 文件(不覆盖;结构破坏降级为告警)").option("--force","全量覆盖装配文件(002 §6.5:唯一覆盖入口)").action(async o=>{try{await Ws({force:o.force})}catch(a){console.error("❌",a.message),process.exitCode=1}}),r.command("diff [module]").description("装配漂移双向 diff(module 可指定,默认全部)").action(async o=>{try{await Ks(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 Gs(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 no({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 io({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 Eo(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 po(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 uo(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 fo({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 mo(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 ho({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 ao({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 fi({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 mi()}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 ui({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 di({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 hp=typeof process.argv[1]=="string"&&gi(gp(import.meta.url))===gi(process.argv[1]);hp&&yp().parseAsync(process.argv);export{yp as createProgram};
|
|
273
|
+
(上次输出无法解析为 JSON,请只输出 JSON,不要任何其他文字)`,u;try{u=await o(`${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?i:[i[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(Mp(a))),raw:a}}catch{}}throw new Error(`judge 输出无法解析为 JSON(${c} 次尝试): ${a.slice(0,300)||"<空响应>"}`)}var Ip=Ue.object({assertions:Ue.array(Ue.object({text:Ue.string().min(1),verdict:Ue.enum(["pass","fail","unclear"]),reason:Ue.string().min(1)})).min(1)});function Op(e,t,r){let n=["你是智能体行为评测裁判。依据评审标准,对每条断言独立判定 pass/fail/unclear。","判定基于证据中的实际回复与工具出参(判「如实」而非「应然」)。","证据轨别:tools 是模型主动调用;preTools 是系统预执行(非模型决策);__tbox_ 前缀是框架伪工具。判定「模型是否调用/误路由」类断言时只依据 tools。","断言按语义满足判定,不逐字匹配文案;断言与评审标准冲突、或断言前提在当前对话不成立时(如要求重试建议但重试已成功),以评审标准和实际行为为准。","证据不足或断言在当前对话中无法验证时判 unclear,不要猜测。",'只输出 JSON:{"assertions":[{"text":"断言原文","verdict":"pass|fail|unclear","reason":"判定理由"}]}',"","评审标准:",...r.standards.map((i,a)=>/^\d+[.、.]\s*/.test(i)?i:`${a+1}. ${i}`)].join(`
|
|
274
|
+
`),s=t.turns.map((i,a)=>({turn:a+1,...ji(i)})),o=[`用例 ${e.id}(priority=${e.priority})`,`证据:${JSON.stringify(s)}`,"断言:",...e.assertions.map((i,a)=>`${a+1}. ${i}`)];return e.judge_hints.length>0&&o.push("评审备注(附加上下文,优先级低于评审标准):",...e.judge_hints.map(i=>`- ${i}`)),o.push("逐条输出判定 JSON。"),{system:n,user:o.join(`
|
|
275
|
+
`)}}function Di(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 Ci(e,t,r,n,s){let{system:o,user:i}=Op(r,n,t),{parsed:a,raw:c}=await Ri(e,o,i,Ip,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 Ti,spawnSync as Ap}from"child_process";import{existsSync as Ii,openSync as Pp,readFileSync as Lp}from"fs";import{rm as _p,writeFile as Fp}from"fs/promises";import{join as $e}from"path";var Yr="apps/server",qr="dist/index.js",Xr=".tbox-server.json",Mi=".tbox-server.log",Oi=60,Np=1e3;function Zr(e){return Ii($e(e,Yr,"package.json"))?e:null}function pt(e){let t;try{t=Lp($e(e,Xr),"utf-8")}catch{return null}try{return oi.parse(JSON.parse(t))}catch{return null}}async function Jp(e,t){await Fp($e(e,Xr),JSON.stringify(t,null,2),"utf-8")}async function or(e){await _p($e(e,Xr),{force:!0})}function zp(e){return`${e.replace(/\/$/,"")}/api/health`}async function sr(e,t){let r=t?.attempts??Oi,n=t?.intervalMs??Np;for(let s=0;s<r;s++){try{if((await fetch(zp(e))).ok)return!0}catch{}await new Promise(o=>setTimeout(o,n))}return!1}async function Ai(e){if(!Zr(e.appDir))throw new Error(`当前目录不是应用根(缺 ${Yr}/package.json): ${e.appDir}`);let t=$e(e.appDir,Yr),r=$e(t,qr);if(e.build){let l=Ap("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(!Ii(r))throw new Error("应用未构建:先 --build,或使用 `tbox-app eval run --base-url` 评测外部实例");let n=pt(e.appDir);if(n&&await sr(n.baseUrl,{attempts:2,intervalMs:500}))throw new Error(`已在运行 ${n.baseUrl}(pid=${n.pid});tbox-app stop 后再启动`);await or(e.appDir);let s=e.port??8e3,o=$e(e.appDir,".eval-journal"),i={version:1,pid:0,port:s,baseUrl:`http://127.0.0.1:${s}`,journalRoot:o,startedAt:new Date().toISOString(),detached:e.detached},a;if(e.detached){let l=Pp($e(e.appDir,Mi),"a");a=Ti("node",[qr],{cwd:t,env:{...process.env,PORT:String(s),TBOX_LOCAL_LOG:"on",TBOX_LOCAL_LOG_DIR:o,TBOX_APP_ID:"eval"},stdio:["ignore",l,l],detached:!0}),a.unref()}else a=Ti("node",[qr],{cwd:t,env:{...process.env,PORT:String(s),TBOX_LOCAL_LOG:"on",TBOX_LOCAL_LOG_DIR:o,TBOX_APP_ID:"eval"},stdio:"inherit"});if(i.pid=a.pid??0,!await sr(i.baseUrl)){try{e.detached?process.kill(-i.pid,"SIGKILL"):a.kill("SIGKILL")}catch{}throw new Error(`server ${Oi}s 未就绪(${i.baseUrl};日志 ${Mi})`)}if(!(()=>{try{return process.kill(i.pid,0),!0}catch{return!1}})()||!e.detached&&a.exitCode!==null)throw new Error(`端口 ${s} 已被其他进程占用(health 来自占用者);--port 换端口后重试`);return await Jp(e.appDir,i),{child:a,state:i}}async function Pi(e,t){await Vp(t.pid,t.detached,void 0),await or(e)}async function Vp(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(o=>setTimeout(o,100));if(n())try{t?process.kill(-e,"SIGKILL"):process.kill(e,"SIGKILL")}catch{}}var _i=5;function me(e,t){e||console.log(`[eval] ${t}`)}function Up(e,t){if(t.length===0)return e;let r=new Map;for(let s of t){let o=s.indexOf("=");if(o<=0)throw new Error(`--filter 形状非法: "${s}"(应为 key=value)`);let i=s.slice(0,o),a=s.slice(o+1);if(!["priority","tag","id","skip"].includes(i))throw new Error(`--filter 未知键: "${i}"(支持 priority/tag/id/skip)`);let c=r.get(i)??[];c.push({key:i,value:a}),r.set(i,c)}let n=(s,o,i)=>{switch(o){case"priority":return s.priority===i;case"id":return s.id===i;case"tag":return s.tags.includes(i);case"skip":return s.skip===i;default:return!1}};return e.filter(s=>[...r.values()].every(o=>o.some(i=>n(s,i.key,i.value))))}async function Fi(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 tr(e.dataset),r=Up(t.cases,e.filters);if(e.rerunFailedDir){let w=await Li(Qr(e.rerunFailedDir,"report.json"),"utf-8"),O=ct.parse(JSON.parse(w)),L=new Set(O.cases.filter(S=>S.status==="fail"||S.status==="inconclusive").map(S=>S.id));r=r.filter(S=>L.has(S.id)),r.length===0&&me(e.quiet,"--rerun-failed: 上次无 fail/inconclusive 用例,空跑")}e.max!==void 0&&(r=r.slice(0,e.max));let n,s=null,o=null,i=e.journalRoot??null;if(e.baseUrl)n=e.baseUrl.replace(/\/$/,"");else{let w=pt(e.cwd);if(!w)throw new Error("未发现可用实例:先 `tbox-app start -d`(状态文件 .tbox-server.json)或传 --base-url <url>");if(!await sr(w.baseUrl,{attempts:3,intervalMs:500}))throw new Error(`状态文件实例不可达(${w.baseUrl}):实例可能已退出,重新 start -d 或传 --base-url`);n=w.baseUrl,s=w.pid,o=w.startedAt,i=i??w.journalRoot}let a=e.judge?Ei(process.env):null,c=Bp(4).toString("hex"),l=`eval-${c}`,p=e.outDir??Qr(e.cwd,".eval","runs",`${Kp()}-${c}`),u=new Date().toISOString();me(e.quiet,`runId=${c} dataset=${t.suite.name} cases=${r.length} concurrency=${e.concurrency} judge=${a?a.model:"off"}`),me(e.quiet,`instance=${n}${i?` journal=${i}`:"(journal 缺席,direct-out 降级)"}`);let d=[];for(let w of r)w.skip&&d.push({spec:w,dims:[]});let f=new Map,m=new Map,h=0,v=0,g=!1,b=0,x=r.filter(w=>!w.skip),j=[...x],D=async()=>{for(;;){if(g)return;let w=j.shift();if(!w)return;let O=`eval-${c}-${w.id}`,L,S;try{L=await mi({baseUrl:n,driver:t.suite.driver??"agui",...e.deps},w,O,c,t.suite.defaultTurnTimeoutMs),i&&(L=await gi(L,i)),f.set(w.id,L)}catch(C){if(C instanceof G){h+=1,v+=1,v>=_i&&(g=!0),d.push({spec:w,dims:[{dim:"run",status:"fail",detail:[C.message]}]}),me(e.quiet,`✗ ${w.id} infra: ${C.message}`),g&&me(e.quiet,`熔断(连续 ${_i} 个 infra 错误),中止余量 ${j.length} 例`);continue}throw C}v=0;let $=yi(w,L,t.suite.driver??"agui");if(a)try{let{verdicts:C,raw:se}=await Ci(a,t.rubric,w,L,e.deps);m.set(w.id,se),S=Di(C)}catch(C){b+=1,S={status:"inconclusive",failures:[`judge 调用失败: ${C.message}`]},me(e.quiet,`⚠ ${w.id} judge 失败: ${C.message}`)}d.push({spec:w,trace:L,dims:$,textStatus:S?.status,textFailures:S?.failures});let _=$.filter(C=>C.status==="fail").map(C=>C.dim);me(e.quiet,`${_.length>0||S?.status==="fail"?"✗":"✓"} ${w.id}${_.length>0?` (${_.join(",")}${S?.status==="fail"?",text":""})`:""}`)}};await Promise.all(Array.from({length:Math.max(1,Math.min(e.concurrency,16))},D));for(let w of x)d.some(O=>O.spec.id===w.id)||d.push({spec:w,dims:[],notExecuted:!0});let R=[...f.values()].reduce((w,O)=>w+(O.journal?.inputTokens??0)+(O.journal?.outputTokens??0),0);await Hp(n,x.map(w=>`eval-${c}-${w.id}`),e.deps?.fetchFn,e.quiet);let A=xi({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:o,journalRoot:i,degraded:!i},devUserPrefix:l,results:d,ops:{infraErrors:h,aborted:g,llmTokens:R>0?R:null,judgeFailures:b}}),E;if(e.baselinePath){let w=await Li(e.baselinePath,"utf-8"),O=ct.parse(JSON.parse(w));E=Si(A,O)}return await Wp(p,{recursive:!0}),await $i(p,A,f,m),console.log(`报告: ${Qr(p,"report.md")}`),process.env.EVAL_REPORT==="json"&&console.log(JSON.stringify(A)),E&&me(e.quiet,`基线对比: 新失败=${E.newFailed.length} 已修复=${E.fixed.length} 翻转=${E.flipped.length}`),{exitCode:g?2:0,runDir:p,report:A}}async function Hp(e,t,r,n){let s=r??fetch;for(let o of t)try{let i=await Gr(e,o,s),a=await s(`${e}/api/memory`,{headers:{authorization:`Bearer ${i}`}});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 ${i}`}}).catch(()=>{});l.length>0&&me(n??!1,`KB 清理 ${o}: ${l.length} 条`)}catch(i){console.warn(`[eval] KB 清理跳过 ${o}: ${i.message}`)}}function Kp(){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 Ni(e){let t=await tr(e.dataset),r=li(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 o=Object.entries(r.sessionHistogram).sort(([a],[c])=>Number(a)-Number(c));console.log(` 会话数分布: ${o.map(([a,c])=>`${a}会话=${c}`).join(" ")}`);let i=Object.entries(r.byCaseType).sort(([a],[c])=>a.localeCompare(c));console.log(` case_type: ${i.map(([a,c])=>`${a}=${c}`).join(" ")}`)}async function Ji(e){let t=await Fi({...e,cwd:process.cwd()});process.exitCode=t.exitCode}async function zi(e){let t=process.cwd();if(!Zr(t))throw new Error("当前目录不是应用根(缺 apps/server/package.json);请在生成应用目录执行");let r=await Ai({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=o=>{n.kill(o)};process.on("SIGINT",()=>s("SIGINT")),process.on("SIGTERM",()=>s("SIGTERM")),console.log(`✅ server 运行中 ${r.state.baseUrl}(前台,Ctrl+C 停止)`),await new Promise(o=>{n.on("exit",()=>o())}),await or(t),console.log("server 已退出")}import{spawnSync as Gp}from"child_process";import{existsSync as qp}from"fs";import{join as Yp}from"path";async function Vi(){let e=process.cwd(),t=pt(e);if(t){if(t.detached)await Pi(e,t);else{console.log(`server 运行中(前台 pid=${t.pid},${t.baseUrl})——在其终端 Ctrl+C 停止`);return}console.log(`✅ 已停止(pid=${t.pid})`);return}let r=Yp(e,"scripts","stop.sh");if(qp(r)){let n=Gp("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 eu(){let e=new Xp,t=Zp(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 源正交)",(a,c)=>[...c,a],[]).option("--env-file <路径>","环境变量文件(结合 --config-dir 使用:拷入应用 config/env.sh——启动脚本 source 的档位环境;镜像部署链 build-app-image 使用)",a=>a).option("--registry <url>","拉取 registry(registry 源时生效;缺省自动解析环境镜像)").option("--profile <名称>","模板画像(profiles/<名称>.json):展开后自动加装画像模块 + 叠加画像配置(configOverlay → config/);模块源 in-repo 优先、registry 兜底").option("--force","覆盖已存在目录").action(async(a,c)=>{try{await fo({name:a,appName:c.name,source:c.source??"",force:c.force,localDeps:c.localDeps,configDir:c.configDir,envFile:c.envFile,registry:c.registry,profile:c.profile})}catch(l){console.error("❌",l.message),process.exitCode=1}}),e.command("starter").description("查询 starter 文档与可用类型").option("--type <agent|agent-mall>","starter 类型").option("--list","列出可用 starter 类型").option("--json","以 JSON 输出(仅配合 --list)").action(a=>{try{ti(a)}catch(c){console.error("❌",c.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(a,c)=>{try{await Bt({pkg:a,mode:c.mode,source:c.source,registry:c.registry,noDeps:!c.deps})}catch(l){console.error("❌",l.message),process.exitCode=1}}),r.command("remove <id>").description("卸载模块(删包 + 清理装配)").option("--force","忽略反向依赖检查强制移除").action(async(a,c)=>{try{await mo(a,{force:c.force})}catch(l){console.error("❌",l.message),process.exitCode=1}}),r.command("list").description("列出已装模块").action(async()=>{try{await bo()}catch(a){console.error("❌",a.message),process.exitCode=1}}),r.command("sync").description("增量装配 4 文件(不覆盖;结构破坏降级为告警)").option("--force","全量覆盖装配文件(002 §6.5:唯一覆盖入口)").action(async a=>{try{await ko({force:a.force})}catch(c){console.error("❌",c.message),process.exitCode=1}}),r.command("diff [module]").description("装配漂移双向 diff(module 可指定,默认全部)").action(async a=>{try{await xo(a)}catch(c){console.error("❌",c.message),process.exitCode=1}}),r.command("doctor").description("结构校验(doctor 完整版 20 条规则)").option("--json","只输出 JSON 结果").option("--only <规则列表>","只运行指定规则(逗号分隔)").option("--skip <规则列表>","跳过指定规则(逗号分隔)").action(async a=>{try{await So(a)}catch(c){console.error("❌",c.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(a,c)=>{try{await Do({module:a,...c})}catch(l){console.error("❌",l.message),process.exitCode=1}}),r.command("pack <module>").description("本地打包不发布(模块目录如 modules/module-member;pnpm pack 内建归一化 → 发布态 tgz)").requiredOption("--out <dir>","产物目录(pnpm 自动创建,同名覆盖写)").option("--check","产物断言(协议残留 / exports / 白名单 / 泄漏 / 文件一致性)").action(async(a,c)=>{try{await Io({module:a,out:c.out,check:c.check})}catch(l){console.error("❌",l.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(a,c)=>{try{await Po({module:a,tgz:c.tgz,dryRun:c.dryRun,access:c.access,registry:c.registry})}catch(l){console.error("❌",l.message),process.exitCode=1}});let n=e.command("miniapp-container").description("小程序容器管理(init)");e.command("dev").description("原地快速开发调试(真源直挂:装配物化 + 包身份种植 + 配置面注入;改源码即热重启)").option("--template <dir>","模板/应用根目录(相对仓库根;缺省 mall 模板)").option("--module <dir>","仓库模块目录,可多次传入(依赖闭包自动展开)",(a,c)=>[...c,a],[]).option("--profile <name>","模板画像(profiles/<name>.json 加装序列;与 --module 并集,configOverlay 不适用 dev)").option("--theme <id>","主题轴(themes/<id> 真源直挂:出厂接线包 redirect + applyTheme shim 拦截;= 出厂 id 时 no-op)").option("--real-env <tier>","real-env 档位(wanda|joycity|mock):config 重定向 + provider 纳入 + env 防呆").option("--config-dir <目录>","独立完整配置档:三路配置(integrations/deployments/config dir)独占注入该目录,不与档位主档叠加(可重复传,后者覆盖前者)。常用 .real-env/config/wanda-intranet 切内网端点",(a,c)=>[...c,a],[]).option("--link-sdk","平台真源在场断言(重定向总表恒含 app-sdk/contracts 兜底;本旗标为显式 identity 强化)").option("--port <n>","服务端口(缺省 8000)",a=>Number(a)).option("--prune","清理构件区与种植链接后退出").action(async a=>{try{await ei(a)}catch(c){console.error("❌",c.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(a,c,l,p)=>{try{await No(a,c,l,{instance:p.instance})}catch(u){console.error("❌",u.message),process.exitCode=1}}),s.command("use <provider>").description("设置默认厂商:root(应用全局)或 --domain(域级);implementation 恒派生,激活仍需槽 instances 在场").option("--domain <domain>","写域级默认(domains.<域前缀>.provider);缺省写应用全局 root.provider").action(async(a,c)=>{try{await Jo(a,{domain:c.domain})}catch(l){console.error("❌",l.message),process.exitCode=1}}),s.command("unuse").description("清除默认厂商:root 或 --domain(空域对象/空 domains 一并清除)").option("--domain <domain>","仅清除该域级默认;缺省清除应用全局 root.provider").action(async a=>{try{await zo({domain:a.domain})}catch(c){console.error("❌",c.message),process.exitCode=1}}),s.command("unbind <service>").description("解绑服务槽(--instance 删实例覆盖位;缺省槽与实例集删除)").option("--instance <mallId>","仅删除该实例覆盖位(回落槽级默认)").action(async(a,c)=>{try{await Vo(a,{instance:c.instance})}catch(l){console.error("❌",l.message),process.exitCode=1}}),s.command("migrate").description("integrations v2 一次性迁移:旧单串 providerId → 双字段(缺省 dry-run,--write 落盘)").option("--write","落盘(缺省 dry-run 仅报告)").action(async a=>{try{await Uo({write:a.write})}catch(c){console.error("❌",c.message),process.exitCode=1}});let o=e.command("theme").description("主题管理(use/list——品牌资产包接线;换主题 = 替换语义)");o.command("use <id>").description("换主题:删旧依赖 + 接线三行替换(main.tsx 自由区);registry 源缺省,私有主题必须 --source").option("--source <目录|tgz>","主题包源(本地目录/tgz;私有主题分发形态)").option("--registry <url>","拉取 registry(registry 源时生效)").action(async(a,c)=>{try{await vo(a,c)}catch(l){console.error("❌",l.message),process.exitCode=1}}),o.command("list").description("列出已装主题与接线状态(● 接线生效 / ○ 已安装未接线)").option("--json","JSON 输出").action(async a=>{try{await wo(a)}catch(c){console.error("❌",c.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(a,c)=>{try{await Lo({platform:a,source:c.source,registry:c.registry,force:c.force})}catch(l){console.error("❌",l.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 a=>{try{await zi({detached:a.d,port:Number(a.port),build:a.build})}catch(c){console.error("❌",c.message),process.exitCode=1}}),e.command("stop").description("停止本应用 server(状态文件优先,scripts/stop.sh 兜底;幂等)").action(async()=>{try{await Vi()}catch(a){console.error("❌",a.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 a=>{try{await Ni({dataset:a.dataset})}catch(c){console.error("❌",c.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)",(a,c)=>[...c,a],[]).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 a=>{try{await Ji({dataset:a.dataset,baseUrl:a.baseUrl,filters:a.filter,concurrency:Number(a.concurrency),judge:a.judge,baselinePath:a.baseline,rerunFailedDir:a.rerunFailed,journalRoot:a.journalRoot,outDir:a.out,max:a.max?Number(a.max):void 0,quiet:a.quiet??!1})}catch(c){console.error("❌",c.message),process.exitCode=1}}),e}var tu=typeof process.argv[1]=="string"&&Bi(Qp(import.meta.url))===Bi(process.argv[1]);tu&&eu().parseAsync(process.argv);export{eu as createProgram};
|