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