@tbox.cn/app-sdk-server 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +11 -0
- package/bin/check-inspector.mjs +85 -0
- package/dist/platform/index.d.ts +9 -0
- package/dist/platform/index.js +1 -0
- package/dist/server/index.d.ts +2179 -0
- package/dist/server/index.js +9 -0
- package/package.json +63 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import{AsyncLocalStorage as or}from"async_hooks";import{createCoreLogger as sr,detailForLog as Te,resolveLevel as ir,setLogContextProvider as ar}from"@tbox.cn/app-sdk-core";var D=new or;ar(()=>D.getStore());function cr(t,e){return D.run(t,e)}var Ie=()=>{};function dr(t){Ie=t}function qe(t){return`${t}_${Date.now()}_${Math.random().toString(36).slice(2,8)}`}function Ee(){return new Date}function je(t){let e=t;return{message:e?.message||String(t??"unknown"),...e?.name?{type:e.name}:{},...e?.stack?{stack:e.stack}:{}}}function y(t){return{...sr({tag:t,getLevel:()=>ir(process.env.TBOX_LOG_LEVEL,"info"),getContext:()=>D.getStore()}),span(r,n){let o=D.getStore();Ie({spanId:qe(`sp_${t.replace(/[^a-zA-Z0-9]/g,"")}`),traceId:o?.requestId??"no-req",name:r,startTime:Ee(),endTime:Ee(),...n?.metadata!==void 0?{metadata:n.metadata}:{},...n?.error!==void 0?{errorInfo:je(n.error)}:{},requestId:o?.requestId??"no-req",...o?.conversationId?{conversationId:o.conversationId}:{},...o?.threadKey?{sessionId:o.threadKey}:{}})}}}var re=3e3;async function ur(t,e,r,n){let o=Date.now();try{let s=await r(),i=Date.now()-o;if(n?.onDone?.(i),n?.logger&&t==="tool"){let a=n.detail?.args!==void 0?` args=${Te(n.detail.args)}`:"",d=` result=${Te(s)}`;n.logger.info(`${e} ok durMs=${i}${a}${d}`)}else n?.logger&&t==="op"&&n.logger.info(`${e} ok durMs=${i}`);return n?.emitSpan&&Tt(t,e,{durMs:i}),s}catch(s){let i=Date.now()-o;if(n?.onDone?.(i),n?.logger){let a=t==="tool"?"error":"warn",d=` err=${s?.message??String(s)}`;n.logger[a](`${e} fail durMs=${i}${d}`)}throw n?.emitSpan&&Tt(t,e,{durMs:i,error:s}),s}}function Tt(t,e,r){let n=D.getStore();Ie({spanId:qe("sp_call"),traceId:n?.requestId??"no-req",name:`${t}:${e}`,startTime:Ee(),endTime:Ee(),metadata:{durMs:r.durMs,kind:t},...r.error!==void 0?{errorInfo:je(r.error)}:{},requestId:n?.requestId??"no-req",...n?.conversationId?{conversationId:n.conversationId}:{},...n?.threadKey?{sessionId:n.threadKey}:{}})}function Se(t,e){return t?{async request(r){t.info(`→ ${r.path} body=${Te(r.body)}`);let n=Date.now(),o=D.getStore(),s=o?new Date(n):void 0;try{let i=await e.request(r),a=Date.now()-n,d=`← ${r.path} durMs=${a} body=${Te(i)}`;return a>re?t.warn(`SLOW ${d}`):t.info(d),Et(o,s,r.path,a),i}catch(i){let a=Date.now()-n;throw t.warn(`← ${r.path} durMs=${a} err=${i?.message??String(i)}`),Et(o,s,r.path,a,i),i}}}:e}function Et(t,e,r,n,o){!t||!e||Ie({spanId:qe("sp_ext"),traceId:t.requestId??"no-req",name:"external-call",startTime:e,endTime:new Date(e.getTime()+n),metadata:{path:r,durMs:n,...n>re?{slow:!0}:{}},...o!==void 0?{errorInfo:je(o)}:{},requestId:t.requestId??"no-req",...t.conversationId?{conversationId:t.conversationId}:{},...t.threadKey?{sessionId:t.threadKey}:{}})}var It=y("HandlerRegistry");function pr(t){let e=[],r=t instanceof Map?[...t.values()]:Object.values(t);for(let o of r)for(let s of o.intents??[])e.push({id:`${o.id}:${s.patterns[0]??"intent"}`,keywords:s.patterns,handlerId:o.id,action:"intent",...s.match?{match:s.match}:{}});let n=e.filter(o=>o.match===void 0||o.match==="bm25");return n.length>0&&It.warn(`顶层意图路由非确定性档(${n.map(o=>o.id).join(", ")}):语义分类职责归 skill 路由与 LLM 工具编排——建议声明 match:"exact"/"prefix"(详见 prompt-pipeline「declaration 与 intents」)`),e}var ne=class{handlers=new Map;register(e){this.handlers.has(e.id)&&It.warn(`overwriting existing handler: ${e.id}`),this.handlers.set(e.id,e)}resolve(e){return this.handlers.get(e)}getAll(){return Object.fromEntries(this.handlers)}has(e){return this.handlers.has(e)}get size(){return this.handlers.size}async dispatch(e,r,n,o,s){let i=this.handlers.get(e);return i?i.handle(r,n,o,s):{cards:[],text:`未知的服务:${e}`}}};var St=y("ToolRegistry"),oe=class{tools=new Map;disabled=new Set;disable(e){e&&(this.disabled.add(e),this.tools.delete(e))}register(e){let r=e.id;if(!r){St.warn("tool without id skipped:",e.description);return}this.disabled.has(r)||(this.tools.has(r)&&St.warn(`overwriting existing tool: ${r}`),this.tools.set(r,e))}registerAll(e){for(let r of e)this.register(r)}resolve(e){return this.tools.get(e)}getAll(){return Object.fromEntries(this.tools)}has(e){return this.tools.has(e)}get size(){return this.tools.size}};var ve=y("CardRegistry"),se=class{cards=new Map;disabled=new Set;owners=new Map;disable(e){e&&(this.disabled.add(e),this.cards.delete(e))}register(e){if(!e.cardType){ve.warn("meta without cardType skipped");return}if(this.disabled.has(e.cardType)){ve.debug(`disabled cardType skipped: ${e.cardType}`);return}this.cards.has(e.cardType)&&ve.warn(`overwriting existing cardType: ${e.cardType}`),this.cards.set(e.cardType,e)}registerAll(e){for(let r of e)this.register(r)}registerMap(e){for(let[r,n]of Object.entries(e))r!==n.cardType&&ve.warn(`registerMap key "${r}" !== meta.cardType "${n.cardType}", registered as meta.cardType`),this.register(n)}resolve(e){return this.cards.get(e)}recordOwner(e,r){!e||!r||this.owners.set(e,r)}ownerOf(e){return this.owners.get(e)}getAll(){return Object.fromEntries(this.cards)}has(e){return this.cards.has(e)}get size(){return this.cards.size}};import{Router as lr}from"express";var vt=y("ApiRouteRegistry"),ie=class{root=lr();moduleIds=[];register(e,r){let n=e.replace(/^\/+|\/+$/g,"");if(!n){vt.warn("register with empty moduleId skipped");return}this.moduleIds.includes(n)&&vt.warn(`overwriting existing module: ${n}`),this.root.use(`/${n}`,r),this.moduleIds.includes(n)||this.moduleIds.push(n)}mount(){return this.root}getModuleIds(){return[...this.moduleIds]}};var gr=y("ServiceRegistry"),W=class{services=new Map;register(e,r){this.services.has(e)&&gr.warn(`overwriting existing service: ${e}`),this.services.set(e,r)}resolve(e){return this.services.get(e)}clear(){this.services.clear()}getAll(){return Object.fromEntries(this.services)}},Be=new W;function fr(t,e){Be.register(t,e)}function mr(t){return Be.resolve(t)}function yr(){Be.clear()}import{SCOPE_ASSEMBLY_KIND as hr}from"@tbox.cn/app-contracts";var xr=y("DomainAssemblyRegistry"),ae=class{assemblies=new Map;register(e,r){this.assemblies.has(e)&&xr.warn(`overwriting existing domain assembly: ${e}`),this.assemblies.set(e,r)}resolve(e){return this.assemblies.get(e)}scopeAssembly(){return this.resolve(hr)}get size(){return this.assemblies.size}};var wt=y("EventBus");function Ge(){let t=new Map;return{on(e,r){t.has(e)||t.set(e,new Set);let n=t.get(e);return n.add(r),()=>n.delete(r)},emit(e,r){let n=t.get(e);if(n)for(let o of n)try{o(r)}catch(s){wt.error(`handler error for "${String(e)}":`,s)}},off(e,r){let n=t.get(e);n&&n.delete(r)},clear(){t.clear()},async publish(e,r){let n=t.get(e);if(!n||n.size===0)return;let o=[];for(let s of n)try{let i=s(r);i&&typeof i.then=="function"&&o.push(i)}catch(i){wt.error(`handler error for "${String(e)}":`,i)}o.length>0&&await Promise.allSettled(o)},subscribe(e,r){return this.on(e,r)}}}function At(t){let e=0;for(let r=0;r<t.length;r++)e=e*31+t.charCodeAt(r)|0;return String(e)}var ce=class{store=new Map;ttlMs;maxEntries;constructor(e){this.ttlMs=e?.ttlMs??1440*60*1e3,this.maxEntries=e?.maxEntries??1e4}async tryAcquire(e,r){let n=Date.now(),o=this.store.get(e);return o&&o.expiresAt>n?{acquired:!1,result:o.result}:(this.store.set(e,{payloadHash:r,result:void 0,expiresAt:n+this.ttlMs}),this.evict(n),{acquired:!0})}async commit(e,r){let n=this.store.get(e);this.store.set(e,{payloadHash:n?.payloadHash??"",result:r,expiresAt:n?.expiresAt??Date.now()+this.ttlMs})}getPayloadHash(e){let r=this.store.get(e);if(!(!r||r.expiresAt<=Date.now()))return r.payloadHash}release(e){this.store.delete(e)}evict(e){for(let[r,n]of this.store)n.expiresAt<=e&&this.store.delete(r);for(;this.store.size>this.maxEntries;){let r=this.store.keys().next().value;if(r===void 0)break;this.store.delete(r)}}clear(){this.store.clear()}};function Ve(t){try{return At(JSON.stringify(t??{}))}catch{return At(String(t))}}import{AUTH_ERROR_CODES as F,authCodeToStatus as Rr}from"@tbox.cn/app-contracts";var w=class t extends Error{code;status;constructor(e,r){super(r||e),this.name="AuthError",this.code=e,this.status=Rr(e)}static unauthorized(e){return new t(F.UNAUTHORIZED,e)}static tokenExpired(e){return new t(F.TOKEN_EXPIRED,e)}static forbidden(e){return new t(F.FORBIDDEN,e)}static methodRequired(e){return new t(F.METHOD_REQUIRED,e)}static browserLoginDisabled(e){return new t(F.BROWSER_LOGIN_DISABLED,e)}static externalError(e){return new t(F.EXTERNAL_ERROR,e)}static missingSecret(e){return new t(F.MISSING_SECRET,e)}};var Tr=y("AUTH");async function de(t,e,r){if(!e){if(r==="optional")return{identity:ze()};throw w.unauthorized("missing Bearer token")}let n=await t.verify(e);return{identity:{userId:n.sub,source:n.source},...n.external?{credentials:n.external}:{}}}var bt=4096;function ue(t){if(!(!t||typeof t!="object"||Array.isArray(t)))try{let e=JSON.stringify(t);if(Buffer.byteLength(e,"utf8")>bt){Tr.warn("context envelope dropped (>4KB): treated as absent (fail-soft)");return}return t}catch{return}}var Ct=y("HTTP");function Er(t){if(typeof t!="string")return;let e=t.trim();return/^[A-Za-z0-9_-]{1,64}$/.test(e)?e:void 0}function Ir(){return`req_${Date.now()}_${Math.random().toString(36).slice(2,8)}`}var _t="tboxAuthContext";function kt(t,e){Object.defineProperty(t,_t,{value:e,enumerable:!0,configurable:!0})}function Y(t){return t[_t]}function ze(t=Date.now()){return{userId:`anonymous_${t}_${Math.random().toString(36).slice(2,8)}`,source:"anonymous"}}function Xe(t,e={}){let r=e.mode??"required",n=(e.publicPaths??[]).map(s=>s.replace(/\/+$/,""));function o(s){let i=(s.originalUrl||s.url||"").split("?")[0].replace(/\/+$/,"");return n.some(a=>i===a||i.startsWith(`${a}/`))}return async(s,i,a)=>{let d=Er(s.headers["x-request-id"])??Ir();i.setHeader?.("X-Request-ID",d),D.enterWith({requestId:d});let f=Date.now();if(i.on?.("finish",()=>{let c=Date.now()-f,u=`${s.method} ${s.originalUrl||s.url} ${i.statusCode} durMs=${c}`,l=(s.originalUrl||s.url||"").split("?")[0];/\/health(z)?$/.test(l)?Ct.debug(u):Ct.info(u)}),o(s))return a();try{let c=s.headers.authorization,u=c?.startsWith("Bearer ")?c.slice(7).trim():void 0,l=await de(t,u,r),p=s.body?.context,g=s.method==="POST"&&p!==void 0?ue(p):void 0;if(g&&e.validateContext&&!e.validateContext(g)){i.status(403).json({error:"context validation failed",code:"AUTH_FORBIDDEN"});return}return kt(s,{identity:l.identity,...l.credentials?{credentials:l.credentials}:{},...g?{attributes:g}:{}}),a()}catch(c){if(c instanceof w){i.status(c.status).json({error:c.message,code:c.code});return}i.status(401).json({error:c.message,code:"AUTH_UNAUTHORIZED"})}}}var Pt=y("ActionGateway");function We(t){let e=new Map,r=t?.idempotencyStore??new ce;function n(c){return{sessionId:(c?.body??{})?.sessionId??"",requestContext:c?Y(c):void 0,emit:()=>{}}}async function o(c,u){for(let l of u.checks??[]){let p=await l.check(c);if(!p.ok)return p}return null}async function s(c,u,l,p){let g=c.policy,m=g.write??!1,R=g.idempotent??!1;if(m&&R){if(!p)return{status:400,body:{success:!1,error:"Missing idempotencyKey (write action)"}};let E=Ve(u),I=await r.tryAcquire(p,E);if(!I.acquired){let S=r.getPayloadHash?.(p);return S&&S!==E?{status:409,body:{success:!1,error:"Idempotency key reused with different payload"}}:I.result!==void 0?{status:200,body:{...I.result,cached:!0}}:{status:200,body:{success:!0,pending:!0,cached:!0}}}try{let S=await c.handler(u,l);return await r.commit(p,S),{status:200,body:S}}catch(S){throw r.release?.(p),S}}return{status:200,body:await c.handler(u,l)}}async function i(c,u,l){if(!c||typeof c!="string")return{status:400,body:{success:!1,error:"Missing action"}};let p=e.get(c);if(!p)return{status:404,body:{success:!1,error:`Unknown action: ${c}`}};if(p.policy.write&&Y(l)?.identity?.source==="anonymous")return{status:403,body:{success:!1,error:"anonymous write is forbidden"}};let g=await o(l,p.policy);if(g)return{status:g.code??403,body:{success:!1,error:g.message}};let m=l.body??{};return s(p,u,n(l),m.idempotencyKey)}function a(){return async(c,u,l)=>{try{let p=c.body??{},g=await i(p.action,p.payload,c);u.status(g.status).json(g.body)}catch(p){Pt.error("handler error:",p),u.status(500).json({success:!1,error:p?.message||"Internal error"})}}}function d(c={}){return async(u,l,p)=>{try{let g=await o(u,c);if(g){l.status(g.code??403).json({success:!1,error:g.message});return}p()}catch(g){p(g)}}}async function f(c,u,l){let p=e.get(c);if(!p)return{success:!1,error:`Unknown action: ${c}`};let g=l?.requestContext?.identity??l?.identity;if(p.policy.write&&g?.source==="anonymous")return{success:!1,error:"anonymous write is forbidden"};let m=await o({},p.policy);return m?{success:!1,error:m.message}:(await s(p,u,{sessionId:l?.sessionId??"",requestContext:l?.requestContext?l.requestContext:l?.identity?{identity:l.identity}:void 0,emit:()=>{}},l?.idempotencyKey)).body}return{registerAction(c,u,l={}){e.has(c)&&Pt.warn(`overwriting existing action: ${c}`),e.set(c,{handler:u,policy:l})},middleware:a,validateAction:d,dispatch:f,getActions:()=>new Map(e)}}var pe=class{store=new Map;async save(e,r){let n=this.store.get(e)??[],o=n.findIndex(s=>s.id===r.id);o>=0?n[o]=r:n.push(r),this.store.set(e,n)}async load(e){return this.store.get(e)??[]}async clear(e){this.store.delete(e)}};import{EventType as Sr}from"@ag-ui/core";import{TBOX_CARD_EVENT_NAME as vr}from"@tbox.cn/app-contracts";var Ot=y("CARD");function Nt(t){return`${t}_${Date.now()}_${Math.random().toString(36).slice(2,8)}`}var J=class{constructor(e){this.cardRegistry=e}cardRegistry;emit(e,r){let n=this.build(e);if(n)return this.send(n,r)}build(e){let r=this.cardRegistry[e.cardType];if(!r){Ot.warn(`unknown cardType: ${e.cardType}`);return}let n=r.dataSchema.safeParse(e.data);return n.success?{id:e.id||Nt("card"),messageId:e.messageId,cardType:e.cardType,data:n.data,status:"ready",schemaVersion:r.schemaVersion??1,meta:{createdAt:Date.now()},...r.project?.(n.data)??{}}:(Ot.warn(`data schema mismatch for ${e.cardType}:`,n.error.message),{id:e.id||Nt("card"),messageId:e.messageId,cardType:e.cardType,data:{},status:"error",error:"卡片数据不符合约定",schemaVersion:r.schemaVersion??1,meta:{createdAt:Date.now()}})}send(e,r){let n={type:Sr.CUSTOM,name:vr,value:e};return r(n),e}};var Ye=class{store=new Map;ttlMs;maxEntries;constructor(e){this.ttlMs=e?.ttlMs??300*1e3,this.maxEntries=e?.maxEntries??1e4}async checkAndSet(e,r){let n=Date.now(),o=this.store.get(e);return o&&o.expiresAt>n?!0:(this.store.set(e,{expiresAt:n+(r??this.ttlMs)}),this.evict(n),!1)}evict(e){for(let[r,n]of this.store)n.expiresAt<=e&&this.store.delete(r);for(;this.store.size>this.maxEntries;){let r=this.store.keys().next().value;if(r===void 0)break;this.store.delete(r)}}clear(){this.store.clear()}};import{SKILL_NAME_PATTERN as Je}from"@tbox.cn/app-contracts";var wr={description:1024,instructions:32*1024,references:64,referenceSize:32*1024};var we=y("SkillRegistry"),le=class{skills=new Map;register(e){if(!Je.test(e.name)){we.warn(`非法 skill 名 "${e.name}" 跳过(须匹配 ^[a-z0-9-]{1,64}$)`);return}for(let r of Object.keys(e.references))r.endsWith(".md")||we.warn(`skill "${e.name}" reference key "${r}" 非 .md 结尾`);this.skills.has(e.name)&&we.warn(`覆盖已注册 skill: ${e.name}`),this.skills.set(e.name,e)}registerAll(e){for(let r of e)this.register(r)}registerMap(e){for(let[r,n]of Object.entries(e))r!==n.name&&we.warn(`registerMap key "${r}" !== skill.name "${n.name}", registered as skill.name`),this.register(n)}getAll(){return[...this.skills.values()]}has(e){return this.skills.has(e)}get size(){return this.skills.size}};import{PRE_TOOL_NAME_PATTERN as Ar}from"@tbox.cn/app-contracts";var Ae=y("PreToolRegistry"),ge=class{preTools=new Map;register(e){let r=e?.tool?.id;if(!r||!Ar.test(r)){Ae.warn(`非法 toolId "${r}" 跳过(须匹配 ^[A-Za-z0-9_-]{1,64}$)`);return}if(e.routing.mode==="keywords"&&e.routing.terms.length===0){Ae.warn(`keywords 路由空 terms 跳过: ${r}(恒不命中;如须每轮候选请用 always 模式)`);return}this.preTools.has(r)&&Ae.warn(`覆盖已注册 pre-tool: ${r}`),this.preTools.set(r,e)}registerAll(e){for(let r of e)this.register(r)}registerMap(e){for(let[r,n]of Object.entries(e)){let o=n?.tool?.id;r!==o&&Ae.warn(`registerMap key "${r}" !== tool.id "${o}", registered as tool.id`),this.register(n)}}getAll(){return[...this.preTools.values()]}has(e){return this.preTools.has(e)}get size(){return this.preTools.size}};import{createHash as br,randomUUID as ss}from"crypto";var q=(t,e)=>`${t}\0${e}`;function Qe(t){if(t!==null&&typeof t=="object"&&!Object.isFrozen(t)){Object.freeze(t);for(let e of Object.values(t))Qe(e)}return t}function Ze(t=()=>new Date){let e=new Map,r=(n,o)=>n.expiresAt===void 0||n.expiresAt>o;return{async get(n,o){let s=e.get(q(n,o));return s?r(s,t().getTime())?s.value:(e.delete(q(n,o)),null):null},async getEntry(n,o){let s=e.get(q(n,o));return s?r(s,t().getTime())?{value:s.value,revision:s.revision,expiresAt:s.expiresAt}:(e.delete(q(n,o)),null):null},async put(n,o,s,i){let a=q(n,o),d=(e.get(a)?.revision??0)+1;return e.set(a,{value:Qe(structuredClone(s)),revision:d,expiresAt:i>0?t().getTime()+i:void 0}),!0},async compareAndSet(n,o,s,i,a){let d=q(n,o),f=t().getTime(),c=e.get(d),u=c&&r(c,f)?c.revision:0;return u!==s?!1:(e.set(d,{value:Qe(structuredClone(i)),revision:u+1,expiresAt:a>0?f+a:void 0}),!0)},async consume(n,o){let s=q(n,o),i=e.get(s);return i?r(i,t().getTime())?(e.delete(s),i.value):(e.delete(s),null):null}}}function Cr(t){return br("sha256").update(t).digest("hex").slice(0,32)}var _r=y("PROVIDER"),Mt="static-headers-v1",Lt={credentialType:Mt,apply(t){let e={};for(let[r,n]of Object.entries(t.credential.value))typeof n=="string"&&(e[r]=n);return e}};function et(t=[Lt]){let e=new Map;for(let r of t)e.set(r.credentialType,r);return{get:r=>e.get(r),register:r=>{e.get(r.credentialType)!==r&&(e.has(r.credentialType)&&_r.warn(`认证策略重复注册(后者覆盖):${r.credentialType}`),e.set(r.credentialType,r))},list:()=>[...e.values()]}}var be=y("auth-exchanges");function j(t){let e=new Map;for(let[n,o]of Object.entries(t??{}))e.set(n,o);let r=[];return{register(n){if(n.matches){r.includes(n)||r.push(n);return}let o=r.findIndex(s=>!s.matches&&s.provider===n.provider);if(o>=0){r[o]=n;return}r.push(n)},get(n,o){let s=r.filter(d=>d.provider===n);for(let d of s){if(!d.matches)continue;let f=!1;try{f=d.matches(o)!==!1}catch(c){be.warn(`matches threw for ${n}(谓词违约视为不命中):`,c.message)}if(f)return be.debug("resolve",n,"source=scoped"),d}let i=s.find(d=>!d.matches);if(i)return be.debug("resolve",n,"source=fallback"),i;let a=e.get(n);return be.debug("resolve",n,a?"source=builtin":"source=none",`explicit=${s.length}`),a},list(){return[...r,...e.values()]}}}function kr(t){let e=new W,r=t?.snapshotStore??new pe,n=new Map,o=We({idempotencyStore:t?.idempotencyStore}),s=new ae,i=t?.scopeDefaults,a=new Proxy({},{get:(h,E)=>{if(typeof E!="string")return;let I=s.scopeAssembly()?.scopeDefaults?.[E];return I!==void 0?I:i?.[E]},has:(h,E)=>typeof E!="string"?!1:s.scopeAssembly()?.scopeDefaults?.[E]!==void 0||i?.[E]!==void 0}),d=t?.state??Ze(),f=t?.authStrategies??et(),c=t?.authExchanges??j(),u=new Map,l={register:h=>{if((!h.kind||h.kind==="moduleAction")&&!h.execute)throw new Error(`moduleAction 动作缺少 execute 回调:${h.moduleId}:${h.actionId}`);u.set(h.actionId,h)},get:h=>u.get(h),getAll:()=>new Map(u)},p=h=>e.resolve(h),g=(h,E)=>n.get(h)?.[E],m=(h,E,I)=>{let S=n.get(h)??{};S[E]=I,n.set(h,S)},R=h=>{n.delete(h)};return{handlers:new ne,tools:new oe,cards:new se,skills:new le,preTools:new ge,routes:new ie,services:e,bus:Ge(),snapshotStore:r,actions:o,resolveService:p,flowStates:{get:g,set:m,clear:R},state:d,...t?.memory?{memory:t.memory}:{},cardActions:l,authStrategies:f,authExchanges:c,domainAssemblies:s,...t?.integrations?{integrations:t.integrations}:{},scopeDefaults:a}}import $r from"path";import{readFileSync as Or}from"fs";import{join as Nr}from"path";import{z as U}from"zod";import{expandEnvVars as Pr}from"@tbox.cn/app-toolkit";function tt(t,e=process.env,r="<config>"){let n=Pr(t,e);if(n.missing.length>0){let o={file:r,missing:n.missing};throw new Error(`配置文件 ${o.file} 引用的环境变量缺失:${o.missing.join("、")}(先 source .real-env/env.sh <vendor>[-<variant>] 或由部署链注入后重试;缺变量集中报错而非静默空值,防带错配置启动)`)}return n.text}var Dt=y("app-config"),Ut=U.object({topK:U.number().int().positive().optional(),scoreThreshold:U.number().optional()}).passthrough(),Mr=U.object({datasetId:U.string().min(1),retrieve_config:Ut.optional()}).passthrough(),Lr=U.object({defaults:Ut.optional(),knowledge_bases:U.record(U.string(),Mr).optional()}).passthrough(),fe={bases:{},defaults:{}};function rt(t){let e=Nr(t,"knowledge.json"),r;try{r=JSON.parse(tt(Or(e,"utf8"),process.env,e))}catch(s){return s.code==="ENOENT"||Dt.warn(`knowledge.json 解析失败(${s.message},文件:${e})——ref 引用将降级缺失`),fe}let n=Lr.safeParse(r);if(!n.success)return Dt.warn(`knowledge.json 校验失败(${n.error.message},文件:${e})——ref 引用将降级缺失`),fe;let o={};for(let[s,i]of Object.entries(n.data.knowledge_bases??{}))o[s]={datasetId:i.datasetId,...i.retrieve_config?.topK!==void 0?{topK:i.retrieve_config.topK}:{},...i.retrieve_config?.scoreThreshold!==void 0?{scoreThreshold:i.retrieve_config.scoreThreshold}:{}};return{bases:o,defaults:{...n.data.defaults?.topK!==void 0?{topK:n.data.defaults.topK}:{},...n.data.defaults?.scoreThreshold!==void 0?{scoreThreshold:n.data.defaults.scoreThreshold}:{}}}}var nt=y("app-config"),Dr=["faq","mall-knowledge"],$t={};function ot(t,e){if(!t)return()=>$t;let n=Object.values(t).some(s=>Object.values(s.resources??{}).some(i=>i.ref!==void 0))?rt(e):fe,o=new Map;for(let[s,i]of Object.entries(t)){let a={};for(let[d,f]of Object.entries(i.resources??{})){let c=Ur(s,d,f,n);c&&(a[d]=c)}o.set(s,a)}return s=>o.get(s)??$t}function Ur(t,e,r,n){if(r.datasetId!==void 0)return r.ref!==void 0&&nt.warn(`模块 ${t} 资源 ${e} 同时声明 datasetId 与 ref——datasetId 优先,ref 忽略`),{type:"knowledge",datasetId:r.datasetId,...r.topK!==void 0?{topK:r.topK}:{},...r.scoreThreshold!==void 0?{scoreThreshold:r.scoreThreshold}:{}};if(r.ref!==void 0){let o=n.bases[r.ref];if(!o){nt.warn(`模块 ${t} 资源 ${e} ref="${r.ref}" 在 knowledge.json 注册表未命中——资源缺失,走模块占位回退`);return}return{type:"knowledge",datasetId:o.datasetId,topK:r.topK??o.topK??n.defaults.topK,scoreThreshold:r.scoreThreshold??o.scoreThreshold??n.defaults.scoreThreshold}}nt.warn(`模块 ${t} 资源 ${e} 无 datasetId/ref(zod 已拦,防御分支)——资源缺失`)}async function Kr(t,e){if(!t.services.resolve("app-config")){let r=t.integrations?.raw,n=r?.source?$r.dirname(r.source):"";t.services.register("app-config",ot(r?.config.modules,n))}for(let r of e){let n={...t,logger:y(`module:${r.declaration?.moduleId??"anonymous"}`)};if("skills"in r&&r.skills&&n.skills.registerMap(r.skills),"preTools"in r&&r.preTools&&n.preTools.registerMap(r.preTools),r.declaration?.moduleId)for(let o of Object.values(r.cards))t.cards.recordOwner(o.cardType,r.declaration.moduleId);await r.register(n)}}var M="https://o.tbox.cn",st="1.0.0",it="X-Tbox-SDK-Version";var Kt=null;function Ht(){if(Kt)return Kt;if(typeof fetch<"u")return fetch;throw new Error("[@tbox-dev-js/sdk] No fetcher available. In mini-program environments, call setFetcher() before using SDK clients.")}function Ft(t){let e=t.success!==!1,r=t.data??t.resultObj??t.result,n=t.errorCode??t.code??t.resultCode,o=t.errorMsg??t.msg??t.resultDesc,s=t.traceId;return e?{success:!0,data:r,traceId:s}:{success:!1,errorCode:n,errorMsg:o,traceId:s}}var b=class{_baseUrl;timeout;auth;constructor(e){this._baseUrl=e.baseUrl.replace(/\/$/,""),this.timeout=e.timeout,"auth"in e&&e.auth?this.auth=e.auth:this.auth={type:"apiKey",apiKey:e.apiKey??""}}get baseUrl(){return this._baseUrl}getAuthHeaders(){return this.buildAuthHeaders()}buildAuthHeaders(){return this.auth.type==="session"?{TBOXSESSIONID:this.auth.session.sessionId,"X-Tbox-Channel":this.auth.session.channel,"X-Tbox-AppId":this.auth.session.appId}:{Authorization:this.auth.apiKey}}buildHeaders(e){return{"Content-Type":"application/json;charset=UTF-8",[it]:st,...this.buildAuthHeaders(),...e}}buildHeadersForFormData(e){return{[it]:st,...this.buildAuthHeaders(),...e}}async fetchWithTimeout(e,r){let n=Ht();if(!this.timeout||typeof AbortController>"u")return n(e,r);let o=new AbortController,s=setTimeout(()=>o.abort(),this.timeout);try{return await n(e,{...r,signal:o.signal})}finally{clearTimeout(s)}}async post(e,r,n){let o=this.buildUrl(e,n),s;try{s=await this.fetchWithTimeout(o,{method:"POST",headers:this.buildHeaders(),body:JSON.stringify(r)})}catch(i){return{success:!1,errorCode:"NETWORK_ERROR",errorMsg:i instanceof Error?i.message:String(i)}}return this.parseResponse(s)}async postFormData(e,r,n){let o=this.buildUrl(e,n),s;try{s=await this.fetchWithTimeout(o,{method:"POST",headers:this.buildHeadersForFormData(),body:r})}catch(i){return{success:!1,errorCode:"NETWORK_ERROR",errorMsg:i instanceof Error?i.message:String(i)}}return this.parseResponse(s)}async get(e,r){let n=this.buildUrl(e,r),o;try{o=await this.fetchWithTimeout(n,{method:"GET",headers:this.buildHeaders()})}catch(s){return{success:!1,errorCode:"NETWORK_ERROR",errorMsg:s instanceof Error?s.message:String(s)}}return this.parseResponse(o)}buildUrl(e,r){let n;if(e.startsWith("http://")||e.startsWith("https://")?n=e:n=`${this._baseUrl}${e}`,!r||Object.keys(r).length===0)return n;let o=new URLSearchParams(r).toString();return`${n}?${o}`}async parseResponse(e){let r;try{r=await e.text()}catch{try{let o=await e.arrayBuffer();r=new TextDecoder("utf-8").decode(o)}catch{return{success:!1,errorCode:`HTTP_${e.status}`,errorMsg:`HTTP error: ${e.status} ${e.statusText}`}}}if(!e.ok)try{let o=JSON.parse(r),s=o.errorCode??o.code??o.resultCode??`HTTP_${e.status}`,i=o.errorMsg??o.msg??o.resultDesc??`HTTP error: ${e.status} ${e.statusText}`;return{success:!1,errorCode:s,errorMsg:i,traceId:o.traceId}}catch{return{success:!1,errorCode:`HTTP_${e.status}`,errorMsg:`HTTP error: ${e.status} ${e.statusText}`}}let n;try{n=JSON.parse(r)}catch{return{success:!1,errorCode:"PARSE_ERROR",errorMsg:"Failed to parse response JSON"}}return Ft(n)}};var Q=class{http;constructor(e){this.http=new b({baseUrl:e.baseUrl??M,apiKey:e.apiKey,timeout:e.timeout})}async generateSession(e){return this.http.post("/openapi/v1/app/generate_session",e)}};var Ce="/openapi/v1/conversation",_e=class{http;constructor(e){this.http=new b({baseUrl:e.baseUrl??M,apiKey:e.apiKey,timeout:e.timeout})}async createConversation(e){return this.http.post(`${Ce}/create`,e)}async listConversations(e){return this.http.post(`${Ce}/list`,e)}async listMessages(e){return this.http.post(`${Ce}/message/list`,e)}async saveMessage(e){return this.http.post(`${Ce}/message/save`,e)}};var Hr={"image/jpeg":".jpg","image/png":".png","image/gif":".gif","image/webp":".webp","image/heic":".heic","image/bmp":".bmp","video/mp4":".mp4","video/quicktime":".mov","video/webm":".webm","audio/webm":".webm","audio/mpeg":".mp3","audio/wav":".wav","audio/ogg":".ogg","application/pdf":".pdf","application/msword":".doc","application/vnd.openxmlformats-officedocument.wordprocessingml.document":".docx","application/vnd.ms-excel":".xls","application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":".xlsx","text/plain":".txt"};function Fr(t,e){if(/\.\w+$/.test(t))return t;let r=e?Hr[e]:void 0;return r?`${t}${r}`:t}var ke=class{http;constructor(e){this.http=new b({baseUrl:e.baseUrl??M,auth:{type:"session",session:e.session},timeout:e.timeout})}async upload(e){let r=Fr(e.fileName??"upload",e.file.type),n=new FormData;return n.append("file",e.file,r),n.append("fileType",e.fileType==="video"?"vedio":e.fileType),this.http.postFormData("/api/files/afts/upload",n)}};var qr="wss://mediafeature.alipay.com:443/tts_stream",at={voiceCode:"lingyue",voiceType:"SYSTEM",sampleRate:24e3,volume:50,speechRate:0,pitchRate:0},Pe=class t{appId;appkey;authUrl;sessionId;channel;upstreamUrl;timeout;constructor(e){this.appId=e.appId,this.appkey=e.appkey,this.authUrl=e.authUrl,this.sessionId=e.sessionId,this.channel=e.channel,this.upstreamUrl=e.upstreamUrl??qr,this.timeout=e.timeout}static async create(e){let n=await new Q({apiKey:e.apiKey,baseUrl:e.baseUrl,timeout:e.timeout}).generateSession({appId:e.appId,userId:e.userId});if(!n.success||!n.data?.sessionId||!n.data?.channel)throw new Error(`Failed to generate session: ${n.errorCode??"UNKNOWN"} - ${n.errorMsg??"No session data returned"}`);return new t({appId:e.appId,appkey:e.appkey,authUrl:e.authUrl,sessionId:n.data.sessionId,channel:n.data.channel??e.channel,upstreamUrl:e.upstreamUrl,baseUrl:e.baseUrl,timeout:e.timeout})}async fetchAuth(){if(!this.authUrl)return{success:!1,errorCode:"CONFIGURATION_ERROR",errorMsg:"TTS authUrl is required"};if(!this.appkey)return{success:!1,errorCode:"CONFIGURATION_ERROR",errorMsg:"TTS appkey is required"};let e={"Content-Type":"application/json;charset=UTF-8","X-Tbox-AppId":this.appId};this.channel&&(e["X-Tbox-Channel"]=this.channel),this.sessionId&&(e.TBOXSESSIONID=this.sessionId);let r;try{let d={method:"POST",headers:e,body:JSON.stringify({appkey:this.appkey})};if(this.timeout){let f=new AbortController,c=setTimeout(()=>f.abort(),this.timeout);try{r=await fetch(this.authUrl,{...d,signal:f.signal})}finally{clearTimeout(c)}}else r=await fetch(this.authUrl,d)}catch(d){return{success:!1,errorCode:"NETWORK_ERROR",errorMsg:d instanceof Error?d.message:String(d)}}let n;try{n=await r.text()}catch{return{success:!1,errorCode:`HTTP_${r.status}`,errorMsg:`Failed to read response body: ${r.status} ${r.statusText}`}}if(!r.ok)try{let d=JSON.parse(n);return{success:!1,errorCode:d.errorCode??d.code??`HTTP_${r.status}`,errorMsg:d.errorMsg??d.msg??`TTS auth failed: ${r.status} ${r.statusText}`}}catch{return{success:!1,errorCode:`HTTP_${r.status}`,errorMsg:`TTS auth failed: ${r.status} - ${n}`}}let o;try{o=JSON.parse(n)}catch{return{success:!1,errorCode:"PARSE_ERROR",errorMsg:"Failed to parse TTS auth response JSON"}}let s=o.result??o,i=s.timestamp,a=s.digest;return!i||!a?{success:!1,errorCode:"INVALID_RESPONSE",errorMsg:"TTS auth response missing timestamp or digest"}:{success:!0,data:{timestamp:i,digest:a}}}buildUpstreamUrl(e){let r=new URLSearchParams({"Alipay-Mf-Appkey":this.appkey,"Alipay-Mf-Timestamp":e.timestamp,"Alipay-Mf-Digest":e.digest});return`${this.upstreamUrl}?${r.toString()}`}static buildPayload(e){let r={...at,...e};return{sdk_language:"javascript",sdk_version:"1.0",tts_voice:r.voiceCode,sample_rate:`RATE_${r.sampleRate}`,format:"pcm",task_id:t.generateTaskId(),tts_volume:r.volume,tts_speech_rate:r.speechRate,tts_pitch_rate:r.pitchRate,tts_enable_timelines:!1,is_upload_to_afts:!1,is_blank_space_break:!1,tts_voice_type:r.voiceType,tts_style_code:"",api:"STREAM_TTS",action:"start",ext_info:"{}"}}static buildTextPayload(e){return JSON.stringify({tts_text:`<speak>${e}</speak>`})}static buildStopPayload(){return JSON.stringify({action:"stop"})}static generateTaskId(){let e="0123456789ABCDEF",r="";for(let n=0;n<32;n++)r+=e[Math.floor(Math.random()*16)];return r}};var jr="https://api.tbox.cn",me=class{http;defaultDatasetId;defaultTopK;defaultScoreThreshold;constructor(e){this.http=new b({baseUrl:e.baseUrl??"https://o.tbox.cn",apiKey:e.apiKey,timeout:e.timeout}),this.defaultDatasetId=e.datasetId,this.defaultTopK=e.topK??5,this.defaultScoreThreshold=e.scoreThreshold}async retrieve(e,r){let n=r?.datasetId??this.defaultDatasetId;if(!n)return{success:!1,errorCode:"MISSING_DATASET_ID",errorMsg:"datasetId is required. Set it in constructor config or pass it in options."};let o={datasetId:n,query:e,topK:r?.topK??this.defaultTopK,scoreThreshold:r?.scoreThreshold??this.defaultScoreThreshold},s=await this.http.post(`${jr}/api/datasets/retrieve`,o,void 0);return s.success&&s.data?{success:!0,data:s.data}:{success:!1,errorCode:s.errorCode,errorMsg:s.errorMsg}}};var Oe=class{http;constructor(e){this.http=new b({baseUrl:e.baseUrl??M,auth:{type:"session",session:e.session},timeout:e.timeout})}async recognize(e){let r={base64_audio:e.base64_audio,sample_rate:e.sample_rate??"16000",audio_format:e.audio_format??"webm",enable_inverse_text:!0};return this.http.post("/agent/v1/plugin/asrBase64",r)}};var Ne=class{http;constructor(e){this.http=new b({baseUrl:e.baseUrl??M,auth:{type:"session",session:e.session},timeout:e.timeout})}async addTag(e){let r={apiName:"agtprod.agtProdMessageFacade.addTag",requestObj:{requestId:e.requestId,feedback:e.feedback}};return this.http.post("/agent/v1/plugin/proxy",r)}};import{createHmac as Gr,hkdfSync as qt,timingSafeEqual as Vr,randomUUID as zr}from"crypto";import{stableSerializeScope as Xr}from"@tbox.cn/app-contracts";import{randomBytes as Br}from"crypto";function Me(t=process.env,e={}){let r=e.firstKeyName?t[e.firstKeyName]?.trim():void 0,n=t.AUTH_TOKEN_SECRET?.trim(),o=t.TBOX_API_KEY?.trim();return r?{secret:r,source:"explicit"}:n?{secret:n,source:"platform"}:o&&o.length>=32?{secret:o,source:"apiKey"}:t.NODE_ENV!=="production"?{secret:Br(32).toString("hex"),source:"dev-temp"}:null}var C=class extends Error{constructor(r,n){super(n);this.code=r;this.name="ActionGrantError"}code},Le=900*1e3,ye="action-grant",jt="action-grant";function Bt(t){let e=Buffer.from(t);if(e.length<32){let r=Buffer.alloc(32);return e.copy(r),new Uint8Array(qt("sha256",r,Buffer.alloc(0),Buffer.from(jt),32))}return new Uint8Array(qt("sha256",e,Buffer.alloc(0),Buffer.from(jt),32))}function Wr(){let t=Me(process.env,{firstKeyName:"ACTION_TOKEN_SECRET"});if(!t)throw new C("TOKEN_INVALID","密钥链缺失:ACTION_TOKEN_SECRET/AUTH_TOKEN_SECRET");return new TextEncoder().encode(t.secret)}function Yr(t){let e=t.store,r=Bt(t.secret??Wr()),n=t.now??(()=>Date.now());function o(a){let d=[a.moduleId,a.actionId,a.cardType,a.cardId??"",a.sessionId,Xr(a.scope),String(a.singleUse),String(a.expiresAt)].join("\0");return Gr("sha256",r).update(d).digest("base64url")}function s(a,d){return`${d}.${Buffer.from(JSON.stringify(a),"utf8").toString("base64url")}`}async function i(a){let d=a.indexOf(".");if(d<=0)throw new C("TOKEN_INVALID","令牌格式非法");let f=a.slice(0,d),c;try{c=JSON.parse(Buffer.from(a.slice(d+1),"base64url").toString("utf8"))}catch{throw new C("TOKEN_INVALID","令牌载荷非法")}let u=o(c),l=Buffer.from(f),p=Buffer.from(u);if(l.length!==p.length||!Vr(l,p))throw new C("TOKEN_INVALID","签名校验失败");if(c.expiresAt<=n())throw new C("TOKEN_EXPIRED","令牌已过期");return{record:c,signature:f}}return{async issue(a){let d=Math.min(a.ttlMs??Le,Le),f=n(),c={grantId:zr(),moduleId:a.moduleId,actionId:a.actionId,cardType:a.cardType,...a.cardId!==void 0?{cardId:a.cardId}:{},sessionId:a.sessionId,scope:{...a.scope},...a.input!==void 0?{input:a.input}:{},singleUse:a.singleUse??!1,expiresAt:f+d},u=s(c,o(c));return c.singleUse&&await e.put(ye,c.grantId,{consumed:!1},d),u},async authorize(a,d){let{record:f}=await i(a);if(f.actionId!==d.actionId||f.sessionId!==d.sessionId)throw new C("GRANT_MISMATCH","授权与期望不匹配");if((f.cardId??void 0)!==(d.cardId??void 0))throw new C("GRANT_MISMATCH","授权 cardId 不匹配");let c=Object.keys(d.scope).sort(),u=Object.keys(f.scope).sort();if(c.length!==u.length||!c.every((p,g)=>p===u[g]&&d.scope[p]===f.scope[p]))throw new C("GRANT_MISMATCH","授权 scope 不匹配");let l=f.grantId;if(f.singleUse){let p=await e.getEntry(ye,l);if(!p)throw new C("TOKEN_CONSUMED","令牌已消费");if(p.value.consumed)throw new C("TOKEN_CONSUMED","令牌已消费");if(!await e.compareAndSet(ye,l,p.revision,{consumed:!0},Le))throw new C("TOKEN_CONSUMED","令牌已消费")}return Object.freeze({...f})},async restore(a){let{record:d}=await i(a);if(!d.singleUse)return;let f=await e.get(ye,d.grantId);!f||!f.consumed||await e.put(ye,d.grantId,{consumed:!1},Math.max(d.expiresAt-n(),1e3))}}}var A=class extends Error{constructor(r,n){super(n);this.code=r;this.name="ModuleActionError"}code};var ct=200;function De(t){return t.length>ct?`${t.slice(0,ct)}…`:t}function Jr(t){if(t instanceof C)return{level:"error",text:{TOKEN_EXPIRED:"操作已过期,请重新查询后再试",TOKEN_CONSUMED:"该操作已执行,请刷新卡片后重试"}[t.code]??"卡片数据已过期,为保护用户安全,请您重新唤起最新卡片数据操作"};if(t instanceof A&&t.code==="INPUT_INVALID")return{level:"error",text:"输入格式不正确,请检查后重试"};let e=t?.message??"";return{level:"error",text:De(e)||"操作执行失败,请稍后重试"}}import{SignJWT as Qr,jwtVerify as Zr}from"jose";var en=7200*1e3,tn="agent",rn=30,Gt=16*1024,nn={local:"local_",external:void 0,guest:"guest_",anonymous:"anonymous_"},Vt=720*60*60*1e3;function dt(t){let e=new TextEncoder().encode(t.secret),r=t.issuer??tn,n=t.ttlMs??en,o=t.clockToleranceSec??rn,s=t.embedExternalToken??!0,i=new Set(["local","external","guest","anonymous"]);async function a(u,l){if(u.source==="anonymous")throw w.forbidden("anonymous identity cannot be issued a token");if(l?.external){let E=Buffer.byteLength(l.external.token,"utf8");if(E>(t.maxExternalTokenBytes??Gt))throw w.externalError(`external token too large (${E} bytes > ${t.maxExternalTokenBytes??Gt}); 请精简外部 claims,或由外部返回 identity 而非透传 token`)}let p=l?.now??Date.now(),g=Math.floor(p/1e3),m=Math.floor((p+n)/1e3);return{token:await new Qr({source:u.source,...s&&l?.external?{external:l.external}:{}}).setProtectedHeader({alg:"HS256"}).setSubject(u.userId).setIssuer(r).setIssuedAt(g).setExpirationTime(m).sign(e),expiresAt:m*1e3}}async function d(u,l){try{let{payload:p}=await Zr(u,e,{issuer:r,algorithms:["HS256"],clockTolerance:o,...l?{ignoreExpiration:!0}:{}}),g=p.sub,m=p.source;if(!g||typeof g!="string")throw w.unauthorized("token subject missing");if(!m||!i.has(m))throw w.unauthorized("token source missing");let R=nn[m];if(R&&!g.startsWith(R))throw w.unauthorized("token subject/source mismatch");return{sub:g,iss:p.iss??r,iat:typeof p.iat=="number"?p.iat*1e3:0,exp:typeof p.exp=="number"?p.exp*1e3:0,source:m,...p.external?{external:p.external}:{}}}catch(p){throw p instanceof w?p:p.code==="ERR_JWT_EXPIRED"?w.tokenExpired("token expired"):w.unauthorized(`invalid token: ${p.message}`)}}async function f(u){return d(u,!1)}async function c(u){return d(u,!0)}return{issue:a,verify:f,verifyIgnoringExpiration:c}}function ut(t,e={}){let r=e.mode??"required";return{async authenticate(n){return de(t,n,r)}}}import{createLogger as bc}from"@tbox.cn/app-sdk-core";import{randomUUID as on}from"crypto";var Z=class extends Error{constructor(r,n){super(r);this.code=n;this.name="ScopeSessionError"}code};function sn(t){let e=t.ttlMs??18e5,r=t.now??(()=>Date.now()),n=new Map;return{bootstrap(o){let s=o.attributes??{};if(!t.validateScope(s))throw new Z("Scope 形状校验未通过(v4:mallId 会话边界键非空——白名单判定已退役)","SCOPE_INVALID");let i=t.extractScopeKey(o),a=r(),d={sessionId:on(),scopeKey:i,scope:structuredClone(s),scopeFlat:t.buildScope(s),createdAt:a,expiresAt:a+e};return n.set(d.sessionId,d),d},get(o){let s=n.get(o);return s?s.expiresAt<=r()?(n.delete(o),null):s:null},assertCurrent(o,s){let i=this.get(o);if(!i)throw new Z("会话不存在或已过期","SESSION_NOT_FOUND");if(i.scopeKey!==s)throw new Z("会话 scope 不匹配(跨 mall 需新会话)","SESSION_SCOPE_MISMATCH");return i},sweep(){let o=r();for(let[s,i]of n)i.expiresAt<=o&&n.delete(s)}}}import{PROVIDER_ERROR_CODES as $}from"@tbox.cn/app-contracts";import{existsSync as an,readFileSync as cn}from"fs";import dn from"path";var x=class extends Error{constructor(r,n){super(n);this.code=r;this.name="ProviderError"}code};function un(t){let e=new Set(t.allowedHeaderNames??[]),r=t.production??process.env.NODE_ENV==="production";return{resolve(n,o){if(!n)throw r?new x($.CREDENTIAL_REF_MISSING,"credentialRef 缺失(生产 fail-fast)"):new x($.CREDENTIAL_REF_MISSING,"credentialRef 缺失");if(!n.startsWith("secret://"))throw new x($.CREDENTIAL_REF_MISSING,`非法 credentialRef 前缀:${n.split("://")[0]}`);let s=n.slice(9),i;if(t.files){let l=dn.join(t.files,`${s.toLowerCase().replaceAll("_","-")}.json`);if(an(l))try{i=cn(l,"utf8")}catch{i=void 0}}if(i??=t.source[s.replaceAll("-","_").toUpperCase()],i??=t.source[s],!i)throw new x($.CREDENTIAL_REF_MISSING,`机密环境键缺失:${s}`);let a;try{a=JSON.parse(i)}catch{throw new x($.CREDENTIAL_REF_MISSING,`机密环境键非法 JSON:${s}`)}let d=a;if(!d||typeof d!="object")throw new x($.CREDENTIAL_REF_MISSING,`机密形状非法:${s}`);let f={};for(let[l,p]of Object.entries(d.scope??{}))typeof p=="string"&&(f[l]=p);if(Object.keys(f).length>0){for(let[l,p]of Object.entries(o))if(f[l]!==p)throw new x($.CREDENTIAL_SCOPE_MISMATCH,`凭证 scope 与实例 scope 不匹配:${l}`)}let c=typeof d.type=="string"?d.type:"static-headers-v1",u=d.value&&typeof d.value=="object"?d.value:{};if(c==="static-headers-v1"){for(let l of Object.keys(u))if(!e.has(l.toLowerCase()))throw new x($.CREDENTIAL_REF_MISSING,`头名不在白名单:${l}`)}return{scope:f,type:c,value:u}}}}import{PROVIDER_ERROR_CODES as k}from"@tbox.cn/app-contracts";import{randomUUID as pn}from"crypto";function pt(t){let e=t.circuitBreaker?.failureThreshold??3,r=t.circuitBreaker?.openMs??3e4,n=t.maxResponseBytes??1024*1024,o=t.maxTimeoutMs??15e3,s=t.now??(()=>Date.now());return{create:(a,d)=>{let f={failures:0,openedAt:0},c=t.strategies.get(d.credentialType);if(!c)throw new x(k.TRANSPORT_FAILURE,`未知认证类型:${d.credentialType}`);let l=(a.config??{}).baseUrl??"",p=a.scope??{},g=c.placement??"header";return{async request(m){if(f.openedAt>0){if(s()-f.openedAt<r)throw new x(k.CIRCUIT_OPEN,"熔断开闸");f.openedAt=0,f.failures=e-1}let R=m.method??"POST",h;try{h=ln(l,m.path,m.query)}catch{throw ee(f,e,r,s),new x(k.TRANSPORT_FAILURE,"URL 构造失败")}gn(h);let E;try{E=t.secretProvider.resolve(a.credentialRef??"",p)}catch(T){throw T instanceof x&&T.code===k.CREDENTIAL_REF_MISSING?T:new x(k.TRANSPORT_FAILURE,`凭证解析失败:${T.message}`)}let I;try{I=c.apply({method:R,url:h,body:m.body,credential:E,headers:m.extraHeaders})}catch{throw ee(f,e,r,s),new x(k.TRANSPORT_FAILURE,"认证参数构造失败")}let S=g==="body"&&(R==="POST"||R==="PUT");if((g==="query"||g==="body"&&!S)&&Object.keys(I).length>0)try{let T=new URL(h);for(let[L,Re]of Object.entries(I))T.searchParams.set(L,Re);h=T.toString()}catch{throw ee(f,e,r,s),new x(k.TRANSPORT_FAILURE,"认证 query 追加失败")}let v={"content-type":"application/json","x-trace-id":pn(),...g==="header"?I:{},...m.extraHeaders},N=m.body;S&&Object.keys(I).length>0&&(N={...m.body&&typeof m.body=="object"&&!Array.isArray(m.body)?m.body:{},...I});let _=Math.min(m.timeoutMs??o,o),O=new AbortController,rr=setTimeout(()=>O.abort(),_),He=R==="GET"||N===void 0?void 0:JSON.stringify(N);t.logger?.debug("[transport] 最终请求",JSON.stringify({method:R,url:h,body:He?JSON.parse(He):null,headers:Object.fromEntries(Object.entries(v).map(([T,L])=>T.toLowerCase()==="token"||T.toLowerCase()==="authorization"||T.toLowerCase()==="visitortoken"?[T,typeof L=="string"?`${L.slice(0,20)}...(len=${L.length})`:L]:[T,L]))}));try{let T=await fetch(h,{method:R,headers:v,body:He,signal:O.signal}),L=T.status>=500||T.status===429;if(!T.ok&&!L)throw new x(k.TRANSPORT_FAILURE,`HTTP ${T.status}`);if(L)throw ee(f,e,r,s),new x(k.TRANSPORT_FAILURE,`HTTP ${T.status}`);let Re=Number(T.headers.get("content-length")??0);if(Re>n)throw new x(k.RESPONSE_TOO_LARGE,`响应超限:${Re}B > ${n}B`);let Fe=T.body?.getReader(),X;if(Fe){let ht=[],xt=0;for(;;){let{done:nr,value:Rt}=await Fe.read();if(nr)break;if(xt+=Rt.byteLength,xt>n)throw await Fe.cancel(),new x(k.RESPONSE_TOO_LARGE,`响应超限:>${n}B`);ht.push(new TextDecoder().decode(Rt))}X=ht.join("")}else if(X=await T.text(),X.length>n)throw new x(k.RESPONSE_TOO_LARGE,`响应超限:${X.length}B > ${n}B`);f.failures=0;try{return JSON.parse(X)}catch{return X}}catch(T){throw T instanceof x?T:T instanceof DOMException&&T.name==="AbortError"?(ee(f,e,r,s),new x(k.TRANSPORT_FAILURE,`超时 ${_}ms`)):(ee(f,e,r,s),new x(k.TRANSPORT_FAILURE,`网络错误:${T.message}`))}finally{clearTimeout(rr)}}}}}}function ee(t,e,r,n){t.failures+=1,t.failures>=e&&(t.openedAt=n())}function ln(t,e,r){let n=t.replace(/\/+$/,""),o=e.startsWith("/")?e:`/${e}`,s=r&&Object.keys(r).length>0?`?${Object.entries(r).map(([i,a])=>`${encodeURIComponent(i)}=${encodeURIComponent(a)}`).join("&")}`:"";return`${n}${o}${s}`}function gn(t){let e=new URL(t);if(e.protocol!=="https:"&&!((e.hostname==="127.0.0.1"||e.hostname==="localhost")&&e.protocol==="http:"))throw new x(k.TRANSPORT_FAILURE,`强制 HTTPS:${e.protocol}`)}import{buildSupplyLookup as hn,PROVIDER_ERROR_CODES as te,resolveEffectiveService as Wt,stableSerializeScope as xn}from"@tbox.cn/app-contracts";var Ue=new Map;function fn(t,e){process.env.NODE_ENV!=="production"&&Ue.set(t,e)}function mn(){Ue.clear()}function zt(t){return Ue.has(t)}function Xt(t,e){let r=Ue.get(t);if(!r)throw new Error(`无 dev fixture:${t}`);return r(e)}function yn(t){let e={async request(n){let o=r;if(!o)throw new Error("fixture transport 未绑定实例");return Xt(o.providerTemplateId,n)}},r;return{create(n,o){return zt(o.providerTemplateId)?(r=n,e):t.create(n,o)}}}function lt(){let t=new Map,e=[],r;return{async request(n){if(e.push({path:n.path,body:n.body}),r){let s=r;throw r=void 0,s}let o=t.get(n.path);if(!o)throw new Error(`未 mock 的路径:${n.path}`);return o(n.body)},mock(n,o){t.set(n,o)},requests:()=>[...e],failNext(n){r=n}}}var Yt=(t,e)=>`${t}\0${e}`,Rn={identity:{userId:"",source:"anonymous"},credentials:{provider:"anonymous",token:""},attributes:{}},Tn=Object.freeze({});function En(t){let e=new Map,r=new Map,n=t.transportFactory??pt({strategies:t.authStrategies,secretProvider:t.secretProvider,logger:t.logger}),o=t.instanceIdOf??(c=>xn(c)),s=hn(t.catalog),i=Tn,a=c=>typeof c=="string"?c:`${c.provider}(inline)`,d=(c,u,l,p)=>{if(typeof c=="string"){let g=t.catalog.providers[c]?.[u];if(!g)throw new x(te.PROVIDER_NOT_FOUND,`provider 未命中 catalog:${c}/${u}(${l}@${p})`);return{provider:c,implementation:u,entry:g,inline:void 0}}return{provider:c.provider,implementation:"inline",entry:void 0,inline:c}};function f(c){let u=o(c.scope),l=Wt(t.integrations,c.service,u,s,t.ownerOf);if(l.status==="service-not-configured")throw new x(te.SERVICE_NOT_CONFIGURED,`服务槽未配置:${c.service}(integrations.services 缺键)`);if(l.status==="instance-not-found")throw new x(te.INSTANCE_NOT_FOUND,`无匹配实例:${c.service}@${u}(instances 缺键)`);if(l.status==="instance-disabled")throw new x(te.INSTANCE_DISABLED,`实例已禁用:${c.service}@${u}`);if(l.status==="binding-unresolved")throw new x(te.BINDING_UNRESOLVED,l.message);let p=l.effective,g=d(p.provider,p.implementation,c.service,u),m=g.entry?.local===!0,R;if(m)R=lt();else{let h=`${c.service}\0${u}\0${g.provider}\0${g.implementation}`;R=r.get(h)??(()=>{let E={providerInstanceId:`${c.service}:${u}:${g.provider}:${g.implementation}`,providerTemplateId:g.implementation,scope:c.scope,config:p.config,...p.credentialRef?{credentialRef:p.credentialRef}:{},enabled:!0},I={providerTemplateId:g.implementation,credentialType:g.entry?.credentialType??g.inline?.credentialType??"-"},S=n.create(E,I);return r.set(h,S),S})()}return{transport:R,local:m,config:p.config,credentialRef:p.credentialRef,provider:g.provider,implementation:g.implementation,providerDisplay:a(p.provider),instanceId:u}}return{registerAdapter(c,u,l){let p=Yt(c,u);e.has(p)&&t.logger?.warn(`adapter 重复注册(后者覆盖):${c}/${u}`),e.set(p,l)},listAdapters(){return[...e.keys()]},declareProviderInstances(c,u){if(u.length!==0){for(let l of u)if(typeof l.id!="string"||!l.id||l.id==="*"||typeof l.name!="string"||!l.name)throw new Error(`declareProviderInstances 非法实例(provider=${c}):id 禁空与 '*'(通配保留字),name 须非空 string`);i[c]!==void 0&&t.logger?.warn(`provider instances 重复声明(后者覆盖):${c}`),i=Object.freeze({...i,[c]:Object.freeze(u.map(l=>Object.freeze({...l})))})}},providerInstanceDeclarations(){return i},async resolveService(c){let u=f(c),l=e.get(Yt(u.provider,u.implementation));if(!l)throw new x(te.PROVIDER_NOT_IMPLEMENTED,`provider 未实施(无 adapter 注册):${u.providerDisplay}/${u.implementation}(${c.service}@${u.instanceId})`);return l({transport:Se(u.local?void 0:t.logger,u.transport),config:u.config??{},...u.credentialRef?{credentialRef:u.credentialRef}:{},...u.credentialRef?{credential:()=>t.secretProvider.resolve(u.credentialRef,c.scope)}:{},scope:c.scope,instanceId:u.instanceId,request:c.request,service:c.service})},async resolveLoginExchange(c){let u=f({...c,request:Rn});return{transport:Se(u.local?void 0:t.logger,u.transport),config:u.config??{},providerId:u.implementation}},hasProviderBinding(c,u,l,p){let g=o(u),m=Wt(t.integrations,c,g,s,t.ownerOf);if(m.status!=="ok")return!1;let R=m.effective;return typeof R.provider=="string"&&R.provider===l&&R.implementation===p},raw:{config:t.integrations,...t.source?{source:t.source}:{}}}}import{loadProviderCatalog as In,buildOwnershipMap as Sn}from"@tbox.cn/app-toolkit";import{EventType as Jt}from"@ag-ui/core";import{TBOX_CARD_UPDATE_EVENT_NAME as An,TBOX_NOTICE_EVENT_NAME as bn}from"@tbox.cn/app-contracts";var vn=y("CARD");function wn(t){return{actionId:t.actionId,...t.label!==void 0?{label:t.label}:{},...t.kind!==void 0?{kind:t.kind}:{},...t.value!==void 0?{value:t.value}:{},...t.visible!==void 0?{visible:t.visible}:{},...t.url!==void 0?{url:t.url}:{},...t.style!==void 0?{style:t.style}:{},...t.enabled!==void 0?{enabled:t.enabled}:{},...t.disabledReason!==void 0?{disabledReason:t.disabledReason}:{}}}async function $e(t){if(t.payload.actions&&t.payload.actions.length>0)return t.payload;let e=[];for(let n of t.actions.getAll().values())n.cardType===t.payload.cardType&&!("hidden"in n&&n.hidden)&&e.push({...wn(n),...n.singleUse?{singleUse:!0}:{}});if(e.length===0)return t.payload;let r={};for(let n of e)if(!(n.kind&&n.kind!=="moduleAction"))try{r[n.actionId]=await t.grants.issue({moduleId:t.actions.get(n.actionId)?.moduleId??"",actionId:n.actionId,cardType:t.payload.cardType,cardId:t.payload.id,sessionId:t.sessionId,scope:t.scope,singleUse:n.singleUse??!1})}catch(o){vn.warn(`动作 token 签发失败(跳过):${n.actionId}`,o.message)}return{...t.payload,actions:e.map(({singleUse:n,...o})=>o),...Object.keys(r).length>0?{actionTokens:r}:{}}}var Cn=600*1e3;function _n(t){return t.type==="payment"||t.type==="authorization.alipay-user-profile"}function kn(t){let e=new Map;return{register(r){if((!r.kind||r.kind==="moduleAction")&&!r.execute)throw new Error(`moduleAction 动作缺少 execute 回调:${r.moduleId}:${r.actionId}`);e.set(r.actionId,r)},get(r){return e.get(r)},async dispatch(r){let n=e.get(r.actionId);if(!n)throw new A("ACTION_NOT_FOUND",`未知模块动作:${r.actionId}`);let o=t.extractScopeKey(r.ctx),s=t.sessions.assertCurrent(r.sessionId,o),i=await t.grants.authorize(r.token,{actionId:r.actionId,sessionId:s.sessionId,scope:s.scopeFlat,cardId:r.surfaceId||void 0}),a,d=[];try{if(i.cardType!==n.cardType)throw new A("GRANT_INVALID","授权 cardType 与动作声明不一致");let c=r.formData&&typeof r.formData=="object"&&!Array.isArray(r.formData)?r.formData:{},u=i.input&&typeof i.input=="object"&&!Array.isArray(i.input)?{...c,...i.input}:c,l=u;if(n.inputSchema){let p=n.inputSchema.safeParse(u);if(!p.success)throw new A("INPUT_INVALID",`入参校验失败:${p.error.message}`);l=p.data}if(!n.execute)throw new A("GRANT_INVALID",`动作无服务端执行体(纯客户端 kind 不应进 dispatch):${r.actionId}`);if(a=await n.execute(l,r.ctx),!a||typeof a!="object")throw new A("RESULT_INVALID","动作结果形状非法");for(let p of a.effects??[]){let g=p;if(g&&_n(g)){let m=e.get(g.resultActionId);if(!m)throw new A("CALLBACK_INVALID",`效果回调动作未注册:${g.resultActionId}`);if(m.moduleId!==i.moduleId)throw new A("CALLBACK_INVALID",`效果回调动作组件归属不一致:${g.resultActionId}`);if(m.cardType!==n.cardType)throw new A("CALLBACK_INVALID",`效果回调动作 cardType 与当前动作不一致:${g.resultActionId}`);if(m.singleUse!==!0)throw new A("CALLBACK_INVALID",`效果回调动作必须 singleUse:${g.resultActionId}`);g.callback={actionId:m.actionId,contextToken:await t.grants.issue({moduleId:i.moduleId,actionId:m.actionId,cardType:n.cardType,sessionId:s.sessionId,scope:s.scopeFlat,singleUse:!0,ttlMs:Cn,...g.type==="payment"?{input:{paymentRef:g.paymentRef}}:{}})}}}if(d=t.effects.project(a),a.update){if(a.card)throw new A("RESULT_INVALID","card 与 update 互斥");if(!r.surfaceId)throw new A("RESULT_INVALID","原地更新需要来源卡 surfaceId");let g=new J(t.cardRegistry).build({cardType:n.cardType,data:a.update.data,conversationId:"",id:r.surfaceId});if(!g||g.status==="error")throw new A("RESULT_INVALID",`更新数据未通过 ${n.cardType} dataSchema 校验`);let m=await $e({payload:g,actions:t.actions,grants:t.grants,sessionId:s.sessionId,scope:s.scopeFlat}),R={...m,meta:{...m.meta,updatedAt:Date.now()}};r.emit({type:Jt.CUSTOM,name:An,value:R})}else if(a.card){let p=new J(t.cardRegistry),g=p.build({cardType:a.card.cardType,data:a.card.data,conversationId:""});if(g){let m=await $e({payload:g,actions:t.actions,grants:t.grants,sessionId:s.sessionId,scope:s.scopeFlat});p.send(m,r.emit)}}}catch(c){throw n.singleUse&&await t.grants.restore(r.token),c}let f=typeof a.text=="string"?a.text.trim():"";f&&r.emit({type:Jt.CUSTOM,name:bn,value:{level:"info",text:De(f),surfaceId:r.surfaceId||void 0}});for(let c of d)r.emit(c);return a}}}import{TBOX_EFFECT_EVENT_NAME as Pn}from"@tbox.cn/app-contracts";var B=class extends Error{constructor(e){super(e),this.name="EffectError"}},On=new Set(["navigation.open","payment","share","download","authorization.alipay-user-profile","session.credentials-refreshed"]);function Nn(t){if(t.type==="payment"){if(!t.paymentRef||!t.orderInfo||!t.resultActionId)throw new B("payment 效果缺少 paymentRef/orderInfo/resultActionId")}else if(t.type==="authorization.alipay-user-profile"&&(!t.resultActionId||t.scopes.length===0))throw new B("authorization 效果缺少 resultActionId/scopes")}function Mn(t={}){return{project(e){let r=[];for(let n of e.effects??[]){let o=n;if(!o||typeof o.type!="string"||!On.has(o.type))throw new B(`未知宿主效果类型:${String(o?.type)}`);if(o.type==="navigation.open"){if(t.routeWhitelist&&!t.routeWhitelist.has(o.routeId))throw new B(`导航路由不在白名单:${o.routeId}`)}else Nn(o);r.push({type:"CUSTOM",name:Pn,value:o})}return r}}}var he=class extends Error{constructor(r,n){super(n);this.code=r;this.name="KnowledgeError"}code};function Ln(t={}){let e=t.apiKey??process.env.TBOX_API_KEY;if(!e)throw new he("MISSING_CREDENTIAL","TBOX_API_KEY 未配置");let r=new me({apiKey:e,...t.appId?{appId:t.appId}:{},...t.datasetId?{datasetId:t.datasetId}:{},...t.topK?{topK:t.topK}:{}});return{async search(n){try{let o=await r.retrieve(n.query,{...n.datasetId?{datasetId:n.datasetId}:{},...n.topK!==void 0?{topK:n.topK}:{},...n.scoreThreshold!==void 0?{scoreThreshold:n.scoreThreshold}:{}});return!o.success||!o.data?[]:o.data.map(s=>({content:s.content,score:s.score,...s.originFileName?{originFileName:s.originFileName}:{},...s.documentId?{documentId:s.documentId}:{},...s.metadata?{metadata:s.metadata}:{}}))}catch(o){throw new he("API_ERROR",`知识库检索失败:${o.message}`)}}}}var K=class extends Error{constructor(r,n){super(n);this.code=r;this.name="PaymentError"}code};function Dn(t={}){return{async createCashier(e){if(!t.createCashierHandler)throw new K("NOT_CONFIGURED","支付通道未装配(宿主注入 createCashierHandler)");try{return await t.createCashierHandler(e)}catch(r){throw r instanceof K?r:new K("API_ERROR",`收银台创建失败:${r.message}`)}},async queryResult(e){if(!t.queryResultHandler)return{status:"UNKNOWN"};try{return await t.queryResultHandler(e)}catch(r){throw r instanceof K?r:new K("API_ERROR",`支付结果查询失败:${r.message}`)}}}}import{parseIntegrationsConfig as Gc,assertNoInlineSecrets as Vc}from"@tbox.cn/app-contracts";import{createSign as Un,createVerify as $n}from"crypto";var Kn="https://openapi.alipay.com/gateway.do";function Hn(t=process.env,e=fetch){function r(){let i=t.ALIPAY_APP_ID?.trim(),a=t.ALIPAY_APP_PRIVATE_KEY?.trim(),d=t.ALIPAY_PUBLIC_KEY?.trim();if(!i||!a||!d)throw new Error("AUTH_NOT_CONFIGURED: Alipay user authorization is unavailable");return Object.freeze({appId:i,privateKey:a,publicKey:d})}async function n(i,a){let{appId:d,privateKey:f}=r(),c={app_id:d,method:i,charset:"utf-8",sign_type:"RSA2",timestamp:Qt(new Date),version:"1.0",...a};c.sign=Fn(c,f);let u=await e(Kn,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded;charset=utf-8"},body:new URLSearchParams(c).toString(),signal:AbortSignal.timeout(1e4)});if(!u.ok)throw new Error(`ALIPAY_API_ERROR: HTTP ${u.status}`);let l=await u.text();try{return JSON.parse(l)}catch{throw new Error("ALIPAY_API_ERROR: response is not valid JSON")}}function o(i){let a=i.error_response;if(!a||typeof a!="object"||Array.isArray(a))return;let d=a,f=P(d.sub_msg)??P(d.msg)??"unknown error";throw new Error(`INVALID_CODE: ${f}`)}async function s(i){if(!i.trim()||i.length>256)throw new Error("INVALID_CODE: invalid Alipay auth code");let a=await n("alipay.system.oauth.token",{grant_type:"authorization_code",code:i});o(a);let d=a.alipay_system_oauth_token_response;if(!d||typeof d!="object"||Array.isArray(d))throw new Error("INVALID_CODE: no token response");return d}return Object.freeze({async authorizeUserProfile(i){let{publicKey:a}=r(),d=await s(i),f=P(d.access_token);if(!f)throw new Error("INVALID_CODE: no access_token in response");let c=await n("alipay.user.info.share",{auth_token:f});o(c);let u=c.alipay_user_info_share_response,l=P(c.sign);if(!u||typeof u!="object"||Array.isArray(u)||!l)throw new Error("ALIPAY_API_ERROR: incomplete authorized profile response");let p=$n("RSA-SHA256");if(p.update(JSON.stringify(u),"utf8"),!p.verify(gt(a,"PUBLIC"),l,"base64"))throw new Error("ALIPAY_API_ERROR: profile response signature is invalid");let g=u,m=P(g.user_id)??P(g.open_id)??P(d.user_id)??P(d.open_id);if(!m)throw new Error("ALIPAY_API_ERROR: authorized user id is missing");let R=P(g.mobile),h=P(g.nick_name)??P(g.nickName),E=P(g.avatar),I=E?.startsWith("https://")?E:void 0,S=qn(g.gender),z=P(g.birthday);return Object.freeze({userId:m,...R?{mobile:R}:{},...h?{displayName:h}:{},...I?{avatarUrl:I}:{},...S?{gender:S}:{},...z?{birthday:z}:{}})}})}function Qt(t){let e=r=>String(r).padStart(2,"0");return`${t.getFullYear()}-${e(t.getMonth()+1)}-${e(t.getDate())} ${e(t.getHours())}:${e(t.getMinutes())}:${e(t.getSeconds())}`}function gt(t,e){let r=t.replace(/\\n/gu,`
|
|
2
|
+
`).trim();if(r.startsWith("-----BEGIN"))return r;let n=r.replace(/\s+/gu,"").match(/.{1,64}/gu)?.join(`
|
|
3
|
+
`);if(!n)throw new Error(`ALIPAY_${e}_KEY is invalid`);return`-----BEGIN ${e} KEY-----
|
|
4
|
+
${n}
|
|
5
|
+
-----END ${e} KEY-----`}function Zt(t,e){let r=Object.keys(t).filter(o=>o!=="sign").sort().map(o=>`${o}=${t[o]}`).join("&"),n=Un("RSA-SHA256");return n.update(r,"utf8"),n.sign(gt(e,"PRIVATE"),"base64")}function Fn(t,e){return Zt(t,e)}function P(t){return typeof t=="string"&&t.trim()?t.trim():void 0}function qn(t){let e=P(t)?.toLowerCase();if(e)return e==="m"||e==="1"||e==="male"?"male":e==="f"||e==="2"||e==="female"?"female":"unknown"}import{importSPKI as oo,jwtVerify as so}from"jose";var jn=y("AUTH");function ft(t=process.env){let e=Me(t),r,n;if(!e)throw w.missingSecret("AUTH_TOKEN_SECRET 未配置(prod 禁用回退)。请由平台注入 AUTH_TOKEN_SECRET(≥32 字节随机串)。");r=e.secret,n=e.source==="explicit"?"platform":e.source,n!=="platform"&&jn.warn(`JWT 密钥回退至 ${n}(${n==="apiKey"?"TBOX_API_KEY":"dev 临时随机"})。生产请配置平台托管 AUTH_TOKEN_SECRET;回退模式下平台运维可伪 token。`);let o=t.AUTH_MODE==="optional"?"optional":"required";return{secret:r,secretSource:n,mode:o,issuer:t.AUTH_TOKEN_ISSUER||void 0,ttlMs:t.AUTH_TOKEN_TTL_MS?Number(t.AUTH_TOKEN_TTL_MS):void 0,embedExternalToken:t.AUTH_EMBED_EXTERNAL_TOKEN!=="false",externalTokenMaxBytes:t.AUTH_EXTERNAL_TOKEN_MAX_BYTES?Number(t.AUTH_EXTERNAL_TOKEN_MAX_BYTES):void 0,external:t.AUTH_EXTERNAL_EXCHANGE_URL&&t.AUTH_EXTERNAL_SECRET?{exchangeUrl:t.AUTH_EXTERNAL_EXCHANGE_URL,secret:t.AUTH_EXTERNAL_SECRET,provider:t.AUTH_EXTERNAL_PROVIDER||"external",timeoutMs:t.AUTH_EXTERNAL_TIMEOUT_MS?Number(t.AUTH_EXTERNAL_TIMEOUT_MS):3e3,jwtPublicKey:t.AUTH_EXTERNAL_JWT_PUBLIC_KEY?.trim()||void 0}:void 0}}function mt(t){return dt({secret:t.secret,issuer:t.issuer,ttlMs:t.ttlMs,embedExternalToken:t.embedExternalToken,...t.externalTokenMaxBytes?{maxExternalTokenBytes:t.externalTokenMaxBytes}:{}})}import{createHmac as Bn,createHash as Gn}from"crypto";function yt(t){let e=t.userId??t.openId??t.openid??t.uid??t.id??t.unionId;if(!e||typeof e!="string")throw new Error("EXTERNAL_IDENTITY_MISSING: 外部响应缺少 userId");return{userId:e,source:"external",displayName:t.displayName??t.nickname??t.name,avatar:t.avatar??t.avatarUrl}}var Vn=y("AUTH:EXTERNAL");function zn(t){let e=t.split(".");if(e.length===3)try{let r=JSON.parse(Buffer.from(e[1],"base64url").toString("utf8"));return typeof r.sub=="string"?r.sub:void 0}catch{return}}var xe=class{constructor(e){this.opts=e;this.provider=e.provider}opts;provider;async exchangeByCode({code:e,platform:r}){let o=JSON.stringify({code:e,platform:r??"external",scopes:["auth_base"]}),s=String(Date.now()),i=Bn("sha256",this.opts.secret).update(o).digest("hex"),a=await this.postWithRetry(o,s,i);return this.converge(a)}async postOnce(e,r,n){let o=new AbortController,s=setTimeout(()=>o.abort(),this.opts.timeoutMs??3e3);try{let i=await fetch(this.opts.exchangeUrl,{method:"POST",headers:{"Content-Type":"application/json","X-Tbox-Sign":n,"X-Tbox-Timestamp":r},body:e,signal:o.signal});if(!i.ok)throw new Error(`EXTERNAL_HTTP_${i.status}`);return await i.json()}finally{clearTimeout(s)}}async postWithRetry(e,r,n){let o;for(let s=0;s<2;s++)try{return await this.postOnce(e,r,n)}catch(i){if(o=i,(i.message??"").startsWith("EXTERNAL_HTTP_4"))break}throw o}async converge(e){let r=this.provider;if(!e||typeof e!="object")throw new Error("EXTERNAL_RESPONSE_INVALID: 外部响应结构缺失");if(e.jwt)return{identity:await this.resolveJwtIdentity(e),credentials:{token:e.jwt,provider:r}};if(e.opaqueToken)return{identity:this.mapIdentity(e),credentials:{token:e.opaqueToken,provider:r}};if(e.token||e.identity){let n=this.mapIdentity(e),o=e.token?{token:e.token,provider:r}:void 0;return{identity:n,...o?{credentials:o}:{}}}throw new Error("EXTERNAL_RESPONSE_INVALID: 三态(jwt/identity/opaqueToken)均缺失")}async resolveJwtIdentity(e){let r=e.jwt;if(e.identity)return this.mapIdentity(e);if(this.opts.verifyJwt){let o=await this.opts.verifyJwt(r);if(!o.sub)throw new Error("EXTERNAL_RESPONSE_INVALID: jwt payload 缺少 sub");return{userId:o.sub,source:"external",...o.displayName?{displayName:o.displayName}:{},...o.avatar?{avatar:o.avatar}:{}}}let n=zn(r);if(!n)throw new Error("EXTERNAL_RESPONSE_INVALID: jwt payload 缺少 sub(外部请返回 identity 或配置 AUTH_EXTERNAL_JWT_PUBLIC_KEY)");return Vn.warn("jwt 响应无 identity 且未配置 AUTH_EXTERNAL_JWT_PUBLIC_KEY,按 payload.sub 解码取身份(未验签,建议配置验签密钥或由外部返回 identity)"),{userId:n,source:"external"}}deriveFallbackId(e){return`opaque_${Gn("sha256").update(e).digest("hex").slice(0,24)}`}mapIdentity(e){let r={...e.identity??{},...!e.identity&&e.opaqueToken?{userId:this.deriveFallbackId(e.opaqueToken)}:{}};return this.opts.mapIdentity?this.opts.mapIdentity(r):yt(r)}};import{createHash as Zn,randomBytes as eo}from"crypto";import{Router as to}from"express";import{createSign as Xn}from"crypto";var Ke=y("AUTH:EXT"),Wn=y("AUTH");async function er(t,e,r){Ke.info(`→ ${t}`);let n=Date.now();try{let o=await fetch(e,r),s=await o.json(),i=Date.now()-n,a=`← ${t} durMs=${i} status=${o.status}`;return i>re?Ke.warn(`SLOW ${a}`):Ke.info(a),s}catch(o){let s=Date.now()-n;throw Ke.warn(`← ${t} durMs=${s} err=${o?.message??String(o)}`),o}}var G=class{provider="weapp";allowsGuestLogin(){return!0}async exchangeByCode({code:e}){let r=process.env.WX_APP_ID,n=process.env.WX_APP_SECRET;if(!r||!n)throw new Error("AUTH_NOT_CONFIGURED: WX_APP_ID or WX_APP_SECRET missing");let o=`https://api.weixin.qq.com/sns/jscode2session?appid=${encodeURIComponent(r)}&secret=${encodeURIComponent(n)}&js_code=${encodeURIComponent(e)}&grant_type=authorization_code`,s=await er("weapp:jscode2session",o);if(s.errcode)throw new Error(`INVALID_CODE: ${s.errmsg||"unknown error"}`);if(!s.openid)throw new Error("INVALID_CODE: no openid in response");return{identity:{userId:`local_${s.openid}`,source:"local"}}}},V=class{provider="alipay";allowsGuestLogin(){return!0}async exchangeByCode({code:e}){let r=process.env.ALIPAY_APP_ID,n=process.env.ALIPAY_APP_PRIVATE_KEY;if(!r||!n)throw new Error("AUTH_NOT_CONFIGURED: ALIPAY_APP_ID or ALIPAY_APP_PRIVATE_KEY missing");let o={app_id:r,method:"alipay.system.oauth.token",charset:"utf-8",sign_type:"RSA2",timestamp:Yn(new Date),version:"1.0",grant_type:"authorization_code",code:e};o.sign=Qn(o,n);let s=Object.entries(o).map(([u,l])=>`${encodeURIComponent(u)}=${encodeURIComponent(l)}`).join("&"),i=await er("alipay:oauth-token","https://openapi.alipay.com/gateway.do",{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded;charset=utf-8"},body:s}),a=i.error_response;if(a){Wn.info("Alipay API error:",a.code,a.msg,a.sub_msg);let u=a.sub_msg||a.msg;throw new Error(`INVALID_CODE: ${u}`)}let d=i.alipay_system_oauth_token_response,f=d?.user_id||d?.open_id;if(!f)throw new Error("INVALID_CODE: no user_id in response");return{identity:{userId:`local_${f}`,source:"local"}}}};function Yn(t){let e=r=>String(r).padStart(2,"0");return`${t.getFullYear()}-${e(t.getMonth()+1)}-${e(t.getDate())} ${e(t.getHours())}:${e(t.getMinutes())}:${e(t.getSeconds())}`}function Jn(t){let e=t.replace(/\s+/g,"");return e.startsWith("-----")?t.replace(/\\n/g,`
|
|
6
|
+
`):`-----BEGIN RSA PRIVATE KEY-----
|
|
7
|
+
${e.match(/.{1,64}/g).join(`
|
|
8
|
+
`)}
|
|
9
|
+
-----END RSA PRIVATE KEY-----`}function Qn(t,e){let r=Object.keys(t).filter(o=>o!=="sign").sort().map(o=>`${o}=${t[o]}`).join("&"),n=Xn("RSA-SHA256");return n.update(r,"utf-8"),n.sign(Jn(e),"base64")}var H=y("AUTH");function ro(){return`guest_${eo(16).toString("hex")}`}function no(t){return{userId:`mock_${Zn("sha256").update(`${t.provider}:${t.token}`).digest("hex").slice(0,24)}`,source:"external"}}function tr(t){let e=to(),r=t.exchanges??j({alipay:new V,weapp:new G,...t.localExchanges});if(t.exchanges&&t.localExchanges)for(let s of Object.values(t.localExchanges))r.register(s);let n=(s,i)=>t.externalExchange&&(s===t.externalExchange.provider||s==="external")?t.externalExchange:r.get(s,i),o=async s=>{if(!(!s||typeof s!="string"))try{let i=await t.tokenService.verifyIgnoringExpiration(s);if(i.iat<=0){H.warn("previousToken 缺 iat,拒绝续签(fail-closed)");return}if(Date.now()-i.iat>Vt){H.warn("previousToken 超出重签窗(30d),按缺席处理");return}return{userId:i.sub,source:i.source}}catch(i){H.warn("previousToken 解析失败(fail-soft 按缺席处理):",i.message);return}};return e.post("/login",async(s,i)=>{let{method:a,code:d,platform:f,context:c,previousToken:u}=s.body??{};if(a!=="code"&&a!=="browser"){i.status(400).json({error:"method is required and must be 'code' or 'browser'",code:"AUTH_METHOD_REQUIRED"});return}let l=ue(c),p=await o(u);if(a==="code"){if(!d||typeof d!="string"){i.status(400).json({error:"code is required"});return}let N=n(typeof f=="string"&&f?f:"alipay",l);if(!N){i.status(400).json({error:`unsupported platform: ${f}`});return}try{let _=await N.exchangeByCode({code:d,...typeof f=="string"&&f?{platform:f}:{},context:l,...p?{previousIdentity:p}:{}}),O=await t.tokenService.issue(_.identity,{..._.credentials?{external:_.credentials}:{}});i.json({token:O.token,identity:_.identity,expiresAt:O.expiresAt})}catch(_){let O=_.message??"unknown";if(H.warn("login failed:",f,O),O.startsWith("INVALID_CODE")||O.startsWith("EXTERNAL_RESPONSE_INVALID")){i.status(401).json({error:O,code:"AUTH_UNAUTHORIZED"});return}if(O.startsWith("AUTH_NOT_CONFIGURED")){i.status(503).json({error:O,code:"AUTH_MISSING_SECRET"});return}H.error("login error:",O),i.status(502).json({error:"platform exchange error",code:"AUTH_EXTERNAL_ERROR"})}return}let g=n("alipay",l),m=process.env.NODE_ENV==="production",R="auto";if(typeof g?.browserLoginMode=="function")try{let v=g.browserLoginMode(l);(v==="guest"||v==="mock"||v==="off")&&(R=v)}catch(v){H.warn(`browserLoginMode 能力口违约(provider=${g?.provider??"none"},视为 auto):`,v.message)}if(R==="off"){i.status(403).json({error:"browser login disabled",code:"AUTH_BROWSER_LOGIN_DISABLED"});return}let h=async()=>{if(typeof g?.mockCredentials=="function")try{return await g.mockCredentials(l)}catch(v){H.warn(`mockCredentials 能力口违约抛异常(provider=${g.provider},视为无物料):`,v.message);return}},E=async()=>{let N={userId:p?.source==="guest"?p.userId:ro(),source:"guest"},_=await t.tokenService.issue(N);i.json({token:_.token,identity:N,expiresAt:_.expiresAt})},I=async v=>{if(m){i.status(403).json({error:"mock login disabled in production",code:"AUTH_BROWSER_LOGIN_DISABLED"});return}let N=no(v),_=await t.tokenService.issue(N,{external:v});i.json({token:_.token,identity:N,expiresAt:_.expiresAt})};if(R==="guest"){await E();return}if(R==="mock"){let v=await h();if(!v){i.status(503).json({error:"AUTH_NOT_CONFIGURED: 显式 mock 档未配置登录物料(查登录槽 config mockCredentials 能力口)",code:"AUTH_MISSING_SECRET"});return}await I(v);return}let S=!1;if(typeof g?.allowsGuestLogin=="function")try{S=g.allowsGuestLogin(l)===!0}catch(v){H.warn(`allowsGuestLogin 能力口违约(provider=${g.provider},视为 false):`,v.message)}if(S){await E();return}let z=await h();if(z&&!m){await I(z);return}i.status(403).json({error:"browser login disabled",code:"AUTH_BROWSER_LOGIN_DISABLED"})}),e.get("/me",(s,i)=>{let a=Y(s);if(!a){i.status(401).json({error:"unauthorized",code:"AUTH_UNAUTHORIZED"});return}i.json({identity:a.identity})}),e}function io(t,e){let r=t&&"secret"in t&&"mode"in t?t:ft(t),n=mt(r),o=Xe(n,{mode:r.mode,publicPaths:["/api/auth/login","/api/health"],...e?.validateContext?{validateContext:e.validateContext}:{}}),s=r.external?new xe({exchangeUrl:r.external.exchangeUrl,secret:r.external.secret,provider:r.external.provider,timeoutMs:r.external.timeoutMs,...r.external.jwtPublicKey?{verifyJwt:ao(r.external.jwtPublicKey)}:{}}):void 0,i=j({alipay:new V,weapp:new G,...e?.exchanges}),a=tr({tokenService:n,externalExchange:s,exchanges:i}),d=ut(n,{mode:r.mode});return{config:r,tokenService:n,middleware:o,router:a,wsAuth:d,externalEnabled:!!s,exchanges:i}}function ao(t){let e=oo(t.trim(),"RS256");return async r=>{let n=await e,{payload:o}=await so(r,n,{algorithms:["RS256"]});return{sub:o.sub,displayName:o.displayName,avatar:o.avatar}}}export{Le as ACTION_GRANT_MAX_TTL_MS,C as ActionGrantError,ie as ApiRouteRegistry,w as AuthError,bt as CONTEXT_ENVELOPE_MAX_BYTES,J as CardEmitter,se as CardRegistry,ae as DomainAssemblyRegistry,re as EXTERNAL_SLOW_MS,B as EffectError,xe as ExternalIdentityExchange,ne as HandlerRegistry,pe as InMemoryCardSnapshotStore,ce as InMemoryIdempotencyStore,Ye as InMemoryLruReplayStore,Dr as KNOWLEDGE_PLACEHOLDER_DATASET_IDS,he as KnowledgeError,V as LocalAlipayExchange,G as LocalWeappExchange,A as ModuleActionError,ct as NOTICE_TEXT_MAX_LEN,K as PaymentError,ge as PreToolRegistry,x as ProviderError,wr as SKILL_LIMITS,Je as SKILL_NAME_PATTERN,Mt as STATIC_HEADERS_CREDENTIAL_TYPE,Z as ScopeSessionError,W as ServiceRegistry,le as SkillRegistry,at as TTS_DEFAULTS,Q as TboxAppClient,Oe as TboxAsrClient,_e as TboxConversationClient,Ne as TboxFeedbackClient,Pe as TboxTtsClient,ke as TboxUploadClient,oe as ToolRegistry,Vc as assertNoInlineSecrets,$e as attachCardActionTokens,Sn as buildOwnershipMap,mn as clearDevProviderFixtures,yr as clearServices,pr as collectIntentRoutes,We as createActionGateway,Yr as createActionGrantService,Hn as createAlipayUserAuthorization,ze as createAnonymousIdentity,j as createAuthExchangeRegistry,Xe as createAuthMiddleware,io as createAuthRuntime,et as createAuthStrategyRegistry,pt as createAuthenticatedTransportFactory,Mn as createEffectRuntime,un as createEnvironmentSecretProvider,yn as createFixtureTransportFactory,Ze as createInMemoryStateStore,lt as createInMemoryTransport,En as createIntegrationsRegistry,Ln as createKnowledgeClient,bc as createLogger,kn as createModuleActionDispatcher,ot as createModuleResourceResolver,Dn as createPaymentClient,sn as createScopeSessionRegistry,kr as createServerContext,y as createServerLogger,dt as createTokenService,mt as createTokenServiceFromConfig,Ge as createTypedBus,ut as createWsAuthenticator,Bt as deriveActionGrantKey,tt as expandEnvVars,Qt as formatAlipayTimestamp,Y as getRequestContext,zt as hasDevProviderFixture,Ve as hashPayload,Cr as hashStateRef,rt as loadKnowledgeRegistry,In as loadProviderCatalog,yt as mapExternalIdentity,ue as normalizeContextEnvelope,Gc as parseIntegrationsConfig,ft as readAuthConfig,ur as recordCall,fn as registerDevProviderFixture,Kr as registerServerModules,fr as registerService,de as resolveBearerAuth,mr as resolveService,Xt as runDevProviderFixture,D as runLogScope,cr as runWithLogContext,kt as setRequestContext,dr as setServerSpanSink,Zt as signAlipayParams,Lt as staticHeadersStrategy,Jr as toNoticeText,gt as toPem,De as truncateNoticeText,Se as withCallLog};
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tbox.cn/app-sdk-server",
|
|
3
|
+
"version": "0.21.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"bin": {
|
|
6
|
+
"tbox-inspector-check": "bin/check-inspector.mjs"
|
|
7
|
+
},
|
|
8
|
+
"description": "tbox 应用平台 SDK server 轴:模块系统(ServerContext/注册表族/装配/动作域)、auth、inspector、Provider 集成运行时、平台客户端、upstream 内化客户端。agent 引擎在 @tbox.cn/app-agent-sdk-server。",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/server/index.d.ts",
|
|
12
|
+
"import": "./dist/server/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./server": {
|
|
15
|
+
"types": "./dist/server/index.d.ts",
|
|
16
|
+
"import": "./dist/server/index.js"
|
|
17
|
+
},
|
|
18
|
+
"./platform": {
|
|
19
|
+
"types": "./dist/platform/index.d.ts",
|
|
20
|
+
"import": "./dist/platform/index.js"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"bin",
|
|
26
|
+
"README.md",
|
|
27
|
+
"LICENSE"
|
|
28
|
+
],
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=20.0.0"
|
|
31
|
+
},
|
|
32
|
+
"publishConfig": {
|
|
33
|
+
"access": "public"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@ag-ui/core": "0.0.47",
|
|
37
|
+
"@tbox.cn/app-contracts": "0.12.0",
|
|
38
|
+
"@tbox.cn/app-sdk-core": "0.20.0",
|
|
39
|
+
"express": "4.22.2",
|
|
40
|
+
"jose": "6.2.8",
|
|
41
|
+
"zod": "3.25.76",
|
|
42
|
+
"@tbox.cn/app-toolkit": "0.5.0"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@mastra/core": "1.59.0",
|
|
46
|
+
"@mastra/schema-compat": "1.3.7",
|
|
47
|
+
"@types/express": "4.17.25",
|
|
48
|
+
"@types/node": "24.13.3",
|
|
49
|
+
"@types/supertest": "7.2.1",
|
|
50
|
+
"supertest": "7.2.2",
|
|
51
|
+
"tsup": "8.5.1",
|
|
52
|
+
"typescript": "5.9.3",
|
|
53
|
+
"vitest": "4.1.10",
|
|
54
|
+
"@tbox.cn/app-sdk-client": "0.21.0",
|
|
55
|
+
"@tbox.cn/app-agent-sdk-server": "0.1.0"
|
|
56
|
+
},
|
|
57
|
+
"license": "MIT",
|
|
58
|
+
"scripts": {
|
|
59
|
+
"build": "tsup",
|
|
60
|
+
"typecheck": "tsc --noEmit",
|
|
61
|
+
"test": "vitest run"
|
|
62
|
+
}
|
|
63
|
+
}
|