licell 0.9.37 → 0.9.38

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.
Files changed (2) hide show
  1. package/dist/licell.js +108 -108
  2. package/package.json +1 -1
package/dist/licell.js CHANGED
@@ -1,22 +1,22 @@
1
1
  #!/usr/bin/env node
2
- import{createRequire as dy}from"node:module";var uy=Object.create;var{getPrototypeOf:iy,defineProperty:Ft,getOwnPropertyNames:xc,getOwnPropertyDescriptor:ly}=Object,Hc=Object.prototype.hasOwnProperty;var gy=(e,n,t)=>{t=e!=null?uy(iy(e)):{};let o=n||!e||!e.__esModule?Ft(t,"default",{value:e,enumerable:!0}):t;for(let s of xc(e))if(!Hc.call(o,s))Ft(o,s,{get:()=>e[s],enumerable:!0});return o},Fc=new WeakMap,yy=(e)=>{var n=Fc.get(e),t;if(n)return n;if(n=Ft({},"__esModule",{value:!0}),e&&typeof e==="object"||typeof e==="function")xc(e).map((o)=>!Hc.call(n,o)&&Ft(n,o,{get:()=>e[o],enumerable:!(t=ly(e,o))||t.enumerable}));return Fc.set(e,n),n};var go=(e,n)=>{for(var t in n)Ft(e,t,{get:n[t],enumerable:!0,configurable:!0,set:(o)=>n[t]=()=>o})};var O=(e,n)=>()=>(e&&(n=e(e=0)),n);var hy=dy(import.meta.url);import{homedir as Bc}from"os";import{join as Ne}from"path";import{chmodSync as Yc,existsSync as st,mkdirSync as my,readFileSync as Oc,renameSync as py,rmSync as as,writeFileSync as ms}from"fs";function Kc(){return Ne(process.cwd(),".licell")}function $y(){return Ne(process.cwd(),".ali")}function jc(){return Ne(Kc(),"project.json")}function ky(){return Ne($y(),"project.json")}function xt(e){if(!st(e))my(e,{recursive:!0,mode:vc});try{Yc(e,vc)}catch{console.error(`⚠️ 无法设置目录权限 ${e},请手动确认权限为 0700`)}}function ys(e,n){try{return JSON.parse(Oc(e,"utf-8"))}catch{return n}}function ds(e,n,t=!1){let o=`${e}.${process.pid}.tmp`;try{if(ms(o,JSON.stringify(n,null,2),t?{mode:Gc}:void 0),t)try{Yc(o,Gc)}catch{throw Error(`无法设置安全文件权限 ${e},凭证未写入`)}py(o,e)}catch(s){try{as(o,{force:!0})}catch{}throw s}}function Mn(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function Wc(e){if(typeof e==="number"){if(!Number.isFinite(e)||e<=0)return;return e}if(typeof e==="string"){let n=e.trim();if(!n)return;let t=Number(n);if(!Number.isFinite(t)||t<=0)return;return t}return}function hs(e){let n=Wc(e);if(n===void 0||!Number.isInteger(n))return;return n}function Mc(e){let n=Mn(e)?e:{},t=Mn(n.envs)?n.envs:{},o={};for(let[p,w]of Object.entries(t))if(typeof w==="string")o[p]=w;let s={envs:o};if(typeof n.appName==="string"&&n.appName.trim().length>0)s.appName=n.appName.trim();if(typeof n.runtime==="string"&&n.runtime.trim().length>0)s.runtime=n.runtime.trim();if(typeof n.acrNamespace==="string"&&n.acrNamespace.trim().length>0)s.acrNamespace=n.acrNamespace.trim();let r=Mn(n.resources)?n.resources:null;if(r){let p=hs(r.memorySize),w=hs(r.timeout),H=Wc(r.cpu),T=hs(r.instanceConcurrency),x={...p!==void 0?{memorySize:p}:{},...w!==void 0?{timeout:w}:{},...H!==void 0?{cpu:H}:{},...T!==void 0?{instanceConcurrency:T}:{}};if(Object.keys(x).length>0)s.resources=x}let c=Mn(n.hooks)?n.hooks:null;if(c){let p=typeof c.preDeploy==="string"?c.preDeploy.trim():"",w=typeof c.postDeploy==="string"?c.postDeploy.trim():"",H={...p?{preDeploy:p}:{},...w?{postDeploy:w}:{}};if(Object.keys(H).length>0)s.hooks=H}let f=Mn(n.network)?n.network:null;if(f&&typeof f.vpcId==="string"&&typeof f.vswId==="string")s.network={vpcId:f.vpcId,vswId:f.vswId,sgId:typeof f.sgId==="string"?f.sgId:void 0,cidrBlock:typeof f.cidrBlock==="string"?f.cidrBlock:void 0};let u=Mn(n.cache)?n.cache:null;if(u&&typeof u.type==="string"&&typeof u.instanceId==="string"){let p={type:u.type,instanceId:u.instanceId,host:typeof u.host==="string"?u.host:void 0,port:typeof u.port==="number"?u.port:void 0,accountName:typeof u.accountName==="string"?u.accountName:void 0};if(typeof u.vkName==="string")p.vkName=u.vkName;if(typeof u.mode==="string")p.mode=u.mode;s.cache=p}let{appName:l,runtime:i,acrNamespace:g,envs:y,resources:d,hooks:m,network:h,cache:b,...$}=n;return{...$,...s,envs:o}}function _y(e){if(!Mn(e))return null;if(typeof e.accountId!=="string"||typeof e.ak!=="string"||typeof e.sk!=="string")return null;let t=(typeof e.region==="string"?e.region:Ge).trim().toLowerCase()||Ge,o=e.authSource==="bootstrap"||e.authSource==="manual"?e.authSource:void 0,s=typeof e.ramUser==="string"&&e.ramUser.trim().length>0?e.ramUser.trim():void 0,r=typeof e.ramPolicy==="string"&&e.ramPolicy.trim().length>0?e.ramPolicy.trim():void 0;return{accountId:e.accountId.trim(),ak:e.ak.trim(),sk:e.sk.trim(),region:t,...o?{authSource:o}:{},...s?{ramUser:s}:{},...r?{ramPolicy:r}:{}}}function by(){let e=Ne(process.cwd(),".gitignore"),n=[".licell/",".ali/"];if(!st(e)){ms(e,`${n.join(`
2
+ import{createRequire as dy}from"node:module";var uy=Object.create;var{getPrototypeOf:iy,defineProperty:Ft,getOwnPropertyNames:xc,getOwnPropertyDescriptor:ly}=Object,Hc=Object.prototype.hasOwnProperty;var gy=(e,n,t)=>{t=e!=null?uy(iy(e)):{};let s=n||!e||!e.__esModule?Ft(t,"default",{value:e,enumerable:!0}):t;for(let o of xc(e))if(!Hc.call(s,o))Ft(s,o,{get:()=>e[o],enumerable:!0});return s},Fc=new WeakMap,yy=(e)=>{var n=Fc.get(e),t;if(n)return n;if(n=Ft({},"__esModule",{value:!0}),e&&typeof e==="object"||typeof e==="function")xc(e).map((s)=>!Hc.call(n,s)&&Ft(n,s,{get:()=>e[s],enumerable:!(t=ly(e,s))||t.enumerable}));return Fc.set(e,n),n};var gs=(e,n)=>{for(var t in n)Ft(e,t,{get:n[t],enumerable:!0,configurable:!0,set:(s)=>n[t]=()=>s})};var O=(e,n)=>()=>(e&&(n=e(e=0)),n);var hy=dy(import.meta.url);import{homedir as Bc}from"os";import{join as Ne}from"path";import{chmodSync as Yc,existsSync as ot,mkdirSync as my,readFileSync as Oc,renameSync as py,rmSync as ao,writeFileSync as mo}from"fs";function Kc(){return Ne(process.cwd(),".licell")}function $y(){return Ne(process.cwd(),".ali")}function jc(){return Ne(Kc(),"project.json")}function ky(){return Ne($y(),"project.json")}function xt(e){if(!ot(e))my(e,{recursive:!0,mode:vc});try{Yc(e,vc)}catch{console.error(`⚠️ 无法设置目录权限 ${e},请手动确认权限为 0700`)}}function go(e,n){try{return JSON.parse(Oc(e,"utf-8"))}catch{return n}}function yo(e,n,t=!1){let s=`${e}.${process.pid}.tmp`;try{if(mo(s,JSON.stringify(n,null,2),t?{mode:Gc}:void 0),t)try{Yc(s,Gc)}catch{throw Error(`无法设置安全文件权限 ${e},凭证未写入`)}py(s,e)}catch(o){try{ao(s,{force:!0})}catch{}throw o}}function Mn(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function Wc(e){if(typeof e==="number"){if(!Number.isFinite(e)||e<=0)return;return e}if(typeof e==="string"){let n=e.trim();if(!n)return;let t=Number(n);if(!Number.isFinite(t)||t<=0)return;return t}return}function ho(e){let n=Wc(e);if(n===void 0||!Number.isInteger(n))return;return n}function Mc(e){let n=Mn(e)?e:{},t=Mn(n.envs)?n.envs:{},s={};for(let[p,w]of Object.entries(t))if(typeof w==="string")s[p]=w;let o={envs:s};if(typeof n.appName==="string"&&n.appName.trim().length>0)o.appName=n.appName.trim();if(typeof n.runtime==="string"&&n.runtime.trim().length>0)o.runtime=n.runtime.trim();if(typeof n.acrNamespace==="string"&&n.acrNamespace.trim().length>0)o.acrNamespace=n.acrNamespace.trim();let r=Mn(n.resources)?n.resources:null;if(r){let p=ho(r.memorySize),w=ho(r.timeout),H=Wc(r.cpu),T=ho(r.instanceConcurrency),x={...p!==void 0?{memorySize:p}:{},...w!==void 0?{timeout:w}:{},...H!==void 0?{cpu:H}:{},...T!==void 0?{instanceConcurrency:T}:{}};if(Object.keys(x).length>0)o.resources=x}let c=Mn(n.hooks)?n.hooks:null;if(c){let p=typeof c.preDeploy==="string"?c.preDeploy.trim():"",w=typeof c.postDeploy==="string"?c.postDeploy.trim():"",H={...p?{preDeploy:p}:{},...w?{postDeploy:w}:{}};if(Object.keys(H).length>0)o.hooks=H}let f=Mn(n.network)?n.network:null;if(f&&typeof f.vpcId==="string"&&typeof f.vswId==="string")o.network={vpcId:f.vpcId,vswId:f.vswId,sgId:typeof f.sgId==="string"?f.sgId:void 0,cidrBlock:typeof f.cidrBlock==="string"?f.cidrBlock:void 0};let u=Mn(n.cache)?n.cache:null;if(u&&typeof u.type==="string"&&typeof u.instanceId==="string"){let p={type:u.type,instanceId:u.instanceId,host:typeof u.host==="string"?u.host:void 0,port:typeof u.port==="number"?u.port:void 0,accountName:typeof u.accountName==="string"?u.accountName:void 0};if(typeof u.vkName==="string")p.vkName=u.vkName;if(typeof u.mode==="string")p.mode=u.mode;o.cache=p}let{appName:l,runtime:i,acrNamespace:g,envs:y,resources:d,hooks:m,network:h,cache:b,...$}=n;return{...$,...o,envs:s}}function _y(e){if(!Mn(e))return null;if(typeof e.accountId!=="string"||typeof e.ak!=="string"||typeof e.sk!=="string")return null;let t=(typeof e.region==="string"?e.region:Ge).trim().toLowerCase()||Ge,s=e.authSource==="bootstrap"||e.authSource==="manual"?e.authSource:void 0,o=typeof e.ramUser==="string"&&e.ramUser.trim().length>0?e.ramUser.trim():void 0,r=typeof e.ramPolicy==="string"&&e.ramPolicy.trim().length>0?e.ramPolicy.trim():void 0;return{accountId:e.accountId.trim(),ak:e.ak.trim(),sk:e.sk.trim(),region:t,...s?{authSource:s}:{},...o?{ramUser:o}:{},...r?{ramPolicy:r}:{}}}function by(){let e=Ne(process.cwd(),".gitignore"),n=[".licell/",".ali/"];if(!ot(e)){mo(e,`${n.join(`
3
3
  `)}
4
- `);return}let t=Oc(e,"utf-8");for(let o of n){let s=o.replace(/\/$/,"");if(t.split(/\r?\n/).some((f)=>{let u=f.trim();return u===o||u===s}))continue;let c=t.endsWith(`
4
+ `);return}let t=Oc(e,"utf-8");for(let s of n){let o=s.replace(/\/$/,"");if(t.split(/\r?\n/).some((f)=>{let u=f.trim();return u===s||u===o}))continue;let c=t.endsWith(`
5
5
  `)||t.length===0?"":`
6
- `;t=`${t}${c}${o}
7
- `}ms(e,t)}function Ty(){if(st(ho))return ho;if(st(ao))return ao;return null}function Cy(){let e=jc();if(st(e))return e;let n=ky();if(st(n))return n;return null}var yo,wy,ho,Uc,ao,vc=448,Gc=384,Ge="cn-hangzhou",k;var K=O(()=>{yo=Ne(Bc(),".licell-cli"),wy=Ne(Bc(),".ali-cli"),ho=Ne(yo,"auth.json"),Uc=Ne(yo,"config.json"),ao=Ne(wy,"auth.json");k={getAuth(){let e=Ty();if(!e)return null;let n=_y(ys(e,null));if(n&&e===ao)this.setAuth(n);return n},requireAuth(){let e=this.getAuth();if(!e)throw Error("未登录,请先执行 `licell login`");return e},setAuth(e){xt(yo),ds(ho,e,!0)},clearAuth(){as(ho,{force:!0}),as(ao,{force:!0})},getProject(){let e=Cy();if(!e)return{envs:{}};return Mc(ys(e,{envs:{}}))},setProject(e,n){xt(Kc()),by();let t=this.getProject(),o=n?.replaceEnvs?{...e.envs||{}}:{...t.envs,...e.envs||{}},s=Mc({...t,...e,envs:o});ds(jc(),s,!0)},getGlobalConfig(){return ys(Uc,{})},setGlobalConfig(e){xt(yo);let t={...this.getGlobalConfig(),...e};ds(Uc,t)}}});function Qc(e,n){return Object.prototype.hasOwnProperty.call(e,n)}function V(e,n,t){if(Qc(e,n))return e[n];if(t&&Qc(e,t))return e[t];return}function se(e,n){return V(e,`LICELL_${n}`,`ALI_${n}`)}var Xc={};go(Xc,{resolveSdkCtor:()=>te,createSharedFcClient:()=>Bn});import Ey from"@alicloud/fc20230330";import*as Vc from"@alicloud/openapi-client";function te(e,n){let t=e,o=typeof e==="function"?e:typeof t?.default==="function"?t.default:typeof t?.default?.default==="function"?(t?.default).default:null;if(!o)throw Error(`无法加载 ${n} SDK 构造器,请检查依赖安装和运行时模块格式`);return o}function Dc(e,n){if(!e)return n;let t=Number(e);if(!Number.isFinite(t)||t<=0)return n;return Math.floor(t)}function Bn(e){let n=e??k.requireAuth(),t=Dc(se(process.env,"FC_CONNECT_TIMEOUT_MS"),Sy),o=Dc(se(process.env,"FC_READ_TIMEOUT_MS"),qy),s=new Py(new Vc.Config({accessKeyId:n.ak,accessKeySecret:n.sk,endpoint:`${n.accountId}.${n.region}.fc.aliyuncs.com`,connectTimeout:t,readTimeout:o}));return{auth:n,client:s}}var Sy=60000,qy=600000,Py;var Ie=O(()=>{K();Py=te(Ey,"@alicloud/fc20230330")});var Zc={};go(Zc,{isTransientError:()=>Ee,isRoleMissingError:()=>wo,isNotFoundError:()=>ue,isInvalidDomainNameError:()=>po,isInstanceClassError:()=>mo,isConflictError:()=>J,isCidrConflictError:()=>$s,isAuthCredentialInvalidError:()=>ln,isAlreadyExistsRoleError:()=>ws,isAccessDeniedError:()=>Fe});function ze(e){if(typeof e!=="object"||e===null)return"";let n=String(e.code||""),t=String(e.message||"");return`${n} ${t}`}function un(e,n){let t=e.toLowerCase();return n.some((o)=>t.includes(o))}function J(e){return un(ze(e),["alreadyexist","alreadyexists","entityalreadyexists","already exists","conflict","duplicate","domainrecordduplicate"])}function ue(e){return un(ze(e),["notfound","no such","404","entitynotexist","not exist"])}function Ee(e){return un(ze(e),["throttling","too many requests","connecttimeout","readtimeout","requesttimeouterror","socket disconnected","econnreset","econnrefused","service unavailable","internal error"])}function mo(e){return un(ze(e),["instanceclass","soldout","outofstock","invalidparameter","not support","unsupported"])}function Fe(e){return un(ze(e),["accessdenied","forbidden","no permission"])}function ln(e){return un(ze(e),["invalidaccesskeyid","invalidaccesskeyid.notfound","signaturedoesnotmatch","incompletesignature","authenticationfailed","invalidsecuritytoken","security token is invalid","access key id does not exist","accesskey secret not found"])}function po(e){return un(ze(e),["invaliddomainname.format","invaliddomainname.noexist","invalid domain name","domain name does not exist"])}function wo(e){return un(ze(e),["servicelinkedrole.notexist"])}function ws(e){return un(ze(e),["entityalreadyexists.role","already exists"])}function $s(e){let n=ze(e).toLowerCase();return n.includes("cidr")&&(n.includes("conflict")||n.includes("overlap")||n.includes("invalid"))}import Ry,*as N from"@alicloud/ram20150501";import*as Jc from"@alicloud/openapi-client";function $o(e){let n=(e||Fy).trim();if(!Hy.test(n))throw Error("bootstrap RAM 用户名不合法:仅支持字母、数字、点、短横线、下划线,长度 1-64");return n}function Nc(e){let n=(e||xy).trim();if(!Ay.test(n))throw Error("bootstrap RAM 策略名不合法:仅支持字母、数字、短横线,长度 1-128");return n}function Ly(){return zc([...ko])}function zc(e){let n=[...new Set(e)].sort();return JSON.stringify({Version:"1",Statement:[{Effect:"Allow",Action:n,Resource:"*"}]})}function Ts(e){return new Iy(new Jc.Config({accessKeyId:e.ak,accessKeySecret:e.sk,endpoint:"ram.aliyuncs.com"}))}async function ef(e,n){try{return await e.getUser(new N.GetUserRequest({userName:n})),{created:!1}}catch(t){if(!ue(t))throw t}return await e.createUser(new N.CreateUserRequest({userName:n,displayName:n,comments:"Managed by licell bootstrap login"})),{created:!0}}async function Uy(e,n){try{return await e.getPolicy(new N.GetPolicyRequest({policyType:"Custom",policyName:n})),{created:!1}}catch(t){if(!ue(t))throw t}return await e.createPolicy(new N.CreatePolicyRequest({policyName:n,description:"Least-privilege policy generated by licell bootstrap login",policyDocument:Ly()})),{created:!0}}function vy(e){if(typeof e==="string")return[e].map((n)=>n.trim()).filter(Boolean);if(!Array.isArray(e))return[];return e.map((n)=>typeof n==="string"?n.trim():"").filter(Boolean)}function Gy(e){if(!e)return null;try{return JSON.parse(e)}catch{try{return JSON.parse(decodeURIComponent(e))}catch{return null}}}function My(e,n){let t=Gy(e);if(!t||typeof t!=="object")return{changed:!0,policyDocument:zc(n)};let o=t.Statement,s=Array.isArray(o)?[...o]:o&&typeof o==="object"?[o]:[],r=-1;for(let l=0;l<s.length;l+=1){let i=s[l];if(!i||typeof i!=="object")continue;if(String(i.Effect||"").toLowerCase()==="allow"){r=l;break}}let c=[...new Set(n)].sort(),f=!1;if(r===-1)s.push({Effect:"Allow",Action:c,Resource:"*"}),f=!0;else{let l=s[r];if(!l||typeof l!=="object")throw Error("策略文档格式错误");let i=vy(l.Action),g=[...new Set([...i,...c])].sort(),y=[...new Set(i)].sort();if(g.join("|")!==y.join("|"))f=!0;if(l.Action=g,l.Resource===void 0)l.Resource="*",f=!0}let u={...t,Version:typeof t.Version==="string"&&t.Version?t.Version:"1",Statement:s};return{changed:f,policyDocument:JSON.stringify(u)}}async function _s(e,n,t){if((await Uy(e,n)).created)return{created:!0,updated:!1};let r=(await e.getPolicy(new N.GetPolicyRequest({policyType:"Custom",policyName:n}))).body?.defaultPolicyVersion?.versionId;if(!r)return{created:!1,updated:!1};let f=(await e.getPolicyVersion(new N.GetPolicyVersionRequest({policyType:"Custom",policyName:n,versionId:r}))).body?.policyVersion?.policyDocument,u=My(f,t);if(!u.changed)return{created:!1,updated:!1};return await e.createPolicyVersion(new N.CreatePolicyVersionRequest({policyName:n,policyDocument:u.policyDocument,setAsDefault:!0,rotateStrategy:"DeleteOldestNonDefaultVersionWhenLimitExceeded"})),{created:!1,updated:!0}}async function bs(e,n,t){try{await e.attachPolicyToUser(new N.AttachPolicyToUserRequest({policyType:"Custom",policyName:n,userName:t}))}catch(o){let s=`${String(o?.code||"")} ${String(o?.message||"")}`.toLowerCase();if(s.includes("entityalreadyexists")||s.includes("already attached")||s.includes("attached already"))return;throw o}}async function nf(e,n){if(((await e.listAccessKeys(new N.ListAccessKeysRequest({userName:n}))).body?.accessKeys?.accessKey||[]).filter((u)=>u.status==="Active").length>=2)throw Error(`RAM 用户 ${n} 的 AccessKey 已达到上限(2 个)。请先在控制台删除旧 key 后重试。`);let r=await e.createAccessKey(new N.CreateAccessKeyRequest({userName:n})),c=r.body?.accessKey?.accessKeyId,f=r.body?.accessKey?.accessKeySecret;if(!c||!f)throw Error("创建 RAM AccessKey 成功但返回字段不完整,请重试");return{accessKeyId:c,accessKeySecret:f}}async function By(e,n){let t=n.trim();if(!t)return;let o=20,s;for(let r=0;r<o;r+=1){let c=await e.listUsers(new N.ListUsersRequest({marker:s,maxItems:100})),f=c.body?.users?.user||[];for(let u of f){let l=u.userName;if(!l)continue;if(((await e.listAccessKeys(new N.ListAccessKeysRequest({userName:l}))).body?.accessKeys?.accessKey||[]).some((y)=>y.accessKeyId===t))return l}if(!c.body?.isTruncated||!c.body.marker)break;s=c.body.marker}return}async function tf(e){let n=$o(e.userName),t=Nc(e.policyName),o=Ts(e.adminAuth),s=await ef(o,n),r=await _s(o,t,[...ko]);await bs(o,t,n);let c=await nf(o,n);try{await of(e.adminAuth)}catch{}return{userName:n,policyName:t,accessKeyId:c.accessKeyId,accessKeySecret:c.accessKeySecret,createdUser:s.created,createdPolicy:r.created,updatedPolicy:r.updated}}async function Cs(e){let n=e.currentAuth||null,t=Nc(e.policyName||n?.ramPolicy),o=Ts(e.adminAuth);try{await of(e.adminAuth)}catch{}let s=e.userName?$o(e.userName):n?.ramUser?$o(n.ramUser):void 0;if(!s&&n?.ak)s=await By(o,n.ak);if(!e.forceRotateKey&&s&&n?.ak&&n.sk){let l=await _s(o,t,[...ko]);return await bs(o,t,s),{mode:"updated-existing-key",userName:s,policyName:t,accessKeyId:n.ak,accessKeySecret:n.sk,createdUser:!1,createdPolicy:l.created,updatedPolicy:l.updated}}let r=s||$o(e.userName||n?.ramUser),c=await ef(o,r),f=await _s(o,t,[...ko]);await bs(o,t,r);let u=await nf(o,r);return{mode:"rotated-new-key",userName:r,policyName:t,accessKeyId:u.accessKeyId,accessKeySecret:u.accessKeySecret,createdUser:c.created,createdPolicy:f.created,updatedPolicy:f.updated}}async function of(e){let n=Ts(e);try{return await n.getRole(new N.GetRoleRequest({roleName:ks})),{created:!1}}catch(t){if(!ue(t))throw t}await n.createRole(new N.CreateRoleRequest({roleName:ks,assumeRolePolicyDocument:Yy,description:"FC default service role for accessing OSS and other Alibaba Cloud services"}));for(let t of Oy)try{await n.attachPolicyToRole(new N.AttachPolicyToRoleRequest({policyType:"System",policyName:t,roleName:ks}))}catch{}return{created:!0}}var Iy,Fy="licell-operator",xy="LicellOperatorPolicy",Hy,Ay,ko,ks="AliyunFCDefaultRole",Yy,Oy;var Es=O(()=>{Ie();Iy=te(Ry,"@alicloud/ram20150501"),Hy=/^[A-Za-z0-9._-]{1,64}$/,Ay=/^[A-Za-z0-9-]{1,128}$/,ko=["fc:CreateFunction","fc:UpdateFunction","fc:GetFunction","fc:ListFunctions","fc:InvokeFunction","fc:CreateTrigger","fc:UpdateTrigger","fc:ListTriggers","fc:CreateCustomDomain","fc:UpdateCustomDomain","fc:DeleteCustomDomain","fc:GetCustomDomain","fc:PublishFunctionVersion","fc:ListFunctionVersions","fc:DeleteFunctionVersion","fc:CreateAlias","fc:UpdateAlias","fc:ListAliases","fc:DeleteAlias","fc:DeleteTrigger","fc:DeleteFunction","rds:DescribeAvailableZones","rds:DescribeAvailableClasses","rds:DescribeDBInstances","rds:CreateDBInstance","rds:DescribeDBInstanceNetInfo","rds:CreateAccount","rds:CreateDatabase","rds:GrantAccountPrivilege","rds:CheckServiceLinkedRole","rds:CreateServiceLinkedRole","kvstore:DescribeInstances","kvstore:DescribeAccounts","kvstore:DescribeServiceLinkedRoleExists","kvstore:InitializeKvstorePermission","kvstore:DescribeTairKVCacheInferInstances","kvstore:DescribeTairKVCacheInferInstanceAttribute","kvstore:CreateTairKVCacheVNode","kvstore:ResetAccountPassword","kvstore:ResetTairKVCacheCustomInstancePassword","kvstore:ModifySecurityIps","oss:PutBucket","oss:GetBucketInfo","oss:PutBucketACL","oss:PutObject","oss:ListBuckets","oss:ListObjects","alidns:DescribeSubDomainRecords","alidns:DescribeDomainRecords","alidns:AddDomainRecord","alidns:UpdateDomainRecord","alidns:DeleteDomainRecord","cdn:DescribeUserDomains","cdn:AddCdnDomain","cdn:BatchSetCdnDomainConfig","cdn:DeleteCdnDomain","cdn:DeleteUserCdnDomain","cdn:SetCdnDomainSSLCertificate","vpc:DescribeVpcs","vpc:DescribeZones","vpc:DescribeVSwitchAttributes","vpc:DescribeVSwitches","vpc:CreateVpc","vpc:CreateVSwitch","ecs:DescribeSecurityGroups","ecs:CreateSecurityGroup","cr:ListInstance","cr:CreateNamespace","cr:CreateRepository","cr:GetAuthorizationToken","log:GetLogs","ram:PassRole","ram:GetRole"];Yy=JSON.stringify({Statement:[{Action:"sts:AssumeRole",Effect:"Allow",Principal:{Service:["fc.aliyuncs.com"]}}],Version:"1"}),Oy=["AliyunOSSFullAccess"]});async function _o(e){try{await e()}catch(n){if(!J(n))throw n}}function R(e){if(typeof e==="object"&&e!==null&&"message"in e){let n=String(e.message),t=e.stack;if(t&&typeof t==="string"&&n.includes("is not a function"))return`${n}
8
- ${t}`;return n}return String(e)}var ce=()=>{};import{parse as Ky}from"tldts";function Qe(e){let n=e.trim().toLowerCase(),t=Ky(n);if(!t.domain)throw Error(`无效域名: ${e}`);let o=t.domain,s=t.subdomain||"@";return{rootDomain:o,subDomain:s}}var Ht=()=>{};function rt(e){Ss.set(e.name,e)}function kn(e){let n=Ss.get(e);if(!n)throw Error(`不支持的运行时: ${e}`);return n}function Yn(){return[...Ss.keys()]}var Ss;var On=O(()=>{Ss=new Map});import{spawn as jy}from"child_process";async function ge(e){if(typeof Bun<"u"&&typeof Bun.sleep==="function"){await Bun.sleep(e);return}await new Promise((n)=>setTimeout(n,e))}async function bo(e,n){if(typeof Bun<"u"&&typeof Bun.build==="function"){let s=await Bun.build({entrypoints:[e],outdir:n,target:"node",format:"cjs",minify:!0});return{success:s.success,logs:s.logs.map((r)=>({message:r.message}))}}let t=["build",e,"--target","node","--format","cjs","--minify","--outdir",n],o=await new Promise((s)=>{let r=jy("bun",t,{stdio:["ignore","pipe","pipe"]}),c="",f="";r.stdout.on("data",(u)=>{c+=String(u)}),r.stderr.on("data",(u)=>{f+=String(u)}),r.on("error",(u)=>{s({code:null,stdout:c,stderr:f,error:u})}),r.on("close",(u)=>{s({code:u,stdout:c,stderr:f})})});if(o.error)return{success:!1,logs:[{message:`调用 bun build 失败: ${o.error.message}`}]};if(o.code!==0)return{success:!1,logs:[{message:o.stderr.trim()||o.stdout.trim()||`bun build 失败,退出码 ${o.code}`}]};return{success:!0,logs:[]}}var De=()=>{};import{existsSync as ff,mkdirSync as Wy,mkdtempSync as Qy,rmSync as Dy,statSync as uf}from"fs";import{tmpdir as Vy}from"os";import{isAbsolute as Xy,join as sf,resolve as lf}from"path";import{spawnSync as Zy}from"child_process";function qs(e){if(typeof e!=="string")return;let n=e.trim();return n.length>0?n:void 0}function rf(e){let n=qs(e)?.toLowerCase();if(!n)return!1;return n==="1"||n==="true"||n==="yes"||n==="on"}function Ny(e,n,t){let o=Zy(e,n,{cwd:t.cwd,encoding:"utf8",stdio:["ignore","pipe","pipe"]});return{status:o.status,stdout:o.stdout||"",stderr:o.stderr||"",error:o.error||void 0}}function gf(e){let n=e.stderr.trim();if(n)return n;let t=e.stdout.trim();if(t)return t;return`exit=${e.status??"unknown"}`}function zy(e,n){let t=qs(se(n,"PYTHON_REQUIREMENTS"));if(!t)return null;let o=Xy(t)?t:lf(e,t);if(!ff(o)||!uf(o).isFile())throw Error(`LICELL_PYTHON_REQUIREMENTS 指定文件不存在: ${t}`);return o}function ed(e,n=process.env){let t=zy(e,n);if(t)return t;for(let o of Jy){let s=lf(e,o);if(!ff(s))continue;if(!uf(s).isFile())continue;return s}return null}function nd(e){if(e==="python3.12")return{pythonVersion:"3.12"};return{pythonVersion:"3.13"}}function cf(e,n,t,o,s){let r=e(n,t,{cwd:o});if(r.error)throw Error(`${s}: ${r.error.message}`);if(r.status!==0)throw Error(`${s}: ${gf(r)}`)}async function yf(e){let n=e.env||process.env;if(rf(se(n,"PYTHON_SKIP_VENDOR")))return;let t=ed(e.sourceRoot,n);if(!t)return;let o=qs(se(n,"PYTHON_PIP"))||"python3",s=e.runCommand||Ny,r=rf(se(n,"PYTHON_ALLOW_SOURCE")),{pythonVersion:c}=nd(e.runtime),f=Qy(sf(Vy(),"licell-pydeps-")),u=sf(f,"wheelhouse");Wy(u,{recursive:!0});try{let i=s(o,["-m","pip","download","--disable-pip-version-check","--dest",u,"--requirement",t,"--only-binary=:all:","--platform","manylinux2014_x86_64","--implementation","cp","--python-version",c],{cwd:e.sourceRoot});if(i.error)throw Error(`准备 Python 依赖失败: ${i.error.message}`);if(i.status!==0){if(!r)throw Error("下载 Linux 兼容 Python 依赖失败。请优先使用可用 manylinux wheel 的依赖,"+"或在 Linux CI 执行部署。若你接受本机编译依赖可设置 LICELL_PYTHON_ALLOW_SOURCE=1。"+` 原因: ${gf(i)}`);cf(s,o,["-m","pip","install","--disable-pip-version-check","--no-cache-dir","--no-input","--no-compile","--target",e.outdir,"--requirement",t],e.sourceRoot,"安装 Python 依赖失败");return}cf(s,o,["-m","pip","install","--disable-pip-version-check","--no-cache-dir","--no-input","--no-compile","--no-index","--find-links",u,"--only-binary=:all:","--platform","manylinux2014_x86_64","--implementation","cp","--python-version",c,"--target",e.outdir,"--requirement",t],e.sourceRoot,"安装 Python 依赖失败")}finally{Dy(f,{recursive:!0,force:!0})}}var Jy;var df=O(()=>{Jy=["requirements.txt","requirements-prod.txt","requirements.prod.txt","requirements/production.txt"]});import{copyFileSync as td,existsSync as od,mkdirSync as sd,readFileSync as rd,readdirSync as mf,statSync as pf}from"fs";import{dirname as cd,join as At}from"path";function Lt(e){let n=mf(e);for(let t of n){let o=At(e,t);if(pf(o).isDirectory()){let s=Lt(o);if(s)return s;continue}if(o.endsWith(".js"))return o}return null}function id(e){return/^\s*(async\s+)?def\s+handler\s*\(/m.test(e)}function hf(e){return[/\bexport\s+(?:async\s+)?function\s+handler\b/,/\bexport\s+(?:const|let|var)\s+handler\b/,/\bexport\s*\{\s*handler(?:\s+as\s+\w+)?\s*\}/,/\bexport\s*\{\s*default\s+as\s+handler\s*\}/,/\bmodule\.exports\.handler\s*=/,/\bexports\.handler\s*=/,/\bmodule\.exports\s*=\s*\{[\s\S]*\bhandler\b[\s\S]*\}/].some((t)=>t.test(e))}function ld(e){return[/\bexport\s+default\b/,/\bexports\.default\s*=/].some((t)=>t.test(e))}function af(e){try{return rd(e,"utf8")}catch(n){let t=n instanceof Error?n.message:String(n);throw Error(`读取入口文件失败: ${e}
9
- ${t}`)}}function To(e,n){if(n==="docker")return;if(n.startsWith("python")){if(!e.toLowerCase().endsWith(".py"))throw Error(`Python runtime=${n} 要求入口文件为 .py,当前为: ${e}`);let o=af(e);if(id(o))return;throw Error(`Python 入口文件缺少 handler 函数: ${e}
6
+ `;t=`${t}${c}${s}
7
+ `}mo(e,t)}function Ty(){if(ot(ds))return ds;if(ot(hs))return hs;return null}function Cy(){let e=jc();if(ot(e))return e;let n=ky();if(ot(n))return n;return null}var ys,wy,ds,Uc,hs,vc=448,Gc=384,Ge="cn-hangzhou",k;var K=O(()=>{ys=Ne(Bc(),".licell-cli"),wy=Ne(Bc(),".ali-cli"),ds=Ne(ys,"auth.json"),Uc=Ne(ys,"config.json"),hs=Ne(wy,"auth.json");k={getAuth(){let e=Ty();if(!e)return null;let n=_y(go(e,null));if(n&&e===hs)this.setAuth(n);return n},requireAuth(){let e=this.getAuth();if(!e)throw Error("未登录,请先执行 `licell login`");return e},setAuth(e){xt(ys),yo(ds,e,!0)},clearAuth(){ao(ds,{force:!0}),ao(hs,{force:!0})},getProject(){let e=Cy();if(!e)return{envs:{}};return Mc(go(e,{envs:{}}))},setProject(e,n){xt(Kc()),by();let t=this.getProject(),s=n?.replaceEnvs?{...e.envs||{}}:{...t.envs,...e.envs||{}},o=Mc({...t,...e,envs:s});yo(jc(),o,!0)},getGlobalConfig(){return go(Uc,{})},setGlobalConfig(e){xt(ys);let t={...this.getGlobalConfig(),...e};yo(Uc,t)}}});function Qc(e,n){return Object.prototype.hasOwnProperty.call(e,n)}function V(e,n,t){if(Qc(e,n))return e[n];if(t&&Qc(e,t))return e[t];return}function oe(e,n){return V(e,`LICELL_${n}`,`ALI_${n}`)}var Xc={};gs(Xc,{resolveSdkCtor:()=>te,createSharedFcClient:()=>Bn});import Ey from"@alicloud/fc20230330";import*as Vc from"@alicloud/openapi-client";function te(e,n){let t=e,s=typeof e==="function"?e:typeof t?.default==="function"?t.default:typeof t?.default?.default==="function"?(t?.default).default:null;if(!s)throw Error(`无法加载 ${n} SDK 构造器,请检查依赖安装和运行时模块格式`);return s}function Dc(e,n){if(!e)return n;let t=Number(e);if(!Number.isFinite(t)||t<=0)return n;return Math.floor(t)}function Bn(e){let n=e??k.requireAuth(),t=Dc(oe(process.env,"FC_CONNECT_TIMEOUT_MS"),Sy),s=Dc(oe(process.env,"FC_READ_TIMEOUT_MS"),qy),o=new Py(new Vc.Config({accessKeyId:n.ak,accessKeySecret:n.sk,endpoint:`${n.accountId}.${n.region}.fc.aliyuncs.com`,connectTimeout:t,readTimeout:s}));return{auth:n,client:o}}var Sy=60000,qy=600000,Py;var Ie=O(()=>{K();Py=te(Ey,"@alicloud/fc20230330")});var Zc={};gs(Zc,{isTransientError:()=>Ee,isRoleMissingError:()=>ps,isNotFoundError:()=>ue,isInvalidDomainNameError:()=>ms,isInstanceClassError:()=>as,isConflictError:()=>J,isCidrConflictError:()=>$o,isAuthCredentialInvalidError:()=>ln,isAlreadyExistsRoleError:()=>wo,isAccessDeniedError:()=>Fe});function ze(e){if(typeof e!=="object"||e===null)return"";let n=String(e.code||""),t=String(e.message||"");return`${n} ${t}`}function un(e,n){let t=e.toLowerCase();return n.some((s)=>t.includes(s))}function J(e){return un(ze(e),["alreadyexist","alreadyexists","entityalreadyexists","already exists","conflict","duplicate","domainrecordduplicate"])}function ue(e){return un(ze(e),["notfound","no such","404","entitynotexist","not exist"])}function Ee(e){return un(ze(e),["throttling","too many requests","connecttimeout","readtimeout","requesttimeouterror","socket disconnected","econnreset","econnrefused","service unavailable","internal error"])}function as(e){return un(ze(e),["instanceclass","soldout","outofstock","invalidparameter","not support","unsupported"])}function Fe(e){return un(ze(e),["accessdenied","forbidden","no permission"])}function ln(e){return un(ze(e),["invalidaccesskeyid","invalidaccesskeyid.notfound","signaturedoesnotmatch","incompletesignature","authenticationfailed","invalidsecuritytoken","security token is invalid","access key id does not exist","accesskey secret not found"])}function ms(e){return un(ze(e),["invaliddomainname.format","invaliddomainname.noexist","invalid domain name","domain name does not exist"])}function ps(e){return un(ze(e),["servicelinkedrole.notexist"])}function wo(e){return un(ze(e),["entityalreadyexists.role","already exists"])}function $o(e){let n=ze(e).toLowerCase();return n.includes("cidr")&&(n.includes("conflict")||n.includes("overlap")||n.includes("invalid"))}import Ry,*as N from"@alicloud/ram20150501";import*as Jc from"@alicloud/openapi-client";function ws(e){let n=(e||Fy).trim();if(!Hy.test(n))throw Error("bootstrap RAM 用户名不合法:仅支持字母、数字、点、短横线、下划线,长度 1-64");return n}function Nc(e){let n=(e||xy).trim();if(!Ly.test(n))throw Error("bootstrap RAM 策略名不合法:仅支持字母、数字、短横线,长度 1-128");return n}function Ay(){return zc([...$s])}function zc(e){let n=[...new Set(e)].sort();return JSON.stringify({Version:"1",Statement:[{Effect:"Allow",Action:n,Resource:"*"}]})}function To(e){return new Iy(new Jc.Config({accessKeyId:e.ak,accessKeySecret:e.sk,endpoint:"ram.aliyuncs.com"}))}async function ef(e,n){try{return await e.getUser(new N.GetUserRequest({userName:n})),{created:!1}}catch(t){if(!ue(t))throw t}return await e.createUser(new N.CreateUserRequest({userName:n,displayName:n,comments:"Managed by licell bootstrap login"})),{created:!0}}async function Uy(e,n){try{return await e.getPolicy(new N.GetPolicyRequest({policyType:"Custom",policyName:n})),{created:!1}}catch(t){if(!ue(t))throw t}return await e.createPolicy(new N.CreatePolicyRequest({policyName:n,description:"Least-privilege policy generated by licell bootstrap login",policyDocument:Ay()})),{created:!0}}function vy(e){if(typeof e==="string")return[e].map((n)=>n.trim()).filter(Boolean);if(!Array.isArray(e))return[];return e.map((n)=>typeof n==="string"?n.trim():"").filter(Boolean)}function Gy(e){if(!e)return null;try{return JSON.parse(e)}catch{try{return JSON.parse(decodeURIComponent(e))}catch{return null}}}function My(e,n){let t=Gy(e);if(!t||typeof t!=="object")return{changed:!0,policyDocument:zc(n)};let s=t.Statement,o=Array.isArray(s)?[...s]:s&&typeof s==="object"?[s]:[],r=-1;for(let l=0;l<o.length;l+=1){let i=o[l];if(!i||typeof i!=="object")continue;if(String(i.Effect||"").toLowerCase()==="allow"){r=l;break}}let c=[...new Set(n)].sort(),f=!1;if(r===-1)o.push({Effect:"Allow",Action:c,Resource:"*"}),f=!0;else{let l=o[r];if(!l||typeof l!=="object")throw Error("策略文档格式错误");let i=vy(l.Action),g=[...new Set([...i,...c])].sort(),y=[...new Set(i)].sort();if(g.join("|")!==y.join("|"))f=!0;if(l.Action=g,l.Resource===void 0)l.Resource="*",f=!0}let u={...t,Version:typeof t.Version==="string"&&t.Version?t.Version:"1",Statement:o};return{changed:f,policyDocument:JSON.stringify(u)}}async function _o(e,n,t){if((await Uy(e,n)).created)return{created:!0,updated:!1};let r=(await e.getPolicy(new N.GetPolicyRequest({policyType:"Custom",policyName:n}))).body?.defaultPolicyVersion?.versionId;if(!r)return{created:!1,updated:!1};let f=(await e.getPolicyVersion(new N.GetPolicyVersionRequest({policyType:"Custom",policyName:n,versionId:r}))).body?.policyVersion?.policyDocument,u=My(f,t);if(!u.changed)return{created:!1,updated:!1};return await e.createPolicyVersion(new N.CreatePolicyVersionRequest({policyName:n,policyDocument:u.policyDocument,setAsDefault:!0,rotateStrategy:"DeleteOldestNonDefaultVersionWhenLimitExceeded"})),{created:!1,updated:!0}}async function bo(e,n,t){try{await e.attachPolicyToUser(new N.AttachPolicyToUserRequest({policyType:"Custom",policyName:n,userName:t}))}catch(s){let o=`${String(s?.code||"")} ${String(s?.message||"")}`.toLowerCase();if(o.includes("entityalreadyexists")||o.includes("already attached")||o.includes("attached already"))return;throw s}}async function nf(e,n){if(((await e.listAccessKeys(new N.ListAccessKeysRequest({userName:n}))).body?.accessKeys?.accessKey||[]).filter((u)=>u.status==="Active").length>=2)throw Error(`RAM 用户 ${n} 的 AccessKey 已达到上限(2 个)。请先在控制台删除旧 key 后重试。`);let r=await e.createAccessKey(new N.CreateAccessKeyRequest({userName:n})),c=r.body?.accessKey?.accessKeyId,f=r.body?.accessKey?.accessKeySecret;if(!c||!f)throw Error("创建 RAM AccessKey 成功但返回字段不完整,请重试");return{accessKeyId:c,accessKeySecret:f}}async function By(e,n){let t=n.trim();if(!t)return;let s=20,o;for(let r=0;r<s;r+=1){let c=await e.listUsers(new N.ListUsersRequest({marker:o,maxItems:100})),f=c.body?.users?.user||[];for(let u of f){let l=u.userName;if(!l)continue;if(((await e.listAccessKeys(new N.ListAccessKeysRequest({userName:l}))).body?.accessKeys?.accessKey||[]).some((y)=>y.accessKeyId===t))return l}if(!c.body?.isTruncated||!c.body.marker)break;o=c.body.marker}return}async function tf(e){let n=ws(e.userName),t=Nc(e.policyName),s=To(e.adminAuth),o=await ef(s,n),r=await _o(s,t,[...$s]);await bo(s,t,n);let c=await nf(s,n);try{await sf(e.adminAuth)}catch{}return{userName:n,policyName:t,accessKeyId:c.accessKeyId,accessKeySecret:c.accessKeySecret,createdUser:o.created,createdPolicy:r.created,updatedPolicy:r.updated}}async function Co(e){let n=e.currentAuth||null,t=Nc(e.policyName||n?.ramPolicy),s=To(e.adminAuth);try{await sf(e.adminAuth)}catch{}let o=e.userName?ws(e.userName):n?.ramUser?ws(n.ramUser):void 0;if(!o&&n?.ak)o=await By(s,n.ak);if(!e.forceRotateKey&&o&&n?.ak&&n.sk){let l=await _o(s,t,[...$s]);return await bo(s,t,o),{mode:"updated-existing-key",userName:o,policyName:t,accessKeyId:n.ak,accessKeySecret:n.sk,createdUser:!1,createdPolicy:l.created,updatedPolicy:l.updated}}let r=o||ws(e.userName||n?.ramUser),c=await ef(s,r),f=await _o(s,t,[...$s]);await bo(s,t,r);let u=await nf(s,r);return{mode:"rotated-new-key",userName:r,policyName:t,accessKeyId:u.accessKeyId,accessKeySecret:u.accessKeySecret,createdUser:c.created,createdPolicy:f.created,updatedPolicy:f.updated}}async function sf(e){let n=To(e);try{return await n.getRole(new N.GetRoleRequest({roleName:ko})),{created:!1}}catch(t){if(!ue(t))throw t}await n.createRole(new N.CreateRoleRequest({roleName:ko,assumeRolePolicyDocument:Yy,description:"FC default service role for accessing OSS and other Alibaba Cloud services"}));for(let t of Oy)try{await n.attachPolicyToRole(new N.AttachPolicyToRoleRequest({policyType:"System",policyName:t,roleName:ko}))}catch{}return{created:!0}}var Iy,Fy="licell-operator",xy="LicellOperatorPolicy",Hy,Ly,$s,ko="AliyunFCDefaultRole",Yy,Oy;var Eo=O(()=>{Ie();Iy=te(Ry,"@alicloud/ram20150501"),Hy=/^[A-Za-z0-9._-]{1,64}$/,Ly=/^[A-Za-z0-9-]{1,128}$/,$s=["fc:CreateFunction","fc:UpdateFunction","fc:GetFunction","fc:ListFunctions","fc:InvokeFunction","fc:CreateTrigger","fc:UpdateTrigger","fc:ListTriggers","fc:CreateCustomDomain","fc:UpdateCustomDomain","fc:DeleteCustomDomain","fc:GetCustomDomain","fc:PublishFunctionVersion","fc:ListFunctionVersions","fc:DeleteFunctionVersion","fc:CreateAlias","fc:UpdateAlias","fc:ListAliases","fc:DeleteAlias","fc:DeleteTrigger","fc:DeleteFunction","rds:DescribeAvailableZones","rds:DescribeAvailableClasses","rds:DescribeDBInstances","rds:CreateDBInstance","rds:DescribeDBInstanceNetInfo","rds:CreateAccount","rds:CreateDatabase","rds:GrantAccountPrivilege","rds:CheckServiceLinkedRole","rds:CreateServiceLinkedRole","kvstore:DescribeInstances","kvstore:DescribeAccounts","kvstore:DescribeServiceLinkedRoleExists","kvstore:InitializeKvstorePermission","kvstore:DescribeTairKVCacheInferInstances","kvstore:DescribeTairKVCacheInferInstanceAttribute","kvstore:CreateTairKVCacheVNode","kvstore:ResetAccountPassword","kvstore:ResetTairKVCacheCustomInstancePassword","kvstore:ModifySecurityIps","oss:PutBucket","oss:GetBucketInfo","oss:PutBucketACL","oss:PutObject","oss:ListBuckets","oss:ListObjects","alidns:DescribeSubDomainRecords","alidns:DescribeDomainRecords","alidns:AddDomainRecord","alidns:UpdateDomainRecord","alidns:DeleteDomainRecord","cdn:DescribeUserDomains","cdn:AddCdnDomain","cdn:BatchSetCdnDomainConfig","cdn:DeleteCdnDomain","cdn:DeleteUserCdnDomain","cdn:SetCdnDomainSSLCertificate","vpc:DescribeVpcs","vpc:DescribeZones","vpc:DescribeVSwitchAttributes","vpc:DescribeVSwitches","vpc:CreateVpc","vpc:CreateVSwitch","ecs:DescribeSecurityGroups","ecs:CreateSecurityGroup","cr:ListInstance","cr:CreateNamespace","cr:CreateRepository","cr:GetAuthorizationToken","log:GetLogs","ram:PassRole","ram:GetRole"];Yy=JSON.stringify({Statement:[{Action:"sts:AssumeRole",Effect:"Allow",Principal:{Service:["fc.aliyuncs.com"]}}],Version:"1"}),Oy=["AliyunOSSFullAccess"]});async function ks(e){try{await e()}catch(n){if(!J(n))throw n}}function R(e){if(typeof e==="object"&&e!==null&&"message"in e){let n=String(e.message),t=e.stack;if(t&&typeof t==="string"&&n.includes("is not a function"))return`${n}
8
+ ${t}`;return n}return String(e)}var ce=()=>{};import{parse as Ky}from"tldts";function Qe(e){let n=e.trim().toLowerCase(),t=Ky(n);if(!t.domain)throw Error(`无效域名: ${e}`);let s=t.domain,o=t.subdomain||"@";return{rootDomain:s,subDomain:o}}var Ht=()=>{};function rt(e){So.set(e.name,e)}function kn(e){let n=So.get(e);if(!n)throw Error(`不支持的运行时: ${e}`);return n}function Yn(){return[...So.keys()]}var So;var On=O(()=>{So=new Map});import{spawn as jy}from"child_process";async function ge(e){if(typeof Bun<"u"&&typeof Bun.sleep==="function"){await Bun.sleep(e);return}await new Promise((n)=>setTimeout(n,e))}async function _s(e,n){if(typeof Bun<"u"&&typeof Bun.build==="function"){let o=await Bun.build({entrypoints:[e],outdir:n,target:"node",format:"cjs",minify:!0});return{success:o.success,logs:o.logs.map((r)=>({message:r.message}))}}let t=["build",e,"--target","node","--format","cjs","--minify","--outdir",n],s=await new Promise((o)=>{let r=jy("bun",t,{stdio:["ignore","pipe","pipe"]}),c="",f="";r.stdout.on("data",(u)=>{c+=String(u)}),r.stderr.on("data",(u)=>{f+=String(u)}),r.on("error",(u)=>{o({code:null,stdout:c,stderr:f,error:u})}),r.on("close",(u)=>{o({code:u,stdout:c,stderr:f})})});if(s.error)return{success:!1,logs:[{message:`调用 bun build 失败: ${s.error.message}`}]};if(s.code!==0)return{success:!1,logs:[{message:s.stderr.trim()||s.stdout.trim()||`bun build 失败,退出码 ${s.code}`}]};return{success:!0,logs:[]}}var De=()=>{};import{existsSync as ff,mkdirSync as Wy,mkdtempSync as Qy,rmSync as Dy,statSync as uf}from"fs";import{tmpdir as Vy}from"os";import{isAbsolute as Xy,join as of,resolve as lf}from"path";import{spawnSync as Zy}from"child_process";function qo(e){if(typeof e!=="string")return;let n=e.trim();return n.length>0?n:void 0}function rf(e){let n=qo(e)?.toLowerCase();if(!n)return!1;return n==="1"||n==="true"||n==="yes"||n==="on"}function Ny(e,n,t){let s=Zy(e,n,{cwd:t.cwd,encoding:"utf8",stdio:["ignore","pipe","pipe"]});return{status:s.status,stdout:s.stdout||"",stderr:s.stderr||"",error:s.error||void 0}}function gf(e){let n=e.stderr.trim();if(n)return n;let t=e.stdout.trim();if(t)return t;return`exit=${e.status??"unknown"}`}function zy(e,n){let t=qo(oe(n,"PYTHON_REQUIREMENTS"));if(!t)return null;let s=Xy(t)?t:lf(e,t);if(!ff(s)||!uf(s).isFile())throw Error(`LICELL_PYTHON_REQUIREMENTS 指定文件不存在: ${t}`);return s}function ed(e,n=process.env){let t=zy(e,n);if(t)return t;for(let s of Jy){let o=lf(e,s);if(!ff(o))continue;if(!uf(o).isFile())continue;return o}return null}function nd(e){if(e==="python3.12")return{pythonVersion:"3.12"};return{pythonVersion:"3.13"}}function cf(e,n,t,s,o){let r=e(n,t,{cwd:s});if(r.error)throw Error(`${o}: ${r.error.message}`);if(r.status!==0)throw Error(`${o}: ${gf(r)}`)}async function yf(e){let n=e.env||process.env;if(rf(oe(n,"PYTHON_SKIP_VENDOR")))return;let t=ed(e.sourceRoot,n);if(!t)return;let s=qo(oe(n,"PYTHON_PIP"))||"python3",o=e.runCommand||Ny,r=rf(oe(n,"PYTHON_ALLOW_SOURCE")),{pythonVersion:c}=nd(e.runtime),f=Qy(of(Vy(),"licell-pydeps-")),u=of(f,"wheelhouse");Wy(u,{recursive:!0});try{let i=o(s,["-m","pip","download","--disable-pip-version-check","--dest",u,"--requirement",t,"--only-binary=:all:","--platform","manylinux2014_x86_64","--implementation","cp","--python-version",c],{cwd:e.sourceRoot});if(i.error)throw Error(`准备 Python 依赖失败: ${i.error.message}`);if(i.status!==0){if(!r)throw Error("下载 Linux 兼容 Python 依赖失败。请优先使用可用 manylinux wheel 的依赖,"+"或在 Linux CI 执行部署。若你接受本机编译依赖可设置 LICELL_PYTHON_ALLOW_SOURCE=1。"+` 原因: ${gf(i)}`);cf(o,s,["-m","pip","install","--disable-pip-version-check","--no-cache-dir","--no-input","--no-compile","--target",e.outdir,"--requirement",t],e.sourceRoot,"安装 Python 依赖失败");return}cf(o,s,["-m","pip","install","--disable-pip-version-check","--no-cache-dir","--no-input","--no-compile","--no-index","--find-links",u,"--only-binary=:all:","--platform","manylinux2014_x86_64","--implementation","cp","--python-version",c,"--target",e.outdir,"--requirement",t],e.sourceRoot,"安装 Python 依赖失败")}finally{Dy(f,{recursive:!0,force:!0})}}var Jy;var df=O(()=>{Jy=["requirements.txt","requirements-prod.txt","requirements.prod.txt","requirements/production.txt"]});import{copyFileSync as td,existsSync as sd,mkdirSync as od,readFileSync as rd,readdirSync as mf,statSync as pf}from"fs";import{dirname as cd,join as Lt}from"path";function At(e){let n=mf(e);for(let t of n){let s=Lt(e,t);if(pf(s).isDirectory()){let o=At(s);if(o)return o;continue}if(s.endsWith(".js"))return s}return null}function id(e){return/^\s*(async\s+)?def\s+handler\s*\(/m.test(e)}function hf(e){return[/\bexport\s+(?:async\s+)?function\s+handler\b/,/\bexport\s+(?:const|let|var)\s+handler\b/,/\bexport\s*\{\s*handler(?:\s+as\s+\w+)?\s*\}/,/\bexport\s*\{\s*default\s+as\s+handler\s*\}/,/\bmodule\.exports\.handler\s*=/,/\bexports\.handler\s*=/,/\bmodule\.exports\s*=\s*\{[\s\S]*\bhandler\b[\s\S]*\}/].some((t)=>t.test(e))}function ld(e){return[/\bexport\s+default\b/,/\bexports\.default\s*=/].some((t)=>t.test(e))}function af(e){try{return rd(e,"utf8")}catch(n){let t=n instanceof Error?n.message:String(n);throw Error(`读取入口文件失败: ${e}
9
+ ${t}`)}}function bs(e,n){if(n==="docker")return;if(n.startsWith("python")){if(!e.toLowerCase().endsWith(".py"))throw Error(`Python runtime=${n} 要求入口文件为 .py,当前为: ${e}`);let s=af(e);if(id(s))return;throw Error(`Python 入口文件缺少 handler 函数: ${e}
10
10
  `+`请导出可调用函数,例如:
11
11
  `+`def handler(event, context):
12
12
  return {"statusCode": 200, "body": "ok"}`)}if(!n.startsWith("nodejs"))return;let t=af(e);if(n==="nodejs22"){if(hf(t)||ld(t))return;throw Error(`Node 入口文件缺少导出函数: ${e}
13
13
  `+"runtime=nodejs22 需导出 handler 或 default 函数。")}if(hf(t))return;throw Error(`Node 入口文件缺少 handler 导出: ${e}
14
- `+"runtime=nodejs20 需导出 handler(event, context) 函数。")}function gd(e){let n=e.replace(/\\/g,"/");if(n.endsWith(".py"))return!0;let t=n.split("/").pop()?.toLowerCase()||"";return ud.has(t)}function wf(e,n,t=""){let o=t?At(e,t):e;for(let s of mf(o)){let r=s.toLowerCase();if(fd.has(r))continue;let c=t?`${t}/${s}`:s,f=At(e,c),u=pf(f);if(u.isDirectory()){wf(e,n,c);continue}if(!u.isFile())continue;if(!gd(c))continue;let l=At(n,c);sd(cd(l),{recursive:!0}),td(f,l)}}async function Co(e,n,t){let o=e.replace(/\\/g,"/");if(!o.endsWith(".py"))throw Error("Python runtime 要求入口文件为 .py(并导出 handler 函数)");wf(process.cwd(),n),await yf({runtime:t,sourceRoot:process.cwd(),outdir:n});let s=At(n,o);if(!od(s))throw Error(`Python 入口文件未打包进部署产物: ${o}`);return o}var fd,ud;var Kn=O(()=>{df();fd=new Set([".git",".github",".licell",".ali",".tmp-build",".pytest_cache","__pycache__","node_modules",".venv","venv","dist","coverage"]),ud=new Set(["requirements.txt","requirements-dev.txt","pyproject.toml","poetry.lock","pipfile","pipfile.lock"])});import{relative as yd}from"path";var $f;var kf=O(()=>{De();Kn();$f={name:"nodejs20",defaultEntry:"src/index.ts",unsupportedMessage:"当前地域暂不支持 runtime=nodejs20。请确认 nodejs20 在目标地域可用后重试。",async prepareBootFile(e,n){let t=await bo(e,n);if(!t.success){let s=t.logs.map((r)=>r.message).join(`
14
+ `+"runtime=nodejs20 需导出 handler(event, context) 函数。")}function gd(e){let n=e.replace(/\\/g,"/");if(n.endsWith(".py"))return!0;let t=n.split("/").pop()?.toLowerCase()||"";return ud.has(t)}function wf(e,n,t=""){let s=t?Lt(e,t):e;for(let o of mf(s)){let r=o.toLowerCase();if(fd.has(r))continue;let c=t?`${t}/${o}`:o,f=Lt(e,c),u=pf(f);if(u.isDirectory()){wf(e,n,c);continue}if(!u.isFile())continue;if(!gd(c))continue;let l=Lt(n,c);od(cd(l),{recursive:!0}),td(f,l)}}async function Ts(e,n,t){let s=e.replace(/\\/g,"/");if(!s.endsWith(".py"))throw Error("Python runtime 要求入口文件为 .py(并导出 handler 函数)");wf(process.cwd(),n),await yf({runtime:t,sourceRoot:process.cwd(),outdir:n});let o=Lt(n,s);if(!sd(o))throw Error(`Python 入口文件未打包进部署产物: ${s}`);return s}var fd,ud;var Kn=O(()=>{df();fd=new Set([".git",".github",".licell",".ali",".tmp-build",".pytest_cache","__pycache__","node_modules",".venv","venv","dist","coverage"]),ud=new Set(["requirements.txt","requirements-dev.txt","pyproject.toml","poetry.lock","pipfile","pipfile.lock"])});import{relative as yd}from"path";var $f;var kf=O(()=>{De();Kn();$f={name:"nodejs20",defaultEntry:"src/index.ts",unsupportedMessage:"当前地域暂不支持 runtime=nodejs20。请确认 nodejs20 在目标地域可用后重试。",async prepareBootFile(e,n){let t=await _s(e,n);if(!t.success){let o=t.logs.map((r)=>r.message).join(`
15
15
  `);throw Error(`构建失败:
16
- ${s}`)}let o=Lt(n);if(!o)throw Error("构建完成但未发现可执行 JS 产物");return yd(n,o).replace(/\\/g,"/")},async resolveConfig(e,n){return{runtime:"nodejs20",handler:`${n.replace(/\.[^.]+$/,"").replace(/\//g,".")}.handler`}}}});import{chmodSync as dd,cpSync as hd,createReadStream as ad,createWriteStream as md,existsSync as Ps,mkdirSync as bf,rmSync as So}from"fs";import{homedir as pd}from"os";import{join as _n}from"path";import wd from"https";import{pipeline as $d}from"stream/promises";import{spawnSync as kd}from"child_process";import{createHash as _d}from"crypto";function Td(e){let n=_n(e,"bin","node");if(!Ps(n))return;dd(n,493)}function Cd(){let e=se(process.env,"NODE22_SHASUMS_URL"),n=["https://nodejs.org/dist/latest-v22.x/SHASUMS256.txt","https://cdn.npmmirror.com/binaries/node/latest-v22.x/SHASUMS256.txt"];return e?[e,...n]:n}function Rs(e,n=5){return new Promise((t,o)=>{let s=wd.get(e,{timeout:Ed},(r)=>{let c=r.statusCode||0,f=r.headers.location;if([301,302,307,308].includes(c)&&f&&n>0){r.resume();let u=new URL(f,e).toString();Rs(u,n-1).then(t).catch(o);return}if(c<200||c>=300){let u=`HTTP ${c} for ${e}`;r.resume(),o(Error(u));return}t(r)});s.on("timeout",()=>{s.destroy(Error(`请求超时: ${e}`))}),s.on("error",o)})}async function Sd(e){let n=await Rs(e),t="";n.setEncoding("utf8");for await(let o of n)t+=o;return t}async function qd(e,n){let t=await Rs(e);await $d(t,md(n))}function Pd(e){for(let n of e.split(`
17
- `)){let o=n.trim().match(/^([a-fA-F0-9]{64})\s+\*?(node-v22\.\d+\.\d+-linux-x64\.tar\.gz)$/);if(!o)continue;return{sha256:o[1].toLowerCase(),tarballName:o[2]}}return null}async function Rd(){let e=Cd(),n=null;for(let t of e)try{let o=await Sd(t),s=Pd(o);if(!s){n=Error(`未在 ${t} 找到 Node 22 Linux x64 安装包`);continue}let r=t.endsWith(_f)?t.slice(0,-_f.length):`${t.replace(/\/+$/,"")}/`;return{tarballName:s.tarballName,downloadUrl:new URL(s.tarballName,r).toString(),sha256:s.sha256}}catch(o){n=o instanceof Error?o:Error(String(o))}throw Error(`无法获取 Node 22 运行时下载信息: ${n?.message||"未知错误"}`)}async function Id(e){let n=_d("sha256"),t=ad(e);for await(let o of t)n.update(o);return n.digest("hex")}function Fd(e,n){let t=kd("tar",["-xzf",e,"-C",n],{encoding:"utf8"});if(t.status===0)return;let o=t.stderr?.trim()||t.stdout?.trim()||"tar extract failed";throw Error(`解压 Node 22 运行时失败: ${o}`)}async function xd(){bf(Eo,{recursive:!0});let{tarballName:e,downloadUrl:n,sha256:t}=await Rd(),o=e.replace(/\.tar\.gz$/,""),s=_n(Eo,o),r=_n(s,"bin","node");if(Ps(r))return{folderName:o,extractedDir:s};let c=_n(Eo,e);So(s,{recursive:!0,force:!0}),await qd(n,c);let f=await Id(c);if(f!==t)throw So(c,{force:!0}),Error(`Node 22 运行时包校验失败: expected=${t}, actual=${f}`);if(Fd(c,Eo),So(c,{force:!0}),!Ps(r))throw Error("Node 22 运行时下载完成但未找到可执行文件 bin/node");return{folderName:o,extractedDir:s}}async function Tf(e){let n=_n(e,".licell-runtime");bf(n,{recursive:!0});let{folderName:t,extractedDir:o}=await xd(),s=_n(n,t);return So(s,{recursive:!0,force:!0}),hd(o,s,{recursive:!0}),Td(s),{nodeBinaryInCode:`/code/.licell-runtime/${t}/bin/node`}}var bd,Eo,_f="SHASUMS256.txt",Ed=60000;var Cf=O(()=>{bd=se(process.env,"RUNTIME_CACHE_DIR")?.trim()||_n(pd(),".licell-cli","runtimes"),Eo=_n(bd,"node22")});import*as Sf from"@alicloud/fc20230330";import{mkdirSync as Hd,writeFileSync as Ad}from"fs";import{dirname as Ld,join as Ef,relative as qf}from"path";function vd(e,n){let t=Ef(e,Is);Hd(Ef(e,".licell"),{recursive:!0});let o=Ld(Is),s=`./${qf(o,n).replace(/\\/g,"/")}`,r=`'use strict';
16
+ ${o}`)}let s=At(n);if(!s)throw Error("构建完成但未发现可执行 JS 产物");return yd(n,s).replace(/\\/g,"/")},async resolveConfig(e,n){return{runtime:"nodejs20",handler:`${n.replace(/\.[^.]+$/,"").replace(/\//g,".")}.handler`}}}});import{chmodSync as dd,cpSync as hd,createReadStream as ad,createWriteStream as md,existsSync as Po,mkdirSync as bf,rmSync as Es}from"fs";import{homedir as pd}from"os";import{join as _n}from"path";import wd from"https";import{pipeline as $d}from"stream/promises";import{spawnSync as kd}from"child_process";import{createHash as _d}from"crypto";function Td(e){let n=_n(e,"bin","node");if(!Po(n))return;dd(n,493)}function Cd(){let e=oe(process.env,"NODE22_SHASUMS_URL"),n=["https://nodejs.org/dist/latest-v22.x/SHASUMS256.txt","https://cdn.npmmirror.com/binaries/node/latest-v22.x/SHASUMS256.txt"];return e?[e,...n]:n}function Ro(e,n=5){return new Promise((t,s)=>{let o=wd.get(e,{timeout:Ed},(r)=>{let c=r.statusCode||0,f=r.headers.location;if([301,302,307,308].includes(c)&&f&&n>0){r.resume();let u=new URL(f,e).toString();Ro(u,n-1).then(t).catch(s);return}if(c<200||c>=300){let u=`HTTP ${c} for ${e}`;r.resume(),s(Error(u));return}t(r)});o.on("timeout",()=>{o.destroy(Error(`请求超时: ${e}`))}),o.on("error",s)})}async function Sd(e){let n=await Ro(e),t="";n.setEncoding("utf8");for await(let s of n)t+=s;return t}async function qd(e,n){let t=await Ro(e);await $d(t,md(n))}function Pd(e){for(let n of e.split(`
17
+ `)){let s=n.trim().match(/^([a-fA-F0-9]{64})\s+\*?(node-v22\.\d+\.\d+-linux-x64\.tar\.gz)$/);if(!s)continue;return{sha256:s[1].toLowerCase(),tarballName:s[2]}}return null}async function Rd(){let e=Cd(),n=null;for(let t of e)try{let s=await Sd(t),o=Pd(s);if(!o){n=Error(`未在 ${t} 找到 Node 22 Linux x64 安装包`);continue}let r=t.endsWith(_f)?t.slice(0,-_f.length):`${t.replace(/\/+$/,"")}/`;return{tarballName:o.tarballName,downloadUrl:new URL(o.tarballName,r).toString(),sha256:o.sha256}}catch(s){n=s instanceof Error?s:Error(String(s))}throw Error(`无法获取 Node 22 运行时下载信息: ${n?.message||"未知错误"}`)}async function Id(e){let n=_d("sha256"),t=ad(e);for await(let s of t)n.update(s);return n.digest("hex")}function Fd(e,n){let t=kd("tar",["-xzf",e,"-C",n],{encoding:"utf8"});if(t.status===0)return;let s=t.stderr?.trim()||t.stdout?.trim()||"tar extract failed";throw Error(`解压 Node 22 运行时失败: ${s}`)}async function xd(){bf(Cs,{recursive:!0});let{tarballName:e,downloadUrl:n,sha256:t}=await Rd(),s=e.replace(/\.tar\.gz$/,""),o=_n(Cs,s),r=_n(o,"bin","node");if(Po(r))return{folderName:s,extractedDir:o};let c=_n(Cs,e);Es(o,{recursive:!0,force:!0}),await qd(n,c);let f=await Id(c);if(f!==t)throw Es(c,{force:!0}),Error(`Node 22 运行时包校验失败: expected=${t}, actual=${f}`);if(Fd(c,Cs),Es(c,{force:!0}),!Po(r))throw Error("Node 22 运行时下载完成但未找到可执行文件 bin/node");return{folderName:s,extractedDir:o}}async function Tf(e){let n=_n(e,".licell-runtime");bf(n,{recursive:!0});let{folderName:t,extractedDir:s}=await xd(),o=_n(n,t);return Es(o,{recursive:!0,force:!0}),hd(s,o,{recursive:!0}),Td(o),{nodeBinaryInCode:`/code/.licell-runtime/${t}/bin/node`}}var bd,Cs,_f="SHASUMS256.txt",Ed=60000;var Cf=O(()=>{bd=oe(process.env,"RUNTIME_CACHE_DIR")?.trim()||_n(pd(),".licell-cli","runtimes"),Cs=_n(bd,"node22")});import*as Sf from"@alicloud/fc20230330";import{mkdirSync as Hd,writeFileSync as Ld}from"fs";import{dirname as Ad,join as Ef,relative as qf}from"path";function vd(e,n){let t=Ef(e,Io);Hd(Ef(e,".licell"),{recursive:!0});let s=Ad(Io),o=`./${qf(s,n).replace(/\\/g,"/")}`,r=`'use strict';
18
18
  const http = require('http');
19
- const mod = require(${JSON.stringify(s)});
19
+ const mod = require(${JSON.stringify(o)});
20
20
  const handler = typeof mod.handler === 'function'
21
21
  ? mod.handler
22
22
  : (typeof mod.default === 'function' ? mod.default : null);
@@ -110,9 +110,9 @@ const server = http.createServer(async (req, res) => {
110
110
  });
111
111
 
112
112
  server.listen(port, '0.0.0.0');
113
- `;return Ad(t,r),Is}var Ud="custom.debian12",Is=".licell/node22-bootstrap.cjs",Pf=9000,Rf;var If=O(()=>{De();Cf();Kn();Rf={name:"nodejs22",defaultEntry:"src/index.ts",unsupportedMessage:"当前地域暂不支持 runtime=nodejs22。请改用 nodejs20,或确认 custom.debian12 在目标地域可用后重试。",async prepareBootFile(e,n){let t=await bo(e,n);if(!t.success){let s=t.logs.map((r)=>r.message).join(`
113
+ `;return Ld(t,r),Io}var Ud="custom.debian12",Io=".licell/node22-bootstrap.cjs",Pf=9000,Rf;var If=O(()=>{De();Cf();Kn();Rf={name:"nodejs22",defaultEntry:"src/index.ts",unsupportedMessage:"当前地域暂不支持 runtime=nodejs22。请改用 nodejs20,或确认 custom.debian12 在目标地域可用后重试。",async prepareBootFile(e,n){let t=await _s(e,n);if(!t.success){let o=t.logs.map((r)=>r.message).join(`
114
114
  `);throw Error(`构建失败:
115
- ${s}`)}let o=Lt(n);if(!o)throw Error("构建完成但未发现可执行 JS 产物");return qf(n,o).replace(/\\/g,"/")},async resolveConfig(e,n){let t=`${n.replace(/\.[^.]+$/,"").replace(/\//g,".")}.handler`,o=await Tf(e),s=vd(e,n).replace(/\\/g,"/");return{runtime:Ud,handler:t,customRuntimeConfig:new Sf.CustomRuntimeConfig({command:[o.nodeBinaryInCode],args:[`/code/${s}`],port:Pf})}}}});var Ff;var xf=O(()=>{Kn();Ff={name:"python3.12",defaultEntry:"src/main.py",unsupportedMessage:"当前地域暂不支持 runtime=python3.12。请改用 nodejs20,或确认 python3.12 在目标地域可用后重试。",async prepareBootFile(e,n){return Co(e,n,"python3.12")},async resolveConfig(e,n){return{runtime:"python3.12",handler:`${n.replace(/\.[^.]+$/,"").replace(/\//g,".")}.handler`}}}});import{chmodSync as Gd,cpSync as Md,createReadStream as Bd,createWriteStream as Yd,existsSync as xs,mkdirSync as Hs,readdirSync as Od,rmSync as qo}from"fs";import{homedir as Kd}from"os";import{join as gn}from"path";import jd from"https";import{pipeline as Wd}from"stream/promises";import{spawnSync as Qd}from"child_process";import{createHash as Dd}from"crypto";function Jd(e){let n=gn(e,"python","bin");if(!xs(n))return;for(let t of Od(n,{withFileTypes:!0})){if(!t.isFile())continue;Gd(gn(n,t.name),493)}}function As(e,n=5,t={}){return new Promise((o,s)=>{let r=jd.get(e,{headers:t,timeout:Nd},(c)=>{let f=c.statusCode||0,u=c.headers.location;if([301,302,307,308].includes(f)&&u&&n>0){c.resume();let l=new URL(u,e).toString();As(l,n-1,t).then(o).catch(s);return}if(f<200||f>=300){let l=`HTTP ${f} for ${e}`;c.resume(),s(Error(l));return}o(c)});r.on("timeout",()=>{r.destroy(Error(`请求超时: ${e}`))}),r.on("error",s)})}async function zd(e,n){let t=await As(e);await Wd(t,Yd(n))}async function eh(e){let n=await As(e,5,{Accept:"application/vnd.github+json","User-Agent":"licell-cli"}),t="";n.setEncoding("utf8");for await(let o of n)t+=o;try{return JSON.parse(t)}catch{throw Error("GitHub API 返回了非 JSON 响应,请检查网络或稍后重试")}}function nh(){let e=se(process.env,"PYTHON313_TARBALL_URL")?.trim();if(!e)return null;let n=se(process.env,"PYTHON313_SHA256")?.trim().toLowerCase();if(!n||!/^[a-f0-9]{64}$/.test(n))throw Error("设置 LICELL_PYTHON313_TARBALL_URL 时,必须同时提供 LICELL_PYTHON313_SHA256(64 位十六进制)");let t=new URL(e).pathname,o=decodeURIComponent(t.split("/").pop()||"");if(!o.endsWith(".tar.gz"))throw Error(`LICELL_PYTHON313_TARBALL_URL 必须指向 .tar.gz 文件,当前为: ${o||"(empty)"}`);return{tarballName:o,downloadUrl:e,sha256:n}}function th(e){if(typeof e!=="object"||e===null)return null;let n=e.assets;if(!Array.isArray(n))return null;for(let t of n){if(typeof t!=="object"||t===null)continue;let o=t,s=typeof o.name==="string"?o.name:"";if(!Zd.test(s))continue;let r=typeof o.browser_download_url==="string"?o.browser_download_url:"";if(!r)continue;let f=(typeof o.digest==="string"?o.digest:"").match(/^sha256:([a-fA-F0-9]{64})$/);if(!f)continue;return{tarballName:s,downloadUrl:r,sha256:f[1].toLowerCase()}}return null}async function oh(){let e=nh();if(e)return e;let n=se(process.env,"PYTHON313_RELEASE_API_URL")?.trim()||Xd,t=await eh(n),o=th(t);if(!o)throw Error(`无法在 ${n} 找到 python3.13 Linux x64 运行时包`);return o}async function sh(e){let n=Dd("sha256"),t=Bd(e);for await(let o of t)n.update(o);return n.digest("hex")}function rh(e,n){let t=Qd("tar",["-xzf",e,"-C",n],{encoding:"utf8"});if(t.status===0)return;let o=t.stderr?.trim()||t.stdout?.trim()||"tar extract failed";throw Error(`解压 Python 3.13 运行时失败: ${o}`)}async function ch(){Hs(Fs,{recursive:!0});let{tarballName:e,downloadUrl:n,sha256:t}=await oh(),o=e.replace(/\.tar\.gz$/,""),s=gn(Fs,o),r=gn(s,"python","bin","python3.13");if(xs(r))return{folderName:o,extractedDir:s};let c=gn(Fs,e);qo(s,{recursive:!0,force:!0}),await zd(n,c);let f=await sh(c);if(f!==t)throw qo(c,{force:!0}),Error(`Python 3.13 运行时包校验失败: expected=${t}, actual=${f}`);if(Hs(s,{recursive:!0}),rh(c,s),qo(c,{force:!0}),!xs(r))throw Error("Python 3.13 运行时下载完成但未找到可执行文件 python/bin/python3.13");return{folderName:o,extractedDir:s}}async function Hf(e){let n=gn(e,".licell-runtime");Hs(n,{recursive:!0});let{folderName:t,extractedDir:o}=await ch(),s=gn(n,t);return qo(s,{recursive:!0,force:!0}),Md(o,s,{recursive:!0}),Jd(s),{pythonBinaryInCode:`/code/.licell-runtime/${t}/python/bin/python3.13`}}var Vd,Fs,Xd="https://api.github.com/repos/indygreg/python-build-standalone/releases/latest",Zd,Nd=60000;var Af=O(()=>{Vd=se(process.env,"RUNTIME_CACHE_DIR")?.trim()||gn(Kd(),".licell-cli","runtimes"),Fs=gn(Vd,"python313"),Zd=/^cpython-3\.13\.\d+\+\d+-x86_64-unknown-linux-gnu-install_only_stripped\.tar\.gz$/});import*as vf from"@alicloud/fc20230330";import{mkdirSync as fh,writeFileSync as uh}from"fs";import{join as Lf}from"path";function lh(e,n){let t=Lf(e,Uf);fh(Lf(e,".licell"),{recursive:!0});let o=`/code/${n.replace(/\\/g,"/")}`,s=`#!/usr/bin/env python3
115
+ ${o}`)}let s=At(n);if(!s)throw Error("构建完成但未发现可执行 JS 产物");return qf(n,s).replace(/\\/g,"/")},async resolveConfig(e,n){let t=`${n.replace(/\.[^.]+$/,"").replace(/\//g,".")}.handler`,s=await Tf(e),o=vd(e,n).replace(/\\/g,"/");return{runtime:Ud,handler:t,customRuntimeConfig:new Sf.CustomRuntimeConfig({command:[s.nodeBinaryInCode],args:[`/code/${o}`],port:Pf})}}}});var Ff;var xf=O(()=>{Kn();Ff={name:"python3.12",defaultEntry:"src/main.py",unsupportedMessage:"当前地域暂不支持 runtime=python3.12。请改用 nodejs20,或确认 python3.12 在目标地域可用后重试。",async prepareBootFile(e,n){return Ts(e,n,"python3.12")},async resolveConfig(e,n){return{runtime:"python3.12",handler:`${n.replace(/\.[^.]+$/,"").replace(/\//g,".")}.handler`}}}});import{chmodSync as Gd,cpSync as Md,createReadStream as Bd,createWriteStream as Yd,existsSync as xo,mkdirSync as Ho,readdirSync as Od,rmSync as Ss}from"fs";import{homedir as Kd}from"os";import{join as gn}from"path";import jd from"https";import{pipeline as Wd}from"stream/promises";import{spawnSync as Qd}from"child_process";import{createHash as Dd}from"crypto";function Jd(e){let n=gn(e,"python","bin");if(!xo(n))return;for(let t of Od(n,{withFileTypes:!0})){if(!t.isFile())continue;Gd(gn(n,t.name),493)}}function Lo(e,n=5,t={}){return new Promise((s,o)=>{let r=jd.get(e,{headers:t,timeout:Nd},(c)=>{let f=c.statusCode||0,u=c.headers.location;if([301,302,307,308].includes(f)&&u&&n>0){c.resume();let l=new URL(u,e).toString();Lo(l,n-1,t).then(s).catch(o);return}if(f<200||f>=300){let l=`HTTP ${f} for ${e}`;c.resume(),o(Error(l));return}s(c)});r.on("timeout",()=>{r.destroy(Error(`请求超时: ${e}`))}),r.on("error",o)})}async function zd(e,n){let t=await Lo(e);await Wd(t,Yd(n))}async function eh(e){let n=await Lo(e,5,{Accept:"application/vnd.github+json","User-Agent":"licell-cli"}),t="";n.setEncoding("utf8");for await(let s of n)t+=s;try{return JSON.parse(t)}catch{throw Error("GitHub API 返回了非 JSON 响应,请检查网络或稍后重试")}}function nh(){let e=oe(process.env,"PYTHON313_TARBALL_URL")?.trim();if(!e)return null;let n=oe(process.env,"PYTHON313_SHA256")?.trim().toLowerCase();if(!n||!/^[a-f0-9]{64}$/.test(n))throw Error("设置 LICELL_PYTHON313_TARBALL_URL 时,必须同时提供 LICELL_PYTHON313_SHA256(64 位十六进制)");let t=new URL(e).pathname,s=decodeURIComponent(t.split("/").pop()||"");if(!s.endsWith(".tar.gz"))throw Error(`LICELL_PYTHON313_TARBALL_URL 必须指向 .tar.gz 文件,当前为: ${s||"(empty)"}`);return{tarballName:s,downloadUrl:e,sha256:n}}function th(e){if(typeof e!=="object"||e===null)return null;let n=e.assets;if(!Array.isArray(n))return null;for(let t of n){if(typeof t!=="object"||t===null)continue;let s=t,o=typeof s.name==="string"?s.name:"";if(!Zd.test(o))continue;let r=typeof s.browser_download_url==="string"?s.browser_download_url:"";if(!r)continue;let f=(typeof s.digest==="string"?s.digest:"").match(/^sha256:([a-fA-F0-9]{64})$/);if(!f)continue;return{tarballName:o,downloadUrl:r,sha256:f[1].toLowerCase()}}return null}async function sh(){let e=nh();if(e)return e;let n=oe(process.env,"PYTHON313_RELEASE_API_URL")?.trim()||Xd,t=await eh(n),s=th(t);if(!s)throw Error(`无法在 ${n} 找到 python3.13 Linux x64 运行时包`);return s}async function oh(e){let n=Dd("sha256"),t=Bd(e);for await(let s of t)n.update(s);return n.digest("hex")}function rh(e,n){let t=Qd("tar",["-xzf",e,"-C",n],{encoding:"utf8"});if(t.status===0)return;let s=t.stderr?.trim()||t.stdout?.trim()||"tar extract failed";throw Error(`解压 Python 3.13 运行时失败: ${s}`)}async function ch(){Ho(Fo,{recursive:!0});let{tarballName:e,downloadUrl:n,sha256:t}=await sh(),s=e.replace(/\.tar\.gz$/,""),o=gn(Fo,s),r=gn(o,"python","bin","python3.13");if(xo(r))return{folderName:s,extractedDir:o};let c=gn(Fo,e);Ss(o,{recursive:!0,force:!0}),await zd(n,c);let f=await oh(c);if(f!==t)throw Ss(c,{force:!0}),Error(`Python 3.13 运行时包校验失败: expected=${t}, actual=${f}`);if(Ho(o,{recursive:!0}),rh(c,o),Ss(c,{force:!0}),!xo(r))throw Error("Python 3.13 运行时下载完成但未找到可执行文件 python/bin/python3.13");return{folderName:s,extractedDir:o}}async function Hf(e){let n=gn(e,".licell-runtime");Ho(n,{recursive:!0});let{folderName:t,extractedDir:s}=await ch(),o=gn(n,t);return Ss(o,{recursive:!0,force:!0}),Md(s,o,{recursive:!0}),Jd(o),{pythonBinaryInCode:`/code/.licell-runtime/${t}/python/bin/python3.13`}}var Vd,Fo,Xd="https://api.github.com/repos/indygreg/python-build-standalone/releases/latest",Zd,Nd=60000;var Lf=O(()=>{Vd=oe(process.env,"RUNTIME_CACHE_DIR")?.trim()||gn(Kd(),".licell-cli","runtimes"),Fo=gn(Vd,"python313"),Zd=/^cpython-3\.13\.\d+\+\d+-x86_64-unknown-linux-gnu-install_only_stripped\.tar\.gz$/});import*as vf from"@alicloud/fc20230330";import{mkdirSync as fh,writeFileSync as uh}from"fs";import{join as Af}from"path";function lh(e,n){let t=Af(e,Uf);fh(Af(e,".licell"),{recursive:!0});let s=`/code/${n.replace(/\\/g,"/")}`,o=`#!/usr/bin/env python3
116
116
  import asyncio
117
117
  import importlib.util
118
118
  import json
@@ -121,7 +121,7 @@ import sys
121
121
  from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
122
122
  from urllib.parse import urlparse, parse_qs
123
123
 
124
- ENTRY_PATH = ${JSON.stringify(o)}
124
+ ENTRY_PATH = ${JSON.stringify(s)}
125
125
  CODE_ROOT = "/code"
126
126
 
127
127
  if CODE_ROOT not in sys.path:
@@ -228,22 +228,22 @@ if __name__ == "__main__":
228
228
  port = int(os.getenv("FC_SERVER_PORT", os.getenv("PORT", "${Gf}")))
229
229
  server = ThreadingHTTPServer(("0.0.0.0", port), RequestHandler)
230
230
  server.serve_forever()
231
- `;return uh(t,s),Uf}var ih="custom.debian12",Uf=".licell/python313-bootstrap.py",Gf=9000,Mf;var Bf=O(()=>{Kn();Af();Mf={name:"python3.13",defaultEntry:"src/main.py",unsupportedMessage:"当前地域暂不支持 runtime=python3.13。请改用 nodejs20,或确认 custom.debian12(含 python3.13)在目标地域可用后重试。",async prepareBootFile(e,n){return Co(e,n,"python3.13")},async resolveConfig(e,n){let t=`${n.replace(/\.[^.]+$/,"").replace(/\//g,".")}.handler`,o=await Hf(e),s=lh(e,n).replace(/\\/g,"/");return{runtime:ih,handler:t,customRuntimeConfig:new vf.CustomRuntimeConfig({command:[o.pythonBinaryInCode],args:[`/code/${s}`],port:Gf})}}}});import{spawnSync as Po}from"child_process";function Ro(){if(Po("docker",["info"],{stdio:"pipe",timeout:1e4}).status!==0)throw Error(`未检测到 Docker 环境。请安装 Docker Desktop 或 Docker Engine 后重试。
231
+ `;return uh(t,o),Uf}var ih="custom.debian12",Uf=".licell/python313-bootstrap.py",Gf=9000,Mf;var Bf=O(()=>{Kn();Lf();Mf={name:"python3.13",defaultEntry:"src/main.py",unsupportedMessage:"当前地域暂不支持 runtime=python3.13。请改用 nodejs20,或确认 custom.debian12(含 python3.13)在目标地域可用后重试。",async prepareBootFile(e,n){return Ts(e,n,"python3.13")},async resolveConfig(e,n){let t=`${n.replace(/\.[^.]+$/,"").replace(/\//g,".")}.handler`,s=await Hf(e),o=lh(e,n).replace(/\\/g,"/");return{runtime:ih,handler:t,customRuntimeConfig:new vf.CustomRuntimeConfig({command:[s.pythonBinaryInCode],args:[`/code/${o}`],port:Gf})}}}});import{spawnSync as qs}from"child_process";function Ps(){if(qs("docker",["info"],{stdio:"pipe",timeout:1e4}).status!==0)throw Error(`未检测到 Docker 环境。请安装 Docker Desktop 或 Docker Engine 后重试。
232
232
  `+` macOS: https://docs.docker.com/desktop/install/mac-install/
233
- Linux: https://docs.docker.com/engine/install/`)}function Yf(e,n,t){let o=["build","--platform","linux/amd64"];if(t)o.push("-f",t);o.push("-t",e,n);let s=Po("docker",o,{stdio:"inherit",timeout:600000});if(s.status!==0)throw Error(`Docker 构建失败 (exit=${s.status}),请检查 Dockerfile 和构建日志`)}function Of(e,n,t){let o=Po("docker",["login","--username",n,"--password-stdin",e],{input:t,stdio:["pipe","pipe","pipe"],timeout:30000});if(o.status!==0){let s=o.stderr?.toString().trim()||"";throw Error(`Docker 登录 ACR 失败: ${s||"未知错误"}`)}}function Kf(e){let n=Po("docker",["push",e],{stdio:"inherit",timeout:600000});if(n.status!==0)throw Error(`Docker 推送失败 (exit=${n.status}),请检查网络连接和镜像仓库权限`)}var Ls=()=>{};import{existsSync as Ke,readFileSync as gh}from"fs";import{join as Me}from"path";function Wf(e){jf.push(e)}function Ut(e){for(let n of jf)if(n.detect(e))return n;return null}function Qf(e,n){let t=Ut(e);if(!t)throw Error("无法自动探测项目类型,请手动创建 Dockerfile");return t.generate(e,n)}function yh(e){try{return JSON.parse(gh(e,"utf-8"))}catch{return null}}var jf;var Us=O(()=>{jf=[];Wf({name:"nodejs",detect(e){return Ke(Me(e,"package.json"))},generate(e,n){let t=Ke(Me(e,"bun.lockb")),o=Ke(Me(e,"bun.lock")),s=Ke(Me(e,"pnpm-lock.yaml")),r=Ke(Me(e,"yarn.lock")),c=Ke(Me(e,"package-lock.json"))||Ke(Me(e,"npm-shrinkwrap.json")),f=yh(Me(e,"package.json")),u=Ke(Me(e,"tsconfig.json")),i="build"in(f?.scripts??{}),g=n||(u?"dist/index.js":"src/index.js");if(!n&&u&&!i)throw Error(`检测到 tsconfig.json 但缺少 build 脚本,无法自动推断容器启动入口。
234
- `+"请在 package.json 中添加 build 脚本,或通过 --entry 指定可运行的 JS 入口,或手动维护 Dockerfile。");if(t||o){let b=["FROM oven/bun:1-slim","WORKDIR /app",`COPY package.json ${t?"bun.lockb":"bun.lock"} ./`,"RUN bun install --frozen-lockfile","COPY . ."];if(i)b.push("RUN bun run build");return b.push("ENV PORT=9000","EXPOSE 9000",`CMD ["bun", "run", "${g}"]`,""),b.join(`
235
- `)}let y=c?"npm ci":"npm install",d="COPY package*.json ./";if(s)y="corepack enable && pnpm install --frozen-lockfile",d="COPY package.json pnpm-lock.yaml ./";else if(r)y="corepack enable && yarn install --frozen-lockfile",d="COPY package.json yarn.lock ./";let m=["FROM node:22-slim","WORKDIR /app",d,`RUN ${y}`,"COPY . ."];if(i)m.push("RUN npm run build");return m.push("ENV PORT=9000","EXPOSE 9000",`CMD ["node", "${g}"]`,""),m.join(`
236
- `)}});Wf({name:"python",detect(e){return Ke(Me(e,"requirements.txt"))||Ke(Me(e,"pyproject.toml"))},generate(e,n){let t=n||"src/main.py",o=Ke(Me(e,"requirements.txt")),s=Ke(Me(e,"pyproject.toml")),r=["FROM python:3.13-slim","WORKDIR /app"];if(o)r.push("COPY requirements.txt ./","RUN pip install --no-cache-dir -r requirements.txt","COPY . .");else if(s)r.push("COPY . .","RUN pip install --no-cache-dir .");else r.push("COPY . .");return r.push("ENV PORT=9000","EXPOSE 9000",`CMD ["python", "${t}"]`,""),r.join(`
237
- `)}})});import{randomInt as ct}from"crypto";function xe(e=20){if(e<4)throw Error("Password length must be at least 4 to include all character types");let n=[vs[ct(vs.length)],Gs[ct(Gs.length)],Ms[ct(Ms.length)],Bs[ct(Bs.length)]];while(n.length<e)n.push(Df[ct(Df.length)]);for(let t=n.length-1;t>0;t-=1){let o=ct(t+1);[n[t],n[o]]=[n[o],n[t]]}return n.join("")}var vs="abcdefghjkmnpqrstuvwxyz",Gs="ABCDEFGHJKLMNPQRSTUVWXYZ",Ms="23456789",Bs="!@#$%^&*()-_=+",Df;var ft=O(()=>{Df=`${vs}${Gs}${Ms}${Bs}`});import dh,*as Tn from"@alicloud/cr20181201";import*as bn from"@alicloud/openapi-client";import*as Nf from"@alicloud/tea-util";function Os(e){let n=e.trim().toLowerCase();if(!n)throw Error("ACR namespace 不能为空");if(n.length<2||n.length>Vf)throw Error(`ACR namespace 长度需在 2-${Vf} 之间`);if(!mh.test(n))throw Error("ACR namespace 仅支持小写字母、数字、点、短横线、下划线,且分隔符不能在首尾");return n}function Ys(e){let n=e??k.requireAuth();return new hh(new bn.Config({accessKeyId:n.ak,accessKeySecret:n.sk,regionId:n.region,endpoint:`cr.${n.region}.aliyuncs.com`}))}async function wh(e){let t=(await e.listInstance(new Tn.ListInstanceRequest({instanceStatus:"RUNNING",pageNo:1,pageSize:30}))).body?.instances||[];if(t.length===0)return null;let o=t[0];if(!o.instanceId||!o.instanceName)return null;return{instanceId:o.instanceId,instanceName:o.instanceName}}async function $h(e,n,t){try{await e.createNamespace(new Tn.CreateNamespaceRequest({instanceId:n,namespaceName:t,autoCreateRepo:!0,defaultRepoType:"PRIVATE"}))}catch(o){if(!J(o)&&!eu(o))throw o}}async function kh(e,n,t,o){try{await e.createRepository(new Tn.CreateRepositoryRequest({instanceId:n,repoNamespaceName:t,repoName:o,repoType:"PRIVATE",summary:`licell deploy: ${o}`}))}catch(s){if(!J(s)&&!nu(s))throw s}}function eu(e){return yn(e,"NAMESPACE_EXIST")||yn(e,"NAMESPACE_ALREADY_EXIST")}function nu(e){return yn(e,"REPO_EXIST")||yn(e,"REPO_ALREADY_EXIST")}function _h(e){return yn(e,"USER_NOT_REGISTERED")||yn(e,"USER_NOT_EXIST")}function bh(e){return yn(e,"USER_ALREADY_EXIST")||yn(e,"USER_EXIST")}function yn(e,n){if(typeof e!=="object"||e===null)return!1;let t="code"in e?String(e.code||""):"",o="message"in e?String(e.message||""):"";return t===n||o.includes(n)}function Zf(e=xe(20)){return{user:{password:e}}}async function ut(e,n,t,o,s){return e.doROARequest(n,ph,"HTTPS",t,"AK",o,"json",s||new bn.OpenApiRequest({}),new Nf.RuntimeOptions({}))}async function Th(e){let n=new bn.OpenApiRequest({body:Zf()});try{await ut(e,"CreateUserInfo","PUT",Xf,n);return}catch(o){if(!bh(o))throw o}let t=new bn.OpenApiRequest({body:Zf()});await ut(e,"UpdateUserInfo","POST",Xf,t)}async function Jf(e){return((await ut(e,"GetNamespaceList","GET",zf)).body?.data?.namespaces||[]).map((t)=>t.namespace||"").filter(Boolean)}async function Ch(e){try{return await Jf(e)}catch(n){if(!_h(n))throw n}return await Th(e),await Jf(e)}async function Eh(e,n){let t=await Ch(e);if(t.includes(n))return;try{await ut(e,"CreateNamespace","PUT",zf,new bn.OpenApiRequest({body:{Namespace:{Namespace:n}}}))}catch(o){if(J(o)||eu(o))return;if(yn(o,"NAMESPACE_LIMIT_EXCEED"))throw new tu(t,n);throw o}}async function Sh(e,n,t){try{await ut(e,"CreateRepo","PUT","/repos",new bn.OpenApiRequest({body:{Repo:{RepoNamespace:n,RepoName:t,RepoType:"PRIVATE",Summary:`licell deploy: ${t}`}}}))}catch(o){if(!J(o)&&!nu(o))throw o}}async function ou(e,n,t,o){let s=Ys(t),r=Os(o||ah),c=e,f=await wh(s);if(f){await $h(s,f.instanceId,r),await kh(s,f.instanceId,r,c);let i=`${f.instanceName}-registry.${n}.cr.aliyuncs.com`,g=`${f.instanceName}-registry-vpc.${n}.cr.aliyuncs.com`;return{instanceId:f.instanceId,registryEndpoint:i,vpcRegistryEndpoint:g,namespace:r,repoName:c}}await Eh(s,r),await Sh(s,r,c);let u=`registry.${n}.aliyuncs.com`,l=`registry-vpc.${n}.aliyuncs.com`;return{instanceId:null,registryEndpoint:u,vpcRegistryEndpoint:l,namespace:r,repoName:c}}async function su(e,n){let t=n??k.requireAuth();if(e.instanceId){let f=await Ys(t).getAuthorizationToken(new Tn.GetAuthorizationTokenRequest({instanceId:e.instanceId}));if(!f.body?.tempUsername||!f.body?.authorizationToken)throw Error("获取 ACR 企业版临时凭证失败,请检查实例状态");return{endpoint:e.registryEndpoint,userName:f.body.tempUsername,password:f.body.authorizationToken}}let o=Ys(t),r=(await ut(o,"GetAuthorizationToken","GET","/tokens")).body?.data;if(!r?.tempUserName||!r?.authorizationToken)throw Error("获取 ACR 个人版临时凭证失败");return{endpoint:e.registryEndpoint,userName:r.tempUserName,password:r.authorizationToken}}function Ks(e,n,t=!1){return`${t?e.vpcRegistryEndpoint:e.registryEndpoint}/${e.namespace}/${e.repoName}:${n}`}function ru(){let e=new Date,n=(t)=>String(t).padStart(2,"0");return`${e.getFullYear()}${n(e.getMonth()+1)}${n(e.getDate())}-${n(e.getHours())}${n(e.getMinutes())}${n(e.getSeconds())}`}var hh,ah="licell",Vf=120,mh,ph="2016-06-07",Xf="/users",zf="/namespace",tu;var js=O(()=>{K();Ie();ce();ft();hh=te(dh,"@alicloud/cr20181201"),mh=/^[a-z0-9](?:[a-z0-9._-]*[a-z0-9])?$/;tu=class tu extends Error{existing;constructor(e,n){super(`ACR 个人版命名空间数量已达上限(当前: ${e.join(", ")})。
233
+ Linux: https://docs.docker.com/engine/install/`)}function Yf(e,n,t){let s=["build","--platform","linux/amd64"];if(t)s.push("-f",t);s.push("-t",e,n);let o=qs("docker",s,{stdio:"inherit",timeout:600000});if(o.status!==0)throw Error(`Docker 构建失败 (exit=${o.status}),请检查 Dockerfile 和构建日志`)}function Of(e,n,t){let s=qs("docker",["login","--username",n,"--password-stdin",e],{input:t,stdio:["pipe","pipe","pipe"],timeout:30000});if(s.status!==0){let o=s.stderr?.toString().trim()||"";throw Error(`Docker 登录 ACR 失败: ${o||"未知错误"}`)}}function Kf(e){let n=qs("docker",["push",e],{stdio:"inherit",timeout:600000});if(n.status!==0)throw Error(`Docker 推送失败 (exit=${n.status}),请检查网络连接和镜像仓库权限`)}var Ao=()=>{};import{existsSync as Ke,readFileSync as gh}from"fs";import{join as Me}from"path";function Wf(e){jf.push(e)}function Ut(e){for(let n of jf)if(n.detect(e))return n;return null}function Qf(e,n){let t=Ut(e);if(!t)throw Error("无法自动探测项目类型,请手动创建 Dockerfile");return t.generate(e,n)}function yh(e){try{return JSON.parse(gh(e,"utf-8"))}catch{return null}}var jf;var Uo=O(()=>{jf=[];Wf({name:"nodejs",detect(e){return Ke(Me(e,"package.json"))},generate(e,n){let t=Ke(Me(e,"bun.lockb")),s=Ke(Me(e,"bun.lock")),o=Ke(Me(e,"pnpm-lock.yaml")),r=Ke(Me(e,"yarn.lock")),c=Ke(Me(e,"package-lock.json"))||Ke(Me(e,"npm-shrinkwrap.json")),f=yh(Me(e,"package.json")),u=Ke(Me(e,"tsconfig.json")),i="build"in(f?.scripts??{}),g=n||(u?"dist/index.js":"src/index.js");if(!n&&u&&!i)throw Error(`检测到 tsconfig.json 但缺少 build 脚本,无法自动推断容器启动入口。
234
+ `+"请在 package.json 中添加 build 脚本,或通过 --entry 指定可运行的 JS 入口,或手动维护 Dockerfile。");if(t||s){let b=["FROM oven/bun:1-slim","WORKDIR /app",`COPY package.json ${t?"bun.lockb":"bun.lock"} ./`,"RUN bun install --frozen-lockfile","COPY . ."];if(i)b.push("RUN bun run build");return b.push("ENV PORT=9000","EXPOSE 9000",`CMD ["bun", "run", "${g}"]`,""),b.join(`
235
+ `)}let y=c?"npm ci":"npm install",d="COPY package*.json ./";if(o)y="corepack enable && pnpm install --frozen-lockfile",d="COPY package.json pnpm-lock.yaml ./";else if(r)y="corepack enable && yarn install --frozen-lockfile",d="COPY package.json yarn.lock ./";let m=["FROM node:22-slim","WORKDIR /app",d,`RUN ${y}`,"COPY . ."];if(i)m.push("RUN npm run build");return m.push("ENV PORT=9000","EXPOSE 9000",`CMD ["node", "${g}"]`,""),m.join(`
236
+ `)}});Wf({name:"python",detect(e){return Ke(Me(e,"requirements.txt"))||Ke(Me(e,"pyproject.toml"))},generate(e,n){let t=n||"src/main.py",s=Ke(Me(e,"requirements.txt")),o=Ke(Me(e,"pyproject.toml")),r=["FROM python:3.13-slim","WORKDIR /app"];if(s)r.push("COPY requirements.txt ./","RUN pip install --no-cache-dir -r requirements.txt","COPY . .");else if(o)r.push("COPY . .","RUN pip install --no-cache-dir .");else r.push("COPY . .");return r.push("ENV PORT=9000","EXPOSE 9000",`CMD ["python", "${t}"]`,""),r.join(`
237
+ `)}})});import{randomInt as ct}from"crypto";function xe(e=20){if(e<4)throw Error("Password length must be at least 4 to include all character types");let n=[vo[ct(vo.length)],Go[ct(Go.length)],Mo[ct(Mo.length)],Bo[ct(Bo.length)]];while(n.length<e)n.push(Df[ct(Df.length)]);for(let t=n.length-1;t>0;t-=1){let s=ct(t+1);[n[t],n[s]]=[n[s],n[t]]}return n.join("")}var vo="abcdefghjkmnpqrstuvwxyz",Go="ABCDEFGHJKLMNPQRSTUVWXYZ",Mo="23456789",Bo="!@#$%^&*()-_=+",Df;var ft=O(()=>{Df=`${vo}${Go}${Mo}${Bo}`});import dh,*as Tn from"@alicloud/cr20181201";import*as bn from"@alicloud/openapi-client";import*as Nf from"@alicloud/tea-util";function Oo(e){let n=e.trim().toLowerCase();if(!n)throw Error("ACR namespace 不能为空");if(n.length<2||n.length>Vf)throw Error(`ACR namespace 长度需在 2-${Vf} 之间`);if(!mh.test(n))throw Error("ACR namespace 仅支持小写字母、数字、点、短横线、下划线,且分隔符不能在首尾");return n}function Yo(e){let n=e??k.requireAuth();return new hh(new bn.Config({accessKeyId:n.ak,accessKeySecret:n.sk,regionId:n.region,endpoint:`cr.${n.region}.aliyuncs.com`}))}async function wh(e){let t=(await e.listInstance(new Tn.ListInstanceRequest({instanceStatus:"RUNNING",pageNo:1,pageSize:30}))).body?.instances||[];if(t.length===0)return null;let s=t[0];if(!s.instanceId||!s.instanceName)return null;return{instanceId:s.instanceId,instanceName:s.instanceName}}async function $h(e,n,t){try{await e.createNamespace(new Tn.CreateNamespaceRequest({instanceId:n,namespaceName:t,autoCreateRepo:!0,defaultRepoType:"PRIVATE"}))}catch(s){if(!J(s)&&!eu(s))throw s}}async function kh(e,n,t,s){try{await e.createRepository(new Tn.CreateRepositoryRequest({instanceId:n,repoNamespaceName:t,repoName:s,repoType:"PRIVATE",summary:`licell deploy: ${s}`}))}catch(o){if(!J(o)&&!nu(o))throw o}}function eu(e){return yn(e,"NAMESPACE_EXIST")||yn(e,"NAMESPACE_ALREADY_EXIST")}function nu(e){return yn(e,"REPO_EXIST")||yn(e,"REPO_ALREADY_EXIST")}function _h(e){return yn(e,"USER_NOT_REGISTERED")||yn(e,"USER_NOT_EXIST")}function bh(e){return yn(e,"USER_ALREADY_EXIST")||yn(e,"USER_EXIST")}function yn(e,n){if(typeof e!=="object"||e===null)return!1;let t="code"in e?String(e.code||""):"",s="message"in e?String(e.message||""):"";return t===n||s.includes(n)}function Zf(e=xe(20)){return{user:{password:e}}}async function ut(e,n,t,s,o){return e.doROARequest(n,ph,"HTTPS",t,"AK",s,"json",o||new bn.OpenApiRequest({}),new Nf.RuntimeOptions({}))}async function Th(e){let n=new bn.OpenApiRequest({body:Zf()});try{await ut(e,"CreateUserInfo","PUT",Xf,n);return}catch(s){if(!bh(s))throw s}let t=new bn.OpenApiRequest({body:Zf()});await ut(e,"UpdateUserInfo","POST",Xf,t)}async function Jf(e){return((await ut(e,"GetNamespaceList","GET",zf)).body?.data?.namespaces||[]).map((t)=>t.namespace||"").filter(Boolean)}async function Ch(e){try{return await Jf(e)}catch(n){if(!_h(n))throw n}return await Th(e),await Jf(e)}async function Eh(e,n){let t=await Ch(e);if(t.includes(n))return;try{await ut(e,"CreateNamespace","PUT",zf,new bn.OpenApiRequest({body:{Namespace:{Namespace:n}}}))}catch(s){if(J(s)||eu(s))return;if(yn(s,"NAMESPACE_LIMIT_EXCEED"))throw new tu(t,n);throw s}}async function Sh(e,n,t){try{await ut(e,"CreateRepo","PUT","/repos",new bn.OpenApiRequest({body:{Repo:{RepoNamespace:n,RepoName:t,RepoType:"PRIVATE",Summary:`licell deploy: ${t}`}}}))}catch(s){if(!J(s)&&!nu(s))throw s}}async function su(e,n,t,s){let o=Yo(t),r=Oo(s||ah),c=e,f=await wh(o);if(f){await $h(o,f.instanceId,r),await kh(o,f.instanceId,r,c);let i=`${f.instanceName}-registry.${n}.cr.aliyuncs.com`,g=`${f.instanceName}-registry-vpc.${n}.cr.aliyuncs.com`;return{instanceId:f.instanceId,registryEndpoint:i,vpcRegistryEndpoint:g,namespace:r,repoName:c}}await Eh(o,r),await Sh(o,r,c);let u=`registry.${n}.aliyuncs.com`,l=`registry-vpc.${n}.aliyuncs.com`;return{instanceId:null,registryEndpoint:u,vpcRegistryEndpoint:l,namespace:r,repoName:c}}async function ou(e,n){let t=n??k.requireAuth();if(e.instanceId){let f=await Yo(t).getAuthorizationToken(new Tn.GetAuthorizationTokenRequest({instanceId:e.instanceId}));if(!f.body?.tempUsername||!f.body?.authorizationToken)throw Error("获取 ACR 企业版临时凭证失败,请检查实例状态");return{endpoint:e.registryEndpoint,userName:f.body.tempUsername,password:f.body.authorizationToken}}let s=Yo(t),r=(await ut(s,"GetAuthorizationToken","GET","/tokens")).body?.data;if(!r?.tempUserName||!r?.authorizationToken)throw Error("获取 ACR 个人版临时凭证失败");return{endpoint:e.registryEndpoint,userName:r.tempUserName,password:r.authorizationToken}}function Ko(e,n,t=!1){return`${t?e.vpcRegistryEndpoint:e.registryEndpoint}/${e.namespace}/${e.repoName}:${n}`}function ru(){let e=new Date,n=(t)=>String(t).padStart(2,"0");return`${e.getFullYear()}${n(e.getMonth()+1)}${n(e.getDate())}-${n(e.getHours())}${n(e.getMinutes())}${n(e.getSeconds())}`}var hh,ah="licell",Vf=120,mh,ph="2016-06-07",Xf="/users",zf="/namespace",tu;var jo=O(()=>{K();Ie();ce();ft();hh=te(dh,"@alicloud/cr20181201"),mh=/^[a-z0-9](?:[a-z0-9._-]*[a-z0-9])?$/;tu=class tu extends Error{existing;constructor(e,n){super(`ACR 个人版命名空间数量已达上限(当前: ${e.join(", ")})。
238
238
  `+`请前往阿里云控制台删除不需要的命名空间,或手动创建 "${n}"。
239
- `+"也可以使用已有命名空间:licell deploy --runtime docker --acr-namespace <name>");this.existing=e}}});import*as cu from"@alicloud/fc20230330";import{existsSync as vt,mkdirSync as Ws,readFileSync as qh,writeFileSync as Qs}from"fs";import{join as je,relative as fu}from"path";function Gt(e){return e.replace(/\\/g,"/")}function Ih(e){let n=je(e,"package.json");if(!vt(n))return{};try{return JSON.parse(qh(n,"utf-8")).scripts||{}}catch{return{}}}function Fh(e,n){if(n)return Gt(n);let t=vt(je(e,"tsconfig.json")),o=Ih(e),s=typeof o.build==="string"&&o.build.trim().length>0;if(t&&!s)throw Error(`检测到 tsconfig.json,但 package.json 未定义 build 脚本,无法推断容器入口文件。
240
- `+"请提供 --entry(例如 dist/index.js),或补充 build 脚本,或手动维护 Dockerfile。");return t?"dist/index.js":"src/index.js"}function xh(e,n){if(n)return Gt(n);if(vt(je(e,"src/main.py")))return"src/main.py";if(vt(je(e,"main.py")))return"main.py";return"src/main.py"}function Hh(e,n){let t=`${jn}/node-bootstrap.cjs`,s=Gt(fu(jn,n)),r=`'use strict';
239
+ `+"也可以使用已有命名空间:licell deploy --runtime docker --acr-namespace <name>");this.existing=e}}});import*as cu from"@alicloud/fc20230330";import{existsSync as vt,mkdirSync as Wo,readFileSync as qh,writeFileSync as Qo}from"fs";import{join as je,relative as fu}from"path";function Gt(e){return e.replace(/\\/g,"/")}function Ih(e){let n=je(e,"package.json");if(!vt(n))return{};try{return JSON.parse(qh(n,"utf-8")).scripts||{}}catch{return{}}}function Fh(e,n){if(n)return Gt(n);let t=vt(je(e,"tsconfig.json")),s=Ih(e),o=typeof s.build==="string"&&s.build.trim().length>0;if(t&&!o)throw Error(`检测到 tsconfig.json,但 package.json 未定义 build 脚本,无法推断容器入口文件。
240
+ `+"请提供 --entry(例如 dist/index.js),或补充 build 脚本,或手动维护 Dockerfile。");return t?"dist/index.js":"src/index.js"}function xh(e,n){if(n)return Gt(n);if(vt(je(e,"src/main.py")))return"src/main.py";if(vt(je(e,"main.py")))return"main.py";return"src/main.py"}function Hh(e,n){let t=`${jn}/node-bootstrap.cjs`,o=Gt(fu(jn,n)),r=`'use strict';
241
241
  const http = require('http');
242
242
  const path = require('path');
243
243
  const { pathToFileURL } = require('url');
244
244
 
245
- const ENTRY_RELATIVE = ${JSON.stringify(s)};
246
- const port = Number(process.env.FC_SERVER_PORT || process.env.PORT || ${Ds});
245
+ const ENTRY_RELATIVE = ${JSON.stringify(o)};
246
+ const port = Number(process.env.FC_SERVER_PORT || process.env.PORT || ${Do});
247
247
  let handlerPromise;
248
248
 
249
249
  async function loadHandler() {
@@ -361,7 +361,7 @@ const server = http.createServer(async (req, res) => {
361
361
  });
362
362
 
363
363
  server.listen(port, '0.0.0.0');
364
- `,c=je(e,t);return Ws(je(e,jn),{recursive:!0}),Qs(c,r),t}function Ah(e,n){let t=`${jn}/python-bootstrap.py`,s=Gt(fu(jn,n)),r=`#!/usr/bin/env python3
364
+ `,c=je(e,t);return Wo(je(e,jn),{recursive:!0}),Qo(c,r),t}function Lh(e,n){let t=`${jn}/python-bootstrap.py`,o=Gt(fu(jn,n)),r=`#!/usr/bin/env python3
365
365
  import asyncio
366
366
  import importlib.util
367
367
  import json
@@ -370,7 +370,7 @@ import sys
370
370
  from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
371
371
  from urllib.parse import urlparse, parse_qs
372
372
 
373
- ENTRY_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), ${JSON.stringify(s)}))
373
+ ENTRY_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), ${JSON.stringify(o)}))
374
374
  CODE_ROOT = "/app"
375
375
 
376
376
  if CODE_ROOT not in sys.path:
@@ -473,31 +473,31 @@ class RequestHandler(BaseHTTPRequestHandler):
473
473
  return
474
474
 
475
475
  if __name__ == "__main__":
476
- port = int(os.getenv("FC_SERVER_PORT", os.getenv("PORT", "${Ds}")))
476
+ port = int(os.getenv("FC_SERVER_PORT", os.getenv("PORT", "${Do}")))
477
477
  server = ThreadingHTTPServer(("0.0.0.0", port), RequestHandler)
478
478
  server.serve_forever()
479
- `,c=je(e,t);return Ws(je(e,jn),{recursive:!0}),Qs(c,r),t}function Lh(e,n){let t=Ut(e);if(!t)throw Error(`未找到 Dockerfile,且无法自动探测项目类型。
480
- `+"请在项目根目录创建 Dockerfile,或确保存在 package.json / requirements.txt");let o=Gt(n||""),s;if(t.name==="nodejs"){let f=Fh(e,o||void 0);s=Hh(e,f)}else if(t.name==="python"){let f=xh(e,o||void 0);s=Ah(e,f)}else throw Error(`不支持为 ${t.name} 自动生成 Dockerfile,请手动维护 Dockerfile`);let r=Qf(e,s),c=je(e,Rh);return Ws(je(e,jn),{recursive:!0}),Qs(c,r),c}var Ph="custom-container",Ds=9000,jn=".licell/docker",Rh=".licell/docker/Dockerfile.generated",uu;var iu=O(()=>{K();Ls();Us();js();uu={name:"docker",defaultEntry:"",unsupportedMessage:"当前地域暂不支持 custom-container 运行时。请确认目标地域支持自定义容器镜像后重试。",async prepareBootFile(e,n){let t=process.cwd();Ro();let o=vt(je(t,"Dockerfile"))?je(t,"Dockerfile"):Lh(t,e),s=k.requireAuth(),r=k.getProject(),c=r.appName;if(!c)throw Error("appName 未设置,请检查项目配置");let f=await ou(c,s.region,s,r.acrNamespace),u=ru(),l=Ks(f,u,!1);Yf(l,t,o);let i=await su(f,s);return Of(i.endpoint,i.userName,i.password),Kf(l),Ks(f,u,!0)},async resolveConfig(e,n){return{runtime:Ph,handler:"not-used",customContainerConfig:new cu.CustomContainerConfig({image:n,port:Ds}),skipCodePackaging:!0}}}});var Vs=O(()=>{On();kf();If();xf();Bf();iu();rt($f);rt(Rf);rt(Ff);rt(Mf);rt(uu)});var Wn="nodejs20";var Xs=O(()=>{Vs();On()});import Uh,*as He from"@alicloud/vpc20160428";import vh,*as Bt from"@alicloud/ecs20140526";import*as Yt from"@alicloud/openapi-client";function Mh(e){let n=[];for(let t of e||[]){if(typeof t!=="string")continue;let o=t.trim();if(!o||n.includes(o))continue;n.push(o)}return n}function lu(e){return e===Zs||(e||"").startsWith(`${Zs}-`)}function Bh(e){let n=(e.status||"").toLowerCase();if(!n)return!0;return n==="available"}async function Yh(e,n,t){let o=[],s=1;while(!0){let r=await e.describeVSwitches(new He.DescribeVSwitchesRequest({regionId:n,vpcId:t,pageNumber:s,pageSize:Gh})),c=r.body.vSwitches?.vSwitch||[];if(c.length===0)break;o.push(...c);let f=r.body.totalCount||o.length;if(o.length>=f)break;if(s+=1,s>20)break}return o}function Oh(e){let n=new Set(e.map((o)=>o.cidrBlock).filter((o)=>typeof o==="string"&&o.length>0)),t=[];for(let o=1;o<255;o+=1)t.push(`10.0.${o}.0/24`);for(let o=0;o<255;o+=1)t.push(`10.1.${o}.0/24`);return t.filter((o)=>!n.has(o))}function Kh(e,n){let t=e.filter(Bh),o=t.length>0?t:e;if(n.length>0){for(let r of n){let c=o.find((f)=>f.zoneId===r&&lu(f.vSwitchName));if(c?.vSwitchId)return c}for(let r of n){let c=o.find((f)=>f.zoneId===r);if(c?.vSwitchId)return c}return null}let s=o.find((r)=>lu(r.vSwitchName));if(s?.vSwitchId)return s;return o.find((r)=>typeof r.vSwitchId==="string"&&r.vSwitchId.length>0)||null}async function jh(e,n,t){if(t.length>0)return t[0];return(await e.describeZones(new He.DescribeZonesRequest({regionId:n}))).body.zones?.zone?.[0]?.zoneId||""}async function Wh(e,n,t,o,s){let r=Oh(s);for(let c of r)try{let f=await e.createVSwitch(new He.CreateVSwitchRequest({regionId:n,vpcId:t,zoneId:o,cidrBlock:c,vSwitchName:`${Zs}-${o}`}));if(!f.body.vSwitchId)throw Error("VSwitch 创建失败:未返回 vSwitchId");return f.body.vSwitchId}catch(f){if($s(f))continue;throw f}throw Error(`VSwitch 创建失败:在 ${o} 未找到可用网段`)}async function du(e,n,t){let s=(await e.describeSecurityGroups(new Bt.DescribeSecurityGroupsRequest({regionId:n,vpcId:t,securityGroupName:"licell-sg"}))).body?.securityGroups?.securityGroup?.[0];if(s?.securityGroupId)return s.securityGroupId;let c=(await e.describeSecurityGroups(new Bt.DescribeSecurityGroupsRequest({regionId:n,vpcId:t}))).body?.securityGroups?.securityGroup?.[0];if(c?.securityGroupId)return c.securityGroupId;let f=await e.createSecurityGroup(new Bt.CreateSecurityGroupRequest({regionId:n,vpcId:t,securityGroupName:"licell-sg",securityGroupType:"normal"}));if(!f.body?.securityGroupId)throw Error("安全组创建失败:未返回 SecurityGroupId");return await ge(2000),f.body.securityGroupId}async function it(e){let n=k.requireAuth(),t=n.region,o=Mh(e?.preferredZoneIds),s=new Yt.Config({accessKeyId:n.ak,accessKeySecret:n.sk,regionId:t,endpoint:`vpc.${t}.aliyuncs.com`}),r=new Yt.Config({accessKeyId:n.ak,accessKeySecret:n.sk,regionId:t,endpoint:`ecs.${t}.aliyuncs.com`}),c=new gu(s),f=new yu(r),u="licell-vpc",l="",i="",g="",y="",d=Mt,h=(await c.describeVpcs(new He.DescribeVpcsRequest({regionId:t,vpcName:"licell-vpc"}))).body.vpcs?.vpc?.[0];if(h?.vpcId)l=h.vpcId,d=h.cidrBlock||Mt;else{let p=await c.createVpc(new He.CreateVpcRequest({regionId:t,vpcName:"licell-vpc",cidrBlock:Mt}));if(!p.body.vpcId)throw Error("VPC 创建失败:未返回 vpcId");l=p.body.vpcId,d=Mt,await ge(5000)}let b=await Yh(c,t,l),$=Kh(b,o);if($?.vSwitchId&&$.zoneId)i=$.vSwitchId,y=$.zoneId;else{if(y=await jh(c,t,o),!y)throw Error(`Region ${t} 未查询到可用区,无法创建 vSwitch`);i=await Wh(c,t,l,y,b),await ge(3000)}if(g=await du(f,t,l),!y)y=(await c.describeVSwitchAttributes(new He.DescribeVSwitchAttributesRequest({regionId:t,vSwitchId:i}))).body?.zoneId||"";if(!y)throw Error(`无法确定 vSwitch 所在可用区 (vswId=${i})`);return{vpcId:l,vswId:i,sgId:g,cidrBlock:d,zoneId:y}}async function lt(e){let n=k.requireAuth(),t=n.region,o=e.vpcId.trim(),s=e.vswId.trim(),r=e.zoneId?.trim();if(!o||!s)throw Error("解析网络信息失败:vpcId 和 vswId 不能为空");let c=new gu(new Yt.Config({accessKeyId:n.ak,accessKeySecret:n.sk,regionId:t,endpoint:`vpc.${t}.aliyuncs.com`})),f=new yu(new Yt.Config({accessKeyId:n.ak,accessKeySecret:n.sk,regionId:t,endpoint:`ecs.${t}.aliyuncs.com`})),l=((await c.describeVpcs(new He.DescribeVpcsRequest({regionId:t,vpcId:o}))).body?.vpcs?.vpc||[]).find((h)=>h.vpcId===o);if(!l?.vpcId)throw Error(`指定 VPC 不存在或不可访问: ${o}`);let i=await c.describeVSwitchAttributes(new He.DescribeVSwitchAttributesRequest({regionId:t,vSwitchId:s})),g=i.body?.vpcId;if(typeof g==="string"&&g.length>0&&g!==o)throw Error(`指定 --vsw (${s}) 不属于 --vpc (${o})`);let y=i.body?.zoneId||"";if(r&&y&&r!==y)throw Error(`指定 --zone (${r}) 与 --vsw 实际可用区 (${y}) 不一致`);let d=y||r||"";if(!d)throw Error(`无法确定 vSwitch 所在可用区 (vswId=${s})`);let m=await du(f,t,o);return{vpcId:o,vswId:s,sgId:m,cidrBlock:l.cidrBlock||Mt,zoneId:d}}var gu,yu,Mt="10.0.0.0/8",Zs="licell-vsw",Gh=50;var Ot=O(()=>{K();De();Ie();gu=te(Uh,"@alicloud/vpc20160428"),yu=te(vh,"@alicloud/ecs20140526")});async function Js(e,n=lt){if(!e)return;let t=e.vpcId?.trim(),o=e.vswId?.trim();if(!t||!o)return;let s=e.sgId?.trim();if(!s)s=(await n({vpcId:t,vswId:o})).sgId?.trim();let r={vpcId:t,vSwitchIds:[o]};if(s)r.securityGroupId=s;return r}function Qn(e){let n=e.trim().toLowerCase(),t=Yn();if(t.includes(n))return n;throw Error(`函数运行时仅支持: ${t.join(", ")}`)}async function hu(e,n,t){return kn(t).prepareBootFile(e,n)}async function au(e,n,t){return kn(e).resolveConfig(n,t)}function Ns(e){try{return kn(e).unsupportedMessage}catch{return`当前地域暂不支持 runtime=${e}。请改用 nodejs20 后重试。`}}function zs(e){if(typeof e!=="object"||e===null)return!1;let n="code"in e?String(e.code||""):"",t="message"in e?String(e.message||""):"";return n==="InvalidArgument"&&t.includes("Runtime is set to an invalid value")}function mu(e){if(typeof e!=="object"||e===null)return!1;let n="code"in e?String(e.code||""):"",t="message"in e?String(e.message||""):"";return n==="InvalidArgument"&&t.toLowerCase().includes("change of runtime")}var er=O(()=>{Ot();On();Vs()});async function Y(e,n={}){let t=n.maxAttempts??Qh,o=n.baseDelayMs??Dh,s=n.shouldRetry??Ee,r;for(let c=1;c<=t;c+=1)try{return await e()}catch(f){if(r=f,c===t||!s(f))throw f;await ge(o*c*(0.5+Math.random()*0.5))}throw r}var Qh=3,Dh=1000;var Cn=O(()=>{De()});function ie(e){return Bn(e)}var Dn=O(()=>{Ie()});import*as en from"@alicloud/fc20230330";function Vh(e="anonymous"){return JSON.stringify({authType:e,disableURLInternet:!1,methods:["GET","POST","PUT","PATCH","DELETE","HEAD","OPTIONS"]})}function Xh(e){return(e.triggerType||"").toLowerCase()==="http"}async function wu(e,n){let t=[],o,s=50;for(let r=0;r<50;r+=1){let c=await n.listTriggers(e,new en.ListTriggersRequest({limit:100,nextToken:o})),f=c.body?.triggers||[];if(t.push(...f),o=c.body?.nextToken,!o||f.length===0)break}return t}function $u(e){for(let n of e){if(!Xh(n))continue;let t=n.httpTrigger?.urlInternet;if(typeof t==="string"&&t.trim().length>0)return t}return null}async function Zh(e,n,t="anonymous"){let o=Vh(t);try{await n.createTrigger(e,new en.CreateTriggerRequest({body:new en.CreateTriggerInput({triggerName:pu,triggerType:"http",description:"managed by licell",triggerConfig:o})}));return}catch(s){if(!J(s))throw s}await n.updateTrigger(e,pu,new en.UpdateTriggerRequest({body:new en.UpdateTriggerInput({triggerConfig:o})}))}async function nr(e,n,t={}){let o=n??ie().client,s=await wu(e,o),r=$u(s);if(r)return r;await Zh(e,o,t.authType);let c=await wu(e,o),f=$u(c);if(f)return f;throw Error("函数部署成功,但未获取到公网 HTTP Trigger URL,请检查函数触发器配置")}var pu="licell-http";var tr=O(()=>{Dn()});import*as En from"@alicloud/fc20230330";import Jh from"adm-zip";import{existsSync as Nh,mkdirSync as zh,readFileSync as ea,rmSync as Tu,statSync as na}from"fs";import{tmpdir as ta}from"os";import{isAbsolute as oa,join as sa,relative as ku,resolve as _u}from"path";import{spawnSync as ra}from"child_process";function or(e){let n=sa(ta(),`licell-code-${Date.now()}-${process.pid}.zip`);try{let t=ra("zip",["-q","-r","-y",n,"."],{cwd:e,encoding:"utf8"});if(t.status===0)return ea(n).toString("base64");let o=t.stderr?.trim(),s=t.stdout?.trim();if(t.error&&t.error.code==="ENOENT"){let r=new Jh;return r.addLocalFolder(e),r.toBuffer().toString("base64")}throw Error(o||s||t.error?.message||"unknown zip error")}finally{Tu(n,{force:!0})}}function la(e,n){if(!Number.isFinite(e)||e<=0)throw Error(`无效的 memorySize: ${String(e)}`);if(!Number.isFinite(n)||n<=0)throw Error(`无效的 vCPU: ${String(n)}`);let t=Math.ceil(n*1024),o=Math.floor(n*4096);if(e<t||e>o)throw Error(`FC 资源规格要求:memory 与 vCPU 需满足 1:1~4:1(memoryGB/vCPU ∈ [1,4])。
479
+ `,c=je(e,t);return Wo(je(e,jn),{recursive:!0}),Qo(c,r),t}function Ah(e,n){let t=Ut(e);if(!t)throw Error(`未找到 Dockerfile,且无法自动探测项目类型。
480
+ `+"请在项目根目录创建 Dockerfile,或确保存在 package.json / requirements.txt");let s=Gt(n||""),o;if(t.name==="nodejs"){let f=Fh(e,s||void 0);o=Hh(e,f)}else if(t.name==="python"){let f=xh(e,s||void 0);o=Lh(e,f)}else throw Error(`不支持为 ${t.name} 自动生成 Dockerfile,请手动维护 Dockerfile`);let r=Qf(e,o),c=je(e,Rh);return Wo(je(e,jn),{recursive:!0}),Qo(c,r),c}var Ph="custom-container",Do=9000,jn=".licell/docker",Rh=".licell/docker/Dockerfile.generated",uu;var iu=O(()=>{K();Ao();Uo();jo();uu={name:"docker",defaultEntry:"",unsupportedMessage:"当前地域暂不支持 custom-container 运行时。请确认目标地域支持自定义容器镜像后重试。",async prepareBootFile(e,n){let t=process.cwd();Ps();let s=vt(je(t,"Dockerfile"))?je(t,"Dockerfile"):Ah(t,e),o=k.requireAuth(),r=k.getProject(),c=r.appName;if(!c)throw Error("appName 未设置,请检查项目配置");let f=await su(c,o.region,o,r.acrNamespace),u=ru(),l=Ko(f,u,!1);Yf(l,t,s);let i=await ou(f,o);return Of(i.endpoint,i.userName,i.password),Kf(l),Ko(f,u,!0)},async resolveConfig(e,n){return{runtime:Ph,handler:"not-used",customContainerConfig:new cu.CustomContainerConfig({image:n,port:Do}),skipCodePackaging:!0}}}});var Vo=O(()=>{On();kf();If();xf();Bf();iu();rt($f);rt(Rf);rt(Ff);rt(Mf);rt(uu)});var Wn="nodejs20";var Xo=O(()=>{Vo();On()});import Uh,*as He from"@alicloud/vpc20160428";import vh,*as Bt from"@alicloud/ecs20140526";import*as Yt from"@alicloud/openapi-client";function Mh(e){let n=[];for(let t of e||[]){if(typeof t!=="string")continue;let s=t.trim();if(!s||n.includes(s))continue;n.push(s)}return n}function lu(e){return e===Zo||(e||"").startsWith(`${Zo}-`)}function Bh(e){let n=(e.status||"").toLowerCase();if(!n)return!0;return n==="available"}async function Yh(e,n,t){let s=[],o=1;while(!0){let r=await e.describeVSwitches(new He.DescribeVSwitchesRequest({regionId:n,vpcId:t,pageNumber:o,pageSize:Gh})),c=r.body.vSwitches?.vSwitch||[];if(c.length===0)break;s.push(...c);let f=r.body.totalCount||s.length;if(s.length>=f)break;if(o+=1,o>20)break}return s}function Oh(e){let n=new Set(e.map((s)=>s.cidrBlock).filter((s)=>typeof s==="string"&&s.length>0)),t=[];for(let s=1;s<255;s+=1)t.push(`10.0.${s}.0/24`);for(let s=0;s<255;s+=1)t.push(`10.1.${s}.0/24`);return t.filter((s)=>!n.has(s))}function Kh(e,n){let t=e.filter(Bh),s=t.length>0?t:e;if(n.length>0){for(let r of n){let c=s.find((f)=>f.zoneId===r&&lu(f.vSwitchName));if(c?.vSwitchId)return c}for(let r of n){let c=s.find((f)=>f.zoneId===r);if(c?.vSwitchId)return c}return null}let o=s.find((r)=>lu(r.vSwitchName));if(o?.vSwitchId)return o;return s.find((r)=>typeof r.vSwitchId==="string"&&r.vSwitchId.length>0)||null}async function jh(e,n,t){if(t.length>0)return t[0];return(await e.describeZones(new He.DescribeZonesRequest({regionId:n}))).body.zones?.zone?.[0]?.zoneId||""}async function Wh(e,n,t,s,o){let r=Oh(o);for(let c of r)try{let f=await e.createVSwitch(new He.CreateVSwitchRequest({regionId:n,vpcId:t,zoneId:s,cidrBlock:c,vSwitchName:`${Zo}-${s}`}));if(!f.body.vSwitchId)throw Error("VSwitch 创建失败:未返回 vSwitchId");return f.body.vSwitchId}catch(f){if($o(f))continue;throw f}throw Error(`VSwitch 创建失败:在 ${s} 未找到可用网段`)}async function du(e,n,t){let o=(await e.describeSecurityGroups(new Bt.DescribeSecurityGroupsRequest({regionId:n,vpcId:t,securityGroupName:"licell-sg"}))).body?.securityGroups?.securityGroup?.[0];if(o?.securityGroupId)return o.securityGroupId;let c=(await e.describeSecurityGroups(new Bt.DescribeSecurityGroupsRequest({regionId:n,vpcId:t}))).body?.securityGroups?.securityGroup?.[0];if(c?.securityGroupId)return c.securityGroupId;let f=await e.createSecurityGroup(new Bt.CreateSecurityGroupRequest({regionId:n,vpcId:t,securityGroupName:"licell-sg",securityGroupType:"normal"}));if(!f.body?.securityGroupId)throw Error("安全组创建失败:未返回 SecurityGroupId");return await ge(2000),f.body.securityGroupId}async function it(e){let n=k.requireAuth(),t=n.region,s=Mh(e?.preferredZoneIds),o=new Yt.Config({accessKeyId:n.ak,accessKeySecret:n.sk,regionId:t,endpoint:`vpc.${t}.aliyuncs.com`}),r=new Yt.Config({accessKeyId:n.ak,accessKeySecret:n.sk,regionId:t,endpoint:`ecs.${t}.aliyuncs.com`}),c=new gu(o),f=new yu(r),u="licell-vpc",l="",i="",g="",y="",d=Mt,h=(await c.describeVpcs(new He.DescribeVpcsRequest({regionId:t,vpcName:"licell-vpc"}))).body.vpcs?.vpc?.[0];if(h?.vpcId)l=h.vpcId,d=h.cidrBlock||Mt;else{let p=await c.createVpc(new He.CreateVpcRequest({regionId:t,vpcName:"licell-vpc",cidrBlock:Mt}));if(!p.body.vpcId)throw Error("VPC 创建失败:未返回 vpcId");l=p.body.vpcId,d=Mt,await ge(5000)}let b=await Yh(c,t,l),$=Kh(b,s);if($?.vSwitchId&&$.zoneId)i=$.vSwitchId,y=$.zoneId;else{if(y=await jh(c,t,s),!y)throw Error(`Region ${t} 未查询到可用区,无法创建 vSwitch`);i=await Wh(c,t,l,y,b),await ge(3000)}if(g=await du(f,t,l),!y)y=(await c.describeVSwitchAttributes(new He.DescribeVSwitchAttributesRequest({regionId:t,vSwitchId:i}))).body?.zoneId||"";if(!y)throw Error(`无法确定 vSwitch 所在可用区 (vswId=${i})`);return{vpcId:l,vswId:i,sgId:g,cidrBlock:d,zoneId:y}}async function lt(e){let n=k.requireAuth(),t=n.region,s=e.vpcId.trim(),o=e.vswId.trim(),r=e.zoneId?.trim();if(!s||!o)throw Error("解析网络信息失败:vpcId 和 vswId 不能为空");let c=new gu(new Yt.Config({accessKeyId:n.ak,accessKeySecret:n.sk,regionId:t,endpoint:`vpc.${t}.aliyuncs.com`})),f=new yu(new Yt.Config({accessKeyId:n.ak,accessKeySecret:n.sk,regionId:t,endpoint:`ecs.${t}.aliyuncs.com`})),l=((await c.describeVpcs(new He.DescribeVpcsRequest({regionId:t,vpcId:s}))).body?.vpcs?.vpc||[]).find((h)=>h.vpcId===s);if(!l?.vpcId)throw Error(`指定 VPC 不存在或不可访问: ${s}`);let i=await c.describeVSwitchAttributes(new He.DescribeVSwitchAttributesRequest({regionId:t,vSwitchId:o})),g=i.body?.vpcId;if(typeof g==="string"&&g.length>0&&g!==s)throw Error(`指定 --vsw (${o}) 不属于 --vpc (${s})`);let y=i.body?.zoneId||"";if(r&&y&&r!==y)throw Error(`指定 --zone (${r}) 与 --vsw 实际可用区 (${y}) 不一致`);let d=y||r||"";if(!d)throw Error(`无法确定 vSwitch 所在可用区 (vswId=${o})`);let m=await du(f,t,s);return{vpcId:s,vswId:o,sgId:m,cidrBlock:l.cidrBlock||Mt,zoneId:d}}var gu,yu,Mt="10.0.0.0/8",Zo="licell-vsw",Gh=50;var Ot=O(()=>{K();De();Ie();gu=te(Uh,"@alicloud/vpc20160428"),yu=te(vh,"@alicloud/ecs20140526")});async function Jo(e,n=lt){if(!e)return;let t=e.vpcId?.trim(),s=e.vswId?.trim();if(!t||!s)return;let o=e.sgId?.trim();if(!o)o=(await n({vpcId:t,vswId:s})).sgId?.trim();let r={vpcId:t,vSwitchIds:[s]};if(o)r.securityGroupId=o;return r}function Qn(e){let n=e.trim().toLowerCase(),t=Yn();if(t.includes(n))return n;throw Error(`函数运行时仅支持: ${t.join(", ")}`)}async function hu(e,n,t){return kn(t).prepareBootFile(e,n)}async function au(e,n,t){return kn(e).resolveConfig(n,t)}function No(e){try{return kn(e).unsupportedMessage}catch{return`当前地域暂不支持 runtime=${e}。请改用 nodejs20 后重试。`}}function zo(e){if(typeof e!=="object"||e===null)return!1;let n="code"in e?String(e.code||""):"",t="message"in e?String(e.message||""):"";return n==="InvalidArgument"&&t.includes("Runtime is set to an invalid value")}function mu(e){if(typeof e!=="object"||e===null)return!1;let n="code"in e?String(e.code||""):"",t="message"in e?String(e.message||""):"";return n==="InvalidArgument"&&t.toLowerCase().includes("change of runtime")}var er=O(()=>{Ot();On();Vo()});async function Y(e,n={}){let t=n.maxAttempts??Qh,s=n.baseDelayMs??Dh,o=n.shouldRetry??Ee,r;for(let c=1;c<=t;c+=1)try{return await e()}catch(f){if(r=f,c===t||!o(f))throw f;await ge(s*c*(0.5+Math.random()*0.5))}throw r}var Qh=3,Dh=1000;var Cn=O(()=>{De()});function ie(e){return Bn(e)}var Dn=O(()=>{Ie()});import*as en from"@alicloud/fc20230330";function Vh(e="anonymous"){return JSON.stringify({authType:e,disableURLInternet:!1,methods:["GET","POST","PUT","PATCH","DELETE","HEAD","OPTIONS"]})}function Xh(e){return(e.triggerType||"").toLowerCase()==="http"}async function wu(e,n){let t=[],s,o=50;for(let r=0;r<50;r+=1){let c=await n.listTriggers(e,new en.ListTriggersRequest({limit:100,nextToken:s})),f=c.body?.triggers||[];if(t.push(...f),s=c.body?.nextToken,!s||f.length===0)break}return t}function $u(e){for(let n of e){if(!Xh(n))continue;let t=n.httpTrigger?.urlInternet;if(typeof t==="string"&&t.trim().length>0)return t}return null}async function Zh(e,n,t="anonymous"){let s=Vh(t);try{await n.createTrigger(e,new en.CreateTriggerRequest({body:new en.CreateTriggerInput({triggerName:pu,triggerType:"http",description:"managed by licell",triggerConfig:s})}));return}catch(o){if(!J(o))throw o}await n.updateTrigger(e,pu,new en.UpdateTriggerRequest({body:new en.UpdateTriggerInput({triggerConfig:s})}))}async function nr(e,n,t={}){let s=n??ie().client,o=await wu(e,s),r=$u(o);if(r)return r;await Zh(e,s,t.authType);let c=await wu(e,s),f=$u(c);if(f)return f;throw Error("函数部署成功,但未获取到公网 HTTP Trigger URL,请检查函数触发器配置")}var pu="licell-http";var tr=O(()=>{Dn()});import*as En from"@alicloud/fc20230330";import Jh from"adm-zip";import{existsSync as Nh,mkdirSync as zh,readFileSync as ea,rmSync as Tu,statSync as na}from"fs";import{tmpdir as ta}from"os";import{isAbsolute as sa,join as oa,relative as ku,resolve as _u}from"path";import{spawnSync as ra}from"child_process";function sr(e){let n=oa(ta(),`licell-code-${Date.now()}-${process.pid}.zip`);try{let t=ra("zip",["-q","-r","-y",n,"."],{cwd:e,encoding:"utf8"});if(t.status===0)return ea(n).toString("base64");let s=t.stderr?.trim(),o=t.stdout?.trim();if(t.error&&t.error.code==="ENOENT"){let r=new Jh;return r.addLocalFolder(e),r.toBuffer().toString("base64")}throw Error(s||o||t.error?.message||"unknown zip error")}finally{Tu(n,{force:!0})}}function la(e,n){if(!Number.isFinite(e)||e<=0)throw Error(`无效的 memorySize: ${String(e)}`);if(!Number.isFinite(n)||n<=0)throw Error(`无效的 vCPU: ${String(n)}`);let t=Math.ceil(n*1024),s=Math.floor(n*4096);if(e<t||e>s)throw Error(`FC 资源规格要求:memory 与 vCPU 需满足 1:1~4:1(memoryGB/vCPU ∈ [1,4])。
481
481
  `+`当前:memory=${e}MB, vCPU=${n}
482
- `+`请调整为:memory ∈ [${t}, ${o}] MB(或调整 vCPU)。`)}function ga(e,n){let t={...e||{},...n||{}},o=t.memorySize??ca,s=t.timeout??fa,r=t.cpu,c=(()=>{if(r!==void 0)return Math.max(1,Math.min(100,Math.round(r*10)));if(o>=2048)return 40;if(o>=1024)return 20;return ua})();return{memorySize:o,timeout:s,...r!==void 0?{cpu:r}:{},instanceConcurrency:t.instanceConcurrency??c}}async function sr(e,n,t=Wn,o={}){let{client:s}=ie(),r=k.getProject(),c="./.licell/dist";Tu("./.licell/dist",{recursive:!0,force:!0}),zh("./.licell/dist",{recursive:!0});let f=t==="docker";if(!f){let T=_u(n),x=ku(process.cwd(),T);if(x.startsWith("..")||oa(x))throw Error(`入口文件必须在项目目录内: ${n}`);if(!Nh(T))throw Error(`入口文件不存在: ${n}`);if(!na(T).isFile())throw Error(`入口文件不是有效文件: ${n}`);To(T,t)}let u=f?n:ku(process.cwd(),_u(n)).replace(/\\/g,"/"),l=await hu(u,"./.licell/dist",t),i=await au(t,"./.licell/dist",l),g={NODE_ENV:"production"};for(let[T,x]of Object.entries(r.envs)){if(!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(T))throw Error(`环境变量键名不合法: "${T}"(仅允许字母、数字、下划线,且不能以数字开头)`);g[T]=x}let y=o.network===void 0?r.network:o.network||void 0,d=await Js(y),m;if(!i.skipCodePackaging)m={zipFile:or("./.licell/dist")};let h=ga(r.resources,o.resources),b=h.memorySize,$=h.timeout,p=h.cpu??ia;la(b,p);let w={functionName:e,runtime:i.runtime,handler:i.handler,memorySize:b,diskSize:bu,timeout:$,environmentVariables:g,vpcConfig:d};if(w.cpu=p,h.instanceConcurrency!==void 0)w.instanceConcurrency=h.instanceConcurrency;if(m)w.code=m;if(i.customRuntimeConfig)w.customRuntimeConfig=i.customRuntimeConfig;if(i.customContainerConfig)w.customContainerConfig=i.customContainerConfig;let H=new En.CreateFunctionRequest({body:new En.CreateFunctionInput(w)});try{await Y(()=>s.createFunction(H))}catch(T){if(J(T)){let x={runtime:i.runtime,handler:i.handler,memorySize:b,diskSize:bu,timeout:$,environmentVariables:g,vpcConfig:d};if(x.cpu=p,h.instanceConcurrency!==void 0)x.instanceConcurrency=h.instanceConcurrency;if(m)x.code=m;if(i.customRuntimeConfig)x.customRuntimeConfig=i.customRuntimeConfig;if(i.customContainerConfig)x.customContainerConfig=i.customContainerConfig;try{await Y(()=>s.updateFunction(e,new En.UpdateFunctionRequest({body:new En.UpdateFunctionInput(x)})))}catch(E){if(zs(E))throw Error(Ns(t));if(mu(E))throw Error(`当前函数运行时无法原地切换到 ${t}。请更换 appName 重新部署,或先手动删除原函数后再重试。`);throw E}}else{if(zs(T))throw Error(Ns(t));throw T}}return nr(e,s)}var ca=512,fa=30,bu=512,ua=10,ia=0.5;var rr=O(()=>{K();ce();Cn();Dn();tr();Kn();er();Xs()});import{existsSync as gt,readFileSync as ya,statSync as da}from"fs";import{isAbsolute as ha,relative as aa,resolve as cr,join as Io}from"path";function pa(e){let n=(()=>{try{return kn(e).defaultEntry||""}catch{return""}})();if(n)return n;return ma[e]||""}function Cu(e){return e.replace(/\\/g,"/")}function xu(e,n){let t=aa(e,n);return t===""||!t.startsWith("..")&&!ha(t)}function wa(e){let n=Io(e,"package.json");if(!gt(n))return{};try{return JSON.parse(ya(n,"utf-8")).scripts||{}}catch{return{}}}function $a(e,n,t,o){let s=cr(e,t);if(!xu(e,s)){o.push({id:"entry.outside_project",level:"error",message:`入口文件必须在项目目录内: ${t}`,remediation:["将 --entry 指向当前项目目录内的文件,例如 src/index.ts 或 src/main.py"]});return}if(!gt(s)){o.push({id:"entry.not_found",level:"error",message:`入口文件不存在: ${t}`,remediation:["确认路径正确,或创建对应入口文件后重试"]});return}if(!da(s).isFile()){o.push({id:"entry.not_file",level:"error",message:`入口路径不是有效文件: ${t}`,remediation:["请将 --entry 指向具体文件"]});return}try{To(s,n)}catch(r){o.push({id:"entry.runtime_contract",level:"error",message:r instanceof Error?r.message:String(r),remediation:["参考 `licell deploy spec <runtime>` 查看该 runtime 的入口与 handler 规则","修复入口文件后再执行 `licell deploy`"]})}if(n.startsWith("python")){if(!["requirements.txt","pyproject.toml","poetry.lock","pipfile","pipfile.lock"].some((c)=>gt(Io(e,c))))o.push({id:"python.dependencies_manifest_missing",level:"warning",message:"未检测到 Python 依赖清单(requirements.txt/pyproject.toml),部署时将无法自动安装第三方依赖。",remediation:["建议添加 requirements.txt 或 pyproject.toml 并声明依赖"]})}}function ka(e,n,t,o){let s=Io(e,"Dockerfile"),r=gt(s),c=r?null:Ut(e);if(!r&&!c)o.push({id:"docker.project_type_undetected",level:"error",message:"未找到 Dockerfile,且无法自动探测项目类型。",remediation:["在项目根目录创建 Dockerfile,或补充 package.json / requirements.txt","然后重新执行部署"]});if(!r&&c?.name==="nodejs"){let f=gt(Io(e,"tsconfig.json")),u=wa(e),l=typeof u.build==="string"&&u.build.trim().length>0;if(f&&!n&&!l)o.push({id:"docker.node_ts_no_build_script",level:"error",message:"检测到 TypeScript 项目,但 package.json 缺少 build 脚本,无法自动推断容器入口。",remediation:["补充 package.json scripts.build(例如 tsc)","或使用 --entry 显式指定容器运行入口(例如 dist/index.js)","或手动维护 Dockerfile"]})}if(n){let f=cr(e,n);if(!xu(e,f))o.push({id:"docker.entry.outside_project",level:"error",message:`Docker 入口必须在项目目录内: ${n}`,remediation:["将 --entry 指向当前项目目录内的路径"]});else if(!gt(f))o.push({id:"docker.entry.not_found",level:"error",message:`Docker 入口不存在: ${n}`,remediation:["确认 --entry 对应文件存在,或移除 --entry 使用自动推断"]})}if(t)try{Ro()}catch(f){o.push({id:"docker.daemon_unavailable",level:"error",message:f instanceof Error?f.message:String(f),remediation:["启动本机 Docker 环境(Docker Desktop / Docker Engine)后重试"]})}}function Fo(){let e=new Set(Yn());return _a.filter((n)=>e.has(n.runtime)).map((n)=>({...n,handlerContract:{...n.handlerContract,entryFileExtensions:[...n.handlerContract.entryFileExtensions],...n.handlerContract.requiredExports?{requiredExports:[...n.handlerContract.requiredExports]}:{},...n.handlerContract.oneOfExports?{oneOfExports:n.handlerContract.oneOfExports.map((t)=>[...t])}:{},...n.handlerContract.containerPortEnv?{containerPortEnv:[...n.handlerContract.containerPortEnv]}:{}},eventSchema:{requiredFields:[...n.eventSchema.requiredFields],optionalFields:[...n.eventSchema.optionalFields],notes:[...n.eventSchema.notes]},responseSchema:{acceptedForms:[...n.responseSchema.acceptedForms],notes:[...n.responseSchema.notes]},examples:{minimalPassExample:n.examples.minimalPassExample,commonFailExample:n.examples.commonFailExample,fixHint:[...n.examples.fixHint]},validationRules:n.validationRules.map((t)=>({...t})),notes:[...n.notes]}))}function Kt(e){let n=e.trim().toLowerCase(),t=Fo().find((o)=>o.runtime===n);if(!t){let o=Fo().map((s)=>s.runtime).join(", ");throw Error(`不支持的 runtime: ${e}(支持: ${o})`)}return t}function jt(){return{runtimes:Fo(),resources:{defaults:{...Eu.defaults},constraints:{memoryToVcpuRatio:{...Eu.constraints.memoryToVcpuRatio}}}}}function Wt(e){let n=e.runtime.trim().toLowerCase(),t=cr(e.projectRoot||process.cwd()),o=[],s=new Set(Yn());if(!s.has(n))return o.push({id:"runtime.unsupported",level:"error",message:`不支持的 runtime: ${n}`,remediation:[`请改用支持的 runtime: ${[...s].join(", ")}`]}),{ok:!1,runtime:n,entry:e.entry?Cu(e.entry.trim()):"",projectRoot:t,issues:o};let r=e.entry?.trim()||pa(n),c=Cu(r);if(!c&&n!=="docker")o.push({id:"entry.required",level:"error",message:`runtime=${n} 需要入口文件,请通过 --entry 指定。`,remediation:["例如: --entry src/index.ts 或 --entry src/main.py"]});if(n==="docker")ka(t,c,Boolean(e.checkDockerDaemon),o);else if(c)$a(t,n,c,o);return{ok:!o.some((f)=>f.level==="error"),runtime:n,entry:c,projectRoot:t,issues:o}}function fr(e){let n={runtime:e.runtime,entry:e.entry,projectRoot:e.projectRoot,issues:e.issues.map((o)=>({id:o.id,level:o.level,message:o.message,...o.remediation?{remediation:[...o.remediation]}:{}}))},t=Error("部署前预检失败(入口/运行时不满足 FC 要求)");return t.code="DEPLOY_PRECHECK_FAILED",t.details=n,t}var Su=512,qu=0.5,Pu=30,Ru=10,Iu=1,Fu=4,ma,_a,Eu;var Hu=O(()=>{On();Kn();Us();Ls();ma={nodejs20:"src/index.ts",nodejs22:"src/index.ts","python3.12":"src/main.py","python3.13":"src/main.py"};_a=[{runtime:"nodejs20",mode:"vendor",defaultEntry:"src/index.ts",entryRule:"入口文件需位于项目目录内,通常为 .ts/.js 文件。",handlerRule:"必须导出 handler(event, context) 函数。",handlerContract:{kind:"function",entryFileExtensions:[".ts",".js",".mjs",".cjs"],requiredExports:["handler"],signature:"handler(event, context)",asyncAllowed:!0},eventSchema:{requiredFields:["path","rawPath","rawQueryString","httpMethod","headers","queryParameters","body","isBase64Encoded","requestContext.http.method","requestContext.http.path","requestContext.http.sourceIp"],optionalFields:[],notes:["event 为 HTTP 请求归一化对象,body 默认是 UTF-8 字符串。"]},responseSchema:{acceptedForms:["{ statusCode, headers?, body? }","string","Buffer/Uint8Array","plain object (自动 JSON 序列化)","undefined/null (204)"],notes:["建议显式返回 statusCode 与 headers,便于跨 runtime 一致。"]},examples:{minimalPassExample:"export async function handler(event, context) { return { statusCode: 200, body: 'ok' }; }",commonFailExample:"export default async function app(event) { return { statusCode: 200, body: 'ok' }; }",fixHint:["nodejs20 仅接受 handler 导出;把 default 导出改为 named export handler。"]},validationRules:[{id:"entry.outside_project",level:"error",summary:"入口文件必须在项目目录内。"},{id:"entry.not_found",level:"error",summary:"入口文件不存在。"},{id:"entry.not_file",level:"error",summary:"入口路径必须是文件。"},{id:"entry.runtime_contract",level:"error",summary:"必须导出 handler(event, context)。"}],notes:["部署前会校验 handler 导出。","代码会由 bun build 打包后上传。"]},{runtime:"nodejs22",mode:"custom-runtime",defaultEntry:"src/index.ts",entryRule:"入口文件需位于项目目录内,通常为 .ts/.js 文件。",handlerRule:"需导出 handler 或 default 函数。",handlerContract:{kind:"function",entryFileExtensions:[".ts",".js",".mjs",".cjs"],oneOfExports:[["handler"],["default"]],signature:"handler(event, context) 或 default(event, context)",asyncAllowed:!0},eventSchema:{requiredFields:["path","rawPath","rawQueryString","httpMethod","headers","queryParameters","body","isBase64Encoded","requestContext.http.method","requestContext.http.path","requestContext.http.sourceIp"],optionalFields:[],notes:["通过内置 node22 bootstrap 适配为 HTTP handler。"]},responseSchema:{acceptedForms:["{ statusCode, headers?, body? }","string","Buffer/Uint8Array","plain object (自动 JSON 序列化)","undefined/null (204)"],notes:["如果返回 plain object,默认 content-type=application/json。"]},examples:{minimalPassExample:"export default async function app(event, context) { return { statusCode: 200, body: 'ok' }; }",commonFailExample:"export const main = async () => ({ statusCode: 200, body: 'ok' });",fixHint:["nodejs22 需要导出 handler 或 default 函数。"]},validationRules:[{id:"entry.outside_project",level:"error",summary:"入口文件必须在项目目录内。"},{id:"entry.not_found",level:"error",summary:"入口文件不存在。"},{id:"entry.not_file",level:"error",summary:"入口路径必须是文件。"},{id:"entry.runtime_contract",level:"error",summary:"必须导出 handler 或 default 函数。"}],notes:["部署前会校验导出函数。","运行在 custom.debian12 + 内置 node22。"]},{runtime:"python3.12",mode:"vendor",defaultEntry:"src/main.py",entryRule:"入口文件必须是 .py,且位于项目目录内。",handlerRule:"必须定义可调用函数 handler(event, context)。",handlerContract:{kind:"function",entryFileExtensions:[".py"],requiredExports:["handler"],signature:"handler(event, context)",asyncAllowed:!0},eventSchema:{requiredFields:["path","rawPath","rawQueryString","httpMethod","headers","queryParameters","body","isBase64Encoded","requestContext.http.method","requestContext.http.path","requestContext.http.sourceIp"],optionalFields:[],notes:["body 是字符串;需要自行 JSON 反序列化。"]},responseSchema:{acceptedForms:["{ statusCode, headers?, body? }","string","bytes/bytearray","dict/list (自动 JSON 序列化)","None (204)"],notes:["推荐统一返回 statusCode/body 结构。"]},examples:{minimalPassExample:`def handler(event, context):
482
+ `+`请调整为:memory ∈ [${t}, ${s}] MB(或调整 vCPU)。`)}function ga(e,n){let t={...e||{},...n||{}},s=t.memorySize??ca,o=t.timeout??fa,r=t.cpu,c=(()=>{if(r!==void 0)return Math.max(1,Math.min(100,Math.round(r*10)));if(s>=2048)return 40;if(s>=1024)return 20;return ua})();return{memorySize:s,timeout:o,...r!==void 0?{cpu:r}:{},instanceConcurrency:t.instanceConcurrency??c}}async function or(e,n,t=Wn,s={}){let{client:o}=ie(),r=k.getProject(),c="./.licell/dist";Tu("./.licell/dist",{recursive:!0,force:!0}),zh("./.licell/dist",{recursive:!0});let f=t==="docker";if(!f){let T=_u(n),x=ku(process.cwd(),T);if(x.startsWith("..")||sa(x))throw Error(`入口文件必须在项目目录内: ${n}`);if(!Nh(T))throw Error(`入口文件不存在: ${n}`);if(!na(T).isFile())throw Error(`入口文件不是有效文件: ${n}`);bs(T,t)}let u=f?n:ku(process.cwd(),_u(n)).replace(/\\/g,"/"),l=await hu(u,"./.licell/dist",t),i=await au(t,"./.licell/dist",l),g={NODE_ENV:"production"};for(let[T,x]of Object.entries(r.envs)){if(!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(T))throw Error(`环境变量键名不合法: "${T}"(仅允许字母、数字、下划线,且不能以数字开头)`);g[T]=x}let y=s.network===void 0?r.network:s.network||void 0,d=await Jo(y),m;if(!i.skipCodePackaging)m={zipFile:sr("./.licell/dist")};let h=ga(r.resources,s.resources),b=h.memorySize,$=h.timeout,p=h.cpu??ia;la(b,p);let w={functionName:e,runtime:i.runtime,handler:i.handler,memorySize:b,diskSize:bu,timeout:$,environmentVariables:g,vpcConfig:d};if(w.cpu=p,h.instanceConcurrency!==void 0)w.instanceConcurrency=h.instanceConcurrency;if(m)w.code=m;if(i.customRuntimeConfig)w.customRuntimeConfig=i.customRuntimeConfig;if(i.customContainerConfig)w.customContainerConfig=i.customContainerConfig;let H=new En.CreateFunctionRequest({body:new En.CreateFunctionInput(w)});try{await Y(()=>o.createFunction(H))}catch(T){if(J(T)){let x={runtime:i.runtime,handler:i.handler,memorySize:b,diskSize:bu,timeout:$,environmentVariables:g,vpcConfig:d};if(x.cpu=p,h.instanceConcurrency!==void 0)x.instanceConcurrency=h.instanceConcurrency;if(m)x.code=m;if(i.customRuntimeConfig)x.customRuntimeConfig=i.customRuntimeConfig;if(i.customContainerConfig)x.customContainerConfig=i.customContainerConfig;try{await Y(()=>o.updateFunction(e,new En.UpdateFunctionRequest({body:new En.UpdateFunctionInput(x)})))}catch(E){if(zo(E))throw Error(No(t));if(mu(E))throw Error(`当前函数运行时无法原地切换到 ${t}。请更换 appName 重新部署,或先手动删除原函数后再重试。`);throw E}}else{if(zo(T))throw Error(No(t));throw T}}return nr(e,o)}var ca=512,fa=30,bu=512,ua=10,ia=0.5;var rr=O(()=>{K();ce();Cn();Dn();tr();Kn();er();Xo()});import{existsSync as gt,readFileSync as ya,statSync as da}from"fs";import{isAbsolute as ha,relative as aa,resolve as cr,join as Rs}from"path";function pa(e){let n=(()=>{try{return kn(e).defaultEntry||""}catch{return""}})();if(n)return n;return ma[e]||""}function Cu(e){return e.replace(/\\/g,"/")}function xu(e,n){let t=aa(e,n);return t===""||!t.startsWith("..")&&!ha(t)}function wa(e){let n=Rs(e,"package.json");if(!gt(n))return{};try{return JSON.parse(ya(n,"utf-8")).scripts||{}}catch{return{}}}function $a(e,n,t,s){let o=cr(e,t);if(!xu(e,o)){s.push({id:"entry.outside_project",level:"error",message:`入口文件必须在项目目录内: ${t}`,remediation:["将 --entry 指向当前项目目录内的文件,例如 src/index.ts 或 src/main.py"]});return}if(!gt(o)){s.push({id:"entry.not_found",level:"error",message:`入口文件不存在: ${t}`,remediation:["确认路径正确,或创建对应入口文件后重试"]});return}if(!da(o).isFile()){s.push({id:"entry.not_file",level:"error",message:`入口路径不是有效文件: ${t}`,remediation:["请将 --entry 指向具体文件"]});return}try{bs(o,n)}catch(r){s.push({id:"entry.runtime_contract",level:"error",message:r instanceof Error?r.message:String(r),remediation:["参考 `licell deploy spec <runtime>` 查看该 runtime 的入口与 handler 规则","修复入口文件后再执行 `licell deploy`"]})}if(n.startsWith("python")){if(!["requirements.txt","pyproject.toml","poetry.lock","pipfile","pipfile.lock"].some((c)=>gt(Rs(e,c))))s.push({id:"python.dependencies_manifest_missing",level:"warning",message:"未检测到 Python 依赖清单(requirements.txt/pyproject.toml),部署时将无法自动安装第三方依赖。",remediation:["建议添加 requirements.txt 或 pyproject.toml 并声明依赖"]})}}function ka(e,n,t,s){let o=Rs(e,"Dockerfile"),r=gt(o),c=r?null:Ut(e);if(!r&&!c)s.push({id:"docker.project_type_undetected",level:"error",message:"未找到 Dockerfile,且无法自动探测项目类型。",remediation:["在项目根目录创建 Dockerfile,或补充 package.json / requirements.txt","然后重新执行部署"]});if(!r&&c?.name==="nodejs"){let f=gt(Rs(e,"tsconfig.json")),u=wa(e),l=typeof u.build==="string"&&u.build.trim().length>0;if(f&&!n&&!l)s.push({id:"docker.node_ts_no_build_script",level:"error",message:"检测到 TypeScript 项目,但 package.json 缺少 build 脚本,无法自动推断容器入口。",remediation:["补充 package.json scripts.build(例如 tsc)","或使用 --entry 显式指定容器运行入口(例如 dist/index.js)","或手动维护 Dockerfile"]})}if(n){let f=cr(e,n);if(!xu(e,f))s.push({id:"docker.entry.outside_project",level:"error",message:`Docker 入口必须在项目目录内: ${n}`,remediation:["将 --entry 指向当前项目目录内的路径"]});else if(!gt(f))s.push({id:"docker.entry.not_found",level:"error",message:`Docker 入口不存在: ${n}`,remediation:["确认 --entry 对应文件存在,或移除 --entry 使用自动推断"]})}if(t)try{Ps()}catch(f){s.push({id:"docker.daemon_unavailable",level:"error",message:f instanceof Error?f.message:String(f),remediation:["启动本机 Docker 环境(Docker Desktop / Docker Engine)后重试"]})}}function Is(){let e=new Set(Yn());return _a.filter((n)=>e.has(n.runtime)).map((n)=>({...n,handlerContract:{...n.handlerContract,entryFileExtensions:[...n.handlerContract.entryFileExtensions],...n.handlerContract.requiredExports?{requiredExports:[...n.handlerContract.requiredExports]}:{},...n.handlerContract.oneOfExports?{oneOfExports:n.handlerContract.oneOfExports.map((t)=>[...t])}:{},...n.handlerContract.containerPortEnv?{containerPortEnv:[...n.handlerContract.containerPortEnv]}:{}},eventSchema:{requiredFields:[...n.eventSchema.requiredFields],optionalFields:[...n.eventSchema.optionalFields],notes:[...n.eventSchema.notes]},responseSchema:{acceptedForms:[...n.responseSchema.acceptedForms],notes:[...n.responseSchema.notes]},examples:{minimalPassExample:n.examples.minimalPassExample,commonFailExample:n.examples.commonFailExample,fixHint:[...n.examples.fixHint]},validationRules:n.validationRules.map((t)=>({...t})),notes:[...n.notes]}))}function Kt(e){let n=e.trim().toLowerCase(),t=Is().find((s)=>s.runtime===n);if(!t){let s=Is().map((o)=>o.runtime).join(", ");throw Error(`不支持的 runtime: ${e}(支持: ${s})`)}return t}function jt(){return{runtimes:Is(),resources:{defaults:{...Eu.defaults},constraints:{memoryToVcpuRatio:{...Eu.constraints.memoryToVcpuRatio}}}}}function Wt(e){let n=e.runtime.trim().toLowerCase(),t=cr(e.projectRoot||process.cwd()),s=[],o=new Set(Yn());if(!o.has(n))return s.push({id:"runtime.unsupported",level:"error",message:`不支持的 runtime: ${n}`,remediation:[`请改用支持的 runtime: ${[...o].join(", ")}`]}),{ok:!1,runtime:n,entry:e.entry?Cu(e.entry.trim()):"",projectRoot:t,issues:s};let r=e.entry?.trim()||pa(n),c=Cu(r);if(!c&&n!=="docker")s.push({id:"entry.required",level:"error",message:`runtime=${n} 需要入口文件,请通过 --entry 指定。`,remediation:["例如: --entry src/index.ts 或 --entry src/main.py"]});if(n==="docker")ka(t,c,Boolean(e.checkDockerDaemon),s);else if(c)$a(t,n,c,s);return{ok:!s.some((f)=>f.level==="error"),runtime:n,entry:c,projectRoot:t,issues:s}}function fr(e){let n={runtime:e.runtime,entry:e.entry,projectRoot:e.projectRoot,issues:e.issues.map((s)=>({id:s.id,level:s.level,message:s.message,...s.remediation?{remediation:[...s.remediation]}:{}}))},t=Error("部署前预检失败(入口/运行时不满足 FC 要求)");return t.code="DEPLOY_PRECHECK_FAILED",t.details=n,t}var Su=512,qu=0.5,Pu=30,Ru=10,Iu=1,Fu=4,ma,_a,Eu;var Hu=O(()=>{On();Kn();Uo();Ao();ma={nodejs20:"src/index.ts",nodejs22:"src/index.ts","python3.12":"src/main.py","python3.13":"src/main.py"};_a=[{runtime:"nodejs20",mode:"vendor",defaultEntry:"src/index.ts",entryRule:"入口文件需位于项目目录内,通常为 .ts/.js 文件。",handlerRule:"必须导出 handler(event, context) 函数。",handlerContract:{kind:"function",entryFileExtensions:[".ts",".js",".mjs",".cjs"],requiredExports:["handler"],signature:"handler(event, context)",asyncAllowed:!0},eventSchema:{requiredFields:["path","rawPath","rawQueryString","httpMethod","headers","queryParameters","body","isBase64Encoded","requestContext.http.method","requestContext.http.path","requestContext.http.sourceIp"],optionalFields:[],notes:["event 为 HTTP 请求归一化对象,body 默认是 UTF-8 字符串。"]},responseSchema:{acceptedForms:["{ statusCode, headers?, body? }","string","Buffer/Uint8Array","plain object (自动 JSON 序列化)","undefined/null (204)"],notes:["建议显式返回 statusCode 与 headers,便于跨 runtime 一致。"]},examples:{minimalPassExample:"export async function handler(event, context) { return { statusCode: 200, body: 'ok' }; }",commonFailExample:"export default async function app(event) { return { statusCode: 200, body: 'ok' }; }",fixHint:["nodejs20 仅接受 handler 导出;把 default 导出改为 named export handler。"]},validationRules:[{id:"entry.outside_project",level:"error",summary:"入口文件必须在项目目录内。"},{id:"entry.not_found",level:"error",summary:"入口文件不存在。"},{id:"entry.not_file",level:"error",summary:"入口路径必须是文件。"},{id:"entry.runtime_contract",level:"error",summary:"必须导出 handler(event, context)。"}],notes:["部署前会校验 handler 导出。","代码会由 bun build 打包后上传。"]},{runtime:"nodejs22",mode:"custom-runtime",defaultEntry:"src/index.ts",entryRule:"入口文件需位于项目目录内,通常为 .ts/.js 文件。",handlerRule:"需导出 handler 或 default 函数。",handlerContract:{kind:"function",entryFileExtensions:[".ts",".js",".mjs",".cjs"],oneOfExports:[["handler"],["default"]],signature:"handler(event, context) 或 default(event, context)",asyncAllowed:!0},eventSchema:{requiredFields:["path","rawPath","rawQueryString","httpMethod","headers","queryParameters","body","isBase64Encoded","requestContext.http.method","requestContext.http.path","requestContext.http.sourceIp"],optionalFields:[],notes:["通过内置 node22 bootstrap 适配为 HTTP handler。"]},responseSchema:{acceptedForms:["{ statusCode, headers?, body? }","string","Buffer/Uint8Array","plain object (自动 JSON 序列化)","undefined/null (204)"],notes:["如果返回 plain object,默认 content-type=application/json。"]},examples:{minimalPassExample:"export default async function app(event, context) { return { statusCode: 200, body: 'ok' }; }",commonFailExample:"export const main = async () => ({ statusCode: 200, body: 'ok' });",fixHint:["nodejs22 需要导出 handler 或 default 函数。"]},validationRules:[{id:"entry.outside_project",level:"error",summary:"入口文件必须在项目目录内。"},{id:"entry.not_found",level:"error",summary:"入口文件不存在。"},{id:"entry.not_file",level:"error",summary:"入口路径必须是文件。"},{id:"entry.runtime_contract",level:"error",summary:"必须导出 handler 或 default 函数。"}],notes:["部署前会校验导出函数。","运行在 custom.debian12 + 内置 node22。"]},{runtime:"python3.12",mode:"vendor",defaultEntry:"src/main.py",entryRule:"入口文件必须是 .py,且位于项目目录内。",handlerRule:"必须定义可调用函数 handler(event, context)。",handlerContract:{kind:"function",entryFileExtensions:[".py"],requiredExports:["handler"],signature:"handler(event, context)",asyncAllowed:!0},eventSchema:{requiredFields:["path","rawPath","rawQueryString","httpMethod","headers","queryParameters","body","isBase64Encoded","requestContext.http.method","requestContext.http.path","requestContext.http.sourceIp"],optionalFields:[],notes:["body 是字符串;需要自行 JSON 反序列化。"]},responseSchema:{acceptedForms:["{ statusCode, headers?, body? }","string","bytes/bytearray","dict/list (自动 JSON 序列化)","None (204)"],notes:["推荐统一返回 statusCode/body 结构。"]},examples:{minimalPassExample:`def handler(event, context):
483
483
  return {'statusCode': 200, 'body': 'ok'}`,commonFailExample:`def main(event, context):
484
484
  return {'statusCode': 200, 'body': 'ok'}`,fixHint:["python runtime 必须存在可调用 handler(event, context) 函数。"]},validationRules:[{id:"entry.outside_project",level:"error",summary:"入口文件必须在项目目录内。"},{id:"entry.not_found",level:"error",summary:"入口文件不存在。"},{id:"entry.not_file",level:"error",summary:"入口路径必须是文件。"},{id:"entry.runtime_contract",level:"error",summary:"入口必须包含 handler(event, context)。"},{id:"python.dependencies_manifest_missing",level:"warning",summary:"缺少 requirements/pyproject,第三方依赖可能不会被安装。"}],notes:["部署前会校验 handler 函数签名。","会自动打包 Python 源码与依赖。"]},{runtime:"python3.13",mode:"custom-runtime",defaultEntry:"src/main.py",entryRule:"入口文件必须是 .py,且位于项目目录内。",handlerRule:"必须定义可调用函数 handler(event, context)。",handlerContract:{kind:"function",entryFileExtensions:[".py"],requiredExports:["handler"],signature:"handler(event, context)",asyncAllowed:!0},eventSchema:{requiredFields:["path","rawPath","rawQueryString","httpMethod","headers","queryParameters","body","isBase64Encoded","requestContext.http.method","requestContext.http.path","requestContext.http.sourceIp"],optionalFields:[],notes:["通过内置 python3.13 bootstrap 适配 HTTP 请求事件。"]},responseSchema:{acceptedForms:["{ statusCode, headers?, body? }","string","bytes/bytearray","dict/list (自动 JSON 序列化)","None (204)"],notes:["返回 dict/list 时会自动 JSON 编码并设置 content-type。"]},examples:{minimalPassExample:`def handler(event, context):
485
485
  return {'statusCode': 200, 'body': 'ok'}`,commonFailExample:`def app(event, context):
486
- return {'statusCode': 200, 'body': 'ok'}`,fixHint:["python3.13 也要求 handler(event, context);函数名不匹配会在预检阶段失败。"]},validationRules:[{id:"entry.outside_project",level:"error",summary:"入口文件必须在项目目录内。"},{id:"entry.not_found",level:"error",summary:"入口文件不存在。"},{id:"entry.not_file",level:"error",summary:"入口路径必须是文件。"},{id:"entry.runtime_contract",level:"error",summary:"入口必须包含 handler(event, context)。"},{id:"python.dependencies_manifest_missing",level:"warning",summary:"缺少 requirements/pyproject,第三方依赖可能不会被安装。"}],notes:["部署前会校验 handler 函数签名。","运行在 custom.debian12 + 内置 python3.13。"]},{runtime:"docker",mode:"custom-container",defaultEntry:null,entryRule:"优先使用 Dockerfile;若缺失则尝试自动生成(Node/Python 项目)。",handlerRule:"容器内需监听 FC_SERVER_PORT/PORT(默认 9000),并响应 HTTP 请求。",handlerContract:{kind:"container-http",entryFileExtensions:[],containerPortEnv:["FC_SERVER_PORT","PORT"],signature:"HTTP server listening on 0.0.0.0:${FC_SERVER_PORT|PORT|9000}"},eventSchema:{requiredFields:["HTTP request at container port 9000"],optionalFields:[],notes:["如果使用 licell 自动生成 Dockerfile,会附带 node/python bootstrap 事件模型。"]},responseSchema:{acceptedForms:["standard HTTP response from your container app"],notes:["容器模式下由应用自行处理 HTTP 协议,不要求固定 handler 函数导出。"]},examples:{minimalPassExample:"const port = Number(process.env.FC_SERVER_PORT || process.env.PORT || 9000); app.listen(port, '0.0.0.0');",commonFailExample:"app.listen(3000, '127.0.0.1')",fixHint:["必须监听 FC_SERVER_PORT 或 PORT(默认 9000)。","绑定地址需为 0.0.0.0,不能只监听 127.0.0.1。"]},validationRules:[{id:"docker.project_type_undetected",level:"error",summary:"无 Dockerfile 且无法识别项目类型。"},{id:"docker.node_ts_no_build_script",level:"error",summary:"TS 项目缺少 build 脚本且未指定 --entry。"},{id:"docker.entry.outside_project",level:"error",summary:"Docker 入口必须在项目目录内。"},{id:"docker.entry.not_found",level:"error",summary:"Docker 入口不存在。"},{id:"docker.daemon_unavailable",level:"error",summary:"本机 Docker daemon 不可用。"}],notes:["部署依赖本机 Docker daemon。","镜像将推送到 ACR,再由 FC custom-container 拉取。"]}],Eu={defaults:{memoryMb:Su,vcpu:qu,timeoutSeconds:Pu,instanceConcurrency:Ru},constraints:{memoryToVcpuRatio:{min:Iu,max:Fu,expression:"memoryGb / vcpu in [1, 4]"}}}});import*as be from"@alicloud/fc20230330";import{Readable as ba}from"stream";async function yt(e,n){let{client:t}=ie();return(await t.getFunction(e,new be.GetFunctionRequest({qualifier:n}))).body?.environmentVariables||{}}function Ta(e){let n=e.functionName;if(!n)return null;return{functionName:n,runtime:e.runtime,state:e.state,lastModifiedTime:e.lastModifiedTime,description:e.description}}async function ur(e=100,n){let{client:t}=ie(),o=[],s=Math.max(1,Math.min(Math.floor(e),500)),r;while(o.length<s){let c=await t.listFunctions(new be.ListFunctionsRequest({limit:Math.min(100,s-o.length),nextToken:r,prefix:n,fcVersion:"v3"})),f=c.body?.functions||[];for(let u of f){let l=Ta(u);if(!l)continue;if(o.push(l),o.length>=s)break}if(r=c.body?.nextToken,!r||f.length===0)break}return o}async function ir(e,n){let t=e.trim();if(!t)throw Error("functionName 不能为空");let{client:o}=ie(),r=(await o.getFunction(t,new be.GetFunctionRequest({qualifier:n}))).body;if(!r?.functionName)throw Error(`未找到函数: ${t}`);return r}async function Ca(e,n){let t=[],o,s=50;for(let r=0;r<50;r+=1){let c=await n.listTriggers(e,new be.ListTriggersRequest({limit:100,nextToken:o})),f=c.body?.triggers||[];if(t.push(...f),o=c.body?.nextToken,!o||f.length===0)break}return t}async function Ea(e,n){let t=[],o,s=50;for(let r=0;r<50;r+=1){let c=await n.listAliases(e,new be.ListAliasesRequest({limit:100,nextToken:o})),f=c.body?.aliases||[];if(t.push(...f),o=c.body?.nextToken,!o||f.length===0)break}return t}async function Sa(e,n){let t=[],o,s=50;for(let r=0;r<50;r+=1){let c=await n.listFunctionVersions(e,new be.ListFunctionVersionsRequest({direction:"BACKWARD",limit:100,nextToken:o})),f=c.body?.versions||[];if(t.push(...f),o=c.body?.nextToken,!o||f.length===0)break}return t}async function lr(e,n={}){let t=e.trim();if(!t)throw Error("functionName 不能为空");let{client:o}=ie();if(!n.force)return await o.deleteFunction(t),{forced:!1,deletedTriggers:[],deletedAliases:[],deletedVersions:[]};let s=[],r=[],c=[],f=await Ca(t,o);for(let i of f){let g=i.triggerName;if(!g)continue;try{await o.deleteTrigger(t,g),s.push(g)}catch(y){if(!ue(y))throw y}}let u=await Ea(t,o);for(let i of u){let g=i.aliasName;if(!g)continue;try{await o.deleteAlias(t,g),r.push(g)}catch(y){if(!ue(y))throw y}}let l=await Sa(t,o);for(let i of l){let g=i.versionId||"";if(!/^\d+$/.test(g))continue;try{await o.deleteFunctionVersion(t,g),c.push(g)}catch(y){if(!ue(y))throw y}}return await o.deleteFunction(t),{forced:!0,deletedTriggers:s,deletedAliases:r,deletedVersions:c}}async function qa(e){if(!e)return"";let n=[];for await(let t of e)if(Buffer.isBuffer(t))n.push(t);else n.push(Buffer.from(String(t)));return Buffer.concat(n).toString("utf8")}async function gr(e,n={}){let t=e.trim();if(!t)throw Error("functionName 不能为空");let{client:o}=ie(),s=typeof n.payload==="string"?ba.from([Buffer.from(n.payload)]):void 0,r=await o.invokeFunction(t,new be.InvokeFunctionRequest({qualifier:n.qualifier,body:s})),c=await qa(r.body);return{statusCode:r.statusCode||0,headers:r.headers||{},body:c}}async function Au(e,n){let{client:t}=ie();await t.updateFunction(e,new be.UpdateFunctionRequest({body:new be.UpdateFunctionInput({environmentVariables:n})}))}async function yr(e,n,t){let s={...await yt(e),[n]:t};return await Au(e,s),s}async function dr(e,n){let t=await yt(e);if(!Object.prototype.hasOwnProperty.call(t,n))return t;let{[n]:o,...s}=t;return await Au(e,s),s}var Lu=O(()=>{Dn()});import*as Se from"@alicloud/fc20230330";function xo(e){let n=Number(e);return Number.isFinite(n)?n:-1}async function dt(e,n=20,t){let o=t??ie().client,s=[],r,c=Math.max(n,1);while(c>0){let f=Math.min(c,100),u=await o.listFunctionVersions(e,new Se.ListFunctionVersionsRequest({direction:"BACKWARD",limit:f,nextToken:r})),l=u.body?.versions||[];if(s.push(...l),c-=l.length,r=u.body?.nextToken,!r||l.length===0)break}return s.sort((f,u)=>xo(u.versionId||"")-xo(f.versionId||"")),s}async function Sn(e,n){let{client:t}=ie(),s=(await t.publishFunctionVersion(e,new Se.PublishFunctionVersionRequest({body:new Se.PublishVersionInput({description:n})}))).body?.versionId;if(!s)throw Error("发布函数版本失败:未返回 versionId");return s}async function qn(e,n,t,o){let{client:s}=ie(),r=new Se.CreateAliasInput({aliasName:n,versionId:t,description:o});try{await s.createAlias(e,new Se.CreateAliasRequest({body:r}))}catch(c){if(!J(c))throw c;await s.updateAlias(e,n,new Se.UpdateAliasRequest({body:new Se.UpdateAliasInput({versionId:t,description:o})}))}}async function Pa(e,n){let t=[],o,s=50;for(let r=0;r<50;r+=1){let c=await n.listAliases(e,new Se.ListAliasesRequest({limit:100,nextToken:o})),f=c.body?.aliases||[];if(t.push(...f),o=c.body?.nextToken,!o||f.length===0)break}return t}function Ra(e){return[...new Set(e)].filter((n)=>/^\d+$/.test(n)).sort((n,t)=>xo(n)-xo(t))}async function hr(e,n,t=!1){let{client:o}=ie(),s=Number.isFinite(n)&&n>0?Math.floor(n):10,r=await dt(e,1000,o),c=await Pa(e,o),f=new Set;for(let y of c){if(y.versionId)f.add(y.versionId);let d=y.additionalVersionWeight||{};for(let m of Object.keys(d))f.add(m)}let u=r.map((y)=>y.versionId||"").filter((y)=>/^\d+$/.test(y)),l=u.slice(0,s),i=u.filter((y)=>!l.includes(y)&&!f.has(y)),g={apply:t,keep:s,totalVersions:u.length,aliasProtectedVersions:Ra(f),candidates:i,deleted:[],failed:[]};if(!t||i.length===0)return g;for(let y of i)try{await o.deleteFunctionVersion(e,y),g.deleted.push(y)}catch(d){let m=R(d);g.failed.push({versionId:y,reason:m})}return g}var Uu=O(()=>{ce();Dn()});var Ve=O(()=>{Xs();On();er();rr();Hu();Lu();tr();Uu()});function Fa(e){if(typeof e==="string")return e;if(e instanceof Error)return`${e.name}: ${e.message}`;try{return JSON.stringify(e)}catch{return String(e)}}function xa(e){return e.map((t)=>Fa(t)).join(" ").replace(Ia,"").trim()}function Ha(e){let n=e.trim();if(!n)return!0;return/^(┌|└|│|◇|◆|◼|◻|▲|▼|◉|◎)/.test(n)}function ht(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function pe(e){if(typeof e!=="string")return;let n=e.trim();return n.length>0?n:void 0}function mr(e){if(typeof e==="number"&&Number.isFinite(e))return e;if(typeof e==="string"&&e.trim().length>0){let n=Number(e);if(Number.isFinite(n))return n}return}function Gu(e){let n=e.trim().toLowerCase();if(n==="text")return"text";if(n==="json")return"json";throw Error("--output 仅支持 text 或 json")}function Aa(e){let n=e.slice(2),t=[];for(let o of n){if(o==="--")break;if(o.startsWith("-"))break;if(t.push(o),t.length>=3)break}if(t.length===0)return"help";return t.join(" ")}function Bu(e){let n=[],t="text";for(let o=0;o<e.length;o+=1){let s=e[o];if(o<2){n.push(s);continue}if(s==="--"){n.push(...e.slice(o));break}if(s==="--output"){let r=e[o+1];if(!r||r.startsWith("-"))throw Error("--output 需要指定 text 或 json");t=Gu(r),o+=1;continue}if(s.startsWith("--output=")){t=Gu(s.slice(9));continue}n.push(s)}return{mode:t,argv:n}}function Yu(e,n){Pe.mode=e,Pe.command=Aa(n),Pe.resultEmitted=!1,Pe.errorEmitted=!1}function Ou(){return Pe.mode}function a(){return Pe.mode==="json"}function $r(e){process.stdout.write(`${pr}${JSON.stringify(e)}
487
- `)}function Mu(e){let n=e.replace(/[^A-Za-z0-9]+/g,"_").replace(/^_+|_+$/g,"");return n.length>0?n.toUpperCase():"UNKNOWN"}function La(e){let n=e.match(/missing required args for command `(.+?)`/);return n?n[1]:void 0}function kr(e){if(!ht(e))return;return pe(e.code)}function Ku(e){if(!ht(e))return;if(!ht(e.details))return;return{...e.details}}function Ua(e,n){let t=e.toLowerCase();if(kr(n)==="DEPLOY_PRECHECK_FAILED")return"input";if(t.includes("unknown command")||t.includes("未知命令")||t.includes("missing required args for command")||t.includes("precheck")||t.includes("预检")||t.includes("缺少")||t.includes("不能为空")||t.includes("无效")||t.includes("不支持")||t.includes("invalid")||t.includes("unsupported"))return"input";if(t.includes("未登录")||t.includes("please login")||ln(n))return"auth";if(Fe(n))return"permission";if(J(n))return"conflict";if(ue(n))return"not_found";if(Ee(n))return"network";if(mo(n))return"quota";return"internal"}function va(e,n,t){let o=e.toLowerCase(),s=kr(n);if(s==="DEPLOY_PRECHECK_FAILED")return"CLI_DEPLOY_PRECHECK_FAILED";if(o.includes("unknown command")||o.includes("未知命令"))return"CLI_UNKNOWN_COMMAND";if(o.includes("missing required args for command"))return"CLI_MISSING_REQUIRED_ARGS";if(t==="auth"){if(ln(n))return"AUTH_INVALID_CREDENTIAL";if(o.includes("未登录")||o.includes("please login"))return"AUTH_MISSING_CREDENTIAL";return"AUTH_ERROR"}if(t==="permission")return"AUTH_PERMISSION_DENIED";if(t==="conflict")return"RESOURCE_CONFLICT";if(t==="not_found")return"RESOURCE_NOT_FOUND";if(t==="network")return"PROVIDER_TRANSIENT";if(t==="quota")return"PROVIDER_QUOTA_OR_CLASS";if(t==="input")return"CLI_INVALID_INPUT";if(s){if(s.startsWith("ALI_"))return s;return`ALI_${Mu(s)}`}if(ht(n)){let r=pe(n.code);if(r)return`ALI_${Mu(r)}`}return"CLI_RUNTIME_ERROR"}function Ga(e){if(!ht(e))return;let n=ht(e.data)?e.data:{},t=pe(e.requestId)||pe(n.RequestId)||pe(n.requestId),o=mr(e.statusCode)||mr(e.status)||mr(n.statusCode),s=pe(e.code)||pe(n.Code),r=pe(e.endpoint)||pe(n.Endpoint)||pe(n.endpoint),c=pe(n.Action)||pe(n.action)||pe(n.Api)||pe(n.api),f=pe(e.service)||pe(n.Service)||pe(n.service);if(!Boolean(t||o!==void 0||r||c||f))return;let l={};if(f)l.service=f;if(c)l.action=c;if(s)l.code=s;if(t)l.requestId=t;if(o!==void 0)l.httpStatus=o;if(r)l.endpoint=r;return Object.keys(l).length>0?l:void 0}function Ma(e,n,t){let o=[],s=La(e),r=kr(t);if(s)o.push({type:"fix_input",reason:"missing required args",commandTemplate:`licell ${s}`});if(r==="DEPLOY_PRECHECK_FAILED"){let c=Ku(t),f=pe(c?.runtime)||"<runtime>",u=pe(c?.entry);o.push({type:"read_spec",reason:"runtime contract must be satisfied before deploy",commandTemplate:`licell deploy spec ${f}`}),o.push({type:"run_precheck",reason:"validate entry and handler contract locally",commandTemplate:u?`licell deploy check --runtime ${f} --entry ${u}`:`licell deploy check --runtime ${f}`})}if(n==="input"&&!s)o.push({type:"fix_input",reason:"invalid or missing CLI arguments",commandTemplate:`licell ${Pe.command} --help`.trim()});if(n==="auth"||n==="permission"){if(o.push({type:"repair_auth",reason:"credentials missing/invalid or insufficient permissions",commandTemplate:"licell auth repair --account-id <id> --ak <super-ak> --sk <super-sk>"}),n==="auth")o.push({type:"login",reason:"no active credential",commandTemplate:"licell login"})}if(n==="network")o.push({type:"retry",reason:"transient network/provider issue",commandTemplate:`licell ${Pe.command}`.trim()});return o}function X(e){if(!a())return;$r({schemaVersion:wr,type:"event",ts:new Date().toISOString(),command:Pe.command,...e})}function ju(){if(!a())return;if(vu)return;vu=!0;let e={log:console.log.bind(console),info:console.info.bind(console),warn:console.warn.bind(console),error:console.error.bind(console)},n=(t,o)=>{if(ar)return;let s=xa(o);if(!s||Ha(s))return;ar=!0;try{X({stage:t==="warn"||t==="error"?"stderr":"stdout",action:t,status:"info",message:s,data:{source:"console",level:t}})}finally{ar=!1}};console.log=(...t)=>{n("log",t)},console.info=(...t)=>{n("info",t)},console.warn=(...t)=>{n("warn",t)},console.error=(...t)=>{n("error",t)}}function S(e){if(!a())return;Pe.resultEmitted=!0,$r({schemaVersion:wr,type:"result",ts:new Date().toISOString(),command:Pe.command,ok:!0,...e})}function Ba(e,n){let t=R(e),o=Ua(t,e),s=va(t,e,o),r=Ga(e),c=o==="network",f={schemaVersion:wr,type:"error",ts:new Date().toISOString(),command:n?.command||Pe.command,stage:n?.stage||"runtime",error:{code:s,category:o,message:t,retryable:c},remediation:Ma(t,o,e)};if(r)f.provider=r;let u={},l=Ku(e);if(l)Object.assign(u,l);if(n?.details&&Object.keys(n.details).length>0)Object.assign(u,n.details);if(Object.keys(u).length>0)f.details=u;return f}function he(e,n){if(!a())return;Pe.errorEmitted=!0,$r(Ba(e,n))}function Wu(){return Pe.resultEmitted}function Qu(){return Pe.errorEmitted}function Du(e){let n=[];for(let t of e.split(/\r?\n/)){if(!t.startsWith(pr))continue;let o=t.slice(pr.length);if(!o)continue;try{n.push(JSON.parse(o))}catch{}}return n}var pr="@@LICELL_JSON@@",wr="1.0",Pe,vu=!1,ar=!1,Ia;var re=O(()=>{ce();Pe={mode:"text",command:"",resultEmitted:!1,errorEmitted:!1},Ia=/\u001B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g});import{confirm as Vu,intro as Ya,outro as Ho,spinner as Oa,isCancel as _r}from"@clack/prompts";import Qt from"picocolors";import{readFileSync as Ka,writeFileSync as ja,existsSync as Wa}from"fs";function A(e,n){if(_r(e))process.exit(0);if(typeof e!=="string")throw Error(`${n} 输入无效`);let t=e.trim();if(!t)throw Error(`${n} 不能为空`);return t}function j(e){if(a())return;Ya(e)}function q(e){if(a())return;Ho(e)}function L(){if(!a()){let e=Oa(),n=!1;return{start:(t)=>{n=!0,e.start(t)},stop:(t)=>{if(n)n=!1,e.stop(t)},message:(t)=>{if(n)e.message(t)}}}return{start:()=>{},stop:()=>{},message:()=>{}}}async function B(){let e=k.getAuth();if(!e){if(a())throw Error("未登录,请先执行 `licell login`");if(P()){Ho(Qt.red("检测到您尚未登录阿里云凭证"));let n=await Vu({message:"是否现在进行登录以继续执行命令?",initialValue:!0});if(n&&!_r(n)){let{runInteractiveLogin:t}=(Go(),yy(ni));await t();let o=k.getAuth();if(o)return console.log(Qt.green(`继续执行原来的命令...
488
- `)),o}}Ho(Qt.red("未登录,请先执行 `licell login`")),process.exit(1)}return e}function Ae(e,n="请先执行 licell deploy 部署项目"){if(!e.appName){if(a())throw Error(n);Ho(Qt.red(n)),process.exit(1)}}async function U(e,n,t,o){if(!a())e.start(n);try{return await o()}catch(s){if(a())throw s;let r=R(s).toLowerCase();if(Fe(s)||ln(s)||r.includes("未登录")||r.includes("先执行 `licell login`"))throw s;e.stop(Qt.red(t)),console.error(R(s)),process.exitCode=1;return}}function Vn(e){let n=e.trim().toLowerCase().replace(/^\.+|\.+$/g,"");if(!n)throw Error("域名后缀不能为空");if(!/^[a-z0-9.-]+$/.test(n))throw Error("域名后缀仅允许小写字母、数字、点和短横线");return Qe(`a.${n}`),n}function Ao(e){let n=e.trim().toLowerCase().replace(/^\.+|\.+$/g,"");if(!n)throw Error("域名不能为空");return Qe(n),n}function Lo(e){if(typeof e!=="string")return;let n=e.trim();if(!n)return;try{return Vn(n)}catch{return}}function at(e){if(typeof e!=="string")return;let n=e.trim();if(!n)return;try{return Qn(n)}catch{return}}function Xu(){if(!Wa(".gitignore"))return;let t=Ka(".gitignore","utf-8");if(/^\.env$/m.test(t))return;let o=t.endsWith(`
486
+ return {'statusCode': 200, 'body': 'ok'}`,fixHint:["python3.13 也要求 handler(event, context);函数名不匹配会在预检阶段失败。"]},validationRules:[{id:"entry.outside_project",level:"error",summary:"入口文件必须在项目目录内。"},{id:"entry.not_found",level:"error",summary:"入口文件不存在。"},{id:"entry.not_file",level:"error",summary:"入口路径必须是文件。"},{id:"entry.runtime_contract",level:"error",summary:"入口必须包含 handler(event, context)。"},{id:"python.dependencies_manifest_missing",level:"warning",summary:"缺少 requirements/pyproject,第三方依赖可能不会被安装。"}],notes:["部署前会校验 handler 函数签名。","运行在 custom.debian12 + 内置 python3.13。"]},{runtime:"docker",mode:"custom-container",defaultEntry:null,entryRule:"优先使用 Dockerfile;若缺失则尝试自动生成(Node/Python 项目)。",handlerRule:"容器内需监听 FC_SERVER_PORT/PORT(默认 9000),并响应 HTTP 请求。",handlerContract:{kind:"container-http",entryFileExtensions:[],containerPortEnv:["FC_SERVER_PORT","PORT"],signature:"HTTP server listening on 0.0.0.0:${FC_SERVER_PORT|PORT|9000}"},eventSchema:{requiredFields:["HTTP request at container port 9000"],optionalFields:[],notes:["如果使用 licell 自动生成 Dockerfile,会附带 node/python bootstrap 事件模型。"]},responseSchema:{acceptedForms:["standard HTTP response from your container app"],notes:["容器模式下由应用自行处理 HTTP 协议,不要求固定 handler 函数导出。"]},examples:{minimalPassExample:"const port = Number(process.env.FC_SERVER_PORT || process.env.PORT || 9000); app.listen(port, '0.0.0.0');",commonFailExample:"app.listen(3000, '127.0.0.1')",fixHint:["必须监听 FC_SERVER_PORT 或 PORT(默认 9000)。","绑定地址需为 0.0.0.0,不能只监听 127.0.0.1。"]},validationRules:[{id:"docker.project_type_undetected",level:"error",summary:"无 Dockerfile 且无法识别项目类型。"},{id:"docker.node_ts_no_build_script",level:"error",summary:"TS 项目缺少 build 脚本且未指定 --entry。"},{id:"docker.entry.outside_project",level:"error",summary:"Docker 入口必须在项目目录内。"},{id:"docker.entry.not_found",level:"error",summary:"Docker 入口不存在。"},{id:"docker.daemon_unavailable",level:"error",summary:"本机 Docker daemon 不可用。"}],notes:["部署依赖本机 Docker daemon。","镜像将推送到 ACR,再由 FC custom-container 拉取。"]}],Eu={defaults:{memoryMb:Su,vcpu:qu,timeoutSeconds:Pu,instanceConcurrency:Ru},constraints:{memoryToVcpuRatio:{min:Iu,max:Fu,expression:"memoryGb / vcpu in [1, 4]"}}}});import*as be from"@alicloud/fc20230330";import{Readable as ba}from"stream";async function yt(e,n){let{client:t}=ie();return(await t.getFunction(e,new be.GetFunctionRequest({qualifier:n}))).body?.environmentVariables||{}}function Ta(e){let n=e.functionName;if(!n)return null;return{functionName:n,runtime:e.runtime,state:e.state,lastModifiedTime:e.lastModifiedTime,description:e.description}}async function ur(e=100,n){let{client:t}=ie(),s=[],o=Math.max(1,Math.min(Math.floor(e),500)),r;while(s.length<o){let c=await t.listFunctions(new be.ListFunctionsRequest({limit:Math.min(100,o-s.length),nextToken:r,prefix:n,fcVersion:"v3"})),f=c.body?.functions||[];for(let u of f){let l=Ta(u);if(!l)continue;if(s.push(l),s.length>=o)break}if(r=c.body?.nextToken,!r||f.length===0)break}return s}async function ir(e,n){let t=e.trim();if(!t)throw Error("functionName 不能为空");let{client:s}=ie(),r=(await s.getFunction(t,new be.GetFunctionRequest({qualifier:n}))).body;if(!r?.functionName)throw Error(`未找到函数: ${t}`);return r}async function Ca(e,n){let t=[],s,o=50;for(let r=0;r<50;r+=1){let c=await n.listTriggers(e,new be.ListTriggersRequest({limit:100,nextToken:s})),f=c.body?.triggers||[];if(t.push(...f),s=c.body?.nextToken,!s||f.length===0)break}return t}async function Ea(e,n){let t=[],s,o=50;for(let r=0;r<50;r+=1){let c=await n.listAliases(e,new be.ListAliasesRequest({limit:100,nextToken:s})),f=c.body?.aliases||[];if(t.push(...f),s=c.body?.nextToken,!s||f.length===0)break}return t}async function Sa(e,n){let t=[],s,o=50;for(let r=0;r<50;r+=1){let c=await n.listFunctionVersions(e,new be.ListFunctionVersionsRequest({direction:"BACKWARD",limit:100,nextToken:s})),f=c.body?.versions||[];if(t.push(...f),s=c.body?.nextToken,!s||f.length===0)break}return t}async function lr(e,n={}){let t=e.trim();if(!t)throw Error("functionName 不能为空");let{client:s}=ie();if(!n.force)return await s.deleteFunction(t),{forced:!1,deletedTriggers:[],deletedAliases:[],deletedVersions:[]};let o=[],r=[],c=[],f=await Ca(t,s);for(let i of f){let g=i.triggerName;if(!g)continue;try{await s.deleteTrigger(t,g),o.push(g)}catch(y){if(!ue(y))throw y}}let u=await Ea(t,s);for(let i of u){let g=i.aliasName;if(!g)continue;try{await s.deleteAlias(t,g),r.push(g)}catch(y){if(!ue(y))throw y}}let l=await Sa(t,s);for(let i of l){let g=i.versionId||"";if(!/^\d+$/.test(g))continue;try{await s.deleteFunctionVersion(t,g),c.push(g)}catch(y){if(!ue(y))throw y}}return await s.deleteFunction(t),{forced:!0,deletedTriggers:o,deletedAliases:r,deletedVersions:c}}async function qa(e){if(!e)return"";let n=[];for await(let t of e)if(Buffer.isBuffer(t))n.push(t);else n.push(Buffer.from(String(t)));return Buffer.concat(n).toString("utf8")}async function gr(e,n={}){let t=e.trim();if(!t)throw Error("functionName 不能为空");let{client:s}=ie(),o=typeof n.payload==="string"?ba.from([Buffer.from(n.payload)]):void 0,r=await s.invokeFunction(t,new be.InvokeFunctionRequest({qualifier:n.qualifier,body:o})),c=await qa(r.body);return{statusCode:r.statusCode||0,headers:r.headers||{},body:c}}async function Lu(e,n){let{client:t}=ie();await t.updateFunction(e,new be.UpdateFunctionRequest({body:new be.UpdateFunctionInput({environmentVariables:n})}))}async function yr(e,n,t){let o={...await yt(e),[n]:t};return await Lu(e,o),o}async function dr(e,n){let t=await yt(e);if(!Object.prototype.hasOwnProperty.call(t,n))return t;let{[n]:s,...o}=t;return await Lu(e,o),o}var Au=O(()=>{Dn()});import*as Se from"@alicloud/fc20230330";function Fs(e){let n=Number(e);return Number.isFinite(n)?n:-1}async function dt(e,n=20,t){let s=t??ie().client,o=[],r,c=Math.max(n,1);while(c>0){let f=Math.min(c,100),u=await s.listFunctionVersions(e,new Se.ListFunctionVersionsRequest({direction:"BACKWARD",limit:f,nextToken:r})),l=u.body?.versions||[];if(o.push(...l),c-=l.length,r=u.body?.nextToken,!r||l.length===0)break}return o.sort((f,u)=>Fs(u.versionId||"")-Fs(f.versionId||"")),o}async function Sn(e,n){let{client:t}=ie(),o=(await t.publishFunctionVersion(e,new Se.PublishFunctionVersionRequest({body:new Se.PublishVersionInput({description:n})}))).body?.versionId;if(!o)throw Error("发布函数版本失败:未返回 versionId");return o}async function qn(e,n,t,s){let{client:o}=ie(),r=new Se.CreateAliasInput({aliasName:n,versionId:t,description:s});try{await o.createAlias(e,new Se.CreateAliasRequest({body:r}))}catch(c){if(!J(c))throw c;await o.updateAlias(e,n,new Se.UpdateAliasRequest({body:new Se.UpdateAliasInput({versionId:t,description:s})}))}}async function Pa(e,n){let t=[],s,o=50;for(let r=0;r<50;r+=1){let c=await n.listAliases(e,new Se.ListAliasesRequest({limit:100,nextToken:s})),f=c.body?.aliases||[];if(t.push(...f),s=c.body?.nextToken,!s||f.length===0)break}return t}function Ra(e){return[...new Set(e)].filter((n)=>/^\d+$/.test(n)).sort((n,t)=>Fs(n)-Fs(t))}async function hr(e,n,t=!1){let{client:s}=ie(),o=Number.isFinite(n)&&n>0?Math.floor(n):10,r=await dt(e,1000,s),c=await Pa(e,s),f=new Set;for(let y of c){if(y.versionId)f.add(y.versionId);let d=y.additionalVersionWeight||{};for(let m of Object.keys(d))f.add(m)}let u=r.map((y)=>y.versionId||"").filter((y)=>/^\d+$/.test(y)),l=u.slice(0,o),i=u.filter((y)=>!l.includes(y)&&!f.has(y)),g={apply:t,keep:o,totalVersions:u.length,aliasProtectedVersions:Ra(f),candidates:i,deleted:[],failed:[]};if(!t||i.length===0)return g;for(let y of i)try{await s.deleteFunctionVersion(e,y),g.deleted.push(y)}catch(d){let m=R(d);g.failed.push({versionId:y,reason:m})}return g}var Uu=O(()=>{ce();Dn()});var Ve=O(()=>{Xo();On();er();rr();Hu();Au();tr();Uu()});function Fa(e){if(typeof e==="string")return e;if(e instanceof Error)return`${e.name}: ${e.message}`;try{return JSON.stringify(e)}catch{return String(e)}}function xa(e){return e.map((t)=>Fa(t)).join(" ").replace(Ia,"").trim()}function Ha(e){let n=e.trim();if(!n)return!0;return/^(┌|└|│|◇|◆|◼|◻|▲|▼|◉|◎)/.test(n)}function ht(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function pe(e){if(typeof e!=="string")return;let n=e.trim();return n.length>0?n:void 0}function mr(e){if(typeof e==="number"&&Number.isFinite(e))return e;if(typeof e==="string"&&e.trim().length>0){let n=Number(e);if(Number.isFinite(n))return n}return}function Gu(e){let n=e.trim().toLowerCase();if(n==="text")return"text";if(n==="json")return"json";throw Error("--output 仅支持 text 或 json")}function La(e){let n=e.slice(2),t=[];for(let s of n){if(s==="--")break;if(s.startsWith("-"))break;if(t.push(s),t.length>=3)break}if(t.length===0)return"help";return t.join(" ")}function Bu(e){let n=[],t="text";for(let s=0;s<e.length;s+=1){let o=e[s];if(s<2){n.push(o);continue}if(o==="--"){n.push(...e.slice(s));break}if(o==="--output"){let r=e[s+1];if(!r||r.startsWith("-"))throw Error("--output 需要指定 text 或 json");t=Gu(r),s+=1;continue}if(o.startsWith("--output=")){t=Gu(o.slice(9));continue}n.push(o)}return{mode:t,argv:n}}function Yu(e,n){Pe.mode=e,Pe.command=La(n),Pe.resultEmitted=!1,Pe.errorEmitted=!1}function Ou(){return Pe.mode}function a(){return Pe.mode==="json"}function $r(e){process.stdout.write(`${pr}${JSON.stringify(e)}
487
+ `)}function Mu(e){let n=e.replace(/[^A-Za-z0-9]+/g,"_").replace(/^_+|_+$/g,"");return n.length>0?n.toUpperCase():"UNKNOWN"}function Aa(e){let n=e.match(/missing required args for command `(.+?)`/);return n?n[1]:void 0}function kr(e){if(!ht(e))return;return pe(e.code)}function Ku(e){if(!ht(e))return;if(!ht(e.details))return;return{...e.details}}function Ua(e,n){let t=e.toLowerCase();if(kr(n)==="DEPLOY_PRECHECK_FAILED")return"input";if(t.includes("unknown command")||t.includes("未知命令")||t.includes("missing required args for command")||t.includes("precheck")||t.includes("预检")||t.includes("缺少")||t.includes("不能为空")||t.includes("无效")||t.includes("不支持")||t.includes("invalid")||t.includes("unsupported"))return"input";if(t.includes("未登录")||t.includes("please login")||ln(n))return"auth";if(Fe(n))return"permission";if(J(n))return"conflict";if(ue(n))return"not_found";if(Ee(n))return"network";if(as(n))return"quota";return"internal"}function va(e,n,t){let s=e.toLowerCase(),o=kr(n);if(o==="DEPLOY_PRECHECK_FAILED")return"CLI_DEPLOY_PRECHECK_FAILED";if(s.includes("unknown command")||s.includes("未知命令"))return"CLI_UNKNOWN_COMMAND";if(s.includes("missing required args for command"))return"CLI_MISSING_REQUIRED_ARGS";if(t==="auth"){if(ln(n))return"AUTH_INVALID_CREDENTIAL";if(s.includes("未登录")||s.includes("please login"))return"AUTH_MISSING_CREDENTIAL";return"AUTH_ERROR"}if(t==="permission")return"AUTH_PERMISSION_DENIED";if(t==="conflict")return"RESOURCE_CONFLICT";if(t==="not_found")return"RESOURCE_NOT_FOUND";if(t==="network")return"PROVIDER_TRANSIENT";if(t==="quota")return"PROVIDER_QUOTA_OR_CLASS";if(t==="input")return"CLI_INVALID_INPUT";if(o){if(o.startsWith("ALI_"))return o;return`ALI_${Mu(o)}`}if(ht(n)){let r=pe(n.code);if(r)return`ALI_${Mu(r)}`}return"CLI_RUNTIME_ERROR"}function Ga(e){if(!ht(e))return;let n=ht(e.data)?e.data:{},t=pe(e.requestId)||pe(n.RequestId)||pe(n.requestId),s=mr(e.statusCode)||mr(e.status)||mr(n.statusCode),o=pe(e.code)||pe(n.Code),r=pe(e.endpoint)||pe(n.Endpoint)||pe(n.endpoint),c=pe(n.Action)||pe(n.action)||pe(n.Api)||pe(n.api),f=pe(e.service)||pe(n.Service)||pe(n.service);if(!Boolean(t||s!==void 0||r||c||f))return;let l={};if(f)l.service=f;if(c)l.action=c;if(o)l.code=o;if(t)l.requestId=t;if(s!==void 0)l.httpStatus=s;if(r)l.endpoint=r;return Object.keys(l).length>0?l:void 0}function Ma(e,n,t){let s=[],o=Aa(e),r=kr(t);if(o)s.push({type:"fix_input",reason:"missing required args",commandTemplate:`licell ${o}`});if(r==="DEPLOY_PRECHECK_FAILED"){let c=Ku(t),f=pe(c?.runtime)||"<runtime>",u=pe(c?.entry);s.push({type:"read_spec",reason:"runtime contract must be satisfied before deploy",commandTemplate:`licell deploy spec ${f}`}),s.push({type:"run_precheck",reason:"validate entry and handler contract locally",commandTemplate:u?`licell deploy check --runtime ${f} --entry ${u}`:`licell deploy check --runtime ${f}`})}if(n==="input"&&!o)s.push({type:"fix_input",reason:"invalid or missing CLI arguments",commandTemplate:`licell ${Pe.command} --help`.trim()});if(n==="auth"||n==="permission"){if(s.push({type:"repair_auth",reason:"credentials missing/invalid or insufficient permissions",commandTemplate:"licell auth repair --account-id <id> --ak <super-ak> --sk <super-sk>"}),n==="auth")s.push({type:"login",reason:"no active credential",commandTemplate:"licell login"})}if(n==="network")s.push({type:"retry",reason:"transient network/provider issue",commandTemplate:`licell ${Pe.command}`.trim()});return s}function X(e){if(!a())return;$r({schemaVersion:wr,type:"event",ts:new Date().toISOString(),command:Pe.command,...e})}function ju(){if(!a())return;if(vu)return;vu=!0;let e={log:console.log.bind(console),info:console.info.bind(console),warn:console.warn.bind(console),error:console.error.bind(console)},n=(t,s)=>{if(ar)return;let o=xa(s);if(!o||Ha(o))return;ar=!0;try{X({stage:t==="warn"||t==="error"?"stderr":"stdout",action:t,status:"info",message:o,data:{source:"console",level:t}})}finally{ar=!1}};console.log=(...t)=>{n("log",t)},console.info=(...t)=>{n("info",t)},console.warn=(...t)=>{n("warn",t)},console.error=(...t)=>{n("error",t)}}function S(e){if(!a())return;Pe.resultEmitted=!0,$r({schemaVersion:wr,type:"result",ts:new Date().toISOString(),command:Pe.command,ok:!0,...e})}function Ba(e,n){let t=R(e),s=Ua(t,e),o=va(t,e,s),r=Ga(e),c=s==="network",f={schemaVersion:wr,type:"error",ts:new Date().toISOString(),command:n?.command||Pe.command,stage:n?.stage||"runtime",error:{code:o,category:s,message:t,retryable:c},remediation:Ma(t,s,e)};if(r)f.provider=r;let u={},l=Ku(e);if(l)Object.assign(u,l);if(n?.details&&Object.keys(n.details).length>0)Object.assign(u,n.details);if(Object.keys(u).length>0)f.details=u;return f}function he(e,n){if(!a())return;Pe.errorEmitted=!0,$r(Ba(e,n))}function Wu(){return Pe.resultEmitted}function Qu(){return Pe.errorEmitted}function Du(e){let n=[];for(let t of e.split(/\r?\n/)){if(!t.startsWith(pr))continue;let s=t.slice(pr.length);if(!s)continue;try{n.push(JSON.parse(s))}catch{}}return n}var pr="@@LICELL_JSON@@",wr="1.0",Pe,vu=!1,ar=!1,Ia;var re=O(()=>{ce();Pe={mode:"text",command:"",resultEmitted:!1,errorEmitted:!1},Ia=/\u001B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g});import{confirm as Vu,intro as Ya,outro as xs,spinner as Oa,isCancel as _r}from"@clack/prompts";import Qt from"picocolors";import{readFileSync as Ka,writeFileSync as ja,existsSync as Wa}from"fs";function L(e,n){if(_r(e))process.exit(0);if(typeof e!=="string")throw Error(`${n} 输入无效`);let t=e.trim();if(!t)throw Error(`${n} 不能为空`);return t}function j(e){if(a())return;Ya(e)}function q(e){if(a())return;xs(e)}function A(){if(!a()){let e=Oa(),n=!1;return{start:(t)=>{n=!0,e.start(t)},stop:(t)=>{if(n)n=!1,e.stop(t)},message:(t)=>{if(n)e.message(t)}}}return{start:()=>{},stop:()=>{},message:()=>{}}}async function B(){let e=k.getAuth();if(!e){if(a())throw Error("未登录,请先执行 `licell login`");if(P()){xs(Qt.red("检测到您尚未登录阿里云凭证"));let n=await Vu({message:"是否现在进行登录以继续执行命令?",initialValue:!0});if(n&&!_r(n)){let{runInteractiveLogin:t}=(vs(),yy(ni));await t();let s=k.getAuth();if(s)return console.log(Qt.green(`继续执行原来的命令...
488
+ `)),s}}xs(Qt.red("未登录,请先执行 `licell login`")),process.exit(1)}return e}function Le(e,n="请先执行 licell deploy 部署项目"){if(!e.appName){if(a())throw Error(n);xs(Qt.red(n)),process.exit(1)}}async function v(e,n,t,s){if(!a())e.start(n);try{return await s()}catch(o){if(a())throw o;let r=R(o).toLowerCase();if(Fe(o)||ln(o)||r.includes("未登录")||r.includes("先执行 `licell login`"))throw o;e.stop(Qt.red(t)),console.error(R(o)),process.exitCode=1;return}}function Vn(e){let n=e.trim().toLowerCase().replace(/^\.+|\.+$/g,"");if(!n)throw Error("域名后缀不能为空");if(!/^[a-z0-9.-]+$/.test(n))throw Error("域名后缀仅允许小写字母、数字、点和短横线");return Qe(`a.${n}`),n}function Hs(e){let n=e.trim().toLowerCase().replace(/^\.+|\.+$/g,"");if(!n)throw Error("域名不能为空");return Qe(n),n}function Ls(e){if(typeof e!=="string")return;let n=e.trim();if(!n)return;try{return Vn(n)}catch{return}}function at(e){if(typeof e!=="string")return;let n=e.trim();if(!n)return;try{return Qn(n)}catch{return}}function Xu(){if(!Wa(".gitignore"))return;let t=Ka(".gitignore","utf-8");if(/^\.env$/m.test(t))return;let s=t.endsWith(`
489
489
  `)||t.length===0?"":`
490
- `;ja(".gitignore",`${t}${o}.env
491
- `)}function P(){return Boolean(process.stdin.isTTY&&process.stdout.isTTY)}async function Be(e,n={}){if(n.yes)return;if(!(n.interactiveTTY??P()))throw Error(`${e} 属于删除操作;非交互模式请添加 --yes 明确确认`);let o=n.confirmPrompt||(async(c)=>{let f=await Vu({message:c});if(_r(f))process.exit(0);return Boolean(f)});if(!await o(`${e} 将删除云端资源,是否继续?`))throw Error("操作已取消");if(!await o(`请再次确认:继续执行${e}?`))throw Error("操作已取消")}function Zu(e){let n=e.trim().toLowerCase();if(n!=="api"&&n!=="static")throw Error("--type 仅支持 api 或 static");return n}function Ju(e){let n=e.trim().toLowerCase();if(n!=="postgres"&&n!=="mysql")throw Error("--type 仅支持 postgres 或 mysql");return n}function C(e){if(e===null||e===void 0)return;let n=String(e).trim();return n.length>0?n:void 0}function br(e){let n=e.trim().toLowerCase();if(!n)throw Error("region 不能为空");if(!/^[a-z0-9-]+$/.test(n))throw Error("region 格式无效");return n}function Nu(e){if(e.length<=8)return`${e.slice(0,2)}***${e.slice(-2)}`;return`${e.slice(0,4)}***${e.slice(-4)}`}function Tr(e){let n=e.trim();if(!n)throw Error("环境变量名不能为空");if(!/^[A-Za-z_][A-Za-z0-9_]*$/.test(n))throw Error("环境变量名仅支持字母、数字、下划线,且不能以数字开头");return n}function zu(e){let n=C(e)?.toLowerCase();if(!n)throw Error("--auto-pause 不能为空");if(n==="on"||n==="true"||n==="1")return!0;if(n==="off"||n==="false"||n==="0")return!1;throw Error("--auto-pause 仅支持 on/off")}function Cr(e,n){let t=C(e);if(!t)return;let o=Number(t);if(!Number.isFinite(o))throw Error(`${n} 必须是数字`);return o}function ei(e,n){let t=C(e);if(!t)return;let o=Number(t);if(!Number.isFinite(o)||o<=0)throw Error(`${n} 必须是正数`);return o}function qe(e,n){let t=C(e);if(!t)return;let o=Number(t);if(!Number.isFinite(o)||o<=0||!Number.isInteger(o))throw Error(`${n} 必须是正整数`);return o}function Ye(e,n,t=500){let o=C(e);if(!o)return n;let s=Number(o);if(!Number.isFinite(s)||s<=0)return n;return Math.min(Math.floor(s),t)}function Uo(e){if(typeof e!=="object"||e===null)return!1;let n="message"in e?String(e.message||""):"";return(("code"in e?String(e.code||""):"")==="VersionPublishError"||n.includes("VersionPublishError"))&&n.includes("No changes were made since last publish")}async function vo(e){let t=(await dt(e,1))[0]?.versionId;if(!t)throw Error("未找到可用的已发布版本,请先执行 deploy 或 release promote 发布版本");return t}var oe=O(()=>{K();ce();Ht();Ve();Ve();re()});import{confirm as si,isCancel as ri,password as Qa,text as Er}from"@clack/prompts";import nn from"picocolors";function Xa(e){let n=`${String(e?.code||"")} ${String(e?.message||e||"")}`.toLowerCase();return n.includes("未登录")||n.includes("先执行 `licell login`")||n.includes("please login")}function Sr(e){if(Xa(e))return"missing_auth";if(ln(e))return"invalid_credentials";if(Fe(e))return"access_denied";return"unknown"}function ci(e){if(e==="missing_auth")return"未登录";if(e==="invalid_credentials")return"AK/SK 无效或已失效";if(e==="access_denied")return"AK/SK 权限不足";if(e==="manual")return"手动触发授权修复";return"未知认证错误"}function qr(e){if(!e||e.length===0)return[];return[...new Set(e)]}function Za(e){let n=qr(e);if(n.length===0)return[];return[...new Set(n.flatMap((t)=>Va[t]||[]))].sort()}function Ja(e){let n=qr(e);if(n.length===0)return"";return n.map((t)=>Da[t]).join(" / ")}function Na(e,n){return si({message:`${e} 检测到 ${ci(n)}。是否现在进入 bootstrap 授权修复流程?`,initialValue:!0})}function za(e,n){let t=C(e.accountId)||V(process.env,"LICELL_BOOTSTRAP_ACCOUNT_ID","LICELL_ACCOUNT_ID")||V(process.env,"ALI_ACCOUNT_ID")||n?.accountId,o=C(e.adminAk)||V(process.env,"LICELL_BOOTSTRAP_ACCESS_KEY_ID","LICELL_ACCESS_KEY_ID")||V(process.env,"ALI_ACCESS_KEY_ID"),s=C(e.adminSk)||V(process.env,"LICELL_BOOTSTRAP_ACCESS_KEY_SECRET","LICELL_ACCESS_KEY_SECRET")||V(process.env,"ALI_ACCESS_KEY_SECRET"),r=C(e.region)||V(process.env,"LICELL_BOOTSTRAP_REGION","LICELL_REGION")||V(process.env,"ALI_REGION")||n?.region||Ge;return{accountId:t,adminAk:o,adminSk:s,region:r}}function em(e,n){throw Error(`${e} 检测到 ${ci(n)},且当前为非交互模式。
492
- `+"请先执行 `licell auth repair --account-id <id> --ak <super-ak> --sk <super-sk> [--region cn-hangzhou]`,然后重试。")}function nm(e){if(Fe(e)||ln(e))return!0;let n=`${String(e?.code||"")} ${String(e?.message||e||"")}`.toLowerCase();return n.includes("forbidden")||n.includes("accessdenied")||n.includes("not authorized")||n.includes("permission")}async function tm(e,n){if(!n||e.forceRotateKey)return null;try{let t=await Cs({adminAuth:{accountId:n.accountId,ak:n.ak,sk:n.sk,region:n.region},currentAuth:n,userName:e.bootstrapUser,policyName:e.bootstrapPolicy,forceRotateKey:!1});return k.setAuth({accountId:n.accountId,ak:t.accessKeyId,sk:t.accessKeySecret,region:n.region,authSource:"bootstrap",ramUser:t.userName,ramPolicy:t.policyName}),{mode:t.mode,userName:t.userName,policyName:t.policyName,accountId:n.accountId,region:n.region,rotatedKey:t.mode==="rotated-new-key"}}catch(t){if(nm(t))return null;throw t}}async function mt(e){let n=e.currentAuth??k.getAuth(),t=e.interactiveTTY??P(),o=za(e,n),s=await tm(e,n);if(s)return s;let{accountId:r,adminAk:c,adminSk:f,region:u}=o;if(t){console.log(nn.gray(`
490
+ `;ja(".gitignore",`${t}${s}.env
491
+ `)}function P(){return Boolean(process.stdin.isTTY&&process.stdout.isTTY)}async function Be(e,n={}){if(n.yes)return;if(!(n.interactiveTTY??P()))throw Error(`${e} 属于删除操作;非交互模式请添加 --yes 明确确认`);let s=n.confirmPrompt||(async(c)=>{let f=await Vu({message:c});if(_r(f))process.exit(0);return Boolean(f)});if(!await s(`${e} 将删除云端资源,是否继续?`))throw Error("操作已取消");if(!await s(`请再次确认:继续执行${e}?`))throw Error("操作已取消")}function Zu(e){let n=e.trim().toLowerCase();if(n!=="api"&&n!=="static")throw Error("--type 仅支持 api 或 static");return n}function Ju(e){let n=e.trim().toLowerCase().replace(/_/g,"-");if(n==="postgresql"||n==="postgres")return"postgres";if(n==="mysql")return"mysql";if(n==="serverless-postgresql"||n==="serverless-postgres")return"serverless-postgresql";throw Error("--type 仅支持 postgresql 或 mysql(serverless-postgresql 即将上线)")}function C(e){if(e===null||e===void 0)return;let n=String(e).trim();return n.length>0?n:void 0}function br(e){let n=e.trim().toLowerCase();if(!n)throw Error("region 不能为空");if(!/^[a-z0-9-]+$/.test(n))throw Error("region 格式无效");return n}function Nu(e){if(e.length<=8)return`${e.slice(0,2)}***${e.slice(-2)}`;return`${e.slice(0,4)}***${e.slice(-4)}`}function Tr(e){let n=e.trim();if(!n)throw Error("环境变量名不能为空");if(!/^[A-Za-z_][A-Za-z0-9_]*$/.test(n))throw Error("环境变量名仅支持字母、数字、下划线,且不能以数字开头");return n}function zu(e){let n=C(e)?.toLowerCase();if(!n)throw Error("--auto-pause 不能为空");if(n==="on"||n==="true"||n==="1")return!0;if(n==="off"||n==="false"||n==="0")return!1;throw Error("--auto-pause 仅支持 on/off")}function Cr(e,n){let t=C(e);if(!t)return;let s=Number(t);if(!Number.isFinite(s))throw Error(`${n} 必须是数字`);return s}function ei(e,n){let t=C(e);if(!t)return;let s=Number(t);if(!Number.isFinite(s)||s<=0)throw Error(`${n} 必须是正数`);return s}function qe(e,n){let t=C(e);if(!t)return;let s=Number(t);if(!Number.isFinite(s)||s<=0||!Number.isInteger(s))throw Error(`${n} 必须是正整数`);return s}function Ye(e,n,t=500){let s=C(e);if(!s)return n;let o=Number(s);if(!Number.isFinite(o)||o<=0)return n;return Math.min(Math.floor(o),t)}function As(e){if(typeof e!=="object"||e===null)return!1;let n="message"in e?String(e.message||""):"";return(("code"in e?String(e.code||""):"")==="VersionPublishError"||n.includes("VersionPublishError"))&&n.includes("No changes were made since last publish")}async function Us(e){let t=(await dt(e,1))[0]?.versionId;if(!t)throw Error("未找到可用的已发布版本,请先执行 deploy 或 release promote 发布版本");return t}var se=O(()=>{K();ce();Ht();Ve();Ve();re()});import{confirm as oi,isCancel as ri,password as Qa,text as Er}from"@clack/prompts";import nn from"picocolors";function Xa(e){let n=`${String(e?.code||"")} ${String(e?.message||e||"")}`.toLowerCase();return n.includes("未登录")||n.includes("先执行 `licell login`")||n.includes("please login")}function Sr(e){if(Xa(e))return"missing_auth";if(ln(e))return"invalid_credentials";if(Fe(e))return"access_denied";return"unknown"}function ci(e){if(e==="missing_auth")return"未登录";if(e==="invalid_credentials")return"AK/SK 无效或已失效";if(e==="access_denied")return"AK/SK 权限不足";if(e==="manual")return"手动触发授权修复";return"未知认证错误"}function qr(e){if(!e||e.length===0)return[];return[...new Set(e)]}function Za(e){let n=qr(e);if(n.length===0)return[];return[...new Set(n.flatMap((t)=>Va[t]||[]))].sort()}function Ja(e){let n=qr(e);if(n.length===0)return"";return n.map((t)=>Da[t]).join(" / ")}function Na(e,n){return oi({message:`${e} 检测到 ${ci(n)}。是否现在进入 bootstrap 授权修复流程?`,initialValue:!0})}function za(e,n){let t=C(e.accountId)||V(process.env,"LICELL_BOOTSTRAP_ACCOUNT_ID","LICELL_ACCOUNT_ID")||V(process.env,"ALI_ACCOUNT_ID")||n?.accountId,s=C(e.adminAk)||V(process.env,"LICELL_BOOTSTRAP_ACCESS_KEY_ID","LICELL_ACCESS_KEY_ID")||V(process.env,"ALI_ACCESS_KEY_ID"),o=C(e.adminSk)||V(process.env,"LICELL_BOOTSTRAP_ACCESS_KEY_SECRET","LICELL_ACCESS_KEY_SECRET")||V(process.env,"ALI_ACCESS_KEY_SECRET"),r=C(e.region)||V(process.env,"LICELL_BOOTSTRAP_REGION","LICELL_REGION")||V(process.env,"ALI_REGION")||n?.region||Ge;return{accountId:t,adminAk:s,adminSk:o,region:r}}function em(e,n){throw Error(`${e} 检测到 ${ci(n)},且当前为非交互模式。
492
+ `+"请先执行 `licell auth repair --account-id <id> --ak <super-ak> --sk <super-sk> [--region cn-hangzhou]`,然后重试。")}function nm(e){if(Fe(e)||ln(e))return!0;let n=`${String(e?.code||"")} ${String(e?.message||e||"")}`.toLowerCase();return n.includes("forbidden")||n.includes("accessdenied")||n.includes("not authorized")||n.includes("permission")}async function tm(e,n){if(!n||e.forceRotateKey)return null;try{let t=await Co({adminAuth:{accountId:n.accountId,ak:n.ak,sk:n.sk,region:n.region},currentAuth:n,userName:e.bootstrapUser,policyName:e.bootstrapPolicy,forceRotateKey:!1});return k.setAuth({accountId:n.accountId,ak:t.accessKeyId,sk:t.accessKeySecret,region:n.region,authSource:"bootstrap",ramUser:t.userName,ramPolicy:t.policyName}),{mode:t.mode,userName:t.userName,policyName:t.policyName,accountId:n.accountId,region:n.region,rotatedKey:t.mode==="rotated-new-key"}}catch(t){if(nm(t))return null;throw t}}async function mt(e){let n=e.currentAuth??k.getAuth(),t=e.interactiveTTY??P(),s=za(e,n),o=await tm(e,n);if(o)return o;let{accountId:r,adminAk:c,adminSk:f,region:u}=s;if(t){console.log(nn.gray(`
493
493
  检测到需要授权修复。`)),console.log(nn.gray("不会配置 RAM 权限可使用 bootstrap:只在内存使用超级 AK/SK,不会写入本地文件。")),console.log(nn.gray(`超级 AK/SK 获取地址: https://ram.console.aliyun.com/profile/access-keys
494
- `));let i=await Na(e.commandLabel,e.reason);if(ri(i))process.exit(0);if(!i)throw Error("操作已取消。你可以稍后执行 `licell auth repair` 或 `licell login --bootstrap-ram`。")}if(!t&&(!r||!c||!f))em(e.commandLabel,e.reason);if(!r)r=A(await Er({message:"输入阿里云 Account ID (主账号ID):",initialValue:n?.accountId||""}),"Account ID");if(!c)c=A(await Er({message:"输入超级 AccessKey ID:"}),"超级 AccessKey ID");if(!f)f=A(await Qa({message:"输入超级 AccessKey Secret:"}),"超级 AccessKey Secret");if(!u)u=A(await Er({message:`默认 Region (回车使用 ${Ge}):`,initialValue:Ge}),"Region");let l=await Cs({adminAuth:{accountId:r,ak:c,sk:f,region:u},currentAuth:n,userName:e.bootstrapUser,policyName:e.bootstrapPolicy,forceRotateKey:Boolean(e.forceRotateKey)});return k.setAuth({accountId:r,ak:l.accessKeyId,sk:l.accessKeySecret,region:u,authSource:"bootstrap",ramUser:l.userName,ramPolicy:l.policyName}),{mode:l.mode,userName:l.userName,policyName:l.policyName,accountId:r,region:u,rotatedKey:l.mode==="rotated-new-key"}}async function pt(e){if(k.getAuth())return;await mt({commandLabel:e.commandLabel,reason:"missing_auth",interactiveTTY:e.interactiveTTY,currentAuth:null})}async function wt(e){let n=qr(e.requiredCapabilities);if(n.length===0)return;let t=k.getAuth();if(!t)return;if(t.authSource==="bootstrap"||t.ramUser||t.ramPolicy)return;let o=Ja(n),s=Za(n).slice(0,6),r=`${e.commandLabel}|${n.join(",")}`,c=e.interactiveTTY??P();if(c){if(ti.has(r))return;if(ti.add(r),console.log(nn.yellow(`
495
- ⚠️ ${e.commandLabel} 需要 ${o} 相关权限。`)),console.log(nn.yellow("当前凭证来自手动登录,若权限不完整会在执行阶段失败。")),s.length>0)console.log(nn.gray(`权限示例: ${s.join(", ")}`));let g=await si({message:"是否现在执行 bootstrap 授权修复(推荐)?",initialValue:!0});if(ri(g))process.exit(0);if(!g)return;await mt({commandLabel:e.commandLabel,reason:"manual",interactiveTTY:c,currentAuth:t,bootstrapUser:e.bootstrapUser,bootstrapPolicy:e.bootstrapPolicy});return}let f=V(process.env,"LICELL_BOOTSTRAP_ACCOUNT_ID","LICELL_ACCOUNT_ID")||V(process.env,"ALI_ACCOUNT_ID")||t.accountId,u=V(process.env,"LICELL_BOOTSTRAP_ACCESS_KEY_ID","LICELL_ACCESS_KEY_ID")||V(process.env,"ALI_ACCESS_KEY_ID"),l=V(process.env,"LICELL_BOOTSTRAP_ACCESS_KEY_SECRET","LICELL_ACCESS_KEY_SECRET")||V(process.env,"ALI_ACCESS_KEY_SECRET"),i=V(process.env,"LICELL_BOOTSTRAP_REGION","LICELL_REGION")||V(process.env,"ALI_REGION")||t.region||Ge;if(f&&u&&l)try{await mt({commandLabel:e.commandLabel,reason:"manual",interactiveTTY:!1,currentAuth:t,accountId:f,region:i,adminAk:u,adminSk:l,bootstrapUser:e.bootstrapUser,bootstrapPolicy:e.bootstrapPolicy});return}catch(g){console.warn(nn.yellow(`⚠️ 自动授权修复未成功,继续执行原命令: ${String(g?.message||g)}`))}if(oi.has(r))return;if(oi.add(r),console.warn(nn.yellow(`⚠️ ${e.commandLabel} 可能需要 ${o} 权限。`)),s.length>0)console.warn(nn.gray(`权限示例: ${s.join(", ")}`));console.warn(nn.yellow("建议先执行 `licell auth repair --account-id <id> --ak <super-ak> --sk <super-sk>` 后再重试。"))}async function Pr(e,n){let t=Sr(e);if(t==="unknown")return!1;return await mt({commandLabel:n.commandLabel,reason:t,interactiveTTY:n.interactiveTTY,bootstrapUser:n.bootstrapUser,bootstrapPolicy:n.bootstrapPolicy,forceRotateKey:t==="invalid_credentials"}),!0}async function M(e,n){let t=e.interactiveTTY??P();if(e.preflight!==!1)await pt({commandLabel:e.commandLabel,interactiveTTY:t}),await wt({commandLabel:e.commandLabel,interactiveTTY:t,bootstrapUser:e.bootstrapUser,bootstrapPolicy:e.bootstrapPolicy,requiredCapabilities:e.requiredCapabilities});let o=!1;while(!0)try{return await n()}catch(s){if(!o){if(await Pr(s,{commandLabel:e.commandLabel,interactiveTTY:t,bootstrapUser:e.bootstrapUser,bootstrapPolicy:e.bootstrapPolicy})){o=!0;continue}}throw s}}var Da,Va,ti,oi;var Le=O(()=>{Es();oe();K();Da={fc:"函数计算",dns:"云解析 DNS",oss:"OSS",rds:"RDS",redis:"Redis/Tair",cdn:"CDN",vpc:"VPC",cr:"容器镜像仓库 CR",logs:"日志服务 SLS"},Va={fc:["fc:ListFunctions","fc:GetFunction","fc:UpdateFunction"],dns:["alidns:DescribeDomainRecords","alidns:AddDomainRecord","alidns:DeleteDomainRecord"],oss:["oss:ListBuckets","oss:GetBucketInfo","oss:PutObject"],rds:["rds:DescribeDBInstances","rds:CreateDBInstance","rds:CreateDatabase"],redis:["kvstore:DescribeInstances","kvstore:CreateTairKVCacheVNode","kvstore:ResetAccountPassword"],cdn:["cdn:DescribeUserDomains","cdn:AddCdnDomain","cdn:BatchSetCdnDomainConfig"],vpc:["vpc:DescribeVpcs","vpc:CreateVpc","vpc:CreateVSwitch"],cr:["cr:ListInstance","cr:CreateNamespace","cr:CreateRepository"],logs:["log:GetLogs"]},ti=new Set,oi=new Set});var ni={};go(ni,{runInteractiveLogin:()=>Bo,registerAuthCommands:()=>Rr});import{text as Mo,password as om,confirm as sm,isCancel as rm}from"@clack/prompts";import ae from"picocolors";async function Bo(e={}){let n=P(),t=C(e.accountId)||V(process.env,"LICELL_ACCOUNT_ID","ALI_ACCOUNT_ID"),o=C(e.ak)||V(process.env,"LICELL_ACCESS_KEY_ID","ALI_ACCESS_KEY_ID"),s=C(e.sk)||V(process.env,"LICELL_ACCESS_KEY_SECRET","ALI_ACCESS_KEY_SECRET"),r=C(e.region)||V(process.env,"LICELL_REGION","ALI_REGION"),c=Boolean(e.bootstrapRam);if(n&&!c&&!t&&!o&&!s){console.log(ae.gray(`
494
+ `));let i=await Na(e.commandLabel,e.reason);if(ri(i))process.exit(0);if(!i)throw Error("操作已取消。你可以稍后执行 `licell auth repair` 或 `licell login --bootstrap-ram`。")}if(!t&&(!r||!c||!f))em(e.commandLabel,e.reason);if(!r)r=L(await Er({message:"输入阿里云 Account ID (主账号ID):",initialValue:n?.accountId||""}),"Account ID");if(!c)c=L(await Er({message:"输入超级 AccessKey ID:"}),"超级 AccessKey ID");if(!f)f=L(await Qa({message:"输入超级 AccessKey Secret:"}),"超级 AccessKey Secret");if(!u)u=L(await Er({message:`默认 Region (回车使用 ${Ge}):`,initialValue:Ge}),"Region");let l=await Co({adminAuth:{accountId:r,ak:c,sk:f,region:u},currentAuth:n,userName:e.bootstrapUser,policyName:e.bootstrapPolicy,forceRotateKey:Boolean(e.forceRotateKey)});return k.setAuth({accountId:r,ak:l.accessKeyId,sk:l.accessKeySecret,region:u,authSource:"bootstrap",ramUser:l.userName,ramPolicy:l.policyName}),{mode:l.mode,userName:l.userName,policyName:l.policyName,accountId:r,region:u,rotatedKey:l.mode==="rotated-new-key"}}async function pt(e){if(k.getAuth())return;await mt({commandLabel:e.commandLabel,reason:"missing_auth",interactiveTTY:e.interactiveTTY,currentAuth:null})}async function wt(e){let n=qr(e.requiredCapabilities);if(n.length===0)return;let t=k.getAuth();if(!t)return;if(t.authSource==="bootstrap"||t.ramUser||t.ramPolicy)return;let s=Ja(n),o=Za(n).slice(0,6),r=`${e.commandLabel}|${n.join(",")}`,c=e.interactiveTTY??P();if(c){if(ti.has(r))return;if(ti.add(r),console.log(nn.yellow(`
495
+ ⚠️ ${e.commandLabel} 需要 ${s} 相关权限。`)),console.log(nn.yellow("当前凭证来自手动登录,若权限不完整会在执行阶段失败。")),o.length>0)console.log(nn.gray(`权限示例: ${o.join(", ")}`));let g=await oi({message:"是否现在执行 bootstrap 授权修复(推荐)?",initialValue:!0});if(ri(g))process.exit(0);if(!g)return;await mt({commandLabel:e.commandLabel,reason:"manual",interactiveTTY:c,currentAuth:t,bootstrapUser:e.bootstrapUser,bootstrapPolicy:e.bootstrapPolicy});return}let f=V(process.env,"LICELL_BOOTSTRAP_ACCOUNT_ID","LICELL_ACCOUNT_ID")||V(process.env,"ALI_ACCOUNT_ID")||t.accountId,u=V(process.env,"LICELL_BOOTSTRAP_ACCESS_KEY_ID","LICELL_ACCESS_KEY_ID")||V(process.env,"ALI_ACCESS_KEY_ID"),l=V(process.env,"LICELL_BOOTSTRAP_ACCESS_KEY_SECRET","LICELL_ACCESS_KEY_SECRET")||V(process.env,"ALI_ACCESS_KEY_SECRET"),i=V(process.env,"LICELL_BOOTSTRAP_REGION","LICELL_REGION")||V(process.env,"ALI_REGION")||t.region||Ge;if(f&&u&&l)try{await mt({commandLabel:e.commandLabel,reason:"manual",interactiveTTY:!1,currentAuth:t,accountId:f,region:i,adminAk:u,adminSk:l,bootstrapUser:e.bootstrapUser,bootstrapPolicy:e.bootstrapPolicy});return}catch(g){console.warn(nn.yellow(`⚠️ 自动授权修复未成功,继续执行原命令: ${String(g?.message||g)}`))}if(si.has(r))return;if(si.add(r),console.warn(nn.yellow(`⚠️ ${e.commandLabel} 可能需要 ${s} 权限。`)),o.length>0)console.warn(nn.gray(`权限示例: ${o.join(", ")}`));console.warn(nn.yellow("建议先执行 `licell auth repair --account-id <id> --ak <super-ak> --sk <super-sk>` 后再重试。"))}async function Pr(e,n){let t=Sr(e);if(t==="unknown")return!1;return await mt({commandLabel:n.commandLabel,reason:t,interactiveTTY:n.interactiveTTY,bootstrapUser:n.bootstrapUser,bootstrapPolicy:n.bootstrapPolicy,forceRotateKey:t==="invalid_credentials"}),!0}async function M(e,n){let t=e.interactiveTTY??P();if(e.preflight!==!1)await pt({commandLabel:e.commandLabel,interactiveTTY:t}),await wt({commandLabel:e.commandLabel,interactiveTTY:t,bootstrapUser:e.bootstrapUser,bootstrapPolicy:e.bootstrapPolicy,requiredCapabilities:e.requiredCapabilities});let s=!1;while(!0)try{return await n()}catch(o){if(!s){if(await Pr(o,{commandLabel:e.commandLabel,interactiveTTY:t,bootstrapUser:e.bootstrapUser,bootstrapPolicy:e.bootstrapPolicy})){s=!0;continue}}throw o}}var Da,Va,ti,si;var Ae=O(()=>{Eo();se();K();Da={fc:"函数计算",dns:"云解析 DNS",oss:"OSS",rds:"RDS",redis:"Redis/Tair",cdn:"CDN",vpc:"VPC",cr:"容器镜像仓库 CR",logs:"日志服务 SLS"},Va={fc:["fc:ListFunctions","fc:GetFunction","fc:UpdateFunction"],dns:["alidns:DescribeDomainRecords","alidns:AddDomainRecord","alidns:DeleteDomainRecord"],oss:["oss:ListBuckets","oss:GetBucketInfo","oss:PutObject"],rds:["rds:DescribeDBInstances","rds:CreateDBInstance","rds:CreateDatabase"],redis:["kvstore:DescribeInstances","kvstore:CreateTairKVCacheVNode","kvstore:ResetAccountPassword"],cdn:["cdn:DescribeUserDomains","cdn:AddCdnDomain","cdn:BatchSetCdnDomainConfig"],vpc:["vpc:DescribeVpcs","vpc:CreateVpc","vpc:CreateVSwitch"],cr:["cr:ListInstance","cr:CreateNamespace","cr:CreateRepository"],logs:["log:GetLogs"]},ti=new Set,si=new Set});var ni={};gs(ni,{runInteractiveLogin:()=>Ms,registerAuthCommands:()=>Rr});import{text as Gs,password as sm,confirm as om,isCancel as rm}from"@clack/prompts";import ae from"picocolors";async function Ms(e={}){let n=P(),t=C(e.accountId)||V(process.env,"LICELL_ACCOUNT_ID","ALI_ACCOUNT_ID"),s=C(e.ak)||V(process.env,"LICELL_ACCESS_KEY_ID","ALI_ACCESS_KEY_ID"),o=C(e.sk)||V(process.env,"LICELL_ACCESS_KEY_SECRET","ALI_ACCESS_KEY_SECRET"),r=C(e.region)||V(process.env,"LICELL_REGION","ALI_REGION"),c=Boolean(e.bootstrapRam);if(n&&!c&&!t&&!s&&!o){console.log(ae.gray(`
496
496
  不会配置 RAM 权限?建议使用 bootstrap 模式自动完成最小权限配置。`)),console.log(ae.gray("超级 AK/SK 获取地址: https://ram.console.aliyun.com/profile/access-keys")),console.log(ae.gray(`安全说明: licell 不会保存你输入的超级 key,仅保存自动创建的 licell 专用 key。
497
- `));let h=await sm({message:"是否启用 bootstrap 模式自动配置 RAM 用户与专用 AccessKey?",initialValue:!0});if(rm(h))process.exit(0);c=Boolean(h)}if(!n&&(!t||!o||!s))throw Error("非交互模式下 login 需要传入 --account-id、--ak、--sk");let f=t?A(t,"Account ID"):A(await Mo({message:"输入阿里云 Account ID (主账号ID):"}),"Account ID"),u=o?A(o,"AccessKey ID"):A(await Mo({message:"输入 AccessKey ID:"}),"AccessKey ID"),l=s?A(s,"AccessKey Secret"):A(await om({message:"输入 AccessKey Secret:"}),"AccessKey Secret"),i=!n&&!r?Ge:r?A(r,"Region").toLowerCase():A(await Mo({message:`默认 Region (回车使用 ${Ge}):`,initialValue:Ge}),"Region").toLowerCase();if(!c){if(k.setAuth({accountId:f,ak:u,sk:l,region:i,authSource:"manual"}),a())S({stage:"auth",action:"login",mode:"manual",accountId:f,region:i});else q(ae.green("✅ 凭证已安全保存至 ~/.licell-cli/auth.json"));return}let g=C(e.bootstrapUser),y=C(e.bootstrapPolicy);console.log(ae.gray(`
498
- bootstrap 模式:正在创建 licell 专用 RAM 子用户与 AccessKey(不会保存你输入的高权限 key)...`));let d=await tf({adminAuth:{accountId:f,ak:u,sk:l,region:i},userName:g||void 0,policyName:y||void 0});k.setAuth({accountId:f,ak:d.accessKeyId,sk:d.accessKeySecret,region:i,authSource:"bootstrap",ramUser:d.userName,ramPolicy:d.policyName});let m=`${d.createdUser?"created-user":"reuse-user"}, ${d.createdPolicy?"created-policy":"reuse-policy"}`;if(a())S({stage:"auth",action:"login",mode:"bootstrap",accountId:f,region:i,ramUser:d.userName,ramPolicy:d.policyName,summary:m});else q(ae.green(`✅ bootstrap 完成,已保存 licell 专用凭证到 ~/.licell-cli/auth.json (${m})`))}function Rr(e){e.command("login","配置阿里云凭证").option("--account-id <id>","阿里云 Account ID(CI 场景)").option("--ak <accessKeyId>","阿里云 AccessKey ID(CI 场景)").option("--sk <accessKeySecret>","阿里云 AccessKey Secret(CI 场景)").option("--region <region>",`默认地域,默认 ${Ge}`).option("--bootstrap-ram","使用高权限 AK/SK 自动创建 licell 专用 RAM 用户与最小权限 AK/SK(仅保存新 key)").option("--bootstrap-user <name>","bootstrap 模式下 RAM 用户名,默认 licell-operator").option("--bootstrap-policy <name>","bootstrap 模式下自定义策略名,默认 LicellOperatorPolicy").action(async(n)=>{if(!a())j(ae.bgBlue(ae.white(" ▲ Licell CLI (AliCloud) ")));else X({stage:"auth",action:"login",status:"start"});await Bo(n)}),e.command("auth repair","修复凭证权限(推荐:用超级 AK/SK 自动补齐 licell 最小权限并继续使用)").option("--account-id <id>","阿里云 Account ID(CI 场景)").option("--ak <accessKeyId>","超级 AccessKey ID(仅用于本次修复,不会保存)").option("--sk <accessKeySecret>","超级 AccessKey Secret(仅用于本次修复,不会保存)").option("--region <region>",`默认地域,默认 ${Ge}`).option("--bootstrap-user <name>","修复目标 RAM 用户名(默认自动识别当前 key 所属用户)").option("--bootstrap-policy <name>","修复使用的自定义策略名(默认 LicellOperatorPolicy)").action(async(n)=>{if(!a())j(ae.bgBlue(ae.white(" ▲ Licell Auth Repair ")));else X({stage:"auth",action:"auth repair",status:"start"});let t=P(),o=C(n.accountId)||V(process.env,"LICELL_BOOTSTRAP_ACCOUNT_ID","LICELL_ACCOUNT_ID")||V(process.env,"ALI_ACCOUNT_ID"),s=C(n.ak)||V(process.env,"LICELL_BOOTSTRAP_ACCESS_KEY_ID","LICELL_ACCESS_KEY_ID")||V(process.env,"ALI_ACCESS_KEY_ID"),r=C(n.sk)||V(process.env,"LICELL_BOOTSTRAP_ACCESS_KEY_SECRET","LICELL_ACCESS_KEY_SECRET")||V(process.env,"ALI_ACCESS_KEY_SECRET"),c=C(n.region)||V(process.env,"LICELL_BOOTSTRAP_REGION","LICELL_REGION")||V(process.env,"ALI_REGION"),f=C(n.bootstrapUser),u=C(n.bootstrapPolicy),l=k.getAuth(),i=await mt({commandLabel:"licell auth repair",reason:"manual",interactiveTTY:t,currentAuth:l,accountId:o||l?.accountId,region:c||l?.region,adminAk:s,adminSk:r,bootstrapUser:f||void 0,bootstrapPolicy:u||void 0,forceRotateKey:!1}),g=i.rotatedKey?"rotated-key":"reuse-current-key";if(a())S({stage:"auth",action:"auth repair",mode:g,accountId:i.accountId,region:i.region,userName:i.userName,policyName:i.policyName});else q(ae.green(`✅ 授权修复完成,已更新 ~/.licell-cli/auth.json (${g}, user=${i.userName}, policy=${i.policyName})`))}),e.command("logout","清除本地凭证").action(()=>{if(!k.getAuth()){if(a())S({stage:"auth",action:"logout",cleared:!1});else q(ae.yellow("当前没有可清理的登录凭证"));return}if(k.clearAuth(),a())S({stage:"auth",action:"logout",cleared:!0});else q(ae.green("✅ 已清除 ~/.licell-cli/auth.json"))}),e.command("whoami","查看当前登录身份").action(()=>{let n=k.getAuth();if(!n){let o=Error("未登录,请先执行 `licell login`");if(a())he(o,{stage:"auth"});else q(ae.red("未登录,请先执行 `licell login`"));process.exitCode=1;return}let t=Nu(n.ak);if(a())S({stage:"auth",action:"whoami",accountId:n.accountId,region:n.region,ak:t});else console.log(`
497
+ `));let h=await om({message:"是否启用 bootstrap 模式自动配置 RAM 用户与专用 AccessKey?",initialValue:!0});if(rm(h))process.exit(0);c=Boolean(h)}if(!n&&(!t||!s||!o))throw Error("非交互模式下 login 需要传入 --account-id、--ak、--sk");let f=t?L(t,"Account ID"):L(await Gs({message:"输入阿里云 Account ID (主账号ID):"}),"Account ID"),u=s?L(s,"AccessKey ID"):L(await Gs({message:"输入 AccessKey ID:"}),"AccessKey ID"),l=o?L(o,"AccessKey Secret"):L(await sm({message:"输入 AccessKey Secret:"}),"AccessKey Secret"),i=!n&&!r?Ge:r?L(r,"Region").toLowerCase():L(await Gs({message:`默认 Region (回车使用 ${Ge}):`,initialValue:Ge}),"Region").toLowerCase();if(!c){if(k.setAuth({accountId:f,ak:u,sk:l,region:i,authSource:"manual"}),a())S({stage:"auth",action:"login",mode:"manual",accountId:f,region:i});else q(ae.green("✅ 凭证已安全保存至 ~/.licell-cli/auth.json"));return}let g=C(e.bootstrapUser),y=C(e.bootstrapPolicy);console.log(ae.gray(`
498
+ bootstrap 模式:正在创建 licell 专用 RAM 子用户与 AccessKey(不会保存你输入的高权限 key)...`));let d=await tf({adminAuth:{accountId:f,ak:u,sk:l,region:i},userName:g||void 0,policyName:y||void 0});k.setAuth({accountId:f,ak:d.accessKeyId,sk:d.accessKeySecret,region:i,authSource:"bootstrap",ramUser:d.userName,ramPolicy:d.policyName});let m=`${d.createdUser?"created-user":"reuse-user"}, ${d.createdPolicy?"created-policy":"reuse-policy"}`;if(a())S({stage:"auth",action:"login",mode:"bootstrap",accountId:f,region:i,ramUser:d.userName,ramPolicy:d.policyName,summary:m});else q(ae.green(`✅ bootstrap 完成,已保存 licell 专用凭证到 ~/.licell-cli/auth.json (${m})`))}function Rr(e){e.command("login","配置阿里云凭证").option("--account-id <id>","阿里云 Account ID(CI 场景)").option("--ak <accessKeyId>","阿里云 AccessKey ID(CI 场景)").option("--sk <accessKeySecret>","阿里云 AccessKey Secret(CI 场景)").option("--region <region>",`默认地域,默认 ${Ge}`).option("--bootstrap-ram","使用高权限 AK/SK 自动创建 licell 专用 RAM 用户与最小权限 AK/SK(仅保存新 key)").option("--bootstrap-user <name>","bootstrap 模式下 RAM 用户名,默认 licell-operator").option("--bootstrap-policy <name>","bootstrap 模式下自定义策略名,默认 LicellOperatorPolicy").action(async(n)=>{if(!a())j(ae.bgBlue(ae.white(" ▲ Licell CLI (AliCloud) ")));else X({stage:"auth",action:"login",status:"start"});await Ms(n)}),e.command("auth repair","修复凭证权限(推荐:用超级 AK/SK 自动补齐 licell 最小权限并继续使用)").option("--account-id <id>","阿里云 Account ID(CI 场景)").option("--ak <accessKeyId>","超级 AccessKey ID(仅用于本次修复,不会保存)").option("--sk <accessKeySecret>","超级 AccessKey Secret(仅用于本次修复,不会保存)").option("--region <region>",`默认地域,默认 ${Ge}`).option("--bootstrap-user <name>","修复目标 RAM 用户名(默认自动识别当前 key 所属用户)").option("--bootstrap-policy <name>","修复使用的自定义策略名(默认 LicellOperatorPolicy)").action(async(n)=>{if(!a())j(ae.bgBlue(ae.white(" ▲ Licell Auth Repair ")));else X({stage:"auth",action:"auth repair",status:"start"});let t=P(),s=C(n.accountId)||V(process.env,"LICELL_BOOTSTRAP_ACCOUNT_ID","LICELL_ACCOUNT_ID")||V(process.env,"ALI_ACCOUNT_ID"),o=C(n.ak)||V(process.env,"LICELL_BOOTSTRAP_ACCESS_KEY_ID","LICELL_ACCESS_KEY_ID")||V(process.env,"ALI_ACCESS_KEY_ID"),r=C(n.sk)||V(process.env,"LICELL_BOOTSTRAP_ACCESS_KEY_SECRET","LICELL_ACCESS_KEY_SECRET")||V(process.env,"ALI_ACCESS_KEY_SECRET"),c=C(n.region)||V(process.env,"LICELL_BOOTSTRAP_REGION","LICELL_REGION")||V(process.env,"ALI_REGION"),f=C(n.bootstrapUser),u=C(n.bootstrapPolicy),l=k.getAuth(),i=await mt({commandLabel:"licell auth repair",reason:"manual",interactiveTTY:t,currentAuth:l,accountId:s||l?.accountId,region:c||l?.region,adminAk:o,adminSk:r,bootstrapUser:f||void 0,bootstrapPolicy:u||void 0,forceRotateKey:!1}),g=i.rotatedKey?"rotated-key":"reuse-current-key";if(a())S({stage:"auth",action:"auth repair",mode:g,accountId:i.accountId,region:i.region,userName:i.userName,policyName:i.policyName});else q(ae.green(`✅ 授权修复完成,已更新 ~/.licell-cli/auth.json (${g}, user=${i.userName}, policy=${i.policyName})`))}),e.command("logout","清除本地凭证").action(()=>{if(!k.getAuth()){if(a())S({stage:"auth",action:"logout",cleared:!1});else q(ae.yellow("当前没有可清理的登录凭证"));return}if(k.clearAuth(),a())S({stage:"auth",action:"logout",cleared:!0});else q(ae.green("✅ 已清除 ~/.licell-cli/auth.json"))}),e.command("whoami","查看当前登录身份").action(()=>{let n=k.getAuth();if(!n){let s=Error("未登录,请先执行 `licell login`");if(a())he(s,{stage:"auth"});else q(ae.red("未登录,请先执行 `licell login`"));process.exitCode=1;return}let t=Nu(n.ak);if(a())S({stage:"auth",action:"whoami",accountId:n.accountId,region:n.region,ak:t});else console.log(`
499
499
  accountId: ${ae.cyan(n.accountId)}`),console.log(`region: ${ae.cyan(n.region)}`),console.log(`ak: ${ae.cyan(t)}
500
- `),q("Done.")}),e.command("switch","切换默认 region").option("--region <region>","目标 region(如 cn-hangzhou)").action(async(n)=>{let t=k.getAuth();if(!t){let c=Error("未登录,请先执行 `licell login`");if(a())he(c,{stage:"auth"});else q(ae.red("未登录,请先执行 `licell login`"));process.exitCode=1;return}let o=P(),s=C(n.region);if(!s&&!o)throw Error("非交互模式下 switch 需要传入 --region");let r=s?br(s):br(A(await Mo({message:"输入新 region:",initialValue:t.region}),"region"));if(r===t.region){if(a())S({stage:"auth",action:"switch",changed:!1,region:r});else q(ae.yellow(`region 未变化,仍为 ${r}`));return}if(k.setAuth({...t,region:r}),a())S({stage:"auth",action:"switch",changed:!0,region:r});else q(ae.green(`✅ 默认 region 已切换为 ${r}`))})}var Go=O(()=>{K();Es();Le();oe();re()});var Zr={};go(Zr,{resolvePublicIp:()=>Bp});import{text as Up,isCancel as vp}from"@clack/prompts";async function Mp(){for(let e of Gp)try{let n=new AbortController,t=setTimeout(()=>n.abort(),5000),o=await fetch(e,{signal:n.signal});if(clearTimeout(t),!o.ok)continue;let s=(await o.text()).trim();if(bl.test(s))return s}catch{}return null}async function Bp(){let e=await Mp();if(e)return e;if(!P())throw Error("无法自动获取公网 IP,请通过 --ip 参数手动指定");let n=await Up({message:"无法自动获取公网 IP,请手动输入你的公网 IP 地址:",placeholder:"例如: 1.2.3.4",validate:(t)=>{if(!bl.test(t.trim()))return"请输入有效的 IPv4 地址"}});if(vp(n))process.exit(0);return String(n).trim()}var Gp,bl;var Jr=O(()=>{oe();Gp=["https://ifconfig.me/ip","https://api.ipify.org","https://checkip.amazonaws.com"],bl=/^(\d{1,3}\.){3}\d{1,3}$/});import{cac as F$}from"cac";import io from"picocolors";var ay=new Set(["fn list","fn info","fn invoke","fn rm","oss list","oss info","oss ls","oss upload","oss bucket","db add","db list","db info","db connect","db public-access","cache add","cache list","cache info","cache connect","cache rotate-password","cache public-access","e2e run","e2e cleanup","e2e list","release list","release promote","release rollback","release prune","domain add","domain rm","auth repair","dns records list","dns records add","dns records rm","env list","env set","env rm","env pull","skills init","deploy spec","deploy check"]);function Ac(e){return typeof e==="string"&&e.startsWith("-")}function Lc(e){if(e.length<4)return e;let n=e.length;for(let t=2;t<e.length;t+=1){if(!Ac(e[t]))continue;n=t;break}for(let t=2;t<n;t+=1)for(let o=3;o>=2;o-=1){if(t+o>n)continue;let s=e.slice(t,t+o);if(s.some((c)=>Ac(c)))continue;let r=s.join(" ");if(!ay.has(r))continue;return[...e.slice(0,t),r,...e.slice(t+o)]}return e}Go();K();ce();oe();re();import{select as am,text as mm,isCancel as pm}from"@clack/prompts";import $t from"picocolors";Ve();import{existsSync as we,mkdirSync as cm,readFileSync as fi,readdirSync as fm,writeFileSync as um}from"fs";import{basename as im,dirname as lm,join as $e}from"path";var ui=new Set(["nodejs20","nodejs22"]),ii=new Set(["python3.12","python3.13"]),gm=new Set(["docker"]),ym=new Set([".licell",".ali",".git",".DS_Store",".vscode",".idea","node_modules"]);function li(e=process.cwd()){return im(e).toLowerCase().replace(/[^a-z0-9-]/g,"-").replace(/-+/g,"-").replace(/^-+|-+$/g,"")||"licell-app"}function gi(e){let n=e.trim().toLowerCase();if(!/^[a-z0-9-]+$/.test(n))throw Error("应用名仅允许小写字母、数字和短横线");if(n.length>128)throw Error("应用名长度不能超过 128 个字符");return n}function dm(e){if(ii.has(e))return"python";if(gm.has(e))return"docker";if(ui.has(e))return"node";throw Error(`不支持的 runtime: ${e}`)}function Ir(e){let n=e?Qn(e):"nodejs20";return{template:dm(n),runtime:n}}function hm(e,n){if(e==="docker")return"docker";if(e==="python"){if(n&&ii.has(n))return n;return"python3.12"}if(n&&ui.has(n))return n;return"nodejs20"}function yi(e,n){let t=hm(e,n);if(e==="docker")return[{path:".gitignore",content:`node_modules
500
+ `),q("Done.")}),e.command("switch","切换默认 region").option("--region <region>","目标 region(如 cn-hangzhou)").action(async(n)=>{let t=k.getAuth();if(!t){let c=Error("未登录,请先执行 `licell login`");if(a())he(c,{stage:"auth"});else q(ae.red("未登录,请先执行 `licell login`"));process.exitCode=1;return}let s=P(),o=C(n.region);if(!o&&!s)throw Error("非交互模式下 switch 需要传入 --region");let r=o?br(o):br(L(await Gs({message:"输入新 region:",initialValue:t.region}),"region"));if(r===t.region){if(a())S({stage:"auth",action:"switch",changed:!1,region:r});else q(ae.yellow(`region 未变化,仍为 ${r}`));return}if(k.setAuth({...t,region:r}),a())S({stage:"auth",action:"switch",changed:!0,region:r});else q(ae.green(`✅ 默认 region 已切换为 ${r}`))})}var vs=O(()=>{K();Eo();Ae();se();re()});var Zr={};gs(Zr,{resolvePublicIp:()=>Bp});import{text as Up,isCancel as vp}from"@clack/prompts";async function Mp(){for(let e of Gp)try{let n=new AbortController,t=setTimeout(()=>n.abort(),5000),s=await fetch(e,{signal:n.signal});if(clearTimeout(t),!s.ok)continue;let o=(await s.text()).trim();if(bl.test(o))return o}catch{}return null}async function Bp(){let e=await Mp();if(e)return e;if(!P())throw Error("无法自动获取公网 IP,请通过 --ip 参数手动指定");let n=await Up({message:"无法自动获取公网 IP,请手动输入你的公网 IP 地址:",placeholder:"例如: 1.2.3.4",validate:(t)=>{if(!bl.test(t.trim()))return"请输入有效的 IPv4 地址"}});if(vp(n))process.exit(0);return String(n).trim()}var Gp,bl;var Jr=O(()=>{se();Gp=["https://ifconfig.me/ip","https://api.ipify.org","https://checkip.amazonaws.com"],bl=/^(\d{1,3}\.){3}\d{1,3}$/});import{cac as F$}from"cac";import is from"picocolors";var ay=new Set(["fn list","fn info","fn invoke","fn rm","oss list","oss info","oss ls","oss upload","oss bucket","db add","db list","db info","db connect","db public-access","cache add","cache list","cache info","cache connect","cache rotate-password","cache public-access","e2e run","e2e cleanup","e2e list","release list","release promote","release rollback","release prune","domain add","domain rm","auth repair","dns records list","dns records add","dns records rm","env list","env set","env rm","env pull","skills init","deploy spec","deploy check"]);function Lc(e){return typeof e==="string"&&e.startsWith("-")}function Ac(e){if(e.length<4)return e;let n=e.length;for(let t=2;t<e.length;t+=1){if(!Lc(e[t]))continue;n=t;break}for(let t=2;t<n;t+=1)for(let s=3;s>=2;s-=1){if(t+s>n)continue;let o=e.slice(t,t+s);if(o.some((c)=>Lc(c)))continue;let r=o.join(" ");if(!ay.has(r))continue;return[...e.slice(0,t),r,...e.slice(t+s)]}return e}vs();K();ce();se();re();import{select as am,text as mm,isCancel as pm}from"@clack/prompts";import $t from"picocolors";Ve();import{existsSync as we,mkdirSync as cm,readFileSync as fi,readdirSync as fm,writeFileSync as um}from"fs";import{basename as im,dirname as lm,join as $e}from"path";var ui=new Set(["nodejs20","nodejs22"]),ii=new Set(["python3.12","python3.13"]),gm=new Set(["docker"]),ym=new Set([".licell",".ali",".git",".DS_Store",".vscode",".idea","node_modules"]);function li(e=process.cwd()){return im(e).toLowerCase().replace(/[^a-z0-9-]/g,"-").replace(/-+/g,"-").replace(/^-+|-+$/g,"")||"licell-app"}function gi(e){let n=e.trim().toLowerCase();if(!/^[a-z0-9-]+$/.test(n))throw Error("应用名仅允许小写字母、数字和短横线");if(n.length>128)throw Error("应用名长度不能超过 128 个字符");return n}function dm(e){if(ii.has(e))return"python";if(gm.has(e))return"docker";if(ui.has(e))return"node";throw Error(`不支持的 runtime: ${e}`)}function Ir(e){let n=e?Qn(e):"nodejs20";return{template:dm(n),runtime:n}}function hm(e,n){if(e==="docker")return"docker";if(e==="python"){if(n&&ii.has(n))return n;return"python3.12"}if(n&&ui.has(n))return n;return"nodejs20"}function yi(e,n){let t=hm(e,n);if(e==="docker")return[{path:".gitignore",content:`node_modules
501
501
  .licell/
502
502
  .ali/
503
503
  .env
@@ -1386,20 +1386,20 @@ bun run dev
1386
1386
  \`\`\`bash
1387
1387
  licell deploy --type api --runtime ${t} --entry src/index.ts --target preview
1388
1388
  \`\`\`
1389
- `}]}function di(e=process.cwd()){if(fm(e,{withFileTypes:!0}).map((s)=>s.name).filter((s)=>!ym.has(s)).length>0)return!1;let t=[".gitignore","package.json","pyproject.toml","requirements.txt","Dockerfile","bun.lock","bun.lockb","pnpm-lock.yaml","yarn.lock","package-lock.json","npm-shrinkwrap.json","tsconfig.json","src","app"];for(let s of t)if(we($e(e,s)))return!1;let o=[".env","README.md"];for(let s of o)if(we($e(e,s)))return!1;return!0}function hi(e=process.cwd()){if(we($e(e,"Dockerfile")))return{template:"docker",runtime:"docker"};if(we($e(e,"requirements.txt"))||we($e(e,"pyproject.toml"))||we($e(e,"src","main.py")))return{template:"python",runtime:"python3.12"};if(we($e(e,"package.json"))||we($e(e,"bun.lock"))||we($e(e,"bun.lockb"))||we($e(e,"pnpm-lock.yaml"))||we($e(e,"yarn.lock"))||we($e(e,"package-lock.json"))||we($e(e,"npm-shrinkwrap.json"))||we($e(e,"tsconfig.json"))||we($e(e,"src","index.ts"))||we($e(e,"src","index.js")))return{template:"node",runtime:"nodejs20"};return{template:"node",runtime:"nodejs20"}}function ai(e,n,t=!1){let o=[],s=[],r=[];for(let c of n){let f=$e(e,c.path);if(!we(f))continue;if(fi(f,"utf-8")===c.content){r.push(c.path);continue}if(!t)o.push(c.path)}if(o.length>0)throw Error(`以下文件已存在且内容不同,请使用 --force 覆盖:
1390
- ${o.join(`
1391
- `)}`);for(let c of n){let f=$e(e,c.path);if(we(f)&&fi(f,"utf-8")===c.content)continue;cm(lm(f),{recursive:!0}),um(f,c.content),s.push(c.path)}return{written:s,skipped:r}}function mi(e){e.command("init","初始化 FC 项目(空目录生成脚手架,已有项目写入 licell 配置)").option("--runtime <runtime>","默认 runtime:nodejs20/nodejs22/python3.12/python3.13/docker").option("--app <name>","应用名(用于 FC functionName)").option("--force","在已有项目目录生成/覆盖脚手架文件").option("--yes","使用默认值,不进入交互").action(async(n)=>{if(!a())j($t.bgBlue($t.white(" ⚡ Licell Project Init ")));else X({stage:"init",action:"init",status:"start"});let t=P(),o=n.yes||!t,s=di(process.cwd()),r=typeof n.runtime==="string"&&n.runtime.trim().length>0,c=s&&!r&&!o;try{let f=n.runtime;if(!f&&c){let p=await am({message:"选择默认 runtime:",options:[{value:"nodejs20",label:"nodejs20 (Node TypeScript)"},{value:"nodejs22",label:"nodejs22 (Node 22 Custom Runtime)"},{value:"python3.12",label:"python3.12 (Python Built-in Runtime)"},{value:"python3.13",label:"python3.13 (Python 3.13 Custom Runtime)"},{value:"docker",label:"docker (Bun + TypeScript + Hono)"}]});if(pm(p)){if(a())throw Error("操作已取消");process.exit(0)}f=String(p)}let u=f?Ir(f):s?Ir():hi(process.cwd()),{template:l,runtime:i}=u,g=s||!s&&r&&Boolean(n.force),y=k.getProject(),d=n.app||y.appName||li();if(!n.app&&!o)d=A(await mm({message:"应用名(用于 FC functionName):",initialValue:d}),"应用名");let m=gi(d),h=L();h.start(g?"正在生成项目脚手架...":"正在写入 licell 项目配置...");let{written:b,skipped:$}=g?ai(process.cwd(),yi(l,i),Boolean(n.force)):{written:[],skipped:[]};if(k.setProject({appName:m,runtime:i}),h.stop($t.green(g?"✅ 脚手架创建完成":"✅ 配置写入完成")),console.log(`runtime: ${$t.cyan(i)}`),console.log(`appName: ${$t.cyan(m)}`),console.log(`mode: ${$t.cyan(g?"scaffold+config":"config-only")}`),!g)console.log(`
1389
+ `}]}function di(e=process.cwd()){if(fm(e,{withFileTypes:!0}).map((o)=>o.name).filter((o)=>!ym.has(o)).length>0)return!1;let t=[".gitignore","package.json","pyproject.toml","requirements.txt","Dockerfile","bun.lock","bun.lockb","pnpm-lock.yaml","yarn.lock","package-lock.json","npm-shrinkwrap.json","tsconfig.json","src","app"];for(let o of t)if(we($e(e,o)))return!1;let s=[".env","README.md"];for(let o of s)if(we($e(e,o)))return!1;return!0}function hi(e=process.cwd()){if(we($e(e,"Dockerfile")))return{template:"docker",runtime:"docker"};if(we($e(e,"requirements.txt"))||we($e(e,"pyproject.toml"))||we($e(e,"src","main.py")))return{template:"python",runtime:"python3.12"};if(we($e(e,"package.json"))||we($e(e,"bun.lock"))||we($e(e,"bun.lockb"))||we($e(e,"pnpm-lock.yaml"))||we($e(e,"yarn.lock"))||we($e(e,"package-lock.json"))||we($e(e,"npm-shrinkwrap.json"))||we($e(e,"tsconfig.json"))||we($e(e,"src","index.ts"))||we($e(e,"src","index.js")))return{template:"node",runtime:"nodejs20"};return{template:"node",runtime:"nodejs20"}}function ai(e,n,t=!1){let s=[],o=[],r=[];for(let c of n){let f=$e(e,c.path);if(!we(f))continue;if(fi(f,"utf-8")===c.content){r.push(c.path);continue}if(!t)s.push(c.path)}if(s.length>0)throw Error(`以下文件已存在且内容不同,请使用 --force 覆盖:
1390
+ ${s.join(`
1391
+ `)}`);for(let c of n){let f=$e(e,c.path);if(we(f)&&fi(f,"utf-8")===c.content)continue;cm(lm(f),{recursive:!0}),um(f,c.content),o.push(c.path)}return{written:o,skipped:r}}function mi(e){e.command("init","初始化 FC 项目(空目录生成脚手架,已有项目写入 licell 配置)").option("--runtime <runtime>","默认 runtime:nodejs20/nodejs22/python3.12/python3.13/docker").option("--app <name>","应用名(用于 FC functionName)").option("--force","在已有项目目录生成/覆盖脚手架文件").option("--yes","使用默认值,不进入交互").action(async(n)=>{if(!a())j($t.bgBlue($t.white(" ⚡ Licell Project Init ")));else X({stage:"init",action:"init",status:"start"});let t=P(),s=n.yes||!t,o=di(process.cwd()),r=typeof n.runtime==="string"&&n.runtime.trim().length>0,c=o&&!r&&!s;try{let f=n.runtime;if(!f&&c){let p=await am({message:"选择默认 runtime:",options:[{value:"nodejs20",label:"nodejs20 (Node TypeScript)"},{value:"nodejs22",label:"nodejs22 (Node 22 Custom Runtime)"},{value:"python3.12",label:"python3.12 (Python Built-in Runtime)"},{value:"python3.13",label:"python3.13 (Python 3.13 Custom Runtime)"},{value:"docker",label:"docker (Bun + TypeScript + Hono)"}]});if(pm(p)){if(a())throw Error("操作已取消");process.exit(0)}f=String(p)}let u=f?Ir(f):o?Ir():hi(process.cwd()),{template:l,runtime:i}=u,g=o||!o&&r&&Boolean(n.force),y=k.getProject(),d=n.app||y.appName||li();if(!n.app&&!s)d=L(await mm({message:"应用名(用于 FC functionName):",initialValue:d}),"应用名");let m=gi(d),h=A();h.start(g?"正在生成项目脚手架...":"正在写入 licell 项目配置...");let{written:b,skipped:$}=g?ai(process.cwd(),yi(l,i),Boolean(n.force)):{written:[],skipped:[]};if(k.setProject({appName:m,runtime:i}),h.stop($t.green(g?"✅ 脚手架创建完成":"✅ 配置写入完成")),console.log(`runtime: ${$t.cyan(i)}`),console.log(`appName: ${$t.cyan(m)}`),console.log(`mode: ${$t.cyan(g?"scaffold+config":"config-only")}`),!g)console.log(`
1392
1392
  检测到当前目录已有项目文件,已跳过脚手架生成。`),console.log("如需在已有目录生成脚手架,请显式指定 --runtime <runtime> --force。");if(b.length>0){console.log(`
1393
1393
  已写入文件:`);for(let p of b)console.log(`- ${p}`)}if($.length>0){console.log(`
1394
1394
  已跳过(内容相同):`);for(let p of $)console.log(`- ${p}`)}if(console.log(`
1395
- 下一步可直接执行:`),console.log(`- licell deploy --type api --runtime ${i} --target preview`),a())S({stage:"init",runtime:i,appName:m,mode:g?"scaffold+config":"config-only",writtenFiles:b,skippedFiles:$});else q("Done.")}catch(f){if(a())he(f,{stage:"init"});else console.error(R(f));process.exitCode=1}})}K();Ve();ce();import Z from"picocolors";import{spawnSync as wm}from"child_process";function Fr(e,n){if(!n||!n.trim())return;let t=wm("sh",["-c",n.trim()],{stdio:"inherit",env:process.env});if(t.status!==0)throw Error(`${e} hook 执行失败(exit=${t.status??"unknown"}): ${n.trim()}`)}function pi(e){if(!e.deploySucceeded)return{};let n={};if(e.cliDomainSuffix&&e.cliDomainSuffix!==e.projectDomainSuffix)n.domainSuffix=e.cliDomainSuffix;if(e.cliRuntime&&e.cliRuntime!==e.projectRuntime)n.runtime=e.cliRuntime;return n}Ve();var $m=new Set(["static","statis","oss","static-site"]);function km(e){if(e===null||e===void 0)return;let n=String(e).trim();return n.length>0?n:void 0}function Yo(e){let n=km(e);if(!n)return{};let t=n.toLowerCase();if($m.has(t))return{deployTypeHint:"static"};return{deployTypeHint:"api",runtime:Qn(n)}}Le();oe();re();K();import{select as _m,text as bm,isCancel as Tm}from"@clack/prompts";function wi(e){return e.replace(/\\/g,"\\\\").replace(/"/g,"\\\"").replace(/\$/g,"\\$").replace(/`/g,"\\`").replace(/\r/g,"\\r").replace(/\n/g,"\\n")}function Pn(e){try{let n=new URL(e);if(n.password)n.password="******";return n.toString()}catch{return e.replace(/:([^@/:]+)@/g,":******@")}}function tn(e){if(typeof e!=="string")return"prod";let n=e.trim().toLowerCase();if(!n)return"prod";if(!/^[a-z0-9-]+$/.test(n))throw Error("发布目标仅允许小写字母、数字和短横线");return n}js();oe();re();function $i(e,n,t,o){return Boolean(e||n||t||o)}async function ki(e){let n=await B(),t=P(),o=k.getProject();if(!o.appName){if(!t)throw Error("缺少应用名,请先配置 .licell/project.json 的 appName,或在交互终端执行 deploy 初始化");let le=A(await bm({message:"为你的应用起个名字 (小写英文):",placeholder:"my-awesome-app"}),"应用名");if(!/^[a-z0-9-]+$/.test(le))throw Error("应用名仅允许小写字母、数字和短横线");if(le.length>128)throw Error("应用名长度不能超过 128 个字符");k.setProject({appName:le}),o=k.getProject()}let s=e.domain?Ao(e.domain):void 0,r=e.domainSuffix?Vn(e.domainSuffix):void 0,c=Lo(o.domainSuffix),f=Lo(se(process.env,"DOMAIN_SUFFIX")),u=Lo(k.getGlobalConfig().domainSuffix),l=Yo(e.runtime),i=l.runtime,g=at(o.runtime),y=at(se(process.env,"FC_RUNTIME")),d=e.acrNamespace?Os(e.acrNamespace):void 0,m=e.type?Zu(e.type):void 0,h;if(m&&l.deployTypeHint&&m!==l.deployTypeHint)throw Error(`--type ${m} 与 --runtime ${e.runtime} 冲突`);if(m)h=m;else if(l.deployTypeHint==="api")h="api";else if(l.deployTypeHint==="static")h="static";else if(t){let le=await _m({message:"选择部署环境:",options:[{value:"api",label:"\uD83D\uDE80 API 服务 (Node/Python/Docker -> FC 3.0)"},{value:"static",label:"\uD83D\uDCE6 前端静态网站 (直推 OSS 托管)"}]});if(Tm(le)){if(a())throw Error("操作已取消");process.exit(0)}if(le!=="api"&&le!=="static")throw Error("未知部署类型");h=le}else h="api";let b=s?void 0:h==="static"?r:r||c||f||u,$=e.target?tn(e.target):void 0,p=h==="static"&&Boolean(s||b),w=h==="static"?Boolean(e.enableCdn||p):Boolean(e.enableCdn),H=h==="static"?Boolean(e.ssl||p||w):$i(e.ssl,s,w,b),T=Boolean(e.sslForceRenew);if(s&&r)throw Error("--domain 与 --domain-suffix 不能同时使用");if($&&h!=="api")throw Error("--target 仅适用于 API 部署");if(e.enableVpc&&e.disableVpc)throw Error("--enable-vpc 与 --disable-vpc 不能同时使用");if(h!=="api"&&e.enableVpc)throw Error("--enable-vpc 仅适用于 API 部署");if(h!=="api"&&e.disableVpc)throw Error("--disable-vpc 仅适用于 API 部署");if(w&&!s&&!b)throw Error("--enable-cdn 需要域名,请提供 --domain(完整域名)或 --domain-suffix");if(h!=="api"&&i)throw Error("--runtime 的 API 运行时仅适用于 API 部署;静态站请使用 --runtime static");if(h!=="api"&&d)throw Error("--acr-namespace 仅适用于 API Docker 部署");if(T&&!H)throw Error("--ssl-force-renew 需要启用 HTTPS(请使用 --domain 或 --ssl)");if(H&&!s&&!b)throw Error("--ssl 需要域名,请提供 --domain(完整域名)或 --domain-suffix");let x=Boolean(e.preview);if(x&&$)throw Error("--preview 与 --target 不能同时使用");if(x&&!b)throw Error("--preview 需要域名后缀,请先执行 licell deploy --domain-suffix your-domain.com 或 licell config domain your-domain.com");if(x&&s)throw Error("--preview 与 --domain 不能同时使用,preview 会自动生成预览域名");let E=o.appName;if(!E)throw Error("appName 未设置,请检查项目配置");let _=qe(e.memory,"--memory"),F=ei(e.vcpu,"--vcpu"),I=qe(e.instanceConcurrency,"--instance-concurrency"),fe=qe(e.timeout,"--timeout"),Ce=_!==void 0||fe!==void 0||F!==void 0||I!==void 0?{..._!==void 0?{memorySize:_}:{},...F!==void 0?{cpu:F}:{},...I!==void 0?{instanceConcurrency:I}:{},...fe!==void 0?{timeout:fe}:{}}:void 0,rn=h==="api"?!Boolean(e.disableVpc):!1;return{appName:E,type:h,releaseTarget:$,cliDomain:s,domainSuffix:b,enableCdn:w,useVpc:rn,enableSSL:H,forceSslRenew:T,preview:x,cliResources:Ce,cliAcrNamespace:d,interactiveTTY:t,auth:n,project:o,cliDomainSuffix:r,projectDomainSuffix:c,cliRuntime:i,projectRuntime:g,envRuntime:y,cliEntry:e.entry,cliDist:e.dist}}K();On();Ot();Ve();import{confirm as Gi,text as C0,isCancel as Mi}from"@clack/prompts";import{existsSync as E0}from"fs";import Bi from"picocolors";K();Ht();Cn();Ie();import Cm,*as Re from"@alicloud/alidns20150109";import*as Rn from"@alicloud/fc20230330";import*as _i from"@alicloud/openapi-client";var Em=te(Cm,"@alicloud/alidns20150109");function Xn(e){return e.toLowerCase().replace(/^https?:\/\//,"").replace(/\.$/,"")}function bi(e,n){let t=e.trim().toLowerCase(),o=n.trim().toLowerCase();if(!t)return o?[o]:[];let r=[o==="@"||!o?t:`${o}.${t}`,o];return[...new Set(r.filter((c)=>c.length>0))]}function kt(){let e=k.requireAuth();return new Em(new _i.Config({accessKeyId:e.ak,accessKeySecret:e.sk,endpoint:"alidns.aliyuncs.com"}))}function Ti(){return Bn().client}async function xr(e,n,t){let o=[],s=bi(n,t);for(let r of s)try{let c=await Y(()=>e.describeSubDomainRecords(new Re.DescribeSubDomainRecordsRequest({domainName:n,subDomain:r,type:"CNAME",pageNumber:1,pageSize:100})));o.push(...c.body?.domainRecords?.record||[])}catch(c){if(po(c))continue;throw c}return o.find((r)=>{let c=r;return(c.RR||"@")===t&&(c.type||"").toUpperCase()==="CNAME"})}async function Sm(e,n,t,o,s){let r=Xn(s),c=await xr(e,t,o);if(!c?.recordId)try{await Y(()=>e.addDomainRecord(new Re.AddDomainRecordRequest({domainName:t,RR:o,type:"CNAME",value:r})));return}catch(u){if(!J(u))throw u;c=await xr(e,t,o)}if(!c?.recordId)throw Error(`DNS 记录已存在但无法定位可更新记录: ${n}`);if(Xn(c.value||"")===r)return;await Y(()=>e.updateDomainRecord(new Re.UpdateDomainRecordRequest({recordId:c.recordId,RR:o,type:"CNAME",value:r})))}async function Hr(e,n){let t=e.trim().toLowerCase();if(!t)throw Error("域名不能为空");let{rootDomain:o,subDomain:s}=Qe(t),r=kt();await Sm(r,t,o,s,n)}async function _t(e,n,t,o={}){let s=k.getProject();if(!s.appName)throw Error("未找到应用名,请先执行 licell deploy");if(!o.skipDnsBind)await Hr(e,n);let r=Ti(),c={routes:[{path:"/*",functionName:s.appName,qualifier:t}]},f=new Rn.CreateCustomDomainInput({domainName:e,protocol:"HTTP",routeConfig:c});try{await Y(()=>r.createCustomDomain(new Rn.CreateCustomDomainRequest({body:f})))}catch(u){if(!J(u))throw u;await Y(()=>r.updateCustomDomain(e,new Rn.UpdateCustomDomainRequest({body:new Rn.UpdateCustomDomainInput({routeConfig:c})})))}return`http://${e}`}async function Ci(e){let n=e.trim().toLowerCase();if(!n)throw Error("域名不能为空");let{rootDomain:t,subDomain:o}=Qe(n),s=kt(),r=Ti();try{await Y(()=>r.deleteCustomDomain(n))}catch(u){if(!ue(u))throw u}let c=new Set,f=bi(t,o);for(let u of f){let l=[];try{l=(await Y(()=>s.describeSubDomainRecords(new Re.DescribeSubDomainRecordsRequest({domainName:t,subDomain:u,type:"CNAME",pageNumber:1,pageSize:200})))).body?.domainRecords?.record||[]}catch(i){if(po(i))continue;throw i}for(let i of l){let g=i;if((g.RR||"@")!==o||(g.type||"").toUpperCase()!=="CNAME")continue;let y=g.recordId;if(!y||c.has(y))continue;c.add(y);try{await Y(()=>s.deleteDomainRecord(new Re.DeleteDomainRecordRequest({recordId:y})))}catch(d){if(!ue(d))throw d}}}}async function Ei(e,n=200){let t=e.trim().toLowerCase();if(!t)throw Error("域名不能为空");let o=kt(),s=[],r=Math.max(1,Math.min(Math.floor(n),1000)),c=Math.min(100,r);for(let f=1;f<=20&&s.length<r;f+=1){let u=await Y(()=>o.describeDomainRecords(new Re.DescribeDomainRecordsRequest({domainName:t,pageNumber:f,pageSize:c}))),l=u.body?.domainRecords?.record||[];for(let g of l){let y=g.recordId;if(!y)continue;if(s.push({recordId:y,rr:g.RR||"@",type:g.type||"",value:g.value||"",ttl:g.TTL,line:g.line,status:g.status}),s.length>=r)break}let i=u.body?.totalCount||0;if(l.length===0||i>0&&s.length>=i)break}return s}async function Si(e,n){let t=e.trim().toLowerCase();if(!t)throw Error("域名不能为空");let o=n.rr.trim(),s=n.type.trim().toUpperCase(),r=n.value.trim();if(!o)throw Error("RR 不能为空");if(!s)throw Error("记录类型不能为空");if(!r)throw Error("记录值不能为空");let c=kt(),u=(await Y(()=>c.addDomainRecord(new Re.AddDomainRecordRequest({domainName:t,RR:o,type:s,value:r,TTL:n.ttl,line:n.line||"default"})))).body?.recordId;if(!u)throw Error("添加 DNS 记录失败:未返回 recordId");return u}async function qi(e){let n=e.trim();if(!n)throw Error("recordId 不能为空");let t=kt();await Y(()=>t.deleteDomainRecord(new Re.DeleteDomainRecordRequest({recordId:n})))}async function Oo(e,n,t){let o=e.trim().toLowerCase(),s=Xn(n),{rootDomain:r,subDomain:c}=Qe(o),f=c?`*.${c}`:"*",u=c?`*.${c}.${r}`:`*.${r}`,l=kt(),i=await xr(l,r,f);if(i?.recordId){if(Xn(i.value||"")!==s)await Y(()=>l.updateDomainRecord(new Re.UpdateDomainRecordRequest({recordId:i.recordId,RR:f,type:"CNAME",value:s})));return{created:!1,skipped:!1,wildcardDomain:u,targetValue:s}}if(!t.skipConfirm){if(!t.interactiveTTY)return{created:!1,skipped:!0,wildcardDomain:u,targetValue:s};if(t.onConfirm){if(!await t.onConfirm())return{created:!1,skipped:!0,wildcardDomain:u,targetValue:s}}}return await Y(()=>l.addDomainRecord(new Re.AddDomainRecordRequest({domainName:r,RR:f,type:"CNAME",value:s}))),{created:!0,skipped:!1,wildcardDomain:u,targetValue:s}}K();ce();Cn();import*as In from"@alicloud/openapi-client";import*as Pi from"@alicloud/tea-util";Ie();var qm=te(In.default,"@alicloud/openapi-client");function Pm(){let e=k.requireAuth();return new qm(new In.Config({accessKeyId:e.ak,accessKeySecret:e.sk,regionId:e.region,endpoint:"cdn.aliyuncs.com"}))}function Rm(e){if(e===null||e===void 0)return;if(typeof e==="boolean")return e?"true":"false";return String(e)}function Im(e){let n={};for(let[t,o]of Object.entries(e)){let s=Rm(o);if(s===void 0)continue;n[t]=s}return n}async function Dt(e,n){let t=Pm(),o=new In.Params({action:e,version:"2018-05-10",protocol:"HTTPS",pathname:"/",method:"POST",authType:"AK",style:"RPC",reqBodyType:"formData",bodyType:"json"}),s=new In.OpenApiRequest({query:Im(n)});return t.callApi(o,s,new Pi.RuntimeOptions({readTimeout:20000,connectTimeout:8000}))}function dn(e){let n=e.trim().toLowerCase();if(!n)throw Error("域名不能为空");return n}function Ri(e){let n=Xn(e);if(!n)throw Error("CDN 回源域名不能为空");return n}function Ii(e){return e==="oss"?"oss":"domain"}function Fm(e){return"domestic"}function Fi(e,n){if(e==="domestic"||e==="overseas"||e==="global")return e;return Fm(n)}function xm(e){return String(e||"").trim().toLowerCase()||void 0}function Hm(e){if(!e||typeof e!=="object")return;let n=e,t=String(n.DomainName||n.domainName||"").trim().toLowerCase();if(!t)return;let o=String(n.Cname||n.cname||"").trim();return{domainName:t,cname:o?Xn(o):void 0,status:xm(n.DomainStatus||n.domainStatus)}}function Am(e){if(!e||typeof e!=="object")return[];let t=e.Domains;if(!t||typeof t!=="object")return[];let o=t.PageData;if(!Array.isArray(o))return[];return o}function Ar(e){if(typeof e!=="object"||e===null)return!1;let n=String(e.code||"").toLowerCase(),t=String(e.message||"").toLowerCase();return n.includes("invaliddomainstatus")||n.includes("domainnotexist")||t.includes("domain status")||t.includes("processing")||t.includes("not ready")}async function Ko(e){let n=dn(e),t=await Y(()=>Dt("DescribeUserDomains",{DomainName:n,PageNumber:1,PageSize:50})),o=Am(t.body);for(let s of o){let r=Hm(s);if(!r)continue;if(r.domainName===n)return r}return}async function Lm(e,n,t={}){let o=dn(e),s=Ri(n),r=Ii(t.sourceType),c=Fi(t.scope,o),f=JSON.stringify([{content:s,type:r,port:80,priority:"20",weight:"10"}]);await Y(()=>Dt("AddCdnDomain",{DomainName:o,CdnType:"web",Scope:c,Sources:f}))}async function Um(e,n=60,t=3000){let o=dn(e);for(let s=1;s<=n;s+=1){let r=await Ko(o);if(r?.cname)return r.cname;await new Promise((c)=>setTimeout(c,t))}throw Error(`CDN 域名已创建,但暂未返回 CNAME: ${o}`)}async function vm(e,n,t){let o=dn(e),s=n.trim(),r=t.trim();if(!s||!r)return;let c=`licell-cdn-cert-${Date.now()}`;await Y(()=>Dt("SetCdnDomainSSLCertificate",{DomainName:o,SSLProtocol:"on",CertType:"upload",CertName:c,SSLPub:s,SSLPri:r}),{maxAttempts:12,baseDelayMs:2000,shouldRetry:(f)=>Ee(f)||ue(f)||Ar(f)})}function Gm(e){let n=R(e).toLowerCase();return n.includes("aliyuncdnaccessingprivateossrole")||n.includes("private oss")||n.includes("private_oss_auth")||n.includes("l2_oss_key")||n.includes("authorize")}async function Mm(e){let n=dn(e),t=JSON.stringify([{functionName:"l2_oss_key",functionArgs:[{argName:"private_oss_auth",argValue:"on"}]}]);try{await Y(()=>Dt("BatchSetCdnDomainConfig",{DomainNames:n,Functions:t}),{maxAttempts:12,baseDelayMs:2000,shouldRetry:(o)=>Ee(o)||Ar(o)})}catch(o){if(!Gm(o))throw o;throw Error("CDN 私有 OSS 回源授权失败,请先在 CDN 控制台完成一次“私有 OSS Bucket 回源授权”,"+`确保服务关联角色 AliyunCDNAccessingPrivateOSSRole 已创建。原始错误: ${R(o)}`)}}async function Bm(e){let n=dn(e),t=JSON.stringify([{functionName:"back_to_origin_url_rewrite",functionArgs:[{argName:"source_url",argValue:"^/$"},{argName:"target_url",argValue:"/index.html"},{argName:"flag",argValue:"break"}]}]);await Y(()=>Dt("BatchSetCdnDomainConfig",{DomainNames:n,Functions:t}),{maxAttempts:12,baseDelayMs:2000,shouldRetry:(o)=>Ee(o)||Ar(o)})}function Ym(e){return e==="online"}function Om(e){return e==="configure_failed"||e==="check_failed"}async function Km(e,n=40,t=3000){let o=dn(e);for(let s=1;s<=n;s+=1){let r=await Ko(o);if(Ym(r?.status))return;if(Om(r?.status))throw Error(`CDN 域名状态异常: ${o} (${r?.status})`);await new Promise((c)=>setTimeout(c,t))}throw Error(`CDN 域名长时间未就绪: ${o}`)}async function jm(e,n,t={}){let o=dn(e),s=Ri(n),r=!1,c=await Ko(o);if(!c)try{await Lm(o,s,t),r=!0}catch(u){if(!J(u))throw u;c=await Ko(o)}return{cdnCname:c?.cname||await Um(o),created:r}}async function Vt(e,n,t={}){let o=dn(e),s=Ii(t.sourceType),r=Fi(t.scope,o),c=await jm(o,n,{sourceType:s,scope:r});if(s==="oss"&&t.enablePrivateOssAuth!==!1)await Mm(o),await Bm(o);let f=!1;if(t.certificate&&t.privateKey)await vm(o,t.certificate,t.privateKey),f=!0;if(await Hr(o,c.cdnCname),t.waitForOnline)await Km(o);return{...c,httpsConfigured:f}}K();Ht();De();Cn();Ie();import*as hn from"acme-client";import Wm,*as Zn from"@alicloud/alidns20150109";import*as Wo from"@alicloud/fc20230330";import*as Li from"@alicloud/openapi-client";import{createPrivateKey as Qm,X509Certificate as Dm}from"crypto";import{existsSync as Vm,readFileSync as Xm,writeFileSync as Zm}from"fs";import{dirname as Jm,join as Nm}from"path";import{homedir as zm}from"os";var jo=Nm(zm(),".licell-cli","acme-account.pem"),e0=te(Wm,"@alicloud/alidns20150109"),n0=86400000,t0=30,xi=180000,o0=5000,s0=600;function r0(e){let n=e.toString("utf8");try{return Qm(n).export({format:"pem",type:"pkcs1"}).toString()}catch{return n}}async function c0(){if(Vm(jo))return Xm(jo);xt(Jm(jo));let e=await hn.crypto.createPrivateKey();return Zm(jo,e,{mode:384}),e}function f0(e){if(typeof e!=="string")return!1;return e.split(",").map((n)=>n.trim().toUpperCase()).includes("HTTPS")}function u0(e,n=t0){let t=typeof e==="number"?String(e):typeof e==="string"?e.trim():"";if(!/^\d+$/.test(t))return n;let o=Number.parseInt(t,10);return o>0?o:n}function i0(e,n=Date.now()){try{let t=new Dm(e),o=Date.parse(t.validTo);if(!Number.isFinite(o))return null;return Math.floor((o-n)/n0)}catch{return null}}function Hi(e){if(e>=0)return`剩余 ${e} 天`;return`已过期 ${Math.abs(e)} 天`}function l0(e){if(!e)return xi;let n=Number(e.trim());if(!Number.isFinite(n)||n<=0)return xi;return Math.floor(n)}function Ai(e){return e.trim().replace(/^"+|"+$/g,"")}async function Ui(e,n,t){let o=[`${t}.${n}`,t],s=[];for(let r of o)try{let c=await Y(()=>e.describeSubDomainRecords(new Zn.DescribeSubDomainRecordsRequest({domainName:n,subDomain:r,type:"TXT",pageNumber:1,pageSize:100})));s.push(...c.body?.domainRecords?.record||[])}catch{}return s}async function g0(e,n,t,o,s){let r=l0(se(process.env,"SSL_DNS_READY_TIMEOUT_MS")),c=Ai(o),f=Date.now();while(!0){if((await Ui(e,n,t)).some((i)=>{if(typeof i.value!=="string")return!1;return Ai(i.value)===c}))return;if(Date.now()-f>r)throw Error(`DNS TXT 记录传播超时: ${t}.${n}`);s.message(`\uD83C\uDF0D 正在等待 DNS 生效 (${t})...`),await ge(o0)}}function y0(e,n,t=i0){if(!e||!f0(e.protocol))return{issue:!0,message:"\uD83D\uDD12 域名尚未开启 HTTPS,开始签发证书..."};if(n.forceRenew)return{issue:!0,message:"\uD83D\uDD01 已启用强制续签,开始重新签发 HTTPS 证书..."};let o=e.certConfig?.certificate;if(typeof o!=="string"||o.trim().length===0)return{issue:!1,message:"\uD83D\uDD10 域名已开启 HTTPS,但无法读取证书有效期,跳过自动续签(可用 --ssl-force-renew 强制续签)。"};let s=t(o);if(s===null)return{issue:!1,message:"\uD83D\uDD10 域名已开启 HTTPS,但无法解析证书有效期,跳过自动续签(可用 --ssl-force-renew 强制续签)。"};if(s>n.renewBeforeDays)return{issue:!1,message:`\uD83D\uDD10 域名已开启 HTTPS,证书${Hi(s)}(续签阈值 ${n.renewBeforeDays} 天),跳过自动续签。`};return{issue:!0,message:`\uD83D\uDD01 检测到证书${Hi(s)}(续签阈值 ${n.renewBeforeDays} 天),开始自动续签...`}}async function Fn(e,n,t){let o=k.requireAuth(),s=t?.bindToFcDomain!==!1,r=new e0(new Li.Config({accessKeyId:o.ak,accessKeySecret:o.sk,endpoint:"alidns.aliyuncs.com",connectTimeout:1e4,readTimeout:600000})),c=s?Bn(o).client:void 0,f={forceRenew:Boolean(t?.forceRenew),renewBeforeDays:u0(t?.renewBeforeDays??se(process.env,"SSL_RENEW_BEFORE_DAYS"))},u=se(process.env,"SSL_SKIP_CHALLENGE_VERIFY")!=="0",l=null;if(c)try{l=(await Y(()=>c.getCustomDomain(e))).body}catch{}let i=c?y0(l,f):{issue:!0,message:f.forceRenew?"\uD83D\uDD01 已启用强制续签,开始签发 CDN HTTPS 证书...":"\uD83D\uDD12 正在签发 CDN HTTPS 证书..."};if(n.message(i.message),!i.issue){let T=typeof l?.certConfig?.certificate==="string"?l.certConfig.certificate:void 0,x=typeof l?.certConfig?.privateKey==="string"?l.certConfig.privateKey:void 0;return{url:`https://${e}`,certificate:T?.trim()?T:void 0,privateKey:x?.trim()?x:void 0,reusedExistingCertificate:!0}}let{rootDomain:g,subDomain:y}=Qe(e),d=[];n.message("\uD83D\uDD12 正在向 Let's Encrypt 注册 ACME 账户并发起证书申请..."),hn.setLogger(()=>{});let m=await c0(),h=new hn.Client({directoryUrl:hn.directory.letsencrypt.production,accountKey:m}),[b,$]=await hn.crypto.createCsr({commonName:e}),p=async(T)=>{let x=new Set,E=await Ui(r,g,T);for(let _ of E){if(!_.recordId||x.has(_.recordId))continue;x.add(_.recordId);try{await Y(()=>r.deleteDomainRecord(new Zn.DeleteDomainRecordRequest({recordId:_.recordId})))}catch{}}},w=await h.auto({csr:$,email:`admin@${g}`,termsOfServiceAgreed:!0,challengePriority:["dns-01"],skipChallengeVerification:u,challengeCreateFn:async(T,x,E)=>{let _=y==="@"?"_acme-challenge":`_acme-challenge.${y}`,F=E;n.message(`\uD83D\uDCDD 正在自动配置 DNS TXT 记录 (${_}) ...`),await p(_);let I=await Y(()=>r.addDomainRecord(new Zn.AddDomainRecordRequest({domainName:g,RR:_,type:"TXT",value:F,TTL:s0})));if(I.body?.recordId)d.push(I.body.recordId);await g0(r,g,_,F,n),n.message(`\uD83C\uDF10 DNS TXT 已就绪,等待 Let's Encrypt 验证 (${_}) ...`)},challengeRemoveFn:async()=>{for(let T of d)try{await Y(()=>r.deleteDomainRecord(new Zn.DeleteDomainRecordRequest({recordId:T})))}catch(x){let E=x instanceof Error?x.message:String(x);if(!E.toLowerCase().includes("notfound")&&!E.toLowerCase().includes("not found"))n.message(`⚠️ DNS TXT 记录清理失败 (recordId=${T}): ${E}`)}}}),H=r0(b);if(c)n.message("\uD83D\uDCE6 证书下发成功,正在自动挂载至云端网关开启 HTTPS..."),await Y(()=>c.updateCustomDomain(e,new Wo.UpdateCustomDomainRequest({body:new Wo.UpdateCustomDomainInput({protocol:"HTTP,HTTPS",certConfig:{certName:`licell-cert-${Date.now()}`,certificate:w.toString(),privateKey:H}})})));else n.message("\uD83D\uDCE6 证书下发成功,正在用于 CDN 边缘 HTTPS 配置...");return{url:`https://${e}`,certificate:w.toString(),privateKey:H,reusedExistingCertificate:!1}}async function vi(e,n,t){return(await Fn(e,n,t)).url}De();import{request as d0}from"http";import{request as h0}from"https";var a0=["/healthz","/"],m0=4,p0=1500,w0=5000;function $0(e){let t=(e&&e.length>0?e:a0).map((o)=>o.trim()).filter((o)=>o.length>0).map((o)=>o.startsWith("/")?o:`/${o}`);return[...new Set(t)]}function k0(e,n){return`${e.replace(/\/+$/g,"")}${n}`}function _0(e){if(e instanceof Error){if(e.name==="AbortError")return"请求超时";return e.message}return String(e)}async function b0(e,n,t){let o=new AbortController,s=Error("请求超时");s.name="AbortError";let r,c=new Promise((f,u)=>{r=setTimeout(()=>{o.abort(),u(s)},n)});try{return await Promise.race([t(e,{method:"GET",redirect:"manual",signal:o.signal,headers:{"user-agent":"licell-health-check/1.0"}}),c])}finally{if(r)clearTimeout(r)}}async function T0(e,n){let t=new URL(e),o=t.protocol==="https:",s=o?h0:d0;return new Promise((r,c)=>{let f=!1,u,l=s(t,{method:"GET",headers:{"user-agent":"licell-health-check/1.0"},...o?{minVersion:"TLSv1.2",maxVersion:"TLSv1.2"}:{}},(i)=>{if(u)clearTimeout(u);let g=i.statusCode??0;if(i.resume(),i.once("error",()=>{}),!f)f=!0,r(g);i.destroy()});u=setTimeout(()=>{l.destroy(Error("请求超时"))},n),l.on("error",(i)=>{if(u)clearTimeout(u);if(f)return;f=!0,c(i)}),l.end()})}async function an(e,n={}){let t=e.trim();if(!t)return{ok:!1,error:"URL 为空",attempt:1};let o=n.fetchImpl??(typeof globalThis.fetch==="function"?globalThis.fetch.bind(globalThis):void 0),s=$0(n.paths),r=Math.max(1,Math.floor(n.maxAttempts??m0)),c=Math.max(0,Math.floor(n.intervalMs??p0)),f=Math.max(1000,Math.floor(n.timeoutMs??w0)),u=n.allowClientError===!1?400:500,l="未知错误";for(let i=1;i<=r;i+=1){for(let g of s){let y=k0(t,g);try{let d=o?(await b0(y,f,o)).status:await T0(y,f);if(d<u){if(g==="/healthz"&&d===404&&s.includes("/")){l=`GET ${y} 返回 404`;continue}return{ok:!0,checkedUrl:y,statusCode:d,attempt:i}}l=`GET ${y} 返回 ${d}`}catch(d){l=`GET ${y} 请求失败: ${_0(d)}`}}if(i<r&&c>0)await ge(c)}return{ok:!1,error:l,attempt:r}}ce();oe();re();function Yi(e){let n=[];for(let t of e){let o=t.level==="error"?"ERROR":"WARN";if(n.push(`[${o}] ${t.id}`),n.push(t.message),t.remediation&&t.remediation.length>0)for(let s of t.remediation)n.push(`- ${s}`)}return n}async function Oi(e,n){let t=e.cliRuntime||e.projectRuntime||e.envRuntime||Wn;if(t!=="docker"&&!e.cliRuntime&&E0("Dockerfile")&&e.interactiveTTY){let b=await Gi({message:"检测到 Dockerfile,是否使用 Docker 容器部署?"});if(Mi(b)){if(a())throw Error("操作已取消");process.exit(0)}if(b)t="docker"}if(e.cliAcrNamespace&&t!=="docker")throw Error("--acr-namespace 仅适用于 --runtime docker");if(t==="docker"&&e.cliAcrNamespace)k.setProject({acrNamespace:e.cliAcrNamespace});let o=kn(t).defaultEntry,s;if(t==="docker")s=e.cliEntry||"";else if(e.cliEntry)s=A(e.cliEntry,"入口文件路径");else if(e.interactiveTTY)s=A(await C0({message:t.startsWith("python")?"入口文件路径 (Python 需包含 handler 函数):":"入口文件路径 (需导出 handler):",initialValue:o}),"入口文件路径");else s=o;let r=Wt({runtime:t,entry:s,checkDockerDaemon:t==="docker"}),c=r.issues.filter((b)=>b.level==="warning");if(!r.ok){let b=Yi(r.issues),$=fr(r);throw $.message=`${$.message}
1395
+ 下一步可直接执行:`),console.log(`- licell deploy --type api --runtime ${i} --target preview`),a())S({stage:"init",runtime:i,appName:m,mode:g?"scaffold+config":"config-only",writtenFiles:b,skippedFiles:$});else q("Done.")}catch(f){if(a())he(f,{stage:"init"});else console.error(R(f));process.exitCode=1}})}K();Ve();ce();import Z from"picocolors";import{spawnSync as wm}from"child_process";function Fr(e,n){if(!n||!n.trim())return;let t=wm("sh",["-c",n.trim()],{stdio:"inherit",env:process.env});if(t.status!==0)throw Error(`${e} hook 执行失败(exit=${t.status??"unknown"}): ${n.trim()}`)}function pi(e){if(!e.deploySucceeded)return{};let n={};if(e.cliDomainSuffix&&e.cliDomainSuffix!==e.projectDomainSuffix)n.domainSuffix=e.cliDomainSuffix;if(e.cliRuntime&&e.cliRuntime!==e.projectRuntime)n.runtime=e.cliRuntime;return n}Ve();var $m=new Set(["static","statis","oss","static-site"]);function km(e){if(e===null||e===void 0)return;let n=String(e).trim();return n.length>0?n:void 0}function Bs(e){let n=km(e);if(!n)return{};let t=n.toLowerCase();if($m.has(t))return{deployTypeHint:"static"};return{deployTypeHint:"api",runtime:Qn(n)}}Ae();se();re();K();import{select as _m,text as bm,isCancel as Tm}from"@clack/prompts";function wi(e){return e.replace(/\\/g,"\\\\").replace(/"/g,"\\\"").replace(/\$/g,"\\$").replace(/`/g,"\\`").replace(/\r/g,"\\r").replace(/\n/g,"\\n")}function Pn(e){try{let n=new URL(e);if(n.password)n.password="******";return n.toString()}catch{return e.replace(/:([^@/:]+)@/g,":******@")}}function tn(e){if(typeof e!=="string")return"prod";let n=e.trim().toLowerCase();if(!n)return"prod";if(!/^[a-z0-9-]+$/.test(n))throw Error("发布目标仅允许小写字母、数字和短横线");return n}jo();se();re();function $i(e,n,t,s){return Boolean(e||n||t||s)}async function ki(e){let n=await B(),t=P(),s=k.getProject();if(!s.appName){if(!t)throw Error("缺少应用名,请先配置 .licell/project.json 的 appName,或在交互终端执行 deploy 初始化");let le=L(await bm({message:"为你的应用起个名字 (小写英文):",placeholder:"my-awesome-app"}),"应用名");if(!/^[a-z0-9-]+$/.test(le))throw Error("应用名仅允许小写字母、数字和短横线");if(le.length>128)throw Error("应用名长度不能超过 128 个字符");k.setProject({appName:le}),s=k.getProject()}let o=e.domain?Hs(e.domain):void 0,r=e.domainSuffix?Vn(e.domainSuffix):void 0,c=Ls(s.domainSuffix),f=Ls(oe(process.env,"DOMAIN_SUFFIX")),u=Ls(k.getGlobalConfig().domainSuffix),l=Bs(e.runtime),i=l.runtime,g=at(s.runtime),y=at(oe(process.env,"FC_RUNTIME")),d=e.acrNamespace?Oo(e.acrNamespace):void 0,m=e.type?Zu(e.type):void 0,h;if(m&&l.deployTypeHint&&m!==l.deployTypeHint)throw Error(`--type ${m} 与 --runtime ${e.runtime} 冲突`);if(m)h=m;else if(l.deployTypeHint==="api")h="api";else if(l.deployTypeHint==="static")h="static";else if(t){let le=await _m({message:"选择部署环境:",options:[{value:"api",label:"\uD83D\uDE80 API 服务 (Node/Python/Docker -> FC 3.0)"},{value:"static",label:"\uD83D\uDCE6 前端静态网站 (直推 OSS 托管)"}]});if(Tm(le)){if(a())throw Error("操作已取消");process.exit(0)}if(le!=="api"&&le!=="static")throw Error("未知部署类型");h=le}else h="api";let b=o?void 0:h==="static"?r:r||c||f||u,$=e.target?tn(e.target):void 0,p=h==="static"&&Boolean(o||b),w=h==="static"?Boolean(e.enableCdn||p):Boolean(e.enableCdn),H=h==="static"?Boolean(e.ssl||p||w):$i(e.ssl,o,w,b),T=Boolean(e.sslForceRenew);if(o&&r)throw Error("--domain 与 --domain-suffix 不能同时使用");if($&&h!=="api")throw Error("--target 仅适用于 API 部署");if(e.enableVpc&&e.disableVpc)throw Error("--enable-vpc 与 --disable-vpc 不能同时使用");if(h!=="api"&&e.enableVpc)throw Error("--enable-vpc 仅适用于 API 部署");if(h!=="api"&&e.disableVpc)throw Error("--disable-vpc 仅适用于 API 部署");if(w&&!o&&!b)throw Error("--enable-cdn 需要域名,请提供 --domain(完整域名)或 --domain-suffix");if(h!=="api"&&i)throw Error("--runtime 的 API 运行时仅适用于 API 部署;静态站请使用 --runtime static");if(h!=="api"&&d)throw Error("--acr-namespace 仅适用于 API Docker 部署");if(T&&!H)throw Error("--ssl-force-renew 需要启用 HTTPS(请使用 --domain 或 --ssl)");if(H&&!o&&!b)throw Error("--ssl 需要域名,请提供 --domain(完整域名)或 --domain-suffix");let x=Boolean(e.preview);if(x&&$)throw Error("--preview 与 --target 不能同时使用");if(x&&!b)throw Error("--preview 需要域名后缀,请先执行 licell deploy --domain-suffix your-domain.com 或 licell config domain your-domain.com");if(x&&o)throw Error("--preview 与 --domain 不能同时使用,preview 会自动生成预览域名");let E=s.appName;if(!E)throw Error("appName 未设置,请检查项目配置");let _=qe(e.memory,"--memory"),F=ei(e.vcpu,"--vcpu"),I=qe(e.instanceConcurrency,"--instance-concurrency"),fe=qe(e.timeout,"--timeout"),Ce=_!==void 0||fe!==void 0||F!==void 0||I!==void 0?{..._!==void 0?{memorySize:_}:{},...F!==void 0?{cpu:F}:{},...I!==void 0?{instanceConcurrency:I}:{},...fe!==void 0?{timeout:fe}:{}}:void 0,rn=h==="api"?!Boolean(e.disableVpc):!1;return{appName:E,type:h,releaseTarget:$,cliDomain:o,domainSuffix:b,enableCdn:w,useVpc:rn,enableSSL:H,forceSslRenew:T,preview:x,cliResources:Ce,cliAcrNamespace:d,interactiveTTY:t,auth:n,project:s,cliDomainSuffix:r,projectDomainSuffix:c,cliRuntime:i,projectRuntime:g,envRuntime:y,cliEntry:e.entry,cliDist:e.dist}}K();On();Ot();Ve();import{confirm as Gi,text as C0,isCancel as Mi}from"@clack/prompts";import{existsSync as E0}from"fs";import Bi from"picocolors";K();Ht();Cn();Ie();import Cm,*as Re from"@alicloud/alidns20150109";import*as Rn from"@alicloud/fc20230330";import*as _i from"@alicloud/openapi-client";var Em=te(Cm,"@alicloud/alidns20150109");function Xn(e){return e.toLowerCase().replace(/^https?:\/\//,"").replace(/\.$/,"")}function bi(e,n){let t=e.trim().toLowerCase(),s=n.trim().toLowerCase();if(!t)return s?[s]:[];let r=[s==="@"||!s?t:`${s}.${t}`,s];return[...new Set(r.filter((c)=>c.length>0))]}function kt(){let e=k.requireAuth();return new Em(new _i.Config({accessKeyId:e.ak,accessKeySecret:e.sk,endpoint:"alidns.aliyuncs.com"}))}function Ti(){return Bn().client}async function xr(e,n,t){let s=[],o=bi(n,t);for(let r of o)try{let c=await Y(()=>e.describeSubDomainRecords(new Re.DescribeSubDomainRecordsRequest({domainName:n,subDomain:r,type:"CNAME",pageNumber:1,pageSize:100})));s.push(...c.body?.domainRecords?.record||[])}catch(c){if(ms(c))continue;throw c}return s.find((r)=>{let c=r;return(c.RR||"@")===t&&(c.type||"").toUpperCase()==="CNAME"})}async function Sm(e,n,t,s,o){let r=Xn(o),c=await xr(e,t,s);if(!c?.recordId)try{await Y(()=>e.addDomainRecord(new Re.AddDomainRecordRequest({domainName:t,RR:s,type:"CNAME",value:r})));return}catch(u){if(!J(u))throw u;c=await xr(e,t,s)}if(!c?.recordId)throw Error(`DNS 记录已存在但无法定位可更新记录: ${n}`);if(Xn(c.value||"")===r)return;await Y(()=>e.updateDomainRecord(new Re.UpdateDomainRecordRequest({recordId:c.recordId,RR:s,type:"CNAME",value:r})))}async function Hr(e,n){let t=e.trim().toLowerCase();if(!t)throw Error("域名不能为空");let{rootDomain:s,subDomain:o}=Qe(t),r=kt();await Sm(r,t,s,o,n)}async function _t(e,n,t,s={}){let o=k.getProject();if(!o.appName)throw Error("未找到应用名,请先执行 licell deploy");if(!s.skipDnsBind)await Hr(e,n);let r=Ti(),c={routes:[{path:"/*",functionName:o.appName,qualifier:t}]},f=new Rn.CreateCustomDomainInput({domainName:e,protocol:"HTTP",routeConfig:c});try{await Y(()=>r.createCustomDomain(new Rn.CreateCustomDomainRequest({body:f})))}catch(u){if(!J(u))throw u;await Y(()=>r.updateCustomDomain(e,new Rn.UpdateCustomDomainRequest({body:new Rn.UpdateCustomDomainInput({routeConfig:c})})))}return`http://${e}`}async function Ci(e){let n=e.trim().toLowerCase();if(!n)throw Error("域名不能为空");let{rootDomain:t,subDomain:s}=Qe(n),o=kt(),r=Ti();try{await Y(()=>r.deleteCustomDomain(n))}catch(u){if(!ue(u))throw u}let c=new Set,f=bi(t,s);for(let u of f){let l=[];try{l=(await Y(()=>o.describeSubDomainRecords(new Re.DescribeSubDomainRecordsRequest({domainName:t,subDomain:u,type:"CNAME",pageNumber:1,pageSize:200})))).body?.domainRecords?.record||[]}catch(i){if(ms(i))continue;throw i}for(let i of l){let g=i;if((g.RR||"@")!==s||(g.type||"").toUpperCase()!=="CNAME")continue;let y=g.recordId;if(!y||c.has(y))continue;c.add(y);try{await Y(()=>o.deleteDomainRecord(new Re.DeleteDomainRecordRequest({recordId:y})))}catch(d){if(!ue(d))throw d}}}}async function Ei(e,n=200){let t=e.trim().toLowerCase();if(!t)throw Error("域名不能为空");let s=kt(),o=[],r=Math.max(1,Math.min(Math.floor(n),1000)),c=Math.min(100,r);for(let f=1;f<=20&&o.length<r;f+=1){let u=await Y(()=>s.describeDomainRecords(new Re.DescribeDomainRecordsRequest({domainName:t,pageNumber:f,pageSize:c}))),l=u.body?.domainRecords?.record||[];for(let g of l){let y=g.recordId;if(!y)continue;if(o.push({recordId:y,rr:g.RR||"@",type:g.type||"",value:g.value||"",ttl:g.TTL,line:g.line,status:g.status}),o.length>=r)break}let i=u.body?.totalCount||0;if(l.length===0||i>0&&o.length>=i)break}return o}async function Si(e,n){let t=e.trim().toLowerCase();if(!t)throw Error("域名不能为空");let s=n.rr.trim(),o=n.type.trim().toUpperCase(),r=n.value.trim();if(!s)throw Error("RR 不能为空");if(!o)throw Error("记录类型不能为空");if(!r)throw Error("记录值不能为空");let c=kt(),u=(await Y(()=>c.addDomainRecord(new Re.AddDomainRecordRequest({domainName:t,RR:s,type:o,value:r,TTL:n.ttl,line:n.line||"default"})))).body?.recordId;if(!u)throw Error("添加 DNS 记录失败:未返回 recordId");return u}async function qi(e){let n=e.trim();if(!n)throw Error("recordId 不能为空");let t=kt();await Y(()=>t.deleteDomainRecord(new Re.DeleteDomainRecordRequest({recordId:n})))}async function Ys(e,n,t){let s=e.trim().toLowerCase(),o=Xn(n),{rootDomain:r,subDomain:c}=Qe(s),f=c?`*.${c}`:"*",u=c?`*.${c}.${r}`:`*.${r}`,l=kt(),i=await xr(l,r,f);if(i?.recordId){if(Xn(i.value||"")!==o)await Y(()=>l.updateDomainRecord(new Re.UpdateDomainRecordRequest({recordId:i.recordId,RR:f,type:"CNAME",value:o})));return{created:!1,skipped:!1,wildcardDomain:u,targetValue:o}}if(!t.skipConfirm){if(!t.interactiveTTY)return{created:!1,skipped:!0,wildcardDomain:u,targetValue:o};if(t.onConfirm){if(!await t.onConfirm())return{created:!1,skipped:!0,wildcardDomain:u,targetValue:o}}}return await Y(()=>l.addDomainRecord(new Re.AddDomainRecordRequest({domainName:r,RR:f,type:"CNAME",value:o}))),{created:!0,skipped:!1,wildcardDomain:u,targetValue:o}}K();ce();Cn();import*as In from"@alicloud/openapi-client";import*as Pi from"@alicloud/tea-util";Ie();var qm=te(In.default,"@alicloud/openapi-client");function Pm(){let e=k.requireAuth();return new qm(new In.Config({accessKeyId:e.ak,accessKeySecret:e.sk,regionId:e.region,endpoint:"cdn.aliyuncs.com"}))}function Rm(e){if(e===null||e===void 0)return;if(typeof e==="boolean")return e?"true":"false";return String(e)}function Im(e){let n={};for(let[t,s]of Object.entries(e)){let o=Rm(s);if(o===void 0)continue;n[t]=o}return n}async function Dt(e,n){let t=Pm(),s=new In.Params({action:e,version:"2018-05-10",protocol:"HTTPS",pathname:"/",method:"POST",authType:"AK",style:"RPC",reqBodyType:"formData",bodyType:"json"}),o=new In.OpenApiRequest({query:Im(n)});return t.callApi(s,o,new Pi.RuntimeOptions({readTimeout:20000,connectTimeout:8000}))}function dn(e){let n=e.trim().toLowerCase();if(!n)throw Error("域名不能为空");return n}function Ri(e){let n=Xn(e);if(!n)throw Error("CDN 回源域名不能为空");return n}function Ii(e){return e==="oss"?"oss":"domain"}function Fm(e){return"domestic"}function Fi(e,n){if(e==="domestic"||e==="overseas"||e==="global")return e;return Fm(n)}function xm(e){return String(e||"").trim().toLowerCase()||void 0}function Hm(e){if(!e||typeof e!=="object")return;let n=e,t=String(n.DomainName||n.domainName||"").trim().toLowerCase();if(!t)return;let s=String(n.Cname||n.cname||"").trim();return{domainName:t,cname:s?Xn(s):void 0,status:xm(n.DomainStatus||n.domainStatus)}}function Lm(e){if(!e||typeof e!=="object")return[];let t=e.Domains;if(!t||typeof t!=="object")return[];let s=t.PageData;if(!Array.isArray(s))return[];return s}function Lr(e){if(typeof e!=="object"||e===null)return!1;let n=String(e.code||"").toLowerCase(),t=String(e.message||"").toLowerCase();return n.includes("invaliddomainstatus")||n.includes("domainnotexist")||t.includes("domain status")||t.includes("processing")||t.includes("not ready")}async function Os(e){let n=dn(e),t=await Y(()=>Dt("DescribeUserDomains",{DomainName:n,PageNumber:1,PageSize:50})),s=Lm(t.body);for(let o of s){let r=Hm(o);if(!r)continue;if(r.domainName===n)return r}return}async function Am(e,n,t={}){let s=dn(e),o=Ri(n),r=Ii(t.sourceType),c=Fi(t.scope,s),f=JSON.stringify([{content:o,type:r,port:80,priority:"20",weight:"10"}]);await Y(()=>Dt("AddCdnDomain",{DomainName:s,CdnType:"web",Scope:c,Sources:f}))}async function Um(e,n=60,t=3000){let s=dn(e);for(let o=1;o<=n;o+=1){let r=await Os(s);if(r?.cname)return r.cname;await new Promise((c)=>setTimeout(c,t))}throw Error(`CDN 域名已创建,但暂未返回 CNAME: ${s}`)}async function vm(e,n,t){let s=dn(e),o=n.trim(),r=t.trim();if(!o||!r)return;let c=`licell-cdn-cert-${Date.now()}`;await Y(()=>Dt("SetCdnDomainSSLCertificate",{DomainName:s,SSLProtocol:"on",CertType:"upload",CertName:c,SSLPub:o,SSLPri:r}),{maxAttempts:12,baseDelayMs:2000,shouldRetry:(f)=>Ee(f)||ue(f)||Lr(f)})}function Gm(e){let n=R(e).toLowerCase();return n.includes("aliyuncdnaccessingprivateossrole")||n.includes("private oss")||n.includes("private_oss_auth")||n.includes("l2_oss_key")||n.includes("authorize")}async function Mm(e){let n=dn(e),t=JSON.stringify([{functionName:"l2_oss_key",functionArgs:[{argName:"private_oss_auth",argValue:"on"}]}]);try{await Y(()=>Dt("BatchSetCdnDomainConfig",{DomainNames:n,Functions:t}),{maxAttempts:12,baseDelayMs:2000,shouldRetry:(s)=>Ee(s)||Lr(s)})}catch(s){if(!Gm(s))throw s;throw Error("CDN 私有 OSS 回源授权失败,请先在 CDN 控制台完成一次“私有 OSS Bucket 回源授权”,"+`确保服务关联角色 AliyunCDNAccessingPrivateOSSRole 已创建。原始错误: ${R(s)}`)}}async function Bm(e){let n=dn(e),t=JSON.stringify([{functionName:"back_to_origin_url_rewrite",functionArgs:[{argName:"source_url",argValue:"^/$"},{argName:"target_url",argValue:"/index.html"},{argName:"flag",argValue:"break"}]}]);await Y(()=>Dt("BatchSetCdnDomainConfig",{DomainNames:n,Functions:t}),{maxAttempts:12,baseDelayMs:2000,shouldRetry:(s)=>Ee(s)||Lr(s)})}function Ym(e){return e==="online"}function Om(e){return e==="configure_failed"||e==="check_failed"}async function Km(e,n=40,t=3000){let s=dn(e);for(let o=1;o<=n;o+=1){let r=await Os(s);if(Ym(r?.status))return;if(Om(r?.status))throw Error(`CDN 域名状态异常: ${s} (${r?.status})`);await new Promise((c)=>setTimeout(c,t))}throw Error(`CDN 域名长时间未就绪: ${s}`)}async function jm(e,n,t={}){let s=dn(e),o=Ri(n),r=!1,c=await Os(s);if(!c)try{await Am(s,o,t),r=!0}catch(u){if(!J(u))throw u;c=await Os(s)}return{cdnCname:c?.cname||await Um(s),created:r}}async function Vt(e,n,t={}){let s=dn(e),o=Ii(t.sourceType),r=Fi(t.scope,s),c=await jm(s,n,{sourceType:o,scope:r});if(o==="oss"&&t.enablePrivateOssAuth!==!1)await Mm(s),await Bm(s);let f=!1;if(t.certificate&&t.privateKey)await vm(s,t.certificate,t.privateKey),f=!0;if(await Hr(s,c.cdnCname),t.waitForOnline)await Km(s);return{...c,httpsConfigured:f}}K();Ht();De();Cn();Ie();import*as hn from"acme-client";import Wm,*as Zn from"@alicloud/alidns20150109";import*as js from"@alicloud/fc20230330";import*as Ai from"@alicloud/openapi-client";import{createPrivateKey as Qm,X509Certificate as Dm}from"crypto";import{existsSync as Vm,readFileSync as Xm,writeFileSync as Zm}from"fs";import{dirname as Jm,join as Nm}from"path";import{homedir as zm}from"os";var Ks=Nm(zm(),".licell-cli","acme-account.pem"),e0=te(Wm,"@alicloud/alidns20150109"),n0=86400000,t0=30,xi=180000,s0=5000,o0=600;function r0(e){let n=e.toString("utf8");try{return Qm(n).export({format:"pem",type:"pkcs1"}).toString()}catch{return n}}async function c0(){if(Vm(Ks))return Xm(Ks);xt(Jm(Ks));let e=await hn.crypto.createPrivateKey();return Zm(Ks,e,{mode:384}),e}function f0(e){if(typeof e!=="string")return!1;return e.split(",").map((n)=>n.trim().toUpperCase()).includes("HTTPS")}function u0(e,n=t0){let t=typeof e==="number"?String(e):typeof e==="string"?e.trim():"";if(!/^\d+$/.test(t))return n;let s=Number.parseInt(t,10);return s>0?s:n}function i0(e,n=Date.now()){try{let t=new Dm(e),s=Date.parse(t.validTo);if(!Number.isFinite(s))return null;return Math.floor((s-n)/n0)}catch{return null}}function Hi(e){if(e>=0)return`剩余 ${e} 天`;return`已过期 ${Math.abs(e)} 天`}function l0(e){if(!e)return xi;let n=Number(e.trim());if(!Number.isFinite(n)||n<=0)return xi;return Math.floor(n)}function Li(e){return e.trim().replace(/^"+|"+$/g,"")}async function Ui(e,n,t){let s=[`${t}.${n}`,t],o=[];for(let r of s)try{let c=await Y(()=>e.describeSubDomainRecords(new Zn.DescribeSubDomainRecordsRequest({domainName:n,subDomain:r,type:"TXT",pageNumber:1,pageSize:100})));o.push(...c.body?.domainRecords?.record||[])}catch{}return o}async function g0(e,n,t,s,o){let r=l0(oe(process.env,"SSL_DNS_READY_TIMEOUT_MS")),c=Li(s),f=Date.now();while(!0){if((await Ui(e,n,t)).some((i)=>{if(typeof i.value!=="string")return!1;return Li(i.value)===c}))return;if(Date.now()-f>r)throw Error(`DNS TXT 记录传播超时: ${t}.${n}`);o.message(`\uD83C\uDF0D 正在等待 DNS 生效 (${t})...`),await ge(s0)}}function y0(e,n,t=i0){if(!e||!f0(e.protocol))return{issue:!0,message:"\uD83D\uDD12 域名尚未开启 HTTPS,开始签发证书..."};if(n.forceRenew)return{issue:!0,message:"\uD83D\uDD01 已启用强制续签,开始重新签发 HTTPS 证书..."};let s=e.certConfig?.certificate;if(typeof s!=="string"||s.trim().length===0)return{issue:!1,message:"\uD83D\uDD10 域名已开启 HTTPS,但无法读取证书有效期,跳过自动续签(可用 --ssl-force-renew 强制续签)。"};let o=t(s);if(o===null)return{issue:!1,message:"\uD83D\uDD10 域名已开启 HTTPS,但无法解析证书有效期,跳过自动续签(可用 --ssl-force-renew 强制续签)。"};if(o>n.renewBeforeDays)return{issue:!1,message:`\uD83D\uDD10 域名已开启 HTTPS,证书${Hi(o)}(续签阈值 ${n.renewBeforeDays} 天),跳过自动续签。`};return{issue:!0,message:`\uD83D\uDD01 检测到证书${Hi(o)}(续签阈值 ${n.renewBeforeDays} 天),开始自动续签...`}}async function Fn(e,n,t){let s=k.requireAuth(),o=t?.bindToFcDomain!==!1,r=new e0(new Ai.Config({accessKeyId:s.ak,accessKeySecret:s.sk,endpoint:"alidns.aliyuncs.com",connectTimeout:1e4,readTimeout:600000})),c=o?Bn(s).client:void 0,f={forceRenew:Boolean(t?.forceRenew),renewBeforeDays:u0(t?.renewBeforeDays??oe(process.env,"SSL_RENEW_BEFORE_DAYS"))},u=oe(process.env,"SSL_SKIP_CHALLENGE_VERIFY")!=="0",l=null;if(c)try{l=(await Y(()=>c.getCustomDomain(e))).body}catch{}let i=c?y0(l,f):{issue:!0,message:f.forceRenew?"\uD83D\uDD01 已启用强制续签,开始签发 CDN HTTPS 证书...":"\uD83D\uDD12 正在签发 CDN HTTPS 证书..."};if(n.message(i.message),!i.issue){let T=typeof l?.certConfig?.certificate==="string"?l.certConfig.certificate:void 0,x=typeof l?.certConfig?.privateKey==="string"?l.certConfig.privateKey:void 0;return{url:`https://${e}`,certificate:T?.trim()?T:void 0,privateKey:x?.trim()?x:void 0,reusedExistingCertificate:!0}}let{rootDomain:g,subDomain:y}=Qe(e),d=[];n.message("\uD83D\uDD12 正在向 Let's Encrypt 注册 ACME 账户并发起证书申请..."),hn.setLogger(()=>{});let m=await c0(),h=new hn.Client({directoryUrl:hn.directory.letsencrypt.production,accountKey:m}),[b,$]=await hn.crypto.createCsr({commonName:e}),p=async(T)=>{let x=new Set,E=await Ui(r,g,T);for(let _ of E){if(!_.recordId||x.has(_.recordId))continue;x.add(_.recordId);try{await Y(()=>r.deleteDomainRecord(new Zn.DeleteDomainRecordRequest({recordId:_.recordId})))}catch{}}},w=await h.auto({csr:$,email:`admin@${g}`,termsOfServiceAgreed:!0,challengePriority:["dns-01"],skipChallengeVerification:u,challengeCreateFn:async(T,x,E)=>{let _=y==="@"?"_acme-challenge":`_acme-challenge.${y}`,F=E;n.message(`\uD83D\uDCDD 正在自动配置 DNS TXT 记录 (${_}) ...`),await p(_);let I=await Y(()=>r.addDomainRecord(new Zn.AddDomainRecordRequest({domainName:g,RR:_,type:"TXT",value:F,TTL:o0})));if(I.body?.recordId)d.push(I.body.recordId);await g0(r,g,_,F,n),n.message(`\uD83C\uDF10 DNS TXT 已就绪,等待 Let's Encrypt 验证 (${_}) ...`)},challengeRemoveFn:async()=>{for(let T of d)try{await Y(()=>r.deleteDomainRecord(new Zn.DeleteDomainRecordRequest({recordId:T})))}catch(x){let E=x instanceof Error?x.message:String(x);if(!E.toLowerCase().includes("notfound")&&!E.toLowerCase().includes("not found"))n.message(`⚠️ DNS TXT 记录清理失败 (recordId=${T}): ${E}`)}}}),H=r0(b);if(c)n.message("\uD83D\uDCE6 证书下发成功,正在自动挂载至云端网关开启 HTTPS..."),await Y(()=>c.updateCustomDomain(e,new js.UpdateCustomDomainRequest({body:new js.UpdateCustomDomainInput({protocol:"HTTP,HTTPS",certConfig:{certName:`licell-cert-${Date.now()}`,certificate:w.toString(),privateKey:H}})})));else n.message("\uD83D\uDCE6 证书下发成功,正在用于 CDN 边缘 HTTPS 配置...");return{url:`https://${e}`,certificate:w.toString(),privateKey:H,reusedExistingCertificate:!1}}async function vi(e,n,t){return(await Fn(e,n,t)).url}De();import{request as d0}from"http";import{request as h0}from"https";var a0=["/healthz","/"],m0=4,p0=1500,w0=5000;function $0(e){let t=(e&&e.length>0?e:a0).map((s)=>s.trim()).filter((s)=>s.length>0).map((s)=>s.startsWith("/")?s:`/${s}`);return[...new Set(t)]}function k0(e,n){return`${e.replace(/\/+$/g,"")}${n}`}function _0(e){if(e instanceof Error){if(e.name==="AbortError")return"请求超时";return e.message}return String(e)}async function b0(e,n,t){let s=new AbortController,o=Error("请求超时");o.name="AbortError";let r,c=new Promise((f,u)=>{r=setTimeout(()=>{s.abort(),u(o)},n)});try{return await Promise.race([t(e,{method:"GET",redirect:"manual",signal:s.signal,headers:{"user-agent":"licell-health-check/1.0"}}),c])}finally{if(r)clearTimeout(r)}}async function T0(e,n){let t=new URL(e),s=t.protocol==="https:",o=s?h0:d0;return new Promise((r,c)=>{let f=!1,u,l=o(t,{method:"GET",headers:{"user-agent":"licell-health-check/1.0"},...s?{minVersion:"TLSv1.2",maxVersion:"TLSv1.2"}:{}},(i)=>{if(u)clearTimeout(u);let g=i.statusCode??0;if(i.resume(),i.once("error",()=>{}),!f)f=!0,r(g);i.destroy()});u=setTimeout(()=>{l.destroy(Error("请求超时"))},n),l.on("error",(i)=>{if(u)clearTimeout(u);if(f)return;f=!0,c(i)}),l.end()})}async function an(e,n={}){let t=e.trim();if(!t)return{ok:!1,error:"URL 为空",attempt:1};let s=n.fetchImpl??(typeof globalThis.fetch==="function"?globalThis.fetch.bind(globalThis):void 0),o=$0(n.paths),r=Math.max(1,Math.floor(n.maxAttempts??m0)),c=Math.max(0,Math.floor(n.intervalMs??p0)),f=Math.max(1000,Math.floor(n.timeoutMs??w0)),u=n.allowClientError===!1?400:500,l="未知错误";for(let i=1;i<=r;i+=1){for(let g of o){let y=k0(t,g);try{let d=s?(await b0(y,f,s)).status:await T0(y,f);if(d<u){if(g==="/healthz"&&d===404&&o.includes("/")){l=`GET ${y} 返回 404`;continue}return{ok:!0,checkedUrl:y,statusCode:d,attempt:i}}l=`GET ${y} 返回 ${d}`}catch(d){l=`GET ${y} 请求失败: ${_0(d)}`}}if(i<r&&c>0)await ge(c)}return{ok:!1,error:l,attempt:r}}ce();se();re();function Yi(e){let n=[];for(let t of e){let s=t.level==="error"?"ERROR":"WARN";if(n.push(`[${s}] ${t.id}`),n.push(t.message),t.remediation&&t.remediation.length>0)for(let o of t.remediation)n.push(`- ${o}`)}return n}async function Oi(e,n){let t=e.cliRuntime||e.projectRuntime||e.envRuntime||Wn;if(t!=="docker"&&!e.cliRuntime&&E0("Dockerfile")&&e.interactiveTTY){let b=await Gi({message:"检测到 Dockerfile,是否使用 Docker 容器部署?"});if(Mi(b)){if(a())throw Error("操作已取消");process.exit(0)}if(b)t="docker"}if(e.cliAcrNamespace&&t!=="docker")throw Error("--acr-namespace 仅适用于 --runtime docker");if(t==="docker"&&e.cliAcrNamespace)k.setProject({acrNamespace:e.cliAcrNamespace});let s=kn(t).defaultEntry,o;if(t==="docker")o=e.cliEntry||"";else if(e.cliEntry)o=L(e.cliEntry,"入口文件路径");else if(e.interactiveTTY)o=L(await C0({message:t.startsWith("python")?"入口文件路径 (Python 需包含 handler 函数):":"入口文件路径 (需导出 handler):",initialValue:s}),"入口文件路径");else o=s;let r=Wt({runtime:t,entry:o,checkDockerDaemon:t==="docker"}),c=r.issues.filter((b)=>b.level==="warning");if(!r.ok){let b=Yi(r.issues),$=fr(r);throw $.message=`${$.message}
1396
1396
  ${b.join(`
1397
1397
  `)}`,$}if(c.length>0){let b=Yi(c);n.message(`⚠️ 部署前预检通过(含 warning):
1398
1398
  ${b.join(`
1399
- `)}`)}let f="\uD83D\uDD28 正在使用 Bun 极速剥离依赖打包,并推送至云端...";if(t==="docker")f="\uD83D\uDC33 正在构建 Docker 镜像并推送至 ACR...";else if(t.startsWith("python"))f="\uD83D\uDC0D 正在打包 Python 源码并推送至云端...";let u=await U(n,f,"❌ 部署失败",async()=>{if(e.useVpc&&!e.project.network){n.message("\uD83C\uDF10 正在自动准备 VPC 网络...");try{let _=await it();k.setProject({network:_}),e.project=k.getProject(),n.message(`✅ VPC 已就绪: ${_.vpcId} / ${_.vswId}`)}catch(_){console.warn(Bi.yellow(`⚠️ VPC 自动接入失败,回退公网模式: ${R(_)}`))}}let b=e.useVpc?e.project.network:null,$={...e.cliResources?{resources:e.cliResources}:{},...b!==void 0?{network:b}:{}},p=await sr(e.appName,s,t,Object.keys($).length>0?$:void 0),w=`${e.auth.accountId}.${e.auth.region}.fc.aliyuncs.com`,H,T,x,E;if(e.preview&&e.domainSuffix){n.message("函数部署完成,正在发布预览版本..."),E=await Sn(e.appName,`preview at ${new Date().toISOString()}`),x=`${e.appName}-preview-v${E}.${e.domainSuffix}`,n.message(`正在确保通配符 DNS (*.${e.domainSuffix}) 存在...`);let _=await Oo(e.domainSuffix,w,{interactiveTTY:e.interactiveTTY,onConfirm:async()=>{let F=await Gi({message:`检测到尚未配置通配符 DNS (*.${e.domainSuffix})。
1399
+ `)}`)}let f="\uD83D\uDD28 正在使用 Bun 极速剥离依赖打包,并推送至云端...";if(t==="docker")f="\uD83D\uDC33 正在构建 Docker 镜像并推送至 ACR...";else if(t.startsWith("python"))f="\uD83D\uDC0D 正在打包 Python 源码并推送至云端...";let u=await v(n,f,"❌ 部署失败",async()=>{if(e.useVpc&&!e.project.network){n.message("\uD83C\uDF10 正在自动准备 VPC 网络...");try{let _=await it();k.setProject({network:_}),e.project=k.getProject(),n.message(`✅ VPC 已就绪: ${_.vpcId} / ${_.vswId}`)}catch(_){console.warn(Bi.yellow(`⚠️ VPC 自动接入失败,回退公网模式: ${R(_)}`))}}let b=e.useVpc?e.project.network:null,$={...e.cliResources?{resources:e.cliResources}:{},...b!==void 0?{network:b}:{}},p=await or(e.appName,o,t,Object.keys($).length>0?$:void 0),w=`${e.auth.accountId}.${e.auth.region}.fc.aliyuncs.com`,H,T,x,E;if(e.preview&&e.domainSuffix){n.message("函数部署完成,正在发布预览版本..."),E=await Sn(e.appName,`preview at ${new Date().toISOString()}`),x=`${e.appName}-preview-v${E}.${e.domainSuffix}`,n.message(`正在确保通配符 DNS (*.${e.domainSuffix}) 存在...`);let _=await Ys(e.domainSuffix,w,{interactiveTTY:e.interactiveTTY,onConfirm:async()=>{let F=await Gi({message:`检测到尚未配置通配符 DNS (*.${e.domainSuffix})。
1400
1400
  `+`创建后,所有 preview 子域名将自动解析到 FC 网关。
1401
1401
  `+`已有的精确 DNS 记录(如 ${e.appName}.${e.domainSuffix})不受影响。
1402
- `+"是否创建?"});if(Mi(F))return!1;return F}});if(_.skipped)n.message(Bi.yellow("⚠️ 已跳过通配符 DNS 创建,preview 域名可能无法访问"));else if(_.created)n.message(`✅ 通配符 DNS 已创建: ${_.wildcardDomain} → ${_.targetValue}`);if(n.message(`正在绑定预览域名 ${x}...`),await _t(x,w,E,{skipDnsBind:!0}),e.enableSSL)n.message(`预览域名绑定完成,正在签发 HTTPS 证书 (${x})...`),await Fn(x,n,{forceRenew:e.forceSslRenew})}else if(e.releaseTarget)n.message(`函数部署完成,正在发布版本并切流到 ${e.releaseTarget}...`),H=await Sn(e.appName,`deploy ${e.releaseTarget} at ${new Date().toISOString()}`),await qn(e.appName,e.releaseTarget,H,`deployed by licell at ${new Date().toISOString()}`);if(e.domainSuffix){T=`${e.appName}.${e.domainSuffix}`,n.message(`函数部署完成,正在按固定规则绑定域名 ${T}...`),await _t(T,w,e.releaseTarget,{skipDnsBind:e.enableCdn});let _;if(e.enableSSL){n.message(`固定域名绑定完成,正在签发并挂载 HTTPS 证书 (${T})...`);let F=await Fn(T,n,{forceRenew:e.forceSslRenew});_={certificate:F.certificate,privateKey:F.privateKey}}if(e.enableCdn){n.message(`固定域名绑定完成,正在启用 CDN 加速 (${T})...`);let F=await Vt(T,w,_);if(n.message(F.created?`✅ CDN 加速已启用,CNAME=${F.cdnCname}`:`✅ CDN 加速已存在,已校准 DNS 到 CNAME=${F.cdnCname}`),e.enableSSL&&F.httpsConfigured)n.message("✅ CDN 边缘 HTTPS 已自动配置。");if(e.enableSSL&&!F.httpsConfigured)n.message("⚠️ 未能自动配置 CDN 边缘 HTTPS(未获取到可用证书),请在 CDN 控制台补充证书。")}}if(e.cliDomain){T=e.cliDomain,n.message(`函数部署完成,正在绑定自定义域名 ${T}...`),await _t(T,w,e.releaseTarget,{skipDnsBind:e.enableCdn});let _;if(e.enableSSL){n.message(`自定义域名绑定完成,正在签发并挂载 HTTPS 证书 (${T})...`);let F=await Fn(T,n,{forceRenew:e.forceSslRenew});_={certificate:F.certificate,privateKey:F.privateKey}}if(e.enableCdn){n.message(`自定义域名绑定完成,正在启用 CDN 加速 (${T})...`);let F=await Vt(T,w,_);if(n.message(F.created?`✅ CDN 加速已启用,CNAME=${F.cdnCname}`:`✅ CDN 加速已存在,已校准 DNS 到 CNAME=${F.cdnCname}`),e.enableSSL&&F.httpsConfigured)n.message("✅ CDN 边缘 HTTPS 已自动配置。");if(e.enableSSL&&!F.httpsConfigured)n.message("⚠️ 未能自动配置 CDN 边缘 HTTPS(未获取到可用证书),请在 CDN 控制台补充证书。")}}return{url:p,promotedVersion:H,fixedDomain:T,previewDomain:x,previewVersion:E}});if(!u)return;let{url:l,promotedVersion:i,fixedDomain:g,previewDomain:y,previewVersion:d}=u;n.message("\uD83E\uDE7A 部署完成,正在做可访问性检测...");let m=[],h=await an(l);if(h.ok)m.push(`✅ 生产地址可访问 (${h.statusCode} ${h.checkedUrl})`);else m.push(`⚠️ 生产地址可访问性检测未通过: ${h.error}`);if(g){let b=`${e.enableSSL?"https":"http"}://${g}`,$=e.enableCdn?10:6,p=e.enableCdn?3000:2000,w=e.enableCdn?6000:5000,H=await an(b,{maxAttempts:$,intervalMs:p,timeoutMs:w});if(H.ok)m.push(`✅ 固定域名可访问 (${H.statusCode} ${H.checkedUrl})`);else m.push(`⚠️ 固定域名检测未通过(可能 DNS 传播中): ${H.error}`)}if(y){let b=`${e.enableSSL?"https":"http"}://${y}`,$=await an(b,{maxAttempts:8,intervalMs:2000,timeoutMs:5000});if($.ok)m.push(`✅ 预览域名可访问 (${$.statusCode} ${$.checkedUrl})`);else m.push(`⚠️ 预览域名检测未通过(可能 DNS 传播中): ${$.error}`)}return{url:l,promotedVersion:i,fixedDomain:g,previewDomain:y,previewVersion:d,healthCheckLogs:m}}K();import{confirm as fp,text as up,isCancel as ip}from"@clack/prompts";import lp from"picocolors";K();import S0,*as We from"@alicloud/oss20190517";import*as xn from"@alicloud/openapi-client";import q0 from"@alicloud/openapi-util";import*as Qi from"@alicloud/tea-util";import{createReadStream as P0,existsSync as R0,lstatSync as I0,readdirSync as F0,realpathSync as Ki,statSync as x0}from"fs";import{isAbsolute as H0,join as A0,relative as L0}from"path";import Ur from"mime-types";function Lr(e){if(!Number.isInteger(e)||e<1)throw Error("concurrency must be a positive integer");let n=0,t=[];function o(){if(t.length>0&&n<e)n+=1,t.shift()()}return async function(r){if(n>=e)await new Promise((c)=>t.push(c));else n+=1;try{return await r()}finally{n-=1,o()}}}Cn();Ie();var Wi=(()=>{let e=q0;if(typeof e?.query==="function")return e;if(typeof e?.default?.query==="function")return e.default;throw Error("Cannot resolve @alicloud/openapi-util")})(),U0=10,v0=8000,G0=120000,M0=te(S0,"@alicloud/oss20190517"),B0="application/octet-stream";function bt(){let e=k.requireAuth(),n=new M0(new xn.Config({accessKeyId:e.ak,accessKeySecret:e.sk,regionId:e.region,endpoint:`oss-${e.region}.aliyuncs.com`})),t=new Qi.RuntimeOptions({connectTimeout:v0,readTimeout:G0});return{auth:e,client:n,runtime:t}}function Y0(e){if(!Fe(e))return!1;return String(e?.message||"").toLowerCase().includes("put public bucket acl is not allowed")}function O0(e){if(!String(e?.message||"").toLowerCase().includes("not a valid value for parameter"))return!1;let t=String(e?.stack||"").toLowerCase();return t.includes("gateway-oss")||t.includes("darabonba-map")}async function K0(e,n,t){try{await e.getBucketInfoWithOptions(n,{},t)}catch(o){if(Fe(o))throw Error(`OSS Bucket 已被占用且当前账号无权限访问: ${n},请更换 appName 后重试`);throw o}}function Di(e){if(Array.isArray(e))return e;if(e===void 0||e===null)return[];return[e]}function Vi(e){if(typeof e==="boolean")return e;if(typeof e==="string")return e.trim().toLowerCase()==="true";return!1}function ji(e){if(typeof e==="number"&&Number.isFinite(e))return e;if(typeof e==="string"){let n=Number(e);if(Number.isFinite(n))return n}return}function z(e){if(typeof e!=="string")return;let n=e.trim();return n.length>0?n:void 0}async function j0(e,n,t){let o=new xn.OpenApiRequest({headers:{},query:Wi.query({...t.marker?{marker:t.marker}:{},"max-keys":t.maxKeys})}),s=new xn.Params({action:"ListBuckets",version:"2019-05-17",protocol:"HTTPS",pathname:"/",method:"GET",authType:"AK",style:"ROA",reqBodyType:"xml",bodyType:"xml"}),c=(await e.execute(s,o,n)).body||{},f=c.Buckets||c.buckets||{};return{rows:Di(f.Bucket||f.bucket),nextMarker:z(c.NextMarker)||z(c.nextMarker),isTruncated:Vi(c.IsTruncated??c.isTruncated)}}async function Xi(e,n,t,o){let s=new xn.OpenApiRequest({hostMap:{bucket:t},headers:{},query:Wi.query({...o.prefix?{prefix:o.prefix}:{},...o.continuationToken?{"continuation-token":o.continuationToken}:{},"max-keys":o.maxKeys})}),r=new xn.Params({action:"ListObjectsV2",version:"2019-05-17",protocol:"HTTPS",pathname:"/?list-type=2",method:"GET",authType:"AK",style:"ROA",reqBodyType:"xml",bodyType:"xml"}),f=(await e.execute(r,s,n)).body||{};return{rows:Di(f.Contents||f.contents),nextContinuationToken:z(f.NextContinuationToken)||z(f.nextContinuationToken),isTruncated:Vi(f.IsTruncated??f.isTruncated)}}function vr(e){if(!e)return;let n=e.trim().replace(/\\/g,"/").replace(/\/{2,}/g,"/").replace(/^\/+|\/+$/g,"");return n.length>0?n:void 0}function W0(e,n){let t=e.replace(/\\/g,"/").replace(/^\/+/,"");if(!t||t===".")throw Error("对象路径不能为空");let o=vr(n);return o?`${o}/${t}`:t}function Q0(e,n){if(!R0(e)||!x0(e).isDirectory())throw Error(`本地目录不存在或不可读: ${e}`);let t=Ki(e),o=vr(n),s=new Set([t]),r=[],c=0;function f(u){for(let l of F0(u)){let i=A0(u,l),g=I0(i);if(g.isSymbolicLink()){c+=1;continue}if(g.isDirectory()){let d=Ki(i);if(s.has(d))continue;s.add(d),f(d);continue}if(!g.isFile())continue;let y=L0(t,i).replace(/\\/g,"/");if(!y||y==="."||y.startsWith("..")||H0(y))throw Error(`检测到越界路径,已拒绝上传: ${i}`);r.push({sourceFile:i,objectName:W0(y,o)})}}return f(t),{sourceRoot:t,files:r,skippedSymlinkCount:c}}function D0(e,n){let t=n?Ur.lookup(n):!1,o=Ur.lookup(e),s=t||o;if(!s)return B0;let r=Ur.contentType(s);return r?String(r):String(s)}async function Gr(e,n,t){let{auth:o,client:s,runtime:r}=bt(),c=e.trim();if(!c)throw Error("bucket 名称不能为空");let f=vr(t?.targetDir),u=Q0(n,f),l=Number.isFinite(t?.concurrency)&&Number(t?.concurrency||0)>0?Math.floor(Number(t?.concurrency)):U0,i=Lr(l);return await Promise.all(u.files.map((g)=>i(async()=>{let y=D0(g.sourceFile,g.objectName);await Y(()=>s.putObjectWithOptions(c,g.objectName,new We.PutObjectRequest({body:P0(g.sourceFile)}),new We.PutObjectHeaders({commonHeaders:{"content-type":y}}),r),{maxAttempts:4,baseDelayMs:1000,shouldRetry:Ee})}))),{bucket:c,targetDir:f,uploadedCount:u.files.length,baseUrl:`https://${c}.oss-${o.region}.aliyuncs.com`,skippedSymlinkCount:u.skippedSymlinkCount}}async function Mr(e,n,t){let{auth:o,client:s,runtime:r}=bt(),c=`licell-${e}-${o.accountId.substring(0,4)}`.toLowerCase();try{await s.putBucketWithOptions(c,new We.PutBucketRequest({}),new We.PutBucketHeaders({}),r)}catch(l){if(!J(l)&&!O0(l))throw l;await K0(s,c,r)}let f=!1;try{await s.putBucketAclWithOptions(c,new We.PutBucketAclHeaders({acl:"public-read"}),r)}catch(l){if(Y0(l))f=!0;if(!f&&Fe(l))throw Error(`OSS Bucket 无权限修改 ACL: ${c},请确认该 Bucket 属于当前账号并可写`);if(!f)throw l}return(await Gr(c,n,{targetDir:t?.targetDir})).baseUrl}function Qo(e){let n=k.requireAuth();return`licell-${e}-${n.accountId.substring(0,4)}`.toLowerCase()}async function Zi(e=200){let n=Math.max(1,Math.min(Math.floor(e),1000)),t=Math.min(100,n),{client:o,runtime:s}=bt(),r=[],c;while(r.length<n){let f=await j0(o,s,{marker:c,maxKeys:t}),u=f.rows;for(let l of u){let i=z(l.Name)||z(l.name)||"";if(!i)continue;if(r.push({name:i,location:z(l.Region)||z(l.region)||z(l.Location)||z(l.location),creationDate:z(l.CreationDate)||z(l.creationDate),extranetEndpoint:z(l.ExtranetEndpoint)||z(l.extranetEndpoint),intranetEndpoint:z(l.IntranetEndpoint)||z(l.intranetEndpoint)}),r.length>=n)break}if(c=f.nextMarker,!f.isTruncated||!c||u.length===0)break}return r}async function Ji(e){let{client:n,runtime:t}=bt(),o=e.trim();if(!o)throw Error("bucket 名称不能为空");let r=(await n.getBucketInfoWithOptions(o,{},t)).body?.bucket;return{name:r?.name||o,location:r?.location,creationDate:r?.creationDate,extranetEndpoint:r?.extranetEndpoint,intranetEndpoint:r?.intranetEndpoint}}async function Do(e,n,t=200){let{client:o,runtime:s}=bt(),r=e.trim();if(!r)throw Error("bucket 名称不能为空");let c=Math.max(1,Math.min(Math.floor(t),2000)),f=Math.min(1000,c),u=[],l;while(u.length<c){let i=await Xi(o,s,r,{prefix:n,continuationToken:l,maxKeys:f}),g=i.rows;for(let y of g){let d=z(y.Key)||z(y.key);if(!d)continue;if(u.push({name:d,size:ji(y.Size)??ji(y.size),lastModified:z(y.LastModified)||z(y.lastModified),etag:z(y.ETag)||z(y.etag),type:z(y.Type)||z(y.type),storageClass:z(y.StorageClass)||z(y.storageClass)}),u.length>=c)break}if(l=i.nextContinuationToken,!i.isTruncated||!l||g.length===0)break}return u}async function Ni(e){let{client:n,runtime:t}=bt(),o=e.trim();if(!o)throw Error("bucket 名称不能为空");let s=0,r,c=Lr(8);while(!0){let f=[];try{let u=await Y(()=>Xi(n,t,o,{continuationToken:r,maxKeys:1000}),{maxAttempts:5,baseDelayMs:800,shouldRetry:Ee});f=u.rows,r=u.nextContinuationToken;let l=f.map((i)=>z(i.Key)||z(i.key)||"").filter((i)=>i.length>0);if(l.length>0)await Promise.all(l.map((i)=>c(async()=>{await Y(()=>n.deleteObjectWithOptions(o,i,new We.DeleteObjectRequest({}),{},t),{maxAttempts:5,baseDelayMs:500,shouldRetry:Ee})}))),s+=l.length;if(!u.isTruncated||!r)break}catch(u){if(ue(u))return{bucket:o,deletedObjects:0,deletedBucket:!1};throw u}}try{return await Y(()=>n.deleteBucketWithOptions(o,{},t),{maxAttempts:5,baseDelayMs:800,shouldRetry:Ee}),{bucket:o,deletedObjects:s,deletedBucket:!0}}catch(f){if(ue(f))return{bucket:o,deletedObjects:s,deletedBucket:!1};throw f}}import{existsSync as nl,readdirSync as V0,statSync as tl}from"fs";import{join as Br}from"path";var zi=["dist","build","out","public","www","site",".output/public"];function X0(e){if(!nl(e))return!1;try{return tl(e).isDirectory()}catch{return!1}}function el(e){let n=Br(e,"index.html");if(!nl(n))return!1;try{return tl(n).isFile()}catch{return!1}}function Z0(e){if(!X0(e))return!1;try{return V0(e).length>0}catch{return!1}}function ol(e=process.cwd()){if(el(e))return".";for(let n of zi){let t=Br(e,n);if(el(t))return n}for(let n of zi){let t=Br(e,n);if(Z0(t))return n}return"dist"}oe();K();ce();Cn();Dn();rr();import*as Ue from"@alicloud/fc20230330";import{mkdirSync as J0,rmSync as N0,writeFileSync as z0}from"fs";import{tmpdir as ep}from"os";import{join as sl}from"path";var np="-static-proxy",tp=256,op=30,sp=0.25;function rp(e,n){return`
1402
+ `+"是否创建?"});if(Mi(F))return!1;return F}});if(_.skipped)n.message(Bi.yellow("⚠️ 已跳过通配符 DNS 创建,preview 域名可能无法访问"));else if(_.created)n.message(`✅ 通配符 DNS 已创建: ${_.wildcardDomain} → ${_.targetValue}`);if(n.message(`正在绑定预览域名 ${x}...`),await _t(x,w,E,{skipDnsBind:!0}),e.enableSSL)n.message(`预览域名绑定完成,正在签发 HTTPS 证书 (${x})...`),await Fn(x,n,{forceRenew:e.forceSslRenew})}else if(e.releaseTarget)n.message(`函数部署完成,正在发布版本并切流到 ${e.releaseTarget}...`),H=await Sn(e.appName,`deploy ${e.releaseTarget} at ${new Date().toISOString()}`),await qn(e.appName,e.releaseTarget,H,`deployed by licell at ${new Date().toISOString()}`);if(e.domainSuffix){T=`${e.appName}.${e.domainSuffix}`,n.message(`函数部署完成,正在按固定规则绑定域名 ${T}...`),await _t(T,w,e.releaseTarget,{skipDnsBind:e.enableCdn});let _;if(e.enableSSL){n.message(`固定域名绑定完成,正在签发并挂载 HTTPS 证书 (${T})...`);let F=await Fn(T,n,{forceRenew:e.forceSslRenew});_={certificate:F.certificate,privateKey:F.privateKey}}if(e.enableCdn){n.message(`固定域名绑定完成,正在启用 CDN 加速 (${T})...`);let F=await Vt(T,w,_);if(n.message(F.created?`✅ CDN 加速已启用,CNAME=${F.cdnCname}`:`✅ CDN 加速已存在,已校准 DNS 到 CNAME=${F.cdnCname}`),e.enableSSL&&F.httpsConfigured)n.message("✅ CDN 边缘 HTTPS 已自动配置。");if(e.enableSSL&&!F.httpsConfigured)n.message("⚠️ 未能自动配置 CDN 边缘 HTTPS(未获取到可用证书),请在 CDN 控制台补充证书。")}}if(e.cliDomain){T=e.cliDomain,n.message(`函数部署完成,正在绑定自定义域名 ${T}...`),await _t(T,w,e.releaseTarget,{skipDnsBind:e.enableCdn});let _;if(e.enableSSL){n.message(`自定义域名绑定完成,正在签发并挂载 HTTPS 证书 (${T})...`);let F=await Fn(T,n,{forceRenew:e.forceSslRenew});_={certificate:F.certificate,privateKey:F.privateKey}}if(e.enableCdn){n.message(`自定义域名绑定完成,正在启用 CDN 加速 (${T})...`);let F=await Vt(T,w,_);if(n.message(F.created?`✅ CDN 加速已启用,CNAME=${F.cdnCname}`:`✅ CDN 加速已存在,已校准 DNS 到 CNAME=${F.cdnCname}`),e.enableSSL&&F.httpsConfigured)n.message("✅ CDN 边缘 HTTPS 已自动配置。");if(e.enableSSL&&!F.httpsConfigured)n.message("⚠️ 未能自动配置 CDN 边缘 HTTPS(未获取到可用证书),请在 CDN 控制台补充证书。")}}return{url:p,promotedVersion:H,fixedDomain:T,previewDomain:x,previewVersion:E}});if(!u)return;let{url:l,promotedVersion:i,fixedDomain:g,previewDomain:y,previewVersion:d}=u;n.message("\uD83E\uDE7A 部署完成,正在做可访问性检测...");let m=[],h=await an(l);if(h.ok)m.push(`✅ 生产地址可访问 (${h.statusCode} ${h.checkedUrl})`);else m.push(`⚠️ 生产地址可访问性检测未通过: ${h.error}`);if(g){let b=`${e.enableSSL?"https":"http"}://${g}`,$=e.enableCdn?10:6,p=e.enableCdn?3000:2000,w=e.enableCdn?6000:5000,H=await an(b,{maxAttempts:$,intervalMs:p,timeoutMs:w});if(H.ok)m.push(`✅ 固定域名可访问 (${H.statusCode} ${H.checkedUrl})`);else m.push(`⚠️ 固定域名检测未通过(可能 DNS 传播中): ${H.error}`)}if(y){let b=`${e.enableSSL?"https":"http"}://${y}`,$=await an(b,{maxAttempts:8,intervalMs:2000,timeoutMs:5000});if($.ok)m.push(`✅ 预览域名可访问 (${$.statusCode} ${$.checkedUrl})`);else m.push(`⚠️ 预览域名检测未通过(可能 DNS 传播中): ${$.error}`)}return{url:l,promotedVersion:i,fixedDomain:g,previewDomain:y,previewVersion:d,healthCheckLogs:m}}K();import{confirm as fp,text as up,isCancel as ip}from"@clack/prompts";import lp from"picocolors";K();import S0,*as We from"@alicloud/oss20190517";import*as xn from"@alicloud/openapi-client";import q0 from"@alicloud/openapi-util";import*as Qi from"@alicloud/tea-util";import{createReadStream as P0,existsSync as R0,lstatSync as I0,readdirSync as F0,realpathSync as Ki,statSync as x0}from"fs";import{isAbsolute as H0,join as L0,relative as A0}from"path";import Ur from"mime-types";function Ar(e){if(!Number.isInteger(e)||e<1)throw Error("concurrency must be a positive integer");let n=0,t=[];function s(){if(t.length>0&&n<e)n+=1,t.shift()()}return async function(r){if(n>=e)await new Promise((c)=>t.push(c));else n+=1;try{return await r()}finally{n-=1,s()}}}Cn();Ie();var Wi=(()=>{let e=q0;if(typeof e?.query==="function")return e;if(typeof e?.default?.query==="function")return e.default;throw Error("Cannot resolve @alicloud/openapi-util")})(),U0=10,v0=8000,G0=120000,M0=te(S0,"@alicloud/oss20190517"),B0="application/octet-stream";function bt(){let e=k.requireAuth(),n=new M0(new xn.Config({accessKeyId:e.ak,accessKeySecret:e.sk,regionId:e.region,endpoint:`oss-${e.region}.aliyuncs.com`})),t=new Qi.RuntimeOptions({connectTimeout:v0,readTimeout:G0});return{auth:e,client:n,runtime:t}}function Y0(e){if(!Fe(e))return!1;return String(e?.message||"").toLowerCase().includes("put public bucket acl is not allowed")}function O0(e){if(!String(e?.message||"").toLowerCase().includes("not a valid value for parameter"))return!1;let t=String(e?.stack||"").toLowerCase();return t.includes("gateway-oss")||t.includes("darabonba-map")}async function K0(e,n,t){try{await e.getBucketInfoWithOptions(n,{},t)}catch(s){if(Fe(s))throw Error(`OSS Bucket 已被占用且当前账号无权限访问: ${n},请更换 appName 后重试`);throw s}}function Di(e){if(Array.isArray(e))return e;if(e===void 0||e===null)return[];return[e]}function Vi(e){if(typeof e==="boolean")return e;if(typeof e==="string")return e.trim().toLowerCase()==="true";return!1}function ji(e){if(typeof e==="number"&&Number.isFinite(e))return e;if(typeof e==="string"){let n=Number(e);if(Number.isFinite(n))return n}return}function z(e){if(typeof e!=="string")return;let n=e.trim();return n.length>0?n:void 0}async function j0(e,n,t){let s=new xn.OpenApiRequest({headers:{},query:Wi.query({...t.marker?{marker:t.marker}:{},"max-keys":t.maxKeys})}),o=new xn.Params({action:"ListBuckets",version:"2019-05-17",protocol:"HTTPS",pathname:"/",method:"GET",authType:"AK",style:"ROA",reqBodyType:"xml",bodyType:"xml"}),c=(await e.execute(o,s,n)).body||{},f=c.Buckets||c.buckets||{};return{rows:Di(f.Bucket||f.bucket),nextMarker:z(c.NextMarker)||z(c.nextMarker),isTruncated:Vi(c.IsTruncated??c.isTruncated)}}async function Xi(e,n,t,s){let o=new xn.OpenApiRequest({hostMap:{bucket:t},headers:{},query:Wi.query({...s.prefix?{prefix:s.prefix}:{},...s.continuationToken?{"continuation-token":s.continuationToken}:{},"max-keys":s.maxKeys})}),r=new xn.Params({action:"ListObjectsV2",version:"2019-05-17",protocol:"HTTPS",pathname:"/?list-type=2",method:"GET",authType:"AK",style:"ROA",reqBodyType:"xml",bodyType:"xml"}),f=(await e.execute(r,o,n)).body||{};return{rows:Di(f.Contents||f.contents),nextContinuationToken:z(f.NextContinuationToken)||z(f.nextContinuationToken),isTruncated:Vi(f.IsTruncated??f.isTruncated)}}function vr(e){if(!e)return;let n=e.trim().replace(/\\/g,"/").replace(/\/{2,}/g,"/").replace(/^\/+|\/+$/g,"");return n.length>0?n:void 0}function W0(e,n){let t=e.replace(/\\/g,"/").replace(/^\/+/,"");if(!t||t===".")throw Error("对象路径不能为空");let s=vr(n);return s?`${s}/${t}`:t}function Q0(e,n){if(!R0(e)||!x0(e).isDirectory())throw Error(`本地目录不存在或不可读: ${e}`);let t=Ki(e),s=vr(n),o=new Set([t]),r=[],c=0;function f(u){for(let l of F0(u)){let i=L0(u,l),g=I0(i);if(g.isSymbolicLink()){c+=1;continue}if(g.isDirectory()){let d=Ki(i);if(o.has(d))continue;o.add(d),f(d);continue}if(!g.isFile())continue;let y=A0(t,i).replace(/\\/g,"/");if(!y||y==="."||y.startsWith("..")||H0(y))throw Error(`检测到越界路径,已拒绝上传: ${i}`);r.push({sourceFile:i,objectName:W0(y,s)})}}return f(t),{sourceRoot:t,files:r,skippedSymlinkCount:c}}function D0(e,n){let t=n?Ur.lookup(n):!1,s=Ur.lookup(e),o=t||s;if(!o)return B0;let r=Ur.contentType(o);return r?String(r):String(o)}async function Gr(e,n,t){let{auth:s,client:o,runtime:r}=bt(),c=e.trim();if(!c)throw Error("bucket 名称不能为空");let f=vr(t?.targetDir),u=Q0(n,f),l=Number.isFinite(t?.concurrency)&&Number(t?.concurrency||0)>0?Math.floor(Number(t?.concurrency)):U0,i=Ar(l);return await Promise.all(u.files.map((g)=>i(async()=>{let y=D0(g.sourceFile,g.objectName);await Y(()=>o.putObjectWithOptions(c,g.objectName,new We.PutObjectRequest({body:P0(g.sourceFile)}),new We.PutObjectHeaders({commonHeaders:{"content-type":y}}),r),{maxAttempts:4,baseDelayMs:1000,shouldRetry:Ee})}))),{bucket:c,targetDir:f,uploadedCount:u.files.length,baseUrl:`https://${c}.oss-${s.region}.aliyuncs.com`,skippedSymlinkCount:u.skippedSymlinkCount}}async function Mr(e,n,t){let{auth:s,client:o,runtime:r}=bt(),c=`licell-${e}-${s.accountId.substring(0,4)}`.toLowerCase();try{await o.putBucketWithOptions(c,new We.PutBucketRequest({}),new We.PutBucketHeaders({}),r)}catch(l){if(!J(l)&&!O0(l))throw l;await K0(o,c,r)}let f=!1;try{await o.putBucketAclWithOptions(c,new We.PutBucketAclHeaders({acl:"public-read"}),r)}catch(l){if(Y0(l))f=!0;if(!f&&Fe(l))throw Error(`OSS Bucket 无权限修改 ACL: ${c},请确认该 Bucket 属于当前账号并可写`);if(!f)throw l}return(await Gr(c,n,{targetDir:t?.targetDir})).baseUrl}function Ws(e){let n=k.requireAuth();return`licell-${e}-${n.accountId.substring(0,4)}`.toLowerCase()}async function Zi(e=200){let n=Math.max(1,Math.min(Math.floor(e),1000)),t=Math.min(100,n),{client:s,runtime:o}=bt(),r=[],c;while(r.length<n){let f=await j0(s,o,{marker:c,maxKeys:t}),u=f.rows;for(let l of u){let i=z(l.Name)||z(l.name)||"";if(!i)continue;if(r.push({name:i,location:z(l.Region)||z(l.region)||z(l.Location)||z(l.location),creationDate:z(l.CreationDate)||z(l.creationDate),extranetEndpoint:z(l.ExtranetEndpoint)||z(l.extranetEndpoint),intranetEndpoint:z(l.IntranetEndpoint)||z(l.intranetEndpoint)}),r.length>=n)break}if(c=f.nextMarker,!f.isTruncated||!c||u.length===0)break}return r}async function Ji(e){let{client:n,runtime:t}=bt(),s=e.trim();if(!s)throw Error("bucket 名称不能为空");let r=(await n.getBucketInfoWithOptions(s,{},t)).body?.bucket;return{name:r?.name||s,location:r?.location,creationDate:r?.creationDate,extranetEndpoint:r?.extranetEndpoint,intranetEndpoint:r?.intranetEndpoint}}async function Qs(e,n,t=200){let{client:s,runtime:o}=bt(),r=e.trim();if(!r)throw Error("bucket 名称不能为空");let c=Math.max(1,Math.min(Math.floor(t),2000)),f=Math.min(1000,c),u=[],l;while(u.length<c){let i=await Xi(s,o,r,{prefix:n,continuationToken:l,maxKeys:f}),g=i.rows;for(let y of g){let d=z(y.Key)||z(y.key);if(!d)continue;if(u.push({name:d,size:ji(y.Size)??ji(y.size),lastModified:z(y.LastModified)||z(y.lastModified),etag:z(y.ETag)||z(y.etag),type:z(y.Type)||z(y.type),storageClass:z(y.StorageClass)||z(y.storageClass)}),u.length>=c)break}if(l=i.nextContinuationToken,!i.isTruncated||!l||g.length===0)break}return u}async function Ni(e){let{client:n,runtime:t}=bt(),s=e.trim();if(!s)throw Error("bucket 名称不能为空");let o=0,r,c=Ar(8);while(!0){let f=[];try{let u=await Y(()=>Xi(n,t,s,{continuationToken:r,maxKeys:1000}),{maxAttempts:5,baseDelayMs:800,shouldRetry:Ee});f=u.rows,r=u.nextContinuationToken;let l=f.map((i)=>z(i.Key)||z(i.key)||"").filter((i)=>i.length>0);if(l.length>0)await Promise.all(l.map((i)=>c(async()=>{await Y(()=>n.deleteObjectWithOptions(s,i,new We.DeleteObjectRequest({}),{},t),{maxAttempts:5,baseDelayMs:500,shouldRetry:Ee})}))),o+=l.length;if(!u.isTruncated||!r)break}catch(u){if(ue(u))return{bucket:s,deletedObjects:0,deletedBucket:!1};throw u}}try{return await Y(()=>n.deleteBucketWithOptions(s,{},t),{maxAttempts:5,baseDelayMs:800,shouldRetry:Ee}),{bucket:s,deletedObjects:o,deletedBucket:!0}}catch(f){if(ue(f))return{bucket:s,deletedObjects:o,deletedBucket:!1};throw f}}import{existsSync as nl,readdirSync as V0,statSync as tl}from"fs";import{join as Br}from"path";var zi=["dist","build","out","public","www","site",".output/public"];function X0(e){if(!nl(e))return!1;try{return tl(e).isDirectory()}catch{return!1}}function el(e){let n=Br(e,"index.html");if(!nl(n))return!1;try{return tl(n).isFile()}catch{return!1}}function Z0(e){if(!X0(e))return!1;try{return V0(e).length>0}catch{return!1}}function sl(e=process.cwd()){if(el(e))return".";for(let n of zi){let t=Br(e,n);if(el(t))return n}for(let n of zi){let t=Br(e,n);if(Z0(t))return n}return"dist"}se();K();ce();Cn();Dn();rr();import*as Ue from"@alicloud/fc20230330";import{mkdirSync as J0,rmSync as N0,writeFileSync as z0}from"fs";import{tmpdir as ep}from"os";import{join as ol}from"path";var np="-static-proxy",tp=256,sp=30,op=0.25;function rp(e,n){return`
1403
1403
  const https = require('https');
1404
1404
  const crypto = require('crypto');
1405
1405
 
@@ -1566,15 +1566,15 @@ module.exports.handler = async (request, context) => {
1566
1566
  };
1567
1567
  }
1568
1568
  };
1569
- `.trim()}function cl(e){return`${e}${np}`}async function Yr(e,n,t){let o=k.requireAuth(),s=k.getProject(),{client:r}=ie(),c=cl(e),f=sl(ep(),`licell-static-proxy-${Date.now()}-${process.pid}`);J0(f,{recursive:!0});try{let u=rp(n,o.region);z0(sl(f,"index.js"),u);let l=or(f),i=`acs:ram::${o.accountId}:role/AliyunFCDefaultRole`;return await cp(r,c,l,s.envs||{},t,i,o)}finally{N0(f,{recursive:!0,force:!0})}}async function cp(e,n,t,o,s,r,c){let f={runtime:"nodejs20",handler:"index.handler",memorySize:tp,timeout:op,cpu:sp,diskSize:512,instanceConcurrency:10,code:new Ue.InputCodeLocation({zipFile:t})};try{let l={...o,PREVIEW_PATH:s};return await rl(e,n,{...f,role:r,environmentVariables:l})}catch(l){if(!Fe(l))throw l}let u={...o,PREVIEW_PATH:s,OSS_ACCESS_KEY_ID:c.ak,OSS_ACCESS_KEY_SECRET:c.sk};return await rl(e,n,{...f,environmentVariables:u})}async function rl(e,n,t){try{await Y(()=>e.createFunction(new Ue.CreateFunctionRequest({body:new Ue.CreateFunctionInput({functionName:n,...t})})))}catch(o){if(!J(o))throw o;await Y(()=>e.updateFunction(n,new Ue.UpdateFunctionRequest({body:new Ue.UpdateFunctionInput(t)})))}return n}async function Or(e,n){let{client:t}=ie(),o=cl(e),r=(await Y(()=>t.publishFunctionVersion(o,new Ue.PublishFunctionVersionRequest({body:new Ue.PublishVersionInput({description:n||`static preview at ${new Date().toISOString()}`})})))).body?.versionId;if(!r)throw Error("发布版本失败:未返回 versionId");return r}function gp(e){if(e.cliDomain)return e.cliDomain;if(e.domainSuffix)return`${e.appName}.${e.domainSuffix}`;return}function yp(e){try{return new URL(e).host}catch{throw Error(`无法解析 OSS 源站域名: ${e}`)}}async function fl(e,n){let t=ol(),o=e.cliDist?A(e.cliDist,"构建产物目录"):e.interactiveTTY?A(await up({message:"前端构建产物目录:",initialValue:t}),"构建产物目录"):t;if(e.preview&&e.domainSuffix)return dp(e,n,o);let s=gp(e),r=await U(n,"☁️ 正在递归上传静态资源到 OSS 边缘节点...","❌ 部署失败",async()=>{let l=await Mr(e.appName,o);if(!s)return{url:l,fixedDomain:void 0};let i=yp(l),g;if(e.enableSSL){n.message(`静态资源上传完成,正在签发 HTTPS 证书 (${s})...`);let d=await Fn(s,n,{forceRenew:e.forceSslRenew,bindToFcDomain:!1});g={certificate:d.certificate,privateKey:d.privateKey}}n.message(`静态资源上传完成,正在接入 CDN 并回源 OSS (${s})...`);let y=await Vt(s,i,{...g,sourceType:"oss"});if(n.message(y.created?`✅ CDN 加速已启用,CNAME=${y.cdnCname}`:`✅ CDN 加速已存在,已校准 DNS 到 CNAME=${y.cdnCname}`),e.enableSSL&&y.httpsConfigured)n.message("✅ CDN 边缘 HTTPS 已自动配置。");if(e.enableSSL&&!y.httpsConfigured)n.message("⚠️ 未能自动配置 CDN 边缘 HTTPS(未获取到可用证书),请在 CDN 控制台补充证书。");return{url:l,fixedDomain:s}});if(!r)return;let{url:c}=r,f=[];n.message("\uD83E\uDE7A 部署完成,正在做可访问性检测...");let u=await an(c,{paths:["/"],maxAttempts:5,intervalMs:1500,timeoutMs:5000,allowClientError:!1});if(u.ok)f.push(`✅ OSS 地址可访问 (${u.statusCode} ${u.checkedUrl})`);else f.push(`⚠️ OSS 地址可访问性检测未通过: ${u.error}`);if(r.fixedDomain){let l=`${e.enableSSL?"https":"http"}://${r.fixedDomain}`,i=await an(l,{paths:["/"],maxAttempts:e.enableCdn?10:6,intervalMs:e.enableCdn?3000:2000,timeoutMs:e.enableCdn?6000:5000,allowClientError:!1});if(i.ok)f.push(`✅ 固定域名可访问 (${i.statusCode} ${i.checkedUrl})`);else f.push(`⚠️ 固定域名检测未通过(可能 DNS/CDN 传播中): ${i.error}`)}return{...r,healthCheckLogs:f}}async function dp(e,n,t){let o=k.requireAuth(),s=Qo(e.appName),r=`${o.accountId}.${o.region}.fc.aliyuncs.com`,c=await U(n,"☁️ 正在部署静态预览...","❌ 部署失败",async()=>{n.message("正在部署静态代理函数...");let y=await Yr(e.appName,s,"_preview/pending");n.message("正在分配预览版本号...");let m=`_preview/${await Or(e.appName)}`;n.message(`正在上传静态资源到 OSS (${m})...`);let h=await Mr(e.appName,t,{targetDir:m});n.message("正在更新代理函数并发布最终版本..."),await Yr(e.appName,s,m);let b=await Or(e.appName),$=`${e.appName}-preview-v${b}.${e.domainSuffix}`;n.message(`正在确保通配符 DNS (*.${e.domainSuffix}) 存在...`);let p=await Oo(e.domainSuffix,r,{interactiveTTY:e.interactiveTTY,onConfirm:async()=>{let w=await fp({message:`检测到尚未配置通配符 DNS (*.${e.domainSuffix})。
1569
+ `.trim()}function cl(e){return`${e}${np}`}async function Yr(e,n,t){let s=k.requireAuth(),o=k.getProject(),{client:r}=ie(),c=cl(e),f=ol(ep(),`licell-static-proxy-${Date.now()}-${process.pid}`);J0(f,{recursive:!0});try{let u=rp(n,s.region);z0(ol(f,"index.js"),u);let l=sr(f),i=`acs:ram::${s.accountId}:role/AliyunFCDefaultRole`;return await cp(r,c,l,o.envs||{},t,i,s)}finally{N0(f,{recursive:!0,force:!0})}}async function cp(e,n,t,s,o,r,c){let f={runtime:"nodejs20",handler:"index.handler",memorySize:tp,timeout:sp,cpu:op,diskSize:512,instanceConcurrency:10,code:new Ue.InputCodeLocation({zipFile:t})};try{let l={...s,PREVIEW_PATH:o};return await rl(e,n,{...f,role:r,environmentVariables:l})}catch(l){if(!Fe(l))throw l}let u={...s,PREVIEW_PATH:o,OSS_ACCESS_KEY_ID:c.ak,OSS_ACCESS_KEY_SECRET:c.sk};return await rl(e,n,{...f,environmentVariables:u})}async function rl(e,n,t){try{await Y(()=>e.createFunction(new Ue.CreateFunctionRequest({body:new Ue.CreateFunctionInput({functionName:n,...t})})))}catch(s){if(!J(s))throw s;await Y(()=>e.updateFunction(n,new Ue.UpdateFunctionRequest({body:new Ue.UpdateFunctionInput(t)})))}return n}async function Or(e,n){let{client:t}=ie(),s=cl(e),r=(await Y(()=>t.publishFunctionVersion(s,new Ue.PublishFunctionVersionRequest({body:new Ue.PublishVersionInput({description:n||`static preview at ${new Date().toISOString()}`})})))).body?.versionId;if(!r)throw Error("发布版本失败:未返回 versionId");return r}function gp(e){if(e.cliDomain)return e.cliDomain;if(e.domainSuffix)return`${e.appName}.${e.domainSuffix}`;return}function yp(e){try{return new URL(e).host}catch{throw Error(`无法解析 OSS 源站域名: ${e}`)}}async function fl(e,n){let t=sl(),s=e.cliDist?L(e.cliDist,"构建产物目录"):e.interactiveTTY?L(await up({message:"前端构建产物目录:",initialValue:t}),"构建产物目录"):t;if(e.preview&&e.domainSuffix)return dp(e,n,s);let o=gp(e),r=await v(n,"☁️ 正在递归上传静态资源到 OSS 边缘节点...","❌ 部署失败",async()=>{let l=await Mr(e.appName,s);if(!o)return{url:l,fixedDomain:void 0};let i=yp(l),g;if(e.enableSSL){n.message(`静态资源上传完成,正在签发 HTTPS 证书 (${o})...`);let d=await Fn(o,n,{forceRenew:e.forceSslRenew,bindToFcDomain:!1});g={certificate:d.certificate,privateKey:d.privateKey}}n.message(`静态资源上传完成,正在接入 CDN 并回源 OSS (${o})...`);let y=await Vt(o,i,{...g,sourceType:"oss"});if(n.message(y.created?`✅ CDN 加速已启用,CNAME=${y.cdnCname}`:`✅ CDN 加速已存在,已校准 DNS 到 CNAME=${y.cdnCname}`),e.enableSSL&&y.httpsConfigured)n.message("✅ CDN 边缘 HTTPS 已自动配置。");if(e.enableSSL&&!y.httpsConfigured)n.message("⚠️ 未能自动配置 CDN 边缘 HTTPS(未获取到可用证书),请在 CDN 控制台补充证书。");return{url:l,fixedDomain:o}});if(!r)return;let{url:c}=r,f=[];n.message("\uD83E\uDE7A 部署完成,正在做可访问性检测...");let u=await an(c,{paths:["/"],maxAttempts:5,intervalMs:1500,timeoutMs:5000,allowClientError:!1});if(u.ok)f.push(`✅ OSS 地址可访问 (${u.statusCode} ${u.checkedUrl})`);else f.push(`⚠️ OSS 地址可访问性检测未通过: ${u.error}`);if(r.fixedDomain){let l=`${e.enableSSL?"https":"http"}://${r.fixedDomain}`,i=await an(l,{paths:["/"],maxAttempts:e.enableCdn?10:6,intervalMs:e.enableCdn?3000:2000,timeoutMs:e.enableCdn?6000:5000,allowClientError:!1});if(i.ok)f.push(`✅ 固定域名可访问 (${i.statusCode} ${i.checkedUrl})`);else f.push(`⚠️ 固定域名检测未通过(可能 DNS/CDN 传播中): ${i.error}`)}return{...r,healthCheckLogs:f}}async function dp(e,n,t){let s=k.requireAuth(),o=Ws(e.appName),r=`${s.accountId}.${s.region}.fc.aliyuncs.com`,c=await v(n,"☁️ 正在部署静态预览...","❌ 部署失败",async()=>{n.message("正在部署静态代理函数...");let y=await Yr(e.appName,o,"_preview/pending");n.message("正在分配预览版本号...");let m=`_preview/${await Or(e.appName)}`;n.message(`正在上传静态资源到 OSS (${m})...`);let h=await Mr(e.appName,t,{targetDir:m});n.message("正在更新代理函数并发布最终版本..."),await Yr(e.appName,o,m);let b=await Or(e.appName),$=`${e.appName}-preview-v${b}.${e.domainSuffix}`;n.message(`正在确保通配符 DNS (*.${e.domainSuffix}) 存在...`);let p=await Ys(e.domainSuffix,r,{interactiveTTY:e.interactiveTTY,onConfirm:async()=>{let w=await fp({message:`检测到尚未配置通配符 DNS (*.${e.domainSuffix})。
1570
1570
  `+`创建后,所有 preview 子域名将自动解析到 FC 网关。
1571
1571
  `+`已有的精确 DNS 记录(如 ${e.appName}.${e.domainSuffix})不受影响。
1572
- `+"是否创建?"});if(ip(w))return!1;return w}});if(p.skipped)n.message(lp.yellow("⚠️ 已跳过通配符 DNS 创建,preview 域名可能无法访问"));else if(p.created)n.message(`✅ 通配符 DNS 已创建: ${p.wildcardDomain} → ${p.targetValue}`);if(n.message(`正在绑定预览域名 ${$}...`),await hp($,r,y,b),e.enableSSL)n.message(`预览域名绑定完成,正在签发 HTTPS 证书 (${$})...`),await Fn($,n,{forceRenew:e.forceSslRenew});return{url:h,previewDomain:$,previewVersion:b}});if(!c)return;let{url:f,previewDomain:u,previewVersion:l}=c,i=[];n.message("\uD83E\uDE7A 部署完成,正在做可访问性检测...");let g=await an(f,{paths:["/"],maxAttempts:5,intervalMs:1500,timeoutMs:5000,allowClientError:!1});if(g.ok)i.push(`✅ OSS 地址可访问 (${g.statusCode} ${g.checkedUrl})`);else i.push(`⚠️ OSS 地址可访问性检测未通过: ${g.error}`);if(u){let y=`${e.enableSSL?"https":"http"}://${u}`,d=await an(y,{maxAttempts:8,intervalMs:2000,timeoutMs:5000});if(d.ok)i.push(`✅ 预览域名可访问 (${d.statusCode} ${d.checkedUrl})`);else i.push(`⚠️ 预览域名检测未通过(可能 DNS 传播中): ${d.error}`)}return{url:f,previewDomain:u,previewVersion:l,healthCheckLogs:i}}async function hp(e,n,t,o){let{createSharedFcClient:s}=await Promise.resolve().then(() => (Ie(),Xc)),r=await import("@alicloud/fc20230330"),{isConflictError:c}=await Promise.resolve().then(() => Zc),{client:f}=s(),u={routes:[{path:"/*",functionName:t,qualifier:o}]},l=new r.CreateCustomDomainInput({domainName:e,protocol:"HTTP",routeConfig:u});try{await f.createCustomDomain(new r.CreateCustomDomainRequest({body:l}))}catch(i){if(!c(i))throw i;await f.updateCustomDomain(e,new r.UpdateCustomDomainRequest({body:new r.UpdateCustomDomainInput({routeConfig:u})}))}}function ap(e){let n=[];if(e.type==="api"){if(n.push("fc"),(e.cliRuntime||e.projectRuntime||e.envRuntime||"").trim().toLowerCase()==="docker")n.push("cr");if(e.useVpc)n.push("vpc")}else n.push("oss");if(e.cliDomain||e.domainSuffix)n.push("dns");if(e.enableCdn)n.push("cdn");return[...new Set(n)]}function Kr(e){if(e&&e.trim()){let o=Yo(e);if(o.deployTypeHint==="static")throw Error("deploy spec/check 仅适用于 FC API runtime(不要传 static/statis)");if(o.runtime)return o.runtime;throw Error(`无法解析 runtime: ${e}`)}let n=at(k.getProject().runtime),t=at(se(process.env,"FC_RUNTIME"));return n||t||Wn}function mp(e,n){if(a()){let r=n||!e?jt():{runtime:Kt(Kr(e))};S({stage:"deploy.spec",...r});return}if(n||!e){let r=jt();console.log(`${Z.bold("FC API Deploy Spec")}`),console.log(`runtime: ${r.runtimes.map((c)=>c.runtime).join(", ")}`),console.log(`defaults: memory=${r.resources.defaults.memoryMb}MB, vcpu=${r.resources.defaults.vcpu}, timeout=${r.resources.defaults.timeoutSeconds}s, instanceConcurrency=${r.resources.defaults.instanceConcurrency}`),console.log(`constraint: ${r.resources.constraints.memoryToVcpuRatio.expression}`);for(let c of r.runtimes){if(console.log(`
1573
- - runtime=${Z.cyan(c.runtime)} (${c.mode})`),console.log(` entry: ${c.defaultEntry||"(按 Dockerfile/项目自动推断)"}`),console.log(` entryRule: ${c.entryRule}`),console.log(` handlerRule: ${c.handlerRule}`),c.handlerContract.signature)console.log(` signature: ${c.handlerContract.signature}`);console.log(` acceptedResponse: ${c.responseSchema.acceptedForms.join(" | ")}`),console.log(` example(pass): ${c.examples.minimalPassExample}`),console.log(` example(fail): ${c.examples.commonFailExample}`);for(let f of c.notes)console.log(` note: ${f}`)}return}let t=Kr(e),o=Kt(t),s=jt();if(console.log(`${Z.bold("FC API Deploy Spec")}`),console.log(`runtime: ${Z.cyan(o.runtime)} (${o.mode})`),console.log(`entry: ${o.defaultEntry||"(按 Dockerfile/项目自动推断)"}`),console.log(`entryRule: ${o.entryRule}`),console.log(`handlerRule: ${o.handlerRule}`),o.handlerContract.signature)console.log(`signature: ${o.handlerContract.signature}`);console.log(`acceptedResponse: ${o.responseSchema.acceptedForms.join(" | ")}`),console.log(`example(pass): ${o.examples.minimalPassExample}`),console.log(`example(fail): ${o.examples.commonFailExample}`);for(let r of o.notes)console.log(`note: ${r}`);console.log(`resources: default memory=${s.resources.defaults.memoryMb}MB, vcpu=${s.resources.defaults.vcpu}, timeout=${s.resources.defaults.timeoutSeconds}s`),console.log(`constraints: ${s.resources.constraints.memoryToVcpuRatio.expression}`)}function pp(e){let n=Kr(e.runtime),t=Kt(n),o=e.entry?.trim()||t.defaultEntry||void 0,s=Wt({runtime:n,entry:o,checkDockerDaemon:Boolean(e.dockerDaemon)});if(a())S({stage:"deploy.check",...s});else if(console.log(`${Z.bold("FC API Deploy Precheck")}`),console.log(`runtime: ${Z.cyan(s.runtime)}`),console.log(`entry: ${s.entry||"-"}`),s.issues.length===0)console.log(Z.green(`
1574
- ✅ 预检通过`));else{for(let r of s.issues){let c=r.level==="error"?Z.red("✖"):Z.yellow("⚠");if(console.log(`
1575
- ${c} [${r.level}] ${r.id}`),console.log(r.message),r.remediation&&r.remediation.length>0)for(let f of r.remediation)console.log(` - ${f}`)}if(s.ok)console.log(Z.yellow(`
1572
+ `+"是否创建?"});if(ip(w))return!1;return w}});if(p.skipped)n.message(lp.yellow("⚠️ 已跳过通配符 DNS 创建,preview 域名可能无法访问"));else if(p.created)n.message(`✅ 通配符 DNS 已创建: ${p.wildcardDomain} → ${p.targetValue}`);if(n.message(`正在绑定预览域名 ${$}...`),await hp($,r,y,b),e.enableSSL)n.message(`预览域名绑定完成,正在签发 HTTPS 证书 (${$})...`),await Fn($,n,{forceRenew:e.forceSslRenew});return{url:h,previewDomain:$,previewVersion:b}});if(!c)return;let{url:f,previewDomain:u,previewVersion:l}=c,i=[];n.message("\uD83E\uDE7A 部署完成,正在做可访问性检测...");let g=await an(f,{paths:["/"],maxAttempts:5,intervalMs:1500,timeoutMs:5000,allowClientError:!1});if(g.ok)i.push(`✅ OSS 地址可访问 (${g.statusCode} ${g.checkedUrl})`);else i.push(`⚠️ OSS 地址可访问性检测未通过: ${g.error}`);if(u){let y=`${e.enableSSL?"https":"http"}://${u}`,d=await an(y,{maxAttempts:8,intervalMs:2000,timeoutMs:5000});if(d.ok)i.push(`✅ 预览域名可访问 (${d.statusCode} ${d.checkedUrl})`);else i.push(`⚠️ 预览域名检测未通过(可能 DNS 传播中): ${d.error}`)}return{url:f,previewDomain:u,previewVersion:l,healthCheckLogs:i}}async function hp(e,n,t,s){let{createSharedFcClient:o}=await Promise.resolve().then(() => (Ie(),Xc)),r=await import("@alicloud/fc20230330"),{isConflictError:c}=await Promise.resolve().then(() => Zc),{client:f}=o(),u={routes:[{path:"/*",functionName:t,qualifier:s}]},l=new r.CreateCustomDomainInput({domainName:e,protocol:"HTTP",routeConfig:u});try{await f.createCustomDomain(new r.CreateCustomDomainRequest({body:l}))}catch(i){if(!c(i))throw i;await f.updateCustomDomain(e,new r.UpdateCustomDomainRequest({body:new r.UpdateCustomDomainInput({routeConfig:u})}))}}function ap(e){let n=[];if(e.type==="api"){if(n.push("fc"),(e.cliRuntime||e.projectRuntime||e.envRuntime||"").trim().toLowerCase()==="docker")n.push("cr");if(e.useVpc)n.push("vpc")}else n.push("oss");if(e.cliDomain||e.domainSuffix)n.push("dns");if(e.enableCdn)n.push("cdn");return[...new Set(n)]}function Kr(e){if(e&&e.trim()){let s=Bs(e);if(s.deployTypeHint==="static")throw Error("deploy spec/check 仅适用于 FC API runtime(不要传 static/statis)");if(s.runtime)return s.runtime;throw Error(`无法解析 runtime: ${e}`)}let n=at(k.getProject().runtime),t=at(oe(process.env,"FC_RUNTIME"));return n||t||Wn}function mp(e,n){if(a()){let r=n||!e?jt():{runtime:Kt(Kr(e))};S({stage:"deploy.spec",...r});return}if(n||!e){let r=jt();console.log(`${Z.bold("FC API Deploy Spec")}`),console.log(`runtime: ${r.runtimes.map((c)=>c.runtime).join(", ")}`),console.log(`defaults: memory=${r.resources.defaults.memoryMb}MB, vcpu=${r.resources.defaults.vcpu}, timeout=${r.resources.defaults.timeoutSeconds}s, instanceConcurrency=${r.resources.defaults.instanceConcurrency}`),console.log(`constraint: ${r.resources.constraints.memoryToVcpuRatio.expression}`);for(let c of r.runtimes){if(console.log(`
1573
+ - runtime=${Z.cyan(c.runtime)} (${c.mode})`),console.log(` entry: ${c.defaultEntry||"(按 Dockerfile/项目自动推断)"}`),console.log(` entryRule: ${c.entryRule}`),console.log(` handlerRule: ${c.handlerRule}`),c.handlerContract.signature)console.log(` signature: ${c.handlerContract.signature}`);console.log(` acceptedResponse: ${c.responseSchema.acceptedForms.join(" | ")}`),console.log(` example(pass): ${c.examples.minimalPassExample}`),console.log(` example(fail): ${c.examples.commonFailExample}`);for(let f of c.notes)console.log(` note: ${f}`)}return}let t=Kr(e),s=Kt(t),o=jt();if(console.log(`${Z.bold("FC API Deploy Spec")}`),console.log(`runtime: ${Z.cyan(s.runtime)} (${s.mode})`),console.log(`entry: ${s.defaultEntry||"(按 Dockerfile/项目自动推断)"}`),console.log(`entryRule: ${s.entryRule}`),console.log(`handlerRule: ${s.handlerRule}`),s.handlerContract.signature)console.log(`signature: ${s.handlerContract.signature}`);console.log(`acceptedResponse: ${s.responseSchema.acceptedForms.join(" | ")}`),console.log(`example(pass): ${s.examples.minimalPassExample}`),console.log(`example(fail): ${s.examples.commonFailExample}`);for(let r of s.notes)console.log(`note: ${r}`);console.log(`resources: default memory=${o.resources.defaults.memoryMb}MB, vcpu=${o.resources.defaults.vcpu}, timeout=${o.resources.defaults.timeoutSeconds}s`),console.log(`constraints: ${o.resources.constraints.memoryToVcpuRatio.expression}`)}function pp(e){let n=Kr(e.runtime),t=Kt(n),s=e.entry?.trim()||t.defaultEntry||void 0,o=Wt({runtime:n,entry:s,checkDockerDaemon:Boolean(e.dockerDaemon)});if(a())S({stage:"deploy.check",...o});else if(console.log(`${Z.bold("FC API Deploy Precheck")}`),console.log(`runtime: ${Z.cyan(o.runtime)}`),console.log(`entry: ${o.entry||"-"}`),o.issues.length===0)console.log(Z.green(`
1574
+ ✅ 预检通过`));else{for(let r of o.issues){let c=r.level==="error"?Z.red("✖"):Z.yellow("⚠");if(console.log(`
1575
+ ${c} [${r.level}] ${r.id}`),console.log(r.message),r.remediation&&r.remediation.length>0)for(let f of r.remediation)console.log(` - ${f}`)}if(o.ok)console.log(Z.yellow(`
1576
1576
  ⚠️ 预检通过(存在 warning)`));else console.log(Z.red(`
1577
- ❌ 预检失败(存在 error)`))}if(!s.ok)process.exitCode=1}function ul(e){e.command("deploy spec [runtime]","查看 FC API 部署规格(给 Agent/开发者在 deploy 前对照)").option("--all","输出全部 runtime 规格").action((n,t)=>{try{mp(n,t.all)}catch(o){if(a())he(o,{stage:"deploy.spec"});else console.error(R(o));process.exitCode=1}}),e.command("deploy check","本地预检 FC API 入口与 runtime 约束(建议 deploy 前执行)").option("--runtime <runtime>","FC runtime:nodejs20/nodejs22/python3.12/python3.13/docker").option("--entry <entry>","入口文件路径(默认按 runtime 推断)").option("--docker-daemon","runtime=docker 时额外检测本机 Docker daemon 可用性").action((n)=>{try{pp(n)}catch(t){if(a())he(t,{stage:"deploy.check"});else console.error(R(t));process.exitCode=1}}),e.command("deploy","一键极速打包部署").option("--type <type>","部署类型:api 或 static(适配 CI 非交互场景)").option("--entry <entry>","API 入口文件(Node 默认 src/index.ts;Python 默认 src/main.py)").option("--dist <dist>","静态站点目录(默认 dist)").option("--runtime <runtime>","运行时(API: nodejs20/nodejs22/python3.12/python3.13/docker;静态站: static/statis)").option("--target <target>","API 部署后自动发布并切流到该 alias(如 prod/preview)").option("--preview","生成预览部署(自动发版 + 绑定预览域名,不影响生产)").option("--domain <domain>","绑定完整自定义域名(如 api.your-domain.xyz)").option("--domain-suffix <suffix>","自动绑定固定子域名后缀(如 your-domain.xyz)").option("--enable-cdn","域名绑定后自动接入 CDN 并将 DNS CNAME 切到 CDN(API 显式开启;Static 提供域名时默认开启)").option("--ssl","启用 HTTPS(API: --domain/--enable-cdn 默认开启;Static: 提供域名时默认开启)").option("--ssl-force-renew","启用 HTTPS 时强制续签证书(忽略到期阈值)").option("--acr-namespace <ns>","Docker 部署时使用的 ACR 命名空间(默认 licell)").option("--enable-vpc","API 部署时启用 VPC 接入(默认启用)").option("--disable-vpc","API 部署时禁用 VPC 接入(使用公网模式)").option("--memory <mb>","函数内存大小(MB,默认 512)").option("--vcpu <n>","函数 vCPU 核数(如 0.5 / 1 / 2,默认 0.5)").option("--instance-concurrency <n>","单实例并发数(默认自动,通常起始 10)").option("--timeout <seconds>","函数超时时间(秒,默认 30)").action(async(n)=>{if(!a())j(Z.bgBlue(Z.white(" ▲ Deploying to Aliyun ")));else X({stage:"deploy",action:"deploy",status:"start"});let t=L(),o=P();try{await pt({commandLabel:"licell deploy",interactiveTTY:o});let s=!1;while(!0){let r=await ki(n),c=k.getAuth(),f=c?`${c.accountId}|${c.region}|${c.ak}`:"";await wt({commandLabel:"licell deploy",interactiveTTY:o,requiredCapabilities:ap(r)});let u=k.getAuth(),l=u?`${u.accountId}|${u.region}|${u.ak}`:"";if(f&&l!==f)continue;try{if(X({stage:"deploy.preflight",action:"resolve-context",status:"info",data:{type:r.type,runtime:r.cliRuntime||r.projectRuntime||r.envRuntime||null,releaseTarget:r.releaseTarget||null,enableCdn:r.enableCdn,enableSSL:r.enableSSL}}),r.project.hooks?.preDeploy)t.start("执行 preDeploy hook..."),Fr("preDeploy",r.project.hooks.preDeploy),t.stop(Z.green("✅ preDeploy hook 完成"));let i,g,y,d,m,h=[];if(r.type==="api"){X({stage:"deploy.api",action:"execute",status:"start"});let $=await Oi(r,t);if(!$)return;X({stage:"deploy.api",action:"execute",status:"ok"}),{url:i,promotedVersion:g,fixedDomain:y,previewDomain:d,previewVersion:m,healthCheckLogs:h}=$}else{X({stage:"deploy.static",action:"execute",status:"start"});let $=await fl(r,t);if(!$)return;X({stage:"deploy.static",action:"execute",status:"ok"}),{url:i,fixedDomain:y,previewDomain:d,previewVersion:m,healthCheckLogs:h}=$}if(t.stop(Z.green("✅ 部署成功!")),console.log(`
1577
+ ❌ 预检失败(存在 error)`))}if(!o.ok)process.exitCode=1}function ul(e){e.command("deploy spec [runtime]","查看 FC API 部署规格(给 Agent/开发者在 deploy 前对照)").option("--all","输出全部 runtime 规格").action((n,t)=>{try{mp(n,t.all)}catch(s){if(a())he(s,{stage:"deploy.spec"});else console.error(R(s));process.exitCode=1}}),e.command("deploy check","本地预检 FC API 入口与 runtime 约束(建议 deploy 前执行)").option("--runtime <runtime>","FC runtime:nodejs20/nodejs22/python3.12/python3.13/docker").option("--entry <entry>","入口文件路径(默认按 runtime 推断)").option("--docker-daemon","runtime=docker 时额外检测本机 Docker daemon 可用性").action((n)=>{try{pp(n)}catch(t){if(a())he(t,{stage:"deploy.check"});else console.error(R(t));process.exitCode=1}}),e.command("deploy","一键极速打包部署").option("--type <type>","部署类型:api 或 static(适配 CI 非交互场景)").option("--entry <entry>","API 入口文件(Node 默认 src/index.ts;Python 默认 src/main.py)").option("--dist <dist>","静态站点目录(默认 dist)").option("--runtime <runtime>","运行时(API: nodejs20/nodejs22/python3.12/python3.13/docker;静态站: static/statis)").option("--target <target>","API 部署后自动发布并切流到该 alias(如 prod/preview)").option("--preview","生成预览部署(自动发版 + 绑定预览域名,不影响生产)").option("--domain <domain>","绑定完整自定义域名(如 api.your-domain.xyz)").option("--domain-suffix <suffix>","自动绑定固定子域名后缀(如 your-domain.xyz)").option("--enable-cdn","域名绑定后自动接入 CDN 并将 DNS CNAME 切到 CDN(API 显式开启;Static 提供域名时默认开启)").option("--ssl","启用 HTTPS(API: --domain/--enable-cdn 默认开启;Static: 提供域名时默认开启)").option("--ssl-force-renew","启用 HTTPS 时强制续签证书(忽略到期阈值)").option("--acr-namespace <ns>","Docker 部署时使用的 ACR 命名空间(默认 licell)").option("--enable-vpc","API 部署时启用 VPC 接入(默认启用)").option("--disable-vpc","API 部署时禁用 VPC 接入(使用公网模式)").option("--memory <mb>","函数内存大小(MB,默认 512)").option("--vcpu <n>","函数 vCPU 核数(如 0.5 / 1 / 2,默认 0.5)").option("--instance-concurrency <n>","单实例并发数(默认自动,通常起始 10)").option("--timeout <seconds>","函数超时时间(秒,默认 30)").action(async(n)=>{if(!a())j(Z.bgBlue(Z.white(" ▲ Deploying to Aliyun ")));else X({stage:"deploy",action:"deploy",status:"start"});let t=A(),s=P();try{await pt({commandLabel:"licell deploy",interactiveTTY:s});let o=!1;while(!0){let r=await ki(n),c=k.getAuth(),f=c?`${c.accountId}|${c.region}|${c.ak}`:"";await wt({commandLabel:"licell deploy",interactiveTTY:s,requiredCapabilities:ap(r)});let u=k.getAuth(),l=u?`${u.accountId}|${u.region}|${u.ak}`:"";if(f&&l!==f)continue;try{if(X({stage:"deploy.preflight",action:"resolve-context",status:"info",data:{type:r.type,runtime:r.cliRuntime||r.projectRuntime||r.envRuntime||null,releaseTarget:r.releaseTarget||null,enableCdn:r.enableCdn,enableSSL:r.enableSSL}}),r.project.hooks?.preDeploy)t.start("执行 preDeploy hook..."),Fr("preDeploy",r.project.hooks.preDeploy),t.stop(Z.green("✅ preDeploy hook 完成"));let i,g,y,d,m,h=[];if(r.type==="api"){X({stage:"deploy.api",action:"execute",status:"start"});let $=await Oi(r,t);if(!$)return;X({stage:"deploy.api",action:"execute",status:"ok"}),{url:i,promotedVersion:g,fixedDomain:y,previewDomain:d,previewVersion:m,healthCheckLogs:h}=$}else{X({stage:"deploy.static",action:"execute",status:"start"});let $=await fl(r,t);if(!$)return;X({stage:"deploy.static",action:"execute",status:"ok"}),{url:i,fixedDomain:y,previewDomain:d,previewVersion:m,healthCheckLogs:h}=$}if(t.stop(Z.green("✅ 部署成功!")),console.log(`
1578
1578
  \uD83C\uDF89 Production URL: ${Z.cyan(Z.underline(i))}
1579
1579
  `),d){let $=`${r.enableSSL?"https":"http"}://${d}`;console.log(`\uD83D\uDD0D Preview URL: ${Z.cyan(Z.underline($))}`),console.log(`\uD83C\uDFF7️ version=${Z.cyan(m||"unknown")}
1580
1580
  `),console.log(Z.gray(`\uD83D\uDCA1 验证后运行 ${Z.bold(`licell release promote ${m}`)} 发布到生产。
@@ -1583,69 +1583,69 @@ ${c} [${r.level}] ${r.id}`),console.log(r.message),r.remediation&&r.remediation.
1583
1583
  `);if(!r.releaseTarget&&!r.preview&&r.type==="api"&&!a())console.log(Z.gray(`\uD83D\uDCA1 代码已更新到预览环境。运行 ${Z.bold("licell release promote")} 发布到生产。
1584
1584
  `));if(h.length>0)console.log(`${h.join(`
1585
1585
  `)}
1586
- `);let b=pi({deploySucceeded:!0,cliDomainSuffix:r.cliDomainSuffix,projectDomainSuffix:r.projectDomainSuffix,cliRuntime:r.cliRuntime,projectRuntime:r.projectRuntime});if(Object.keys(b).length>0)k.setProject(b);if(r.project.hooks?.postDeploy)try{Fr("postDeploy",r.project.hooks.postDeploy)}catch($){console.warn(Z.yellow(`⚠️ postDeploy hook 执行失败,已忽略: ${R($)}`))}if(a())S({stage:"deploy",type:r.type,runtime:r.cliRuntime||r.projectRuntime||r.envRuntime||null,url:i,fixedDomain:y||null,releaseTarget:r.releaseTarget||null,promotedVersion:g||null,healthCheckLogs:h,...!r.releaseTarget&&r.type==="api"?{hint:"运行 licell release promote 发布到生产"}:{}});else q("Done!");return}catch(i){if(!s&&Sr(i)!=="unknown"){if(t.stop(Z.yellow("⚠️ 检测到鉴权/权限问题,正在尝试自动修复并重试...")),await Pr(i,{commandLabel:"licell deploy",interactiveTTY:o})){s=!0;continue}}throw i}}}catch(s){if(t.stop(Z.red("❌ 部署失败")),a())he(s,{stage:"deploy"});else console.error(R(s));process.exitCode=1}})}K();Ve();oe();Le();re();import ye from"picocolors";import{readFileSync as wp,realpathSync as $p}from"fs";import{resolve as kp,relative as _p,isAbsolute as bp}from"path";function il(e){e.command("fn list","查看函数列表").option("--limit <n>","返回数量,默认 20").option("--prefix <prefix>","按函数名前缀过滤").action(async(n)=>{await M({commandLabel:"licell fn list",interactiveTTY:P(),requiredCapabilities:["fc"]},async()=>{B();let t=Ye(n.limit,20,200),o=C(n.prefix),s=L(),r=await U(s,"正在拉取函数列表...","❌ 获取函数列表失败",()=>ur(t,o));if(!r)return;if(!a())s.stop(ye.green(`✅ 共获取 ${r.length} 个函数`));if(a()){S({stage:"fn.list",count:r.length,functions:r});return}if(r.length===0){q("当前地域没有函数");return}for(let c of r)console.log(`${ye.cyan(c.functionName)} runtime=${ye.gray(c.runtime||"-")} state=${ye.gray(c.state||"-")} updated=${ye.gray(c.lastModifiedTime||"-")}`);console.log(""),q("Done.")})}),e.command("fn info [name]","查看函数详情").option("--target <target>","指定 alias/version(如 prod/preview/1)").action(async(n,t)=>{await M({commandLabel:"licell fn info",interactiveTTY:P(),requiredCapabilities:["fc"]},async()=>{B();let o=k.getProject(),s=C(n)||o.appName;if(!s)throw Error("请传入函数名,或先在当前项目执行 licell deploy 生成 appName");let r=C(t.target),c=L(),f=await U(c,`正在拉取函数 ${s} 详情...`,"❌ 获取函数详情失败",()=>ir(s,r||void 0));if(!f)return;if(!a())c.stop(ye.green("✅ 获取成功"));else{S({stage:"fn.info",functionName:f.functionName||s,qualifier:r||null,runtime:f.runtime||null,handler:f.handler||null,state:f.state||null,memorySize:f.memorySize??null,cpu:f.cpu??null,instanceConcurrency:f.instanceConcurrency??null,timeout:f.timeout??null,vpcConfig:f.vpcConfig??null,updatedAt:f.lastModifiedTime||null,envCount:Object.keys(f.environmentVariables||{}).length});return}if(console.log(`
1587
- function: ${ye.cyan(f.functionName||s)}`),r)console.log(`qualifier: ${ye.cyan(r)}`);console.log(`runtime: ${ye.cyan(f.runtime||"-")}`),console.log(`handler: ${ye.cyan(f.handler||"-")}`),console.log(`state: ${ye.cyan(f.state||"-")}`),console.log(`memory: ${ye.cyan(String(f.memorySize||"-"))}`),console.log(`vcpu: ${ye.cyan(String(f.cpu??"-"))}`),console.log(`concur: ${ye.cyan(String(f.instanceConcurrency??"-"))}`),console.log(`timeout: ${ye.cyan(String(f.timeout||"-"))}`);let u=f.vpcConfig;if(u?.vpcId)console.log(`vpc: ${ye.cyan(`${u.vpcId} / ${(u.vSwitchIds||[]).join(",")||"-"} / ${u.securityGroupId||"-"}`)}`);else console.log(`vpc: ${ye.cyan("-")}`);console.log(`updated: ${ye.cyan(f.lastModifiedTime||"-")}`),console.log(`envCount: ${ye.cyan(String(Object.keys(f.environmentVariables||{}).length))}`),console.log(""),q("Done.")})}),e.command("fn invoke [name]","调用函数(同步)").option("--target <target>","指定 alias/version(如 prod/preview/1)").option("--payload <text>","传入原始 payload 文本").option("--file <path>","从文件读取 payload").action(async(n,t)=>{await M({commandLabel:"licell fn invoke",interactiveTTY:P(),requiredCapabilities:["fc"]},async()=>{B();let o=k.getProject(),s=C(n)||o.appName;if(!s)throw Error("请传入函数名,或先在当前项目执行 licell deploy 生成 appName");let r=C(t.target),c=C(t.payload),f=C(t.file);if(c&&f)throw Error("--payload 与 --file 不能同时使用");let u;if(f){let y;try{y=$p(kp(f))}catch{throw Error(`文件不存在或无法访问: ${f}`)}let d=_p(process.cwd(),y);if(d.startsWith("..")||bp(d))throw Error("--file 路径必须在当前工作目录内");u=wp(y,"utf-8")}else u=c;let l=L(),i=await U(l,`正在调用函数 ${s}...`,"❌ 函数调用失败",()=>gr(s,{qualifier:r||void 0,payload:u||void 0}));if(!i)return;if(!a())l.stop(ye.green(`✅ 调用完成 (status=${i.statusCode})`));let g=i.body&&i.body.trim().length>0?i.body:"";if(a()){S({stage:"fn.invoke",functionName:s,qualifier:r||null,statusCode:i.statusCode,body:g});return}if(console.log(""),g)console.log(g);else console.log("<empty response>");console.log(""),q("Done.")})}),e.command("fn rm [name]","删除函数").option("--force","级联删除触发器、alias、已发布版本后再删除函数").option("--yes","跳过二次确认(危险)").action(async(n,t)=>{await M({commandLabel:"licell fn rm",interactiveTTY:P(),requiredCapabilities:["fc"]},async()=>{B();let o=k.getProject(),s=C(n)||o.appName;if(!s)throw Error("请传入函数名,或先在当前项目执行 licell deploy 生成 appName");await Be(t.force?`删除函数 ${s}(含触发器/alias/版本)`:`删除函数 ${s}`,{yes:Boolean(t.yes)});let r=L(),c=await U(r,t.force?`正在级联清理并删除函数 ${s}...`:`正在删除函数 ${s}...`,"❌ 删除函数失败",()=>lr(s,{force:Boolean(t.force)}));if(!c)return;if(!a())r.stop(ye.green("✅ 函数已删除"));if(a()){S({stage:"fn.rm",functionName:s,force:Boolean(t.force),forced:c.forced,deletedTriggers:c.deletedTriggers,deletedAliases:c.deletedAliases,deletedVersions:c.deletedVersions});return}if(c.forced)console.log(`
1588
- cleanup: triggers=${c.deletedTriggers.length} aliases=${c.deletedAliases.length} versions=${c.deletedVersions.length}`);q("Done.")})})}import{text as ll}from"@clack/prompts";import me from"picocolors";Le();oe();re();function gl(e){e.command("oss list","查看 OSS Bucket 列表").option("--limit <n>","返回数量,默认 50").action(async(t)=>{await M({commandLabel:"licell oss list",interactiveTTY:P(),requiredCapabilities:["oss"]},async()=>{B();let o=Ye(t.limit,50,500),s=L(),r=await U(s,"正在拉取 OSS Bucket 列表...","❌ 获取 Bucket 列表失败",()=>Zi(o));if(!r)return;if(!a())s.stop(me.green(`✅ 共获取 ${r.length} 个 Bucket`));if(a()){S({stage:"oss.list",count:r.length,buckets:r});return}if(r.length===0){q("当前账号没有 Bucket");return}for(let c of r)console.log(`${me.cyan(c.name)} region=${me.gray(c.location||"-")} created=${me.gray(c.creationDate||"-")}`);console.log(""),q("Done.")})}),e.command("oss info <bucket>","查看 OSS Bucket 详情").action(async(t)=>{await M({commandLabel:"licell oss info",interactiveTTY:P(),requiredCapabilities:["oss"]},async()=>{B();let o=A(t,"bucket"),s=L(),r=await U(s,`正在拉取 Bucket ${o} 详情...`,"❌ 获取 Bucket 详情失败",()=>Ji(o));if(!r)return;if(!a())s.stop(me.green("✅ 获取成功"));else{S({stage:"oss.info",bucket:o,info:r});return}console.log(`
1589
- name: ${me.cyan(r.name)}`),console.log(`location: ${me.cyan(r.location||"-")}`),console.log(`created: ${me.cyan(r.creationDate||"-")}`),console.log(`endpoint: ${me.cyan(r.extranetEndpoint||"-")}`),console.log(`intranet: ${me.cyan(r.intranetEndpoint||"-")}`),console.log(""),q("Done.")})}),e.command("oss ls <bucket> [prefix]","列出 Bucket 对象").option("--limit <n>","返回数量,默认 100").action(async(t,o,s)=>{await M({commandLabel:"licell oss ls",interactiveTTY:P(),requiredCapabilities:["oss"]},async()=>{B();let r=A(t,"bucket"),c=C(o),f=Ye(s.limit,100,2000),u=L(),l=await U(u,`正在列出 ${r} 对象...`,"❌ 获取对象列表失败",()=>Do(r,c||void 0,f));if(!l)return;if(!a())u.stop(me.green(`✅ 共获取 ${l.length} 个对象`));if(a()){S({stage:"oss.ls",bucket:r,prefix:c||null,count:l.length,objects:l});return}if(l.length===0){q("当前条件下无对象");return}for(let i of l)console.log(`${me.cyan(i.name)} size=${me.gray(String(i.size??"-"))} modified=${me.gray(i.lastModified||"-")}`);console.log(""),q("Done.")})});let n=(t,o)=>{e.command(t,o).option("--bucket <bucket>","Bucket 名称(可替代位置参数)").option("--source-dir <dir>","本地目录(默认 dist)").option("--target-dir <dir>","Bucket 内目标目录前缀(如 mysite 或 mysite/v2)").action(async(s,r)=>{await M({commandLabel:"licell oss upload",interactiveTTY:P(),requiredCapabilities:["oss"]},async()=>{B();let c=P(),f=C(r.bucket)||C(s)||(c?A(await ll({message:"Bucket 名称:"}),"bucket"):void 0);if(!f)throw Error("请通过 <bucket> 或 --bucket 指定 Bucket 名称");let u=C(r.sourceDir)||(c?A(await ll({message:"本地目录:",initialValue:"dist"}),"source-dir"):"dist"),l=C(r.targetDir),i=L(),g=await U(i,`正在上传 ${u} 到 OSS Bucket ${f}${l?`/${l}`:""}...`,"❌ OSS 目录上传失败",()=>Gr(f,u,{targetDir:l}));if(!g)return;if(!a())i.stop(me.green(`✅ 上传完成,共 ${g.uploadedCount} 个文件`));else{S({stage:"oss.upload",bucket:g.bucket,sourceDir:u,targetDir:g.targetDir||null,uploadedCount:g.uploadedCount,skippedSymlinkCount:g.skippedSymlinkCount,baseUrl:g.baseUrl});return}let y=g.targetDir?`${g.targetDir}/`:"";if(console.log(`
1590
- bucket: ${me.cyan(g.bucket)}`),console.log(`prefix: ${me.cyan(g.targetDir||"(root)")}`),console.log(`base: ${me.cyan(g.baseUrl)}`),console.log(`hint: ${me.gray(`${g.baseUrl}/${y}<file>`)}`),g.skippedSymlinkCount>0)console.log(me.yellow(`warning: 已跳过 ${g.skippedSymlinkCount} 个符号链接(为避免目录逃逸与递归风险)`));console.log(""),q("Done.")})})};n("oss upload [bucket]","上传本地目录到 OSS Bucket 指定目录"),n("oss bucket [bucket]","上传本地目录到 OSS Bucket 指定目录(兼容命令,等同 oss upload)")}import{select as Yp,isCancel as Op}from"@clack/prompts";import G from"picocolors";Le();K();ft();ce();De();Ot();import*as de from"@alicloud/rds20140815";K();Ie();import Tp from"@alicloud/rds20140815";import*as yl from"@alicloud/openapi-client";var Cp=60000,Ep=120000,Sp=te(Tp,"@alicloud/rds20140815");function mn(){let e=k.requireAuth(),n=new Sp(new yl.Config({accessKeyId:e.ak,accessKeySecret:e.sk,endpoint:`rds.${e.region}.aliyuncs.com`,connectTimeout:Cp,readTimeout:Ep}));return{auth:e,client:n}}var qp=1200000,ml=5000,Pp=300000,jr=5,Rp={postgres:"pg.n2.serverless.1c",mysql:"mysql.n2.serverless.1c"},Ip={postgres:20,mysql:20},Wr={postgres:{minCapacity:0.5,maxCapacity:8,autoPause:!0},mysql:{minCapacity:0.5,maxCapacity:2,autoPause:!0}},dl={postgres:"AliyunServiceRoleForRdsPgsqlOnEcs",mysql:"AliyunServiceRoleForRds"};function Fp(e,n){if(!n)return!1;if(n===e)return!0;let t=e.split(".")[0],o=n.split(".")[0];return t.length>0&&t===o}async function xp(e,n,t,o){try{let r=((await e.describeAvailableZones(new de.DescribeAvailableZonesRequest({regionId:n,engine:t,engineVersion:o,category:"serverless_basic"}))).body?.availableZones||[]).map((c)=>c.zoneId).filter((c)=>typeof c==="string"&&c.length>0);if(r.length>0)return[...new Set(r)]}catch{}try{let r=(await e.describeAvailableZones(new de.DescribeAvailableZonesRequest({regionId:n,engine:t,engineVersion:o}))).body?.availableZones||[],c=[];for(let u of r){let l=u.zoneId;if(typeof l!=="string"||l.length===0)continue;if((u.supportedEngines||[]).some((g)=>{if((g.engine||"").toLowerCase()!==t.toLowerCase())return!1;return(g.supportedEngineVersions||[]).some((y)=>{if(!Fp(o,y.version))return!1;return(y.supportedCategorys||[]).some((d)=>d.category==="serverless_basic")})}))c.push(l)}if(c.length>0)return[...new Set(c)];let f=r.map((u)=>u.zoneId).filter((u)=>typeof u==="string"&&u.length>0);return[...new Set(f)]}catch{return[]}}async function hl(e,n,t){let o=t==="postgres"?["AliyunServiceRoleForRds",dl.postgres]:[dl.mysql];for(let s of o){let r=!1;try{let f=((await e.checkServiceLinkedRole(new de.CheckServiceLinkedRoleRequest({regionId:n,serviceLinkedRole:s}))).body?.hasServiceLinkedRole||"").toString().toLowerCase();r=f==="true"||f==="1"}catch{}if(r)continue;try{await e.createServiceLinkedRole(new de.CreateServiceLinkedRoleRequest({regionId:n,serviceLinkedRole:s}))}catch(c){if(ws(c))continue;throw c}}}async function al(e,n,t){let o;for(let s=1;s<=jr;s+=1)try{return await e.createDBInstance(n)}catch(r){if(o=r,!Ee(r)||s===jr)throw r;t.message(`\uD83C\uDF10 RDS API 网络抖动,${s}/${jr} 次失败,正在重试...`),await ge(1500*s)}throw o instanceof Error?o:Error("RDS 创建失败")}async function Hp(e,n,t,o){let s=t==="postgres"?"5432":"3306",r=Date.now();while(!0){if(Date.now()-r>Pp)throw Error("数据库网络信息未就绪,等待连接地址超时");let f=(await e.describeDBInstanceNetInfo(new de.DescribeDBInstanceNetInfoRequest({DBInstanceId:n}))).body?.DBInstanceNetInfos?.DBInstanceNetInfo||[],u=f.find((y)=>y.IPType==="Private")||f[0],l=u?.connectionString?.trim(),i=u?.port||s;if(l)return{host:l,port:i};let g=f.map((y)=>`${y.IPType||"Unknown"}:${y.connectionString||"-"}`).join(",")||"pending";o.message(`☕ 数据库连接地址初始化中,请稍候... [${g}]`),await ge(ml)}}function pl(e){let n=e.toLowerCase().replace(/[^a-z0-9_]/g,"_");if(!/^[a-z]/.test(n))n=`a_${n}`;if(n=n.replace(/_+/g,"_").replace(/_$/,""),n.length<2||!/[a-z0-9]/.test(n.slice(1)))n="licell_user";return n.slice(0,16)}async function Qr(e,n,t={}){let{auth:o,client:s}=mn(),r=k.getProject(),c="main",f=pl(r.appName||"licell_app"),u=xe(),l=e==="postgres"?"PostgreSQL":"MySQL",i=t.engineVersion?.trim()||(e==="postgres"?"18.0":"8.0"),g=t.category?.trim()||"serverless_basic",y=t.storageType?.trim()||"cloud_essd";n.message("\uD83D\uDD0E 正在查询 RDS Serverless 可用区...");let d=await xp(s,o.region,l,i);n.message("\uD83D\uDD0D 正在探测/拉起专属私有网络平面 (VPC & VSwitch)...");let m=t.zoneId?.trim(),h=t.vpcId?.trim(),b=t.vSwitchId?.trim(),$;if(h||b){if(!h||!b)throw Error("自定义网络时需同时提供 --vpc 与 --vsw");if(!m)throw Error("自定义网络时需提供 --zone");$=await lt({vpcId:h,vswId:b,zoneId:m})}else $=await it({preferredZoneIds:m?[m]:d});let p=t.instanceClass?.trim()||Rp[e],w=t.storageGb||Ip[e];try{let fn=(await s.describeAvailableClasses(new de.DescribeAvailableClassesRequest({regionId:o.region,zoneId:$.zoneId,engine:l,engineVersion:i,instanceChargeType:"Serverless",category:g,DBInstanceStorageType:y}))).body?.DBInstanceClasses?.find((It)=>{if(typeof It.DBInstanceClass!=="string"||It.DBInstanceClass.length===0)return!1;if(!t.instanceClass)return!0;return It.DBInstanceClass===t.instanceClass});if(!t.instanceClass&&typeof fn?.DBInstanceClass==="string"&&fn.DBInstanceClass.length>0)p=fn.DBInstanceClass;let ot=fn?.DBInstanceStorageRange?.minValue;if(!t.storageGb&&typeof ot==="number"&&Number.isFinite(ot)&&ot>0)w=Math.floor(ot)}catch{n.message("⚠️ 未能查询到可售规格,回退到内置默认规格继续创建...")}n.message("\uD83D\uDD10 正在确保 RDS 服务关联角色已就绪..."),await hl(s,o.region,e),n.message(`\uD83D\uDCE6 正在拉起 Serverless ${e.toUpperCase()} (按量计费)...`);let H={engine:l,engineVersion:i,payType:"Serverless",category:g,regionId:o.region,zoneId:$.zoneId,DBInstanceClass:p,DBInstanceStorage:w,DBInstanceStorageType:y,securityIPList:t.securityIpList?.trim()||$.cidrBlock||"10.0.0.0/8",instanceNetworkType:"VPC",DBInstanceNetType:"Intranet",DBInstanceDescription:t.description?.trim()||`${r.appName||"licell-app"}-${e}`,serverlessConfig:{minCapacity:t.minCapacity??Wr[e].minCapacity,maxCapacity:t.maxCapacity??Wr[e].maxCapacity,autoPause:t.autoPause??Wr[e].autoPause},VPCId:$.vpcId,vSwitchId:$.vswId},T=t.zoneIdSlave1?.trim(),x=t.zoneIdSlave2?.trim();if(T)H.zoneIdSlave1=T;if(x)H.zoneIdSlave2=x;let E=new de.CreateDBInstanceRequest(H),_;try{_=await al(s,E,n)}catch(Je){if(!wo(Je))throw Je;n.message("\uD83D\uDD10 首次使用检测到缺少服务关联角色,正在自动创建并重试..."),await hl(s,o.region,e);try{_=await al(s,E,n)}catch(fn){if(wo(fn))throw Error("RDS PostgreSQL 仍提示缺少服务关联角色。请先在阿里云控制台开通 RDS PostgreSQL 服务后重试,或先创建 MySQL Serverless 实例。");throw fn}}let F=_.body?.DBInstanceId;if(!F)throw Error("RDS 创建失败:未返回 DBInstanceId");let I="Creating",fe=Date.now();while(I!=="Running"){if(Date.now()-fe>qp)throw Error(`数据库创建超时,最后状态: ${I}`);if(await ge(ml),I=(await s.describeDBInstances(new de.DescribeDBInstancesRequest({DBInstanceId:F}))).body?.items?.DBInstance?.[0]?.DBInstanceStatus||"Creating",I==="Deleted"||I==="Failed")throw Error(`数据库创建失败,实例状态: ${I}`);n.message(`☕ 数据库底层初始化中,请稍候... [${I}]`)}if(n.message("\uD83E\uDDF1 正在创建数据库与应用账号..."),await _o(()=>s.createAccount(new de.CreateAccountRequest({DBInstanceId:F,accountName:f,accountPassword:u,accountType:"Normal",accountDescription:"licell managed account"}))),await _o(()=>s.createDatabase(new de.CreateDatabaseRequest({DBInstanceId:F,DBName:"main",characterSetName:e==="postgres"?"UTF8":"utf8mb4",ownerAccount:e==="postgres"?f:void 0}))),e!=="postgres")await _o(()=>s.grantAccountPrivilege(new de.GrantAccountPrivilegeRequest({DBInstanceId:F,DBName:"main",accountName:f,accountPrivilege:"ReadWrite"})));let{host:Ce,port:rn}=await Hp(s,F,e,n),cn=`${e==="postgres"?"postgresql":"mysql"}://${encodeURIComponent(f)}:${encodeURIComponent(u)}@${Ce}:${rn}/main`;return r.envs={...r.envs,DATABASE_URL:cn},r.network=$,r.database={type:e,instanceId:F,user:f,name:"main"},k.setProject(r),n.message("⚠️ DATABASE_URL(含密码)已写入 .licell/project.json,请确认该目录已在 .gitignore 中"),cn}K();import*as pn from"@alicloud/rds20140815";function wl(e){if(!e)return null;try{let n=new URL(e),t=n.protocol==="postgresql:"?"postgresql":n.protocol==="mysql:"?"mysql":null;if(!t||!n.hostname)return null;let o=n.port||(t==="postgresql"?"5432":"3306"),s=Number(o);if(!Number.isFinite(s)||s<=0)return null;let r=n.pathname.replace(/^\//,"")||"main";return{protocol:t,host:n.hostname,port:s,database:r,username:decodeURIComponent(n.username||""),password:decodeURIComponent(n.password||"")}}catch{return null}}function $l(e){let n=e.database;if(typeof n!=="object"||n===null)return{};let t=n;return{instanceId:typeof t.instanceId==="string"?t.instanceId:void 0,user:typeof t.user==="string"?t.user:void 0,name:typeof t.name==="string"?t.name:void 0}}function kl(e){return(e||"").toLowerCase().includes("postgres")?"postgresql":"mysql"}function _l(e){return{instanceId:e.DBInstanceId||"",description:e.DBInstanceDescription,engine:e.engine,engineVersion:e.engineVersion,status:e.DBInstanceStatus,payType:e.payType,category:e.category,instanceClass:e.DBInstanceClass,zoneId:e.zoneId,vpcId:e.vpcId,vSwitchId:e.vSwitchId}}async function Ap(e){let{auth:n,client:t}=mn(),s=(await t.describeDBInstances(new pn.DescribeDBInstancesRequest({regionId:n.region,DBInstanceId:e,pageNumber:1,pageSize:30}))).body?.items?.DBInstance||[],r=s.find((c)=>c.DBInstanceId===e)||s[0];if(!r?.DBInstanceId)throw Error(`未找到数据库实例: ${e}`);return _l(r)}async function Dr(e=200){let{auth:n,client:t}=mn(),o=[],s=Math.max(1,Math.min(Math.floor(e),500)),r=Math.min(100,s);for(let c=1;c<=20&&o.length<s;c+=1){let f=await t.describeDBInstances(new pn.DescribeDBInstancesRequest({regionId:n.region,pageNumber:c,pageSize:r})),u=f.body?.items?.DBInstance||[];for(let g of u){let y=_l(g);if(!y.instanceId)continue;if(o.push(y),o.length>=s)break}let l=f.body||{},i=Number(l.totalRecordCount||l.totalCount||0);if(u.length===0||Number.isFinite(i)&&i>0&&o.length>=i)break}return o}async function Vo(e){let n=e.trim();if(!n)throw Error("instanceId 不能为空");let{client:t}=mn(),o=await Ap(n),r=((await t.describeDBInstanceNetInfo(new pn.DescribeDBInstanceNetInfoRequest({DBInstanceId:n}))).body?.DBInstanceNetInfos?.DBInstanceNetInfo||[]).map((i)=>({type:i.connectionStringType,ipType:i.IPType,host:i.connectionString,port:i.port,vpcId:i.VPCId,vSwitchId:i.vSwitchId})),f=((await t.describeDatabases(new pn.DescribeDatabasesRequest({DBInstanceId:n,pageNumber:1,pageSize:100}))).body?.databases?.database||[]).map((i)=>i.DBName).filter((i)=>typeof i==="string"&&i.length>0),l=((await t.describeAccounts(new pn.DescribeAccountsRequest({DBInstanceId:n}))).body?.accounts?.DBInstanceAccount||[]).map((i)=>i.accountName).filter((i)=>typeof i==="string"&&i.length>0);return{summary:o,endpoints:r,databases:f,accounts:l}}async function Xo(e){let n=k.getProject(),t=$l(n),o=e?.trim()||t.instanceId||"";if(!o)throw Error("未指定 DB 实例 ID,且当前项目未绑定数据库实例");let s=await Vo(o),r=kl(s.summary.engine),c=s.endpoints.find((x)=>x.ipType==="Private"&&x.host)||s.endpoints.find((x)=>x.host),f=c?.host?.trim();if(!f)throw Error(`未获取到实例 ${o} 的连接地址`);let u=Number(c?.port||(r==="postgresql"?"5432":"3306"));if(!Number.isFinite(u)||u<=0)throw Error(`实例 ${o} 返回了无效端口`);let l=s.endpoints.find((x)=>x.ipType==="Public"&&x.host),g=t.instanceId===o?wl(n.envs?.DATABASE_URL):null,y=g?.username||t.user||"<username>",d=g?.database||t.name||s.databases[0]||"main",m=g?.password||"",h=m.length>0,b=h?encodeURIComponent(m):"<password>",$=y==="<username>"?y:encodeURIComponent(y),p=`${r}://${$}:${b}@${f}:${u}/${d}`,w,H,T;if(l?.host)w=l.host.trim(),H=Number(l.port||u),T=`${r}://${$}:${b}@${w}:${H}/${d}`;return{instanceId:o,engine:r,host:f,port:u,database:d,username:y,passwordKnown:h,connectionString:p,publicHost:w,publicPort:H,publicConnectionString:T}}import*as Jn from"@alicloud/rds20140815";ce();var Lp="licell_public";async function Vr(e,n){let{client:t}=mn(),r=((await t.describeDBInstanceNetInfo(new Jn.DescribeDBInstanceNetInfoRequest({DBInstanceId:e}))).body?.DBInstanceNetInfos?.DBInstanceNetInfo||[]).find((i)=>i.IPType==="Public");if(r?.connectionString)return{host:r.connectionString,port:r.port||"3306"};let c=`${e}-pub`.toLowerCase().replace(/[^a-z0-9]/g,"").slice(0,40);try{await t.allocateInstancePublicConnection(new Jn.AllocateInstancePublicConnectionRequest({DBInstanceId:e,connectionStringPrefix:c,port:"3306"}))}catch(i){let g=R(i);if(g.includes("NetTypeExists")||g.includes("already exists"))n.message("公网地址已存在,正在获取...");else throw i}let l=((await t.describeDBInstanceNetInfo(new Jn.DescribeDBInstanceNetInfoRequest({DBInstanceId:e}))).body?.DBInstanceNetInfos?.DBInstanceNetInfo||[]).find((i)=>i.IPType==="Public");if(!l?.connectionString)return null;return{host:l.connectionString,port:l.port||"3306"}}async function Xr(e,n,t){let{client:o}=mn(),s=`${n}/32`;try{await o.modifySecurityIps(new Jn.ModifySecurityIpsRequest({DBInstanceId:e,DBInstanceIPArrayName:Lp,securityIps:s,modifyMode:"Cover"}))}catch(r){t.message(`⚠️ 公网白名单设置失败: ${R(r)}`)}}oe();re();function Tl(e){e.command("db add","分配 Serverless 数据库").option("--type <type>","数据库类型:postgres 或 mysql(CI 场景建议显式传入)").option("--engine-version <version>","数据库引擎版本(postgres 默认 18.0,mysql 默认 8.0)").option("--category <category>","RDS Category(默认 serverless_basic)").option("--class <instanceClass>","实例规格(如 pg.n2.serverless.1c)").option("--storage <gb>","存储空间 GB(默认 20)").option("--storage-type <storageType>","存储类型(默认 cloud_essd)").option("--min-rcu <n>","Serverless 最小 RCU(如 0.5)").option("--max-rcu <n>","Serverless 最大 RCU(如 8)").option("--auto-pause <mode>","自动启停:on/off").option("--zone <zoneId>","主可用区(如 cn-hangzhou-b)").option("--zone-slave1 <zoneId>","备可用区 1(多可用区部署)").option("--zone-slave2 <zoneId>","备可用区 2(多可用区部署)").option("--vpc <vpcId>","指定 VPC ID").option("--vsw <vSwitchId>","指定 VSwitch ID").option("--security-ip-list <cidrs>","白名单 CIDR(逗号分隔)").option("--description <text>","实例描述").action(async(n)=>{await M({commandLabel:"licell db add",interactiveTTY:P(),requiredCapabilities:["rds"]},async()=>{j(G.bgMagenta(G.white(" \uD83D\uDDC4️ Database Provisioning (IaC) "))),B();let t=P(),o,s=C(n.type);if(s)o=Ju(s);else if(t){let g=await Yp({message:"选择数据库引擎:",options:[{value:"postgres",label:"\uD83D\uDC18 RDS Serverless PostgreSQL"},{value:"mysql",label:"\uD83D\uDC2C RDS Serverless MySQL"}]});if(Op(g))process.exit(0);if(g!=="postgres"&&g!=="mysql")throw Error("未知数据库类型");o=g}else throw Error("非交互模式下请传入 --type postgres|mysql");let r=qe(n.storage,"storage"),c=Cr(n.minRcu,"min-rcu"),f=Cr(n.maxRcu,"max-rcu");if(typeof c==="number"&&c<=0)throw Error("min-rcu 必须大于 0");if(typeof f==="number"&&f<=0)throw Error("max-rcu 必须大于 0");if(typeof c==="number"&&typeof f==="number"&&c>f)throw Error("min-rcu 不能大于 max-rcu");let u=C(n.autoPause)?zu(n.autoPause):void 0,l=L(),i=await U(l,"正在初始化基础设施编排引擎...","❌ 拉起失败",()=>Qr(o,l,{engineVersion:C(n.engineVersion),category:C(n.category),instanceClass:C(n.class),storageGb:r,storageType:C(n.storageType),minCapacity:c,maxCapacity:f,autoPause:u,zoneId:C(n.zone),zoneIdSlave1:C(n.zoneSlave1),zoneIdSlave2:C(n.zoneSlave2),vpcId:C(n.vpc),vSwitchId:C(n.vsw),securityIpList:C(n.securityIpList),description:C(n.description)}));if(!i)return;if(!a())l.stop(G.green("✅ 数据库实例已就绪并绑定到本工程内网!"));if(a()){S({stage:"db.add",type:o,connectionStringMasked:Pn(i)});return}console.log(`
1591
- \uD83D\uDD11 内网直连凭证已生成: ${G.cyan(Pn(i))}
1592
- `),q("下次执行 licell deploy 时,将自动作为 process.env.DATABASE_URL 注入!")})}),e.command("db list","查看数据库实例列表").option("--limit <n>","返回数量,默认 20").action(async(n)=>{await M({commandLabel:"licell db list",interactiveTTY:P(),requiredCapabilities:["rds"]},async()=>{B();let t=Ye(n.limit,20,200),o=L(),s=await U(o,"正在拉取数据库实例列表...","❌ 获取数据库实例列表失败",()=>Dr(t));if(!s)return;if(!a())o.stop(G.green(`✅ 共获取 ${s.length} 个实例`));if(a()){S({stage:"db.list",count:s.length,instances:s});return}if(s.length===0){q("当前地域没有数据库实例");return}for(let r of s)console.log(`${G.cyan(r.instanceId)} engine=${G.gray(`${r.engine||"-"} ${r.engineVersion||""}`.trim())} status=${G.gray(r.status||"-")} class=${G.gray(r.instanceClass||"-")}`);console.log(""),q("Done.")})}),e.command("db info <instanceId>","查看数据库实例详情").action(async(n)=>{await M({commandLabel:"licell db info",interactiveTTY:P(),requiredCapabilities:["rds"]},async()=>{B();let t=A(n,"instanceId"),o=L(),s=await U(o,`正在拉取实例 ${t} 详情...`,"❌ 获取数据库实例详情失败",()=>Vo(t));if(!s)return;let r=s.summary;if(!a())o.stop(G.green("✅ 获取成功"));else{S({stage:"db.info",instanceId:t,detail:s});return}if(console.log(`
1593
- instanceId: ${G.cyan(r.instanceId)}`),console.log(`engine: ${G.cyan(`${r.engine||"-"} ${r.engineVersion||""}`.trim())}`),console.log(`status: ${G.cyan(r.status||"-")}`),console.log(`class: ${G.cyan(r.instanceClass||"-")}`),console.log(`payType: ${G.cyan(r.payType||"-")}`),console.log(`vpc/vsw: ${G.cyan(`${r.vpcId||"-"} / ${r.vSwitchId||"-"}`)}`),console.log(`zone: ${G.cyan(r.zoneId||"-")}`),s.endpoints.length>0)console.log(`endpoints: ${G.cyan(s.endpoints.map((c)=>`${c.ipType||c.type||"-"}:${c.host||"-"}:${c.port||"-"}`).join(", "))}`);if(s.databases.length>0)console.log(`databases: ${G.cyan(s.databases.join(", "))}`);if(s.accounts.length>0)console.log(`accounts: ${G.cyan(s.accounts.join(", "))}`);console.log(""),q("Done.")})}),e.command("db connect [instanceId]","输出数据库连接信息").action(async(n)=>{await M({commandLabel:"licell db connect",interactiveTTY:P(),requiredCapabilities:["rds"]},async()=>{B();let t=C(n),o=L(),s=await U(o,"正在解析数据库连接信息...","❌ 连接信息解析失败",()=>Xo(t));if(!s)return;if(!a())o.stop(G.green("✅ 连接信息已生成"));else{S({stage:"db.connect",instanceId:s.instanceId,connection:s});return}if(console.log(`
1594
- instanceId: ${G.cyan(s.instanceId)}`),console.log(`engine: ${G.cyan(s.engine)}`),console.log(`host: ${G.cyan(s.host)}`),console.log(`port: ${G.cyan(String(s.port))}`),console.log(`database: ${G.cyan(s.database)}`),console.log(`username: ${G.cyan(s.username)}`),console.log(`password: ${G.cyan(s.passwordKnown?"<known in project>":"<unknown, please provide manually>")}`),console.log(`url: ${G.cyan(s.connectionString)}`),s.publicHost)console.log(""),console.log(G.yellow("── 公网访问 ──")),console.log(`public host: ${G.cyan(s.publicHost)}`),console.log(`public port: ${G.cyan(String(s.publicPort))}`),console.log(`public url: ${G.cyan(s.publicConnectionString)}`);console.log(""),q("Done.")})}),e.command("db public-access [instanceId]","开通数据库公网访问并添加当前 IP 到白名单").option("--ip <ip>","手动指定公网 IP(不传则自动获取)").action(async(n,t)=>{await M({commandLabel:"licell db public-access",interactiveTTY:P(),requiredCapabilities:["rds"]},async()=>{let{resolvePublicIp:o}=await Promise.resolve().then(() => (Jr(),Zr));j(G.bgMagenta(G.white(" \uD83C\uDF10 DB Public Access "))),B();let s=C(n),r=L();r.start("正在获取公网 IP...");let c=t.ip?.trim()||await o();r.stop(`公网 IP: ${G.cyan(c)}`);let f=await U(r,"正在解析数据库连接信息...","❌ 连接信息解析失败",()=>Xo(s));if(!f)return;await U(r,`正在将 ${c}/32 添加到白名单 (licell_public)...`,"❌ 白名单设置失败",()=>Xr(f.instanceId,c,r));let u=await U(r,"正在开通公网访问...","❌ 公网访问开通失败",()=>Vr(f.instanceId,r));if(!a())r.stop(G.green("✅ 公网访问已开通"));else{S({stage:"db.public-access",instanceId:f.instanceId,publicIp:c,publicHost:u?.host||null,publicPort:u?.port||null});return}if(console.log(""),console.log(G.yellow("── 内网访问 ──")),console.log(`host: ${G.cyan(f.host)}`),console.log(`port: ${G.cyan(String(f.port))}`),console.log(`url: ${G.cyan(f.connectionString)}`),u){console.log(""),console.log(G.yellow("── 公网访问 ──")),console.log(`host: ${G.cyan(u.host)}`),console.log(`port: ${G.cyan(u.port)}`);let l=f.engine,i=f.username==="<username>"?f.username:encodeURIComponent(f.username),g=f.passwordKnown?"<password>":"<password>";console.log(`url: ${G.cyan(`${l}://${i}:${g}@${u.host}:${u.port}/${f.database}`)}`)}else console.log(G.yellow(`
1586
+ `);let b=pi({deploySucceeded:!0,cliDomainSuffix:r.cliDomainSuffix,projectDomainSuffix:r.projectDomainSuffix,cliRuntime:r.cliRuntime,projectRuntime:r.projectRuntime});if(Object.keys(b).length>0)k.setProject(b);if(r.project.hooks?.postDeploy)try{Fr("postDeploy",r.project.hooks.postDeploy)}catch($){console.warn(Z.yellow(`⚠️ postDeploy hook 执行失败,已忽略: ${R($)}`))}if(a())S({stage:"deploy",type:r.type,runtime:r.cliRuntime||r.projectRuntime||r.envRuntime||null,url:i,fixedDomain:y||null,releaseTarget:r.releaseTarget||null,promotedVersion:g||null,healthCheckLogs:h,...!r.releaseTarget&&r.type==="api"?{hint:"运行 licell release promote 发布到生产"}:{}});else q("Done!");return}catch(i){if(!o&&Sr(i)!=="unknown"){if(t.stop(Z.yellow("⚠️ 检测到鉴权/权限问题,正在尝试自动修复并重试...")),await Pr(i,{commandLabel:"licell deploy",interactiveTTY:s})){o=!0;continue}}throw i}}}catch(o){if(t.stop(Z.red("❌ 部署失败")),a())he(o,{stage:"deploy"});else console.error(R(o));process.exitCode=1}})}K();Ve();se();Ae();re();import ye from"picocolors";import{readFileSync as wp,realpathSync as $p}from"fs";import{resolve as kp,relative as _p,isAbsolute as bp}from"path";function il(e){e.command("fn list","查看函数列表").option("--limit <n>","返回数量,默认 20").option("--prefix <prefix>","按函数名前缀过滤").action(async(n)=>{await M({commandLabel:"licell fn list",interactiveTTY:P(),requiredCapabilities:["fc"]},async()=>{B();let t=Ye(n.limit,20,200),s=C(n.prefix),o=A(),r=await v(o,"正在拉取函数列表...","❌ 获取函数列表失败",()=>ur(t,s));if(!r)return;if(!a())o.stop(ye.green(`✅ 共获取 ${r.length} 个函数`));if(a()){S({stage:"fn.list",count:r.length,functions:r});return}if(r.length===0){q("当前地域没有函数");return}for(let c of r)console.log(`${ye.cyan(c.functionName)} runtime=${ye.gray(c.runtime||"-")} state=${ye.gray(c.state||"-")} updated=${ye.gray(c.lastModifiedTime||"-")}`);console.log(""),q("Done.")})}),e.command("fn info [name]","查看函数详情").option("--target <target>","指定 alias/version(如 prod/preview/1)").action(async(n,t)=>{await M({commandLabel:"licell fn info",interactiveTTY:P(),requiredCapabilities:["fc"]},async()=>{B();let s=k.getProject(),o=C(n)||s.appName;if(!o)throw Error("请传入函数名,或先在当前项目执行 licell deploy 生成 appName");let r=C(t.target),c=A(),f=await v(c,`正在拉取函数 ${o} 详情...`,"❌ 获取函数详情失败",()=>ir(o,r||void 0));if(!f)return;if(!a())c.stop(ye.green("✅ 获取成功"));else{S({stage:"fn.info",functionName:f.functionName||o,qualifier:r||null,runtime:f.runtime||null,handler:f.handler||null,state:f.state||null,memorySize:f.memorySize??null,cpu:f.cpu??null,instanceConcurrency:f.instanceConcurrency??null,timeout:f.timeout??null,vpcConfig:f.vpcConfig??null,updatedAt:f.lastModifiedTime||null,envCount:Object.keys(f.environmentVariables||{}).length});return}if(console.log(`
1587
+ function: ${ye.cyan(f.functionName||o)}`),r)console.log(`qualifier: ${ye.cyan(r)}`);console.log(`runtime: ${ye.cyan(f.runtime||"-")}`),console.log(`handler: ${ye.cyan(f.handler||"-")}`),console.log(`state: ${ye.cyan(f.state||"-")}`),console.log(`memory: ${ye.cyan(String(f.memorySize||"-"))}`),console.log(`vcpu: ${ye.cyan(String(f.cpu??"-"))}`),console.log(`concur: ${ye.cyan(String(f.instanceConcurrency??"-"))}`),console.log(`timeout: ${ye.cyan(String(f.timeout||"-"))}`);let u=f.vpcConfig;if(u?.vpcId)console.log(`vpc: ${ye.cyan(`${u.vpcId} / ${(u.vSwitchIds||[]).join(",")||"-"} / ${u.securityGroupId||"-"}`)}`);else console.log(`vpc: ${ye.cyan("-")}`);console.log(`updated: ${ye.cyan(f.lastModifiedTime||"-")}`),console.log(`envCount: ${ye.cyan(String(Object.keys(f.environmentVariables||{}).length))}`),console.log(""),q("Done.")})}),e.command("fn invoke [name]","调用函数(同步)").option("--target <target>","指定 alias/version(如 prod/preview/1)").option("--payload <text>","传入原始 payload 文本").option("--file <path>","从文件读取 payload").action(async(n,t)=>{await M({commandLabel:"licell fn invoke",interactiveTTY:P(),requiredCapabilities:["fc"]},async()=>{B();let s=k.getProject(),o=C(n)||s.appName;if(!o)throw Error("请传入函数名,或先在当前项目执行 licell deploy 生成 appName");let r=C(t.target),c=C(t.payload),f=C(t.file);if(c&&f)throw Error("--payload 与 --file 不能同时使用");let u;if(f){let y;try{y=$p(kp(f))}catch{throw Error(`文件不存在或无法访问: ${f}`)}let d=_p(process.cwd(),y);if(d.startsWith("..")||bp(d))throw Error("--file 路径必须在当前工作目录内");u=wp(y,"utf-8")}else u=c;let l=A(),i=await v(l,`正在调用函数 ${o}...`,"❌ 函数调用失败",()=>gr(o,{qualifier:r||void 0,payload:u||void 0}));if(!i)return;if(!a())l.stop(ye.green(`✅ 调用完成 (status=${i.statusCode})`));let g=i.body&&i.body.trim().length>0?i.body:"";if(a()){S({stage:"fn.invoke",functionName:o,qualifier:r||null,statusCode:i.statusCode,body:g});return}if(console.log(""),g)console.log(g);else console.log("<empty response>");console.log(""),q("Done.")})}),e.command("fn rm [name]","删除函数").option("--force","级联删除触发器、alias、已发布版本后再删除函数").option("--yes","跳过二次确认(危险)").action(async(n,t)=>{await M({commandLabel:"licell fn rm",interactiveTTY:P(),requiredCapabilities:["fc"]},async()=>{B();let s=k.getProject(),o=C(n)||s.appName;if(!o)throw Error("请传入函数名,或先在当前项目执行 licell deploy 生成 appName");await Be(t.force?`删除函数 ${o}(含触发器/alias/版本)`:`删除函数 ${o}`,{yes:Boolean(t.yes)});let r=A(),c=await v(r,t.force?`正在级联清理并删除函数 ${o}...`:`正在删除函数 ${o}...`,"❌ 删除函数失败",()=>lr(o,{force:Boolean(t.force)}));if(!c)return;if(!a())r.stop(ye.green("✅ 函数已删除"));if(a()){S({stage:"fn.rm",functionName:o,force:Boolean(t.force),forced:c.forced,deletedTriggers:c.deletedTriggers,deletedAliases:c.deletedAliases,deletedVersions:c.deletedVersions});return}if(c.forced)console.log(`
1588
+ cleanup: triggers=${c.deletedTriggers.length} aliases=${c.deletedAliases.length} versions=${c.deletedVersions.length}`);q("Done.")})})}import{text as ll}from"@clack/prompts";import me from"picocolors";Ae();se();re();function gl(e){e.command("oss list","查看 OSS Bucket 列表").option("--limit <n>","返回数量,默认 50").action(async(t)=>{await M({commandLabel:"licell oss list",interactiveTTY:P(),requiredCapabilities:["oss"]},async()=>{B();let s=Ye(t.limit,50,500),o=A(),r=await v(o,"正在拉取 OSS Bucket 列表...","❌ 获取 Bucket 列表失败",()=>Zi(s));if(!r)return;if(!a())o.stop(me.green(`✅ 共获取 ${r.length} 个 Bucket`));if(a()){S({stage:"oss.list",count:r.length,buckets:r});return}if(r.length===0){q("当前账号没有 Bucket");return}for(let c of r)console.log(`${me.cyan(c.name)} region=${me.gray(c.location||"-")} created=${me.gray(c.creationDate||"-")}`);console.log(""),q("Done.")})}),e.command("oss info <bucket>","查看 OSS Bucket 详情").action(async(t)=>{await M({commandLabel:"licell oss info",interactiveTTY:P(),requiredCapabilities:["oss"]},async()=>{B();let s=L(t,"bucket"),o=A(),r=await v(o,`正在拉取 Bucket ${s} 详情...`,"❌ 获取 Bucket 详情失败",()=>Ji(s));if(!r)return;if(!a())o.stop(me.green("✅ 获取成功"));else{S({stage:"oss.info",bucket:s,info:r});return}console.log(`
1589
+ name: ${me.cyan(r.name)}`),console.log(`location: ${me.cyan(r.location||"-")}`),console.log(`created: ${me.cyan(r.creationDate||"-")}`),console.log(`endpoint: ${me.cyan(r.extranetEndpoint||"-")}`),console.log(`intranet: ${me.cyan(r.intranetEndpoint||"-")}`),console.log(""),q("Done.")})}),e.command("oss ls <bucket> [prefix]","列出 Bucket 对象").option("--limit <n>","返回数量,默认 100").action(async(t,s,o)=>{await M({commandLabel:"licell oss ls",interactiveTTY:P(),requiredCapabilities:["oss"]},async()=>{B();let r=L(t,"bucket"),c=C(s),f=Ye(o.limit,100,2000),u=A(),l=await v(u,`正在列出 ${r} 对象...`,"❌ 获取对象列表失败",()=>Qs(r,c||void 0,f));if(!l)return;if(!a())u.stop(me.green(`✅ 共获取 ${l.length} 个对象`));if(a()){S({stage:"oss.ls",bucket:r,prefix:c||null,count:l.length,objects:l});return}if(l.length===0){q("当前条件下无对象");return}for(let i of l)console.log(`${me.cyan(i.name)} size=${me.gray(String(i.size??"-"))} modified=${me.gray(i.lastModified||"-")}`);console.log(""),q("Done.")})});let n=(t,s)=>{e.command(t,s).option("--bucket <bucket>","Bucket 名称(可替代位置参数)").option("--source-dir <dir>","本地目录(默认 dist)").option("--target-dir <dir>","Bucket 内目标目录前缀(如 mysite 或 mysite/v2)").action(async(o,r)=>{await M({commandLabel:"licell oss upload",interactiveTTY:P(),requiredCapabilities:["oss"]},async()=>{B();let c=P(),f=C(r.bucket)||C(o)||(c?L(await ll({message:"Bucket 名称:"}),"bucket"):void 0);if(!f)throw Error("请通过 <bucket> 或 --bucket 指定 Bucket 名称");let u=C(r.sourceDir)||(c?L(await ll({message:"本地目录:",initialValue:"dist"}),"source-dir"):"dist"),l=C(r.targetDir),i=A(),g=await v(i,`正在上传 ${u} 到 OSS Bucket ${f}${l?`/${l}`:""}...`,"❌ OSS 目录上传失败",()=>Gr(f,u,{targetDir:l}));if(!g)return;if(!a())i.stop(me.green(`✅ 上传完成,共 ${g.uploadedCount} 个文件`));else{S({stage:"oss.upload",bucket:g.bucket,sourceDir:u,targetDir:g.targetDir||null,uploadedCount:g.uploadedCount,skippedSymlinkCount:g.skippedSymlinkCount,baseUrl:g.baseUrl});return}let y=g.targetDir?`${g.targetDir}/`:"";if(console.log(`
1590
+ bucket: ${me.cyan(g.bucket)}`),console.log(`prefix: ${me.cyan(g.targetDir||"(root)")}`),console.log(`base: ${me.cyan(g.baseUrl)}`),console.log(`hint: ${me.gray(`${g.baseUrl}/${y}<file>`)}`),g.skippedSymlinkCount>0)console.log(me.yellow(`warning: 已跳过 ${g.skippedSymlinkCount} 个符号链接(为避免目录逃逸与递归风险)`));console.log(""),q("Done.")})})};n("oss upload [bucket]","上传本地目录到 OSS Bucket 指定目录"),n("oss bucket [bucket]","上传本地目录到 OSS Bucket 指定目录(兼容命令,等同 oss upload)")}import{select as Yp,isCancel as Op}from"@clack/prompts";import U from"picocolors";Ae();K();ft();ce();De();Ot();import*as de from"@alicloud/rds20140815";K();Ie();import Tp from"@alicloud/rds20140815";import*as yl from"@alicloud/openapi-client";var Cp=60000,Ep=120000,Sp=te(Tp,"@alicloud/rds20140815");function mn(){let e=k.requireAuth(),n=new Sp(new yl.Config({accessKeyId:e.ak,accessKeySecret:e.sk,endpoint:`rds.${e.region}.aliyuncs.com`,connectTimeout:Cp,readTimeout:Ep}));return{auth:e,client:n}}var qp=1200000,ml=5000,Pp=300000,jr=5,Rp={postgres:"pg.n2.serverless.1c",mysql:"mysql.n2.serverless.1c"},Ip={postgres:20,mysql:20},Wr={postgres:{minCapacity:0.5,maxCapacity:8,autoPause:!0},mysql:{minCapacity:0.5,maxCapacity:2,autoPause:!0}},dl={postgres:"AliyunServiceRoleForRdsPgsqlOnEcs",mysql:"AliyunServiceRoleForRds"};function Fp(e,n){if(!n)return!1;if(n===e)return!0;let t=e.split(".")[0],s=n.split(".")[0];return t.length>0&&t===s}async function xp(e,n,t,s){try{let r=((await e.describeAvailableZones(new de.DescribeAvailableZonesRequest({regionId:n,engine:t,engineVersion:s,category:"serverless_basic"}))).body?.availableZones||[]).map((c)=>c.zoneId).filter((c)=>typeof c==="string"&&c.length>0);if(r.length>0)return[...new Set(r)]}catch{}try{let r=(await e.describeAvailableZones(new de.DescribeAvailableZonesRequest({regionId:n,engine:t,engineVersion:s}))).body?.availableZones||[],c=[];for(let u of r){let l=u.zoneId;if(typeof l!=="string"||l.length===0)continue;if((u.supportedEngines||[]).some((g)=>{if((g.engine||"").toLowerCase()!==t.toLowerCase())return!1;return(g.supportedEngineVersions||[]).some((y)=>{if(!Fp(s,y.version))return!1;return(y.supportedCategorys||[]).some((d)=>d.category==="serverless_basic")})}))c.push(l)}if(c.length>0)return[...new Set(c)];let f=r.map((u)=>u.zoneId).filter((u)=>typeof u==="string"&&u.length>0);return[...new Set(f)]}catch{return[]}}async function hl(e,n,t){let s=t==="postgres"?["AliyunServiceRoleForRds",dl.postgres]:[dl.mysql];for(let o of s){let r=!1;try{let f=((await e.checkServiceLinkedRole(new de.CheckServiceLinkedRoleRequest({regionId:n,serviceLinkedRole:o}))).body?.hasServiceLinkedRole||"").toString().toLowerCase();r=f==="true"||f==="1"}catch{}if(r)continue;try{await e.createServiceLinkedRole(new de.CreateServiceLinkedRoleRequest({regionId:n,serviceLinkedRole:o}))}catch(c){if(wo(c))continue;throw c}}}async function al(e,n,t){let s;for(let o=1;o<=jr;o+=1)try{return await e.createDBInstance(n)}catch(r){if(s=r,!Ee(r)||o===jr)throw r;t.message(`\uD83C\uDF10 RDS API 网络抖动,${o}/${jr} 次失败,正在重试...`),await ge(1500*o)}throw s instanceof Error?s:Error("RDS 创建失败")}async function Hp(e,n,t,s){let o=t==="postgres"?"5432":"3306",r=Date.now();while(!0){if(Date.now()-r>Pp)throw Error("数据库网络信息未就绪,等待连接地址超时");let f=(await e.describeDBInstanceNetInfo(new de.DescribeDBInstanceNetInfoRequest({DBInstanceId:n}))).body?.DBInstanceNetInfos?.DBInstanceNetInfo||[],u=f.find((y)=>y.IPType==="Private")||f[0],l=u?.connectionString?.trim(),i=u?.port||o;if(l)return{host:l,port:i};let g=f.map((y)=>`${y.IPType||"Unknown"}:${y.connectionString||"-"}`).join(",")||"pending";s.message(`☕ 数据库连接地址初始化中,请稍候... [${g}]`),await ge(ml)}}function pl(e){let n=e.toLowerCase().replace(/[^a-z0-9_]/g,"_");if(!/^[a-z]/.test(n))n=`a_${n}`;if(n=n.replace(/_+/g,"_").replace(/_$/,""),n.length<2||!/[a-z0-9]/.test(n.slice(1)))n="licell_user";return n.slice(0,16)}async function Qr(e,n,t={}){let{auth:s,client:o}=mn(),r=k.getProject(),c="main",f=pl(r.appName||"licell_app"),u=xe(),l=e==="postgres"?"PostgreSQL":"MySQL",i=t.engineVersion?.trim()||(e==="postgres"?"18.0":"8.0"),g=t.category?.trim()||"serverless_basic",y=t.storageType?.trim()||"cloud_essd";n.message("\uD83D\uDD0E 正在查询 RDS Serverless 可用区...");let d=await xp(o,s.region,l,i);n.message("\uD83D\uDD0D 正在探测/拉起专属私有网络平面 (VPC & VSwitch)...");let m=t.zoneId?.trim(),h=t.vpcId?.trim(),b=t.vSwitchId?.trim(),$;if(h||b){if(!h||!b)throw Error("自定义网络时需同时提供 --vpc 与 --vsw");if(!m)throw Error("自定义网络时需提供 --zone");$=await lt({vpcId:h,vswId:b,zoneId:m})}else $=await it({preferredZoneIds:m?[m]:d});let p=t.instanceClass?.trim()||Rp[e],w=t.storageGb||Ip[e];try{let fn=(await o.describeAvailableClasses(new de.DescribeAvailableClassesRequest({regionId:s.region,zoneId:$.zoneId,engine:l,engineVersion:i,instanceChargeType:"Serverless",category:g,DBInstanceStorageType:y}))).body?.DBInstanceClasses?.find((It)=>{if(typeof It.DBInstanceClass!=="string"||It.DBInstanceClass.length===0)return!1;if(!t.instanceClass)return!0;return It.DBInstanceClass===t.instanceClass});if(!t.instanceClass&&typeof fn?.DBInstanceClass==="string"&&fn.DBInstanceClass.length>0)p=fn.DBInstanceClass;let st=fn?.DBInstanceStorageRange?.minValue;if(!t.storageGb&&typeof st==="number"&&Number.isFinite(st)&&st>0)w=Math.floor(st)}catch{n.message("⚠️ 未能查询到可售规格,回退到内置默认规格继续创建...")}n.message("\uD83D\uDD10 正在确保 RDS 服务关联角色已就绪..."),await hl(o,s.region,e),n.message(`\uD83D\uDCE6 正在拉起 Serverless ${e.toUpperCase()} (按量计费)...`);let H={engine:l,engineVersion:i,payType:"Serverless",category:g,regionId:s.region,zoneId:$.zoneId,DBInstanceClass:p,DBInstanceStorage:w,DBInstanceStorageType:y,securityIPList:t.securityIpList?.trim()||$.cidrBlock||"10.0.0.0/8",instanceNetworkType:"VPC",DBInstanceNetType:"Intranet",DBInstanceDescription:t.description?.trim()||`${r.appName||"licell-app"}-${e}`,serverlessConfig:{minCapacity:t.minCapacity??Wr[e].minCapacity,maxCapacity:t.maxCapacity??Wr[e].maxCapacity,autoPause:t.autoPause??Wr[e].autoPause},VPCId:$.vpcId,vSwitchId:$.vswId},T=t.zoneIdSlave1?.trim(),x=t.zoneIdSlave2?.trim();if(T)H.zoneIdSlave1=T;if(x)H.zoneIdSlave2=x;let E=new de.CreateDBInstanceRequest(H),_;try{_=await al(o,E,n)}catch(Je){if(!ps(Je))throw Je;n.message("\uD83D\uDD10 首次使用检测到缺少服务关联角色,正在自动创建并重试..."),await hl(o,s.region,e);try{_=await al(o,E,n)}catch(fn){if(ps(fn))throw Error("RDS PostgreSQL 仍提示缺少服务关联角色。请先在阿里云控制台开通 RDS PostgreSQL 服务后重试,或先创建 MySQL Serverless 实例。");throw fn}}let F=_.body?.DBInstanceId;if(!F)throw Error("RDS 创建失败:未返回 DBInstanceId");let I="Creating",fe=Date.now();while(I!=="Running"){if(Date.now()-fe>qp)throw Error(`数据库创建超时,最后状态: ${I}`);if(await ge(ml),I=(await o.describeDBInstances(new de.DescribeDBInstancesRequest({DBInstanceId:F}))).body?.items?.DBInstance?.[0]?.DBInstanceStatus||"Creating",I==="Deleted"||I==="Failed")throw Error(`数据库创建失败,实例状态: ${I}`);n.message(`☕ 数据库底层初始化中,请稍候... [${I}]`)}if(n.message("\uD83E\uDDF1 正在创建数据库与应用账号..."),await ks(()=>o.createAccount(new de.CreateAccountRequest({DBInstanceId:F,accountName:f,accountPassword:u,accountType:"Normal",accountDescription:"licell managed account"}))),await ks(()=>o.createDatabase(new de.CreateDatabaseRequest({DBInstanceId:F,DBName:"main",characterSetName:e==="postgres"?"UTF8":"utf8mb4",ownerAccount:e==="postgres"?f:void 0}))),e!=="postgres")await ks(()=>o.grantAccountPrivilege(new de.GrantAccountPrivilegeRequest({DBInstanceId:F,DBName:"main",accountName:f,accountPrivilege:"ReadWrite"})));let{host:Ce,port:rn}=await Hp(o,F,e,n),cn=`${e==="postgres"?"postgresql":"mysql"}://${encodeURIComponent(f)}:${encodeURIComponent(u)}@${Ce}:${rn}/main`;return r.envs={...r.envs,DATABASE_URL:cn},r.network=$,r.database={type:e,instanceId:F,user:f,name:"main"},k.setProject(r),n.message("⚠️ DATABASE_URL(含密码)已写入 .licell/project.json,请确认该目录已在 .gitignore 中"),cn}K();import*as pn from"@alicloud/rds20140815";function wl(e){if(!e)return null;try{let n=new URL(e),t=n.protocol==="postgresql:"?"postgresql":n.protocol==="mysql:"?"mysql":null;if(!t||!n.hostname)return null;let s=n.port||(t==="postgresql"?"5432":"3306"),o=Number(s);if(!Number.isFinite(o)||o<=0)return null;let r=n.pathname.replace(/^\//,"")||"main";return{protocol:t,host:n.hostname,port:o,database:r,username:decodeURIComponent(n.username||""),password:decodeURIComponent(n.password||"")}}catch{return null}}function $l(e){let n=e.database;if(typeof n!=="object"||n===null)return{};let t=n;return{instanceId:typeof t.instanceId==="string"?t.instanceId:void 0,user:typeof t.user==="string"?t.user:void 0,name:typeof t.name==="string"?t.name:void 0}}function kl(e){return(e||"").toLowerCase().includes("postgres")?"postgresql":"mysql"}function _l(e){return{instanceId:e.DBInstanceId||"",description:e.DBInstanceDescription,engine:e.engine,engineVersion:e.engineVersion,status:e.DBInstanceStatus,payType:e.payType,category:e.category,instanceClass:e.DBInstanceClass,zoneId:e.zoneId,vpcId:e.vpcId,vSwitchId:e.vSwitchId}}async function Lp(e){let{auth:n,client:t}=mn(),o=(await t.describeDBInstances(new pn.DescribeDBInstancesRequest({regionId:n.region,DBInstanceId:e,pageNumber:1,pageSize:30}))).body?.items?.DBInstance||[],r=o.find((c)=>c.DBInstanceId===e)||o[0];if(!r?.DBInstanceId)throw Error(`未找到数据库实例: ${e}`);return _l(r)}async function Dr(e=200){let{auth:n,client:t}=mn(),s=[],o=Math.max(1,Math.min(Math.floor(e),500)),r=Math.min(100,o);for(let c=1;c<=20&&s.length<o;c+=1){let f=await t.describeDBInstances(new pn.DescribeDBInstancesRequest({regionId:n.region,pageNumber:c,pageSize:r})),u=f.body?.items?.DBInstance||[];for(let g of u){let y=_l(g);if(!y.instanceId)continue;if(s.push(y),s.length>=o)break}let l=f.body||{},i=Number(l.totalRecordCount||l.totalCount||0);if(u.length===0||Number.isFinite(i)&&i>0&&s.length>=i)break}return s}async function Ds(e){let n=e.trim();if(!n)throw Error("instanceId 不能为空");let{client:t}=mn(),s=await Lp(n),r=((await t.describeDBInstanceNetInfo(new pn.DescribeDBInstanceNetInfoRequest({DBInstanceId:n}))).body?.DBInstanceNetInfos?.DBInstanceNetInfo||[]).map((i)=>({type:i.connectionStringType,ipType:i.IPType,host:i.connectionString,port:i.port,vpcId:i.VPCId,vSwitchId:i.vSwitchId})),f=((await t.describeDatabases(new pn.DescribeDatabasesRequest({DBInstanceId:n,pageNumber:1,pageSize:100}))).body?.databases?.database||[]).map((i)=>i.DBName).filter((i)=>typeof i==="string"&&i.length>0),l=((await t.describeAccounts(new pn.DescribeAccountsRequest({DBInstanceId:n}))).body?.accounts?.DBInstanceAccount||[]).map((i)=>i.accountName).filter((i)=>typeof i==="string"&&i.length>0);return{summary:s,endpoints:r,databases:f,accounts:l}}async function Vs(e){let n=k.getProject(),t=$l(n),s=e?.trim()||t.instanceId||"";if(!s)throw Error("未指定 DB 实例 ID,且当前项目未绑定数据库实例");let o=await Ds(s),r=kl(o.summary.engine),c=o.endpoints.find((x)=>x.ipType==="Private"&&x.host)||o.endpoints.find((x)=>x.host),f=c?.host?.trim();if(!f)throw Error(`未获取到实例 ${s} 的连接地址`);let u=Number(c?.port||(r==="postgresql"?"5432":"3306"));if(!Number.isFinite(u)||u<=0)throw Error(`实例 ${s} 返回了无效端口`);let l=o.endpoints.find((x)=>x.ipType==="Public"&&x.host),g=t.instanceId===s?wl(n.envs?.DATABASE_URL):null,y=g?.username||t.user||"<username>",d=g?.database||t.name||o.databases[0]||"main",m=g?.password||"",h=m.length>0,b=h?encodeURIComponent(m):"<password>",$=y==="<username>"?y:encodeURIComponent(y),p=`${r}://${$}:${b}@${f}:${u}/${d}`,w,H,T;if(l?.host)w=l.host.trim(),H=Number(l.port||u),T=`${r}://${$}:${b}@${w}:${H}/${d}`;return{instanceId:s,engine:r,host:f,port:u,database:d,username:y,passwordKnown:h,connectionString:p,publicHost:w,publicPort:H,publicConnectionString:T}}import*as Jn from"@alicloud/rds20140815";ce();var Ap="licell_public";async function Vr(e,n){let{client:t}=mn(),r=((await t.describeDBInstanceNetInfo(new Jn.DescribeDBInstanceNetInfoRequest({DBInstanceId:e}))).body?.DBInstanceNetInfos?.DBInstanceNetInfo||[]).find((i)=>i.IPType==="Public");if(r?.connectionString)return{host:r.connectionString,port:r.port||"3306"};let c=`${e}-pub`.toLowerCase().replace(/[^a-z0-9]/g,"").slice(0,40);try{await t.allocateInstancePublicConnection(new Jn.AllocateInstancePublicConnectionRequest({DBInstanceId:e,connectionStringPrefix:c,port:"3306"}))}catch(i){let g=R(i);if(g.includes("NetTypeExists")||g.includes("already exists"))n.message("公网地址已存在,正在获取...");else throw i}let l=((await t.describeDBInstanceNetInfo(new Jn.DescribeDBInstanceNetInfoRequest({DBInstanceId:e}))).body?.DBInstanceNetInfos?.DBInstanceNetInfo||[]).find((i)=>i.IPType==="Public");if(!l?.connectionString)return null;return{host:l.connectionString,port:l.port||"3306"}}async function Xr(e,n,t){let{client:s}=mn(),o=`${n}/32`;try{await s.modifySecurityIps(new Jn.ModifySecurityIpsRequest({DBInstanceId:e,DBInstanceIPArrayName:Ap,securityIps:o,modifyMode:"Cover"}))}catch(r){t.message(`⚠️ 公网白名单设置失败: ${R(r)}`)}}se();re();function Tl(e){e.command("db add","分配数据库实例").option("--type <type>","数据库类型:postgresql 或 mysql(默认 serverless-postgresql,即将上线)").option("--engine-version <version>","数据库引擎版本(postgres 默认 18.0,mysql 默认 8.0)").option("--category <category>","RDS Category(默认 serverless_basic)").option("--class <instanceClass>","实例规格(如 pg.n2.serverless.1c)").option("--storage <gb>","存储空间 GB(默认 20)").option("--storage-type <storageType>","存储类型(默认 cloud_essd)").option("--min-rcu <n>","Serverless 最小 RCU(如 0.5)").option("--max-rcu <n>","Serverless 最大 RCU(如 8)").option("--auto-pause <mode>","自动启停:on/off").option("--zone <zoneId>","主可用区(如 cn-hangzhou-b)").option("--zone-slave1 <zoneId>","备可用区 1(多可用区部署)").option("--zone-slave2 <zoneId>","备可用区 2(多可用区部署)").option("--vpc <vpcId>","指定 VPC ID").option("--vsw <vSwitchId>","指定 VSwitch ID").option("--security-ip-list <cidrs>","白名单 CIDR(逗号分隔)").option("--description <text>","实例描述").action(async(n)=>{await M({commandLabel:"licell db add",interactiveTTY:P(),requiredCapabilities:["rds"]},async()=>{j(U.bgMagenta(U.white(" \uD83D\uDDC4️ Database Provisioning (IaC) "))),B();let t=P(),s,o=C(n.type);if(o)s=Ju(o);else if(t){let y=await Yp({message:"选择数据库引擎:",options:[{value:"postgres",label:"\uD83D\uDC18 RDS PostgreSQL(按量付费)"},{value:"mysql",label:"\uD83D\uDC2C RDS Serverless MySQL"},{value:"serverless-postgresql",label:"\uD83D\uDC18 RDS Serverless PostgreSQL(即将上线)"}]});if(Op(y))process.exit(0);s=y}else throw Error("非交互模式下请传入 --type postgresql|mysql");if(s==="serverless-postgresql"){console.log(U.yellow("⏳ Serverless PostgreSQL 即将上线,敬请期待。")),console.log(U.gray(`当前支持的类型:${U.bold("postgresql")}(按量付费)和 ${U.bold("mysql")}(Serverless)`)),q("");return}let r=s,c=qe(n.storage,"storage"),f=Cr(n.minRcu,"min-rcu"),u=Cr(n.maxRcu,"max-rcu");if(typeof f==="number"&&f<=0)throw Error("min-rcu 必须大于 0");if(typeof u==="number"&&u<=0)throw Error("max-rcu 必须大于 0");if(typeof f==="number"&&typeof u==="number"&&f>u)throw Error("min-rcu 不能大于 max-rcu");let l=C(n.autoPause)?zu(n.autoPause):void 0,i=A(),g=await v(i,"正在初始化基础设施编排引擎...","❌ 拉起失败",()=>Qr(r,i,{engineVersion:C(n.engineVersion),category:C(n.category),instanceClass:C(n.class),storageGb:c,storageType:C(n.storageType),minCapacity:f,maxCapacity:u,autoPause:l,zoneId:C(n.zone),zoneIdSlave1:C(n.zoneSlave1),zoneIdSlave2:C(n.zoneSlave2),vpcId:C(n.vpc),vSwitchId:C(n.vsw),securityIpList:C(n.securityIpList),description:C(n.description)}));if(!g)return;if(!a())i.stop(U.green("✅ 数据库实例已就绪并绑定到本工程内网!"));if(a()){S({stage:"db.add",type:s,connectionStringMasked:Pn(g)});return}console.log(`
1591
+ \uD83D\uDD11 内网直连凭证已生成: ${U.cyan(Pn(g))}
1592
+ `),q("下次执行 licell deploy 时,将自动作为 process.env.DATABASE_URL 注入!")})}),e.command("db list","查看数据库实例列表").option("--limit <n>","返回数量,默认 20").action(async(n)=>{await M({commandLabel:"licell db list",interactiveTTY:P(),requiredCapabilities:["rds"]},async()=>{B();let t=Ye(n.limit,20,200),s=A(),o=await v(s,"正在拉取数据库实例列表...","❌ 获取数据库实例列表失败",()=>Dr(t));if(!o)return;if(!a())s.stop(U.green(`✅ 共获取 ${o.length} 个实例`));if(a()){S({stage:"db.list",count:o.length,instances:o});return}if(o.length===0){q("当前地域没有数据库实例");return}for(let r of o)console.log(`${U.cyan(r.instanceId)} engine=${U.gray(`${r.engine||"-"} ${r.engineVersion||""}`.trim())} status=${U.gray(r.status||"-")} class=${U.gray(r.instanceClass||"-")}`);console.log(""),q("Done.")})}),e.command("db info <instanceId>","查看数据库实例详情").action(async(n)=>{await M({commandLabel:"licell db info",interactiveTTY:P(),requiredCapabilities:["rds"]},async()=>{B();let t=L(n,"instanceId"),s=A(),o=await v(s,`正在拉取实例 ${t} 详情...`,"❌ 获取数据库实例详情失败",()=>Ds(t));if(!o)return;let r=o.summary;if(!a())s.stop(U.green("✅ 获取成功"));else{S({stage:"db.info",instanceId:t,detail:o});return}if(console.log(`
1593
+ instanceId: ${U.cyan(r.instanceId)}`),console.log(`engine: ${U.cyan(`${r.engine||"-"} ${r.engineVersion||""}`.trim())}`),console.log(`status: ${U.cyan(r.status||"-")}`),console.log(`class: ${U.cyan(r.instanceClass||"-")}`),console.log(`payType: ${U.cyan(r.payType||"-")}`),console.log(`vpc/vsw: ${U.cyan(`${r.vpcId||"-"} / ${r.vSwitchId||"-"}`)}`),console.log(`zone: ${U.cyan(r.zoneId||"-")}`),o.endpoints.length>0)console.log(`endpoints: ${U.cyan(o.endpoints.map((c)=>`${c.ipType||c.type||"-"}:${c.host||"-"}:${c.port||"-"}`).join(", "))}`);if(o.databases.length>0)console.log(`databases: ${U.cyan(o.databases.join(", "))}`);if(o.accounts.length>0)console.log(`accounts: ${U.cyan(o.accounts.join(", "))}`);console.log(""),q("Done.")})}),e.command("db connect [instanceId]","输出数据库连接信息").action(async(n)=>{await M({commandLabel:"licell db connect",interactiveTTY:P(),requiredCapabilities:["rds"]},async()=>{B();let t=C(n),s=A(),o=await v(s,"正在解析数据库连接信息...","❌ 连接信息解析失败",()=>Vs(t));if(!o)return;if(!a())s.stop(U.green("✅ 连接信息已生成"));else{S({stage:"db.connect",instanceId:o.instanceId,connection:o});return}if(console.log(`
1594
+ instanceId: ${U.cyan(o.instanceId)}`),console.log(`engine: ${U.cyan(o.engine)}`),console.log(`host: ${U.cyan(o.host)}`),console.log(`port: ${U.cyan(String(o.port))}`),console.log(`database: ${U.cyan(o.database)}`),console.log(`username: ${U.cyan(o.username)}`),console.log(`password: ${U.cyan(o.passwordKnown?"<known in project>":"<unknown, please provide manually>")}`),console.log(`url: ${U.cyan(o.connectionString)}`),o.publicHost)console.log(""),console.log(U.yellow("── 公网访问 ──")),console.log(`public host: ${U.cyan(o.publicHost)}`),console.log(`public port: ${U.cyan(String(o.publicPort))}`),console.log(`public url: ${U.cyan(o.publicConnectionString)}`);console.log(""),q("Done.")})}),e.command("db public-access [instanceId]","开通数据库公网访问并添加当前 IP 到白名单").option("--ip <ip>","手动指定公网 IP(不传则自动获取)").action(async(n,t)=>{await M({commandLabel:"licell db public-access",interactiveTTY:P(),requiredCapabilities:["rds"]},async()=>{let{resolvePublicIp:s}=await Promise.resolve().then(() => (Jr(),Zr));j(U.bgMagenta(U.white(" \uD83C\uDF10 DB Public Access "))),B();let o=C(n),r=A();r.start("正在获取公网 IP...");let c=t.ip?.trim()||await s();r.stop(`公网 IP: ${U.cyan(c)}`);let f=await v(r,"正在解析数据库连接信息...","❌ 连接信息解析失败",()=>Vs(o));if(!f)return;await v(r,`正在将 ${c}/32 添加到白名单 (licell_public)...`,"❌ 白名单设置失败",()=>Xr(f.instanceId,c,r));let u=await v(r,"正在开通公网访问...","❌ 公网访问开通失败",()=>Vr(f.instanceId,r));if(!a())r.stop(U.green("✅ 公网访问已开通"));else{S({stage:"db.public-access",instanceId:f.instanceId,publicIp:c,publicHost:u?.host||null,publicPort:u?.port||null});return}if(console.log(""),console.log(U.yellow("── 内网访问 ──")),console.log(`host: ${U.cyan(f.host)}`),console.log(`port: ${U.cyan(String(f.port))}`),console.log(`url: ${U.cyan(f.connectionString)}`),u){console.log(""),console.log(U.yellow("── 公网访问 ──")),console.log(`host: ${U.cyan(u.host)}`),console.log(`port: ${U.cyan(u.port)}`);let l=f.engine,i=f.username==="<username>"?f.username:encodeURIComponent(f.username),g=f.passwordKnown?"<password>":"<password>";console.log(`url: ${U.cyan(`${l}://${i}:${g}@${u.host}:${u.port}/${f.database}`)}`)}else console.log(U.yellow(`
1595
1595
  ⚠️ 公网地址尚未就绪,请稍后通过 db connect 查看`));console.log(`
1596
- 白名单 IP: ${G.cyan(`${c}/32`)} (分组: licell_public)`),q("Done.")})})}import{select as ow,isCancel as sw}from"@clack/prompts";import v from"picocolors";Le();K();ft();De();Ot();import*as on from"@alicloud/r-kvstore20150101";import{randomUUID as Ml}from"crypto";Ie();import Kp from"@alicloud/r-kvstore20150101";import*as wn from"@alicloud/openapi-client";import*as Cl from"@alicloud/tea-util";var jp=te(Kp,"@alicloud/r-kvstore20150101"),Wp=te(wn.default,"@alicloud/openapi-client");function Oe(e){return new jp(new wn.Config({accessKeyId:e.ak,accessKeySecret:e.sk,regionId:e.region,endpoint:"r-kvstore.aliyuncs.com",connectTimeout:30000,readTimeout:60000}))}function Qp(e){return new Wp(new wn.Config({accessKeyId:e.ak,accessKeySecret:e.sk,regionId:e.region,endpoint:"r-kvstore.aliyuncs.com"}))}function Dp(e){if(e===null||e===void 0)return;if(typeof e==="boolean")return e?"true":"false";return String(e)}function Vp(e){let n={};for(let[t,o]of Object.entries(e)){let s=Dp(o);if(s===void 0)continue;n[t]=s}return n}async function El(e,n,t){let o=Qp(e),s=new wn.Params({action:n,version:"2015-01-01",protocol:"HTTPS",pathname:"/",method:"POST",authType:"AK",style:"RPC",reqBodyType:"formData",bodyType:"json"}),r=new wn.OpenApiRequest({query:Vp(t)});return o.callApi(s,r,new Cl.RuntimeOptions({readTimeout:20000,connectTimeout:8000}))}ft();ce();De();import*as ke from"@alicloud/r-kvstore20150101";import{randomUUID as Xp}from"crypto";ce();function Nr(e){return["Error","Released","Inactive","Unavailable","Flushing"].includes(e)}function Xe(e,n,t,o){if(!e)return`redis://:${encodeURIComponent(n)}@${t}:${o}`;return`redis://${encodeURIComponent(e)}:${encodeURIComponent(n)}@${t}:${o}`}function Xt(e,n,t,o,s){let r=s?encodeURIComponent(n):"<password>";if(!e)return`redis://:${r}@${t}:${o}`;return`redis://${e==="<username>"?e:encodeURIComponent(e)}:${r}@${t}:${o}`}function Zo(e){if(typeof e!=="object"||e===null)return"";let n="code"in e?String(e.code||""):"";if(n)return n;if("data"in e&&typeof e.data==="object"&&e.data!==null){let t=e.data?.Code;if(t)return String(t)}return""}function Sl(e){return/InvalidInstanceId\.NotFound/i.test(Zo(e)||R(e))}function ql(e){let n=`${Zo(e)} ${R(e)}`;return/NotSupport|Unsupported|InvalidInstanceId|InvalidParameter|OperationDenied|AccessDenied/i.test(n)}function Jo(e){let n=new Set;for(let t of e){if(!t)continue;let o=t.trim();if(o)n.add(o)}return[...n]}function Nn(e){let n=(e||"").trim();if(!n)return null;let t=n.split(",")[0]?.trim();if(!t)return null;let o=/^[a-z][a-z0-9+.-]*:\/\//i.test(t)?t:`redis://${t}`;try{let s=new URL(o),r=s.hostname;if(!r)return null;let c=s.port?Number(s.port):6379;if(!Number.isFinite(c)||c<=0)return null;let f=s.username?decodeURIComponent(s.username):void 0,u=s.password?decodeURIComponent(s.password):void 0,l=s.toString();return{host:r,port:c,accountName:f,password:u,url:l}}catch{return null}}function zn(e){return e.startsWith("r-")}function Zt(e){return e.startsWith("tk-")||e.startsWith("tt-")}function Tt(e,n){return{vpcId:n.vpcId,vswId:n.vswId,sgId:n.sgId??e?.sgId,cidrBlock:n.cidrBlock??e?.cidrBlock}}function zr(e){return{instanceId:e.instanceId||"",mode:"classic-redis",instanceName:e.instanceName,status:e.instanceStatus,instanceClass:e.instanceClass,engineVersion:e.engineVersion,host:e.connectionDomain,port:e.port,zoneId:e.zoneId,vpcId:e.vpcId,vSwitchId:e.vSwitchId}}function Pl(e){return{instanceId:e.instanceId||"",mode:"tair-serverless-kv",instanceName:e.instanceName,status:e.instanceStatus,zoneId:e.zoneId,vpcId:e.vpcId,vSwitchId:e.vSwitchId}}var Rl=1200000,Il=5000,Fl=60000,xl=180000,No="kvcache.cu.g4b.2",Hl=1;async function Jt(e,n,t){let s=(await e.describeAccounts(new ke.DescribeAccountsRequest({instanceId:n}))).body?.accounts?.account||[],r=s.filter((f)=>f.accountStatus!=="Unavailable");return(r.find((f)=>f.accountType==="Normal")||r[0]||s[0])?.accountName||t||""}async function Al(e,n,t){try{if((await e.describeServiceLinkedRoleExists(new ke.DescribeServiceLinkedRoleExistsRequest({}))).body?.existsServiceLinkedRole)return;t.message("\uD83D\uDD10 正在初始化 Kvstore 服务关联角色..."),await e.initializeKvstorePermission(new ke.InitializeKvstorePermissionRequest({regionId:n}))}catch(o){t.message(`⚠️ 服务关联角色检查失败 (${R(o)}),将继续尝试创建缓存`)}}async function Ll(e,n,t,o,s){let r=o.instanceClass?.trim()||No,c=`${s||"licell-app"}-redis`,f=xe(),u=o.securityIpList?.trim()||t.cidrBlock||"10.0.0.0/8",i=(await El(e,"CreateTairKVCacheInferInstance",{RegionId:e.region,InstanceName:c,InstanceClass:r,ZoneId:t.zoneId,VpcId:t.vpcId,VSwitchId:t.vswId,ChargeType:"PostPaid",AutoPay:!0,Password:f,SecurityIPList:u,ClientToken:Xp()})).body||{},g=typeof i.InstanceId==="string"?i.InstanceId:typeof i.instanceId==="string"?i.instanceId:"";if(!g)return null;let y=typeof i.ConnectionString==="string"?i.ConnectionString:typeof i.connectionString==="string"?i.connectionString:"",d=Nn(y),m=d?.host||"",h=d?.port||6379,b=d?.accountName||"",$=Oe(e);if(!m){let w=await Ct($,n,[g]);m=w.host,h=w.port,b=w.accountName||b}if(!m)throw Error(`CreateTairKVCacheInferInstance 返回成功但未获取连接地址 (${g})`);let p=Xe(b||void 0,f,m,h);return{instanceId:g,host:m,port:h,accountName:b||void 0,password:f,redisUrl:p}}async function zo(e,n){let t=[],o=30;for(let s=1;s<=50;s+=1){let r=await e.describeTairKVCacheInferInstances(new ke.DescribeTairKVCacheInferInstancesRequest({regionId:n,pageNumber:s,pageSize:30})),c=r.body?.instances?.tairInferInstanceDTO||[];t.push(...c);let f=r.body?.totalCount||0;if(c.length===0||t.length>=f)break}return t}function Ul(e,n,t){let o=t?.trim();if(o)return o;let s=e.filter((f)=>{if(!(f.instanceId||"").startsWith("tk-"))return!1;let l=f.instanceStatus||"";if(l&&Nr(l))return!1;return!0}),r=s.filter((f)=>{if(f.vpcId&&f.vpcId!==n.vpcId)return!1;if(f.vSwitchId&&f.vSwitchId!==n.vswId)return!1;if(n.zoneId&&f.zoneId&&f.zoneId!==n.zoneId)return!1;return!0}),c=r.length>0?r:s;if(c.length===1)return c[0].instanceId||"";if(c.length>1){let f=c.map((u)=>u.instanceId).filter(Boolean).join(", ");throw Error(`发现多个可用 vkName (${f}),请使用 --vk-name 显式指定`)}return""}async function vl(e,n){try{return(await e.describeTairKVCacheInferInstanceAttribute(new ke.DescribeTairKVCacheInferInstanceAttributeRequest({instanceId:n}))).body?.instances?.DBInstanceAttribute?.[0]}catch(t){if(Sl(t))return null;throw t}}async function Ct(e,n,t,o={}){let s=Jo(t);if(s.length===0)throw Error("未找到可查询的 Tair KVCache 实例 ID");let r=o.waitTimeoutMs||Rl,c=Date.now(),f="Creating",u=0;while(!0){if(Date.now()-c>r)throw Error(`Tair Serverless KV 初始化超时,最后状态: ${f}`);let l=!1;for(let i of s){let g=await vl(e,i);if(!g)continue;l=!0;let y=g.instanceStatus||"Creating";if(f=`${i}:${y}`,Nr(y))throw Error(`Tair KVCache 创建失败,实例状态: ${y} (${i})`);let d=Nn(g.connectionString);if(y==="Normal"&&d?.host)return{...d,sourceInstanceId:i}}if(!l){if(!u)u=Date.now();if(Date.now()-u>=Fl)throw Error(`未查询到可用实例信息,请检查实例 ID 是否正确: ${s.join(", ")}`)}else u=0;n.message(`☕ Tair Serverless KV 初始化中,请稍候... [${f}]`),await ge(Il)}}async function Nt(e,n,t,o){let s=Jo(n);for(let r of s)try{let c=await Jt(e,r,o);if(!c)continue;return await e.resetAccountPassword(new ke.ResetAccountPasswordRequest({instanceId:r,accountName:c,accountPassword:t})),{instanceId:r,accountName:c}}catch{continue}return null}async function zt(e,n,t){let o=Jo(n);for(let s of o)try{return await e.resetTairKVCacheCustomInstancePassword(new ke.ResetTairKVCacheCustomInstancePasswordRequest({instanceId:s,password:t})),{instanceId:s}}catch{continue}return null}async function Et(e,n,t,o){try{await e.modifySecurityIps(new ke.ModifySecurityIpsRequest({instanceId:n,securityIpGroupName:"default",modifyMode:"Append",securityIps:t}))}catch(s){if(J(s)||ql(s)){o.message(`⚠️ 当前实例未应用白名单配置 (${R(s)})`);return}throw s}}async function eo(e,n,t){return((await e.describeInstances(new ke.DescribeInstancesRequest({regionId:n.region,instanceIds:t,pageNumber:1,pageSize:30}))).body?.instances?.KVStoreInstance||[]).find((r)=>r.instanceId===t)||null}async function ec(e,n){return vl(e,n)}function Gl(e){let n=Zo(e),t=(()=>{if(typeof e!=="object"||e===null)return"";if("data"in e&&typeof e.data==="object"&&e.data!==null){let s=e.data?.RequestId;if(s)return String(s)}return""})(),o=t?`, requestId=${t}`:"";return`⚠️ 直连 API 创建失败 [${n||"Unknown"}] ${R(e)}${o}`}async function Zp(e,n,t,o,s,r){let c=s.instanceId?.trim()||"";e.message(`\uD83D\uDD17 正在绑定已有 Redis 实例 (${c})...`);let u=(await n.describeInstances(new on.DescribeInstancesRequest({regionId:t.region,instanceIds:c,pageNumber:1,pageSize:30}))).body?.instances?.KVStoreInstance?.find((h)=>h.instanceId===c),l=u?.connectionDomain||o.cache?.host,i=u?.port||o.cache?.port||6379;if(!l)throw Error(`未查询到 Redis 连接地址,请确认实例 ${c} 可用`);let g=s.accountName?.trim()||o.cache?.accountName||"";if(!g)g=await Jt(n,c,o.cache?.accountName);let y=s.existingPassword?.trim()||"";if(!y){if(!g)throw Error("未查询到 Redis 账号,请使用 --username 指定实例账号,或使用 --password 直接绑定");let h=xe();await n.resetAccountPassword(new on.ResetAccountPasswordRequest({instanceId:c,accountName:g,accountPassword:h})),y=h}let d=s.securityIpList?.trim()||r.cidrBlock||"10.0.0.0/8";e.message("\uD83D\uDD10 正在配置 Redis 内网白名单..."),await Et(n,c,d,e);let m=Xe(g||void 0,y,l,i);return o.envs={...o.envs,REDIS_URL:m,REDIS_HOST:l,REDIS_PORT:String(i),REDIS_PASSWORD:y,REDIS_USERNAME:g},o.network=Tt(o.network,r),o.cache={type:"redis",instanceId:c,host:l,port:i,accountName:g,mode:"classic-redis"},k.setProject(o),m}async function Jp(e,n,t,o,s){let r=o.instanceId?.trim()||"";e.message(`\uD83D\uDD17 正在绑定已有 Tair Serverless KV 实例 (${r})...`);let c=await Ct(n,e,[r,o.vkName,t.cache?.vkName],{waitTimeoutMs:xl});if(!c.host&&t.cache?.host)c={host:t.cache.host,port:t.cache.port||6379,url:`redis://${t.cache.host}:${t.cache.port||6379}`,sourceInstanceId:r};let f=o.accountName?.trim()||c.accountName||t.cache?.accountName||"",u=o.existingPassword?.trim()||c.password||"";if(!u){e.message("\uD83D\uDD10 未传入 --password,正在自动轮换实例密码...");let g=xe(),y=await Nt(n,[c.sourceInstanceId,o.vkName,r],g,f);if(y)f=y.accountName,u=g;else{if(!await zt(n,[c.sourceInstanceId,r,o.vkName],g))throw Error("未能自动重置已存在实例密码。请使用 --password 传入控制台已设置密码,或先执行 `licell cache rotate-password --instance <id>` 再重试");u=g}}let l=o.securityIpList?.trim()||s.cidrBlock||"10.0.0.0/8";e.message("\uD83D\uDD10 正在配置 Redis 内网白名单..."),await Et(n,c.sourceInstanceId,l,e);let i=Xe(f||void 0,u,c.host,c.port);return t.envs={...t.envs,REDIS_URL:i,REDIS_HOST:c.host,REDIS_PORT:String(c.port),REDIS_PASSWORD:u,REDIS_USERNAME:f},t.network=Tt(t.network,s),t.cache={type:"redis",instanceId:c.sourceInstanceId,host:c.host,port:c.port,accountName:f,vkName:o.vkName?.trim()||t.cache?.vkName||(c.sourceInstanceId.startsWith("tk-")?c.sourceInstanceId:void 0),mode:"tair-serverless-kv"},k.setProject(t),i}async function nc(e,n={}){let t=k.requireAuth(),o=k.getProject();if(n.engineVersion||n.nodeType||n.capacityMb)throw Error("Tair Serverless KV 不支持 --engine-version/--node-type/--capacity 参数");let s=n.zoneId?.trim(),r=n.vpcId?.trim(),c=n.vSwitchId?.trim(),f=await(r||c?(()=>{if(!r||!c)throw Error("自定义网络时需同时提供 --vpc 与 --vsw");if(!s)throw Error("自定义网络时需提供 --zone");return lt({vpcId:r,vswId:c,zoneId:s})})():it({preferredZoneIds:s?[s]:void 0})),u=Oe(t);await Al(u,t.region,e);let l=n.instanceId?.trim();if(l){if(Zt(l))return Jp(e,u,o,n,f);if(zn(l))return Zp(e,u,t,o,n,f);throw Error("--instance 仅支持 tt-/tk-(Tair)或 r-(经典 Redis)开头的实例 ID")}let i;try{e.message("⚡ 正在通过直连 API 创建 Tair Serverless KV...");let I=await Ll(t,e,f,n,o.appName);if(I){let fe=n.securityIpList?.trim()||f.cidrBlock||"10.0.0.0/8";return e.message("\uD83D\uDD10 正在配置 Redis 内网白名单..."),await Et(u,I.instanceId,fe,e),o.envs={...o.envs,REDIS_URL:I.redisUrl,REDIS_HOST:I.host,REDIS_PORT:String(I.port),REDIS_PASSWORD:I.password,REDIS_USERNAME:I.accountName||""},o.network=Tt(o.network,f),o.cache={type:"redis",instanceId:I.instanceId,host:I.host,port:I.port,accountName:I.accountName,mode:"tair-serverless-kv"},k.setProject(o),I.redisUrl}}catch(I){i=I,e.message(Gl(I))}e.message("\uD83D\uDD0E 正在查询可用的 Tair Serverless KV 虚拟集群...");let g=await zo(u,t.region),y=Ul(g,f,n.vkName);if(!y)return e.message("⚠️ Tair Serverless KV 不可用,正在兜底创建云原生 Redis 社区版..."),nw(e,u,t,o,f,n);let d=n.instanceClass?.trim()||No,m=n.computeUnitNum||Hl;if(!Number.isInteger(m)||m<=0)throw Error("--compute-unit 必须是正整数");if(m!==1)throw Error("当前阿里云 CreateTairKVCacheVNode 仅支持 --compute-unit 1");let h=`${o.appName||"licell-app"}-redis`;e.message(`⚡ 正在创建 Tair Serverless KV: class=${d}, cu=${m}, vk=${y}`);let b=await u.createTairKVCacheVNode(new on.CreateTairKVCacheVNodeRequest({regionId:t.region,instanceName:h,instanceClass:d,computeUnitNum:m,zoneId:f.zoneId||s,vSwitchId:f.vswId,vkName:y,clientToken:Ml()})),$=b.body?.instanceId,p=b.body?.vkName||y;if(!$)throw Error("Tair Serverless KV 创建失败:未返回 instanceId");let w=await Ct(u,e,[p,$]),H=w.host,T=w.port,x=w.accountName||o.cache?.accountName||"",E=w.password||"",_=w.url;if(!E){e.message("\uD83D\uDD10 正在设置 Redis 密码...");let I=xe(),fe=await Nt(u,[w.sourceInstanceId,p,$],I,x);if(fe)x=fe.accountName,E=I,_=Xe(x,E,H,T);else{if(!await zt(u,[w.sourceInstanceId,$,p],I))throw Error("未能自动设置 Tair Serverless KV 密码,请在控制台手动设置后重试");E=I,_=Xe(x||void 0,E,H,T)}}let F=n.securityIpList?.trim()||f.cidrBlock||"10.0.0.0/8";return e.message("\uD83D\uDD10 正在配置 Redis 内网白名单..."),await Et(u,w.sourceInstanceId,F,e),o.envs={...o.envs,REDIS_URL:_,REDIS_HOST:H,REDIS_PORT:String(T),REDIS_PASSWORD:E,REDIS_USERNAME:x},o.network=Tt(o.network,f),o.cache={type:"redis",instanceId:w.sourceInstanceId,host:H,port:T,accountName:x,vkName:p,mode:"tair-serverless-kv"},k.setProject(o),_}var Np=600000,zp=5000,ew="redis.master.small.default";async function nw(e,n,t,o,s,r){let c=`${o.appName||"licell-app"}-redis`,f=xe(),u=r.instanceClass?.trim()||ew;e.message(`\uD83D\uDCE6 正在创建云原生 Redis 社区版 (${u}, 按量付费)...`);let l=await n.createInstance(new on.CreateInstanceRequest({regionId:t.region,instanceType:"Redis",engineVersion:"5.0",instanceClass:u,instanceName:c,chargeType:"PostPaid",nodeType:"double",networkType:"VPC",vpcId:s.vpcId,vSwitchId:s.vswId,zoneId:s.zoneId,password:f,token:Ml()})),i=l.body?.instanceId;if(!i)throw Error("Redis 创建失败:未返回 instanceId");let g=l.body?.connectionDomain||"",y=l.body?.port||6379,d=Date.now();while(!0){if(Date.now()-d>Np)throw Error("Redis 实例创建超时");await ge(zp);let h=(await n.describeInstanceAttribute(new on.DescribeInstanceAttributeRequest({instanceId:i}))).body?.instances?.DBInstanceAttribute?.[0],b=h?.instanceStatus||"Creating";if(b==="Normal"){let $=h?.connectionDomain||g,p=h?.port||y;if(!$)throw Error("Redis 实例已就绪但未获取到连接地址");let w=r.securityIpList?.trim()||s.cidrBlock||"10.0.0.0/8";e.message("\uD83D\uDD10 正在配置 Redis 内网白名单..."),await Et(n,i,w,e);let H=Xe(void 0,f,$,p);return o.envs={...o.envs,REDIS_URL:H,REDIS_HOST:$,REDIS_PORT:String(p),REDIS_PASSWORD:f,REDIS_USERNAME:""},o.network=Tt(o.network,s),o.cache={type:"redis",instanceId:i,host:$,port:p,accountName:void 0,mode:"classic-redis"},k.setProject(o),H}e.message(`☕ Redis 实例初始化中,请稍候... [${b}]`)}}K();ft();import*as Bl from"@alicloud/r-kvstore20150101";async function tc(e,n){let t=k.requireAuth(),o=k.getProject(),s=Oe(t),r=n||o.cache?.instanceId;if(!r)throw Error("未找到 Redis 实例 ID,请先执行 licell cache add");if(zn(r)){e.message("\uD83D\uDD0E 正在获取 Redis 账号...");let g=await Jt(s,r,o.cache?.accountName);if(!g)throw Error("未找到可用 Redis 账号,无法轮换密码");let y=xe();e.message("\uD83D\uDD10 正在轮换 Redis 密码..."),await s.resetAccountPassword(new Bl.ResetAccountPasswordRequest({instanceId:r,accountName:g,accountPassword:y}));let d=await eo(s,t,r),m=d?.connectionDomain||o.cache?.host,h=d?.port||o.cache?.port||6379;if(!m)throw Error("未查询到 Redis 连接地址");let b=Xe(g,y,m,h);return o.envs={...o.envs,REDIS_URL:b,REDIS_HOST:m,REDIS_PORT:String(h),REDIS_PASSWORD:y,REDIS_USERNAME:g},o.cache={...o.cache||{type:"redis",instanceId:r},type:"redis",instanceId:r,host:m,port:h,accountName:g},k.setProject(o),b}e.message("\uD83D\uDD0E 正在解析 Tair Serverless KV 连接地址...");let c=await Ct(s,e,[r,o.cache?.vkName]);if(!c.host&&o.cache?.host)c={host:o.cache.host,port:o.cache.port||6379,url:`redis://${o.cache.host}:${o.cache.port||6379}`,sourceInstanceId:r};let f=xe();e.message("\uD83D\uDD10 正在轮换 Tair Serverless KV 密码...");let u=await Nt(s,[c.sourceInstanceId,o.cache?.vkName,r],f,o.cache?.accountName),l=u?.accountName||o.cache?.accountName||"";if(!u){if(!await zt(s,[c.sourceInstanceId,r,o.cache?.vkName],f))throw Error("轮换密码失败:当前实例不支持自动密码重置")}let i=Xe(l||void 0,f,c.host,c.port);return o.envs={...o.envs,REDIS_URL:i,REDIS_HOST:c.host,REDIS_PORT:String(c.port),REDIS_PASSWORD:f,REDIS_USERNAME:l},o.cache={...o.cache||{type:"redis",instanceId:r},type:"redis",instanceId:c.sourceInstanceId,host:c.host,port:c.port,accountName:l,vkName:o.cache?.vkName,mode:"tair-serverless-kv"},k.setProject(o),i}K();import*as et from"@alicloud/r-kvstore20150101";async function Yl(e,n){try{let s=((await e.describeDBInstanceNetInfo(new et.DescribeDBInstanceNetInfoRequest({instanceId:n}))).body?.netInfoItems?.instanceNetInfo||[]).find((r)=>r.IPType==="Public");if(s?.connectionString)return{host:s.connectionString,port:Number(s.port)||6379}}catch{}return null}async function oc(e=200){let n=k.requireAuth(),t=Oe(n),o=[],s=Math.max(1,Math.min(Math.floor(e),500));for(let r=1;r<=20&&o.length<s;r+=1){let c=await t.describeInstances(new et.DescribeInstancesRequest({regionId:n.region,pageNumber:r,pageSize:50})),f=c.body?.instances?.KVStoreInstance||[];for(let l of f){let i=zr(l);if(!i.instanceId)continue;if(o.push(i),o.length>=s)break}let u=c.body?.totalCount||0;if(f.length===0||u>0&&o.length>=u)break}if(o.length<s){let r=await zo(t,n.region);for(let c of r){let f=Pl(c);if(!f.instanceId)continue;if(o.some((u)=>u.instanceId===f.instanceId))continue;if(o.push(f),o.length>=s)break}}return o.slice(0,s)}async function sc(e){let n=e.trim();if(!n)throw Error("instanceId 不能为空");let t=k.requireAuth(),o=Oe(t);if(zn(n)){let u=await eo(o,t,n);if(!u?.instanceId)throw Error(`未找到 Redis 实例: ${n}`);let i=((await o.describeAccounts(new et.DescribeAccountsRequest({instanceId:n}))).body?.accounts?.account||[]).map((g)=>g.accountName).filter((g)=>typeof g==="string"&&g.length>0);return{summary:zr(u),accountNames:i}}if(!Zt(n))throw Error("cache info 仅支持 tt-/tk-/r- 开头的实例 ID");let s=await ec(o,n);if(!s?.instanceId)throw Error(`未找到 Tair 实例: ${n}`);let r=Nn(s.connectionString),f=((await o.describeAccounts(new et.DescribeAccountsRequest({instanceId:n}))).body?.accounts?.account||[]).map((u)=>u.accountName).filter((u)=>typeof u==="string"&&u.length>0);return{summary:{instanceId:s.instanceId,mode:"tair-serverless-kv",instanceName:s.instanceName,status:s.instanceStatus,instanceClass:s.instanceClass,host:r?.host,port:r?.port,zoneId:s.zoneId,vpcId:s.vpcId,vSwitchId:s.vSwitchId},accountNames:f}}async function es(e){let n=k.requireAuth(),t=Oe(n),o=k.getProject(),s=e?.trim()||o.cache?.instanceId||"";if(!s)throw Error("未指定缓存实例 ID,且当前项目未绑定缓存实例");let c=o.cache?.instanceId===s?Nn(o.envs?.REDIS_URL):null;if(zn(s)){let m=await eo(t,n,s);if(!m?.instanceId)throw Error(`未找到 Redis 实例: ${s}`);let h=m.connectionDomain||o.cache?.host||"",b=m.port||o.cache?.port||6379;if(!h)throw Error(`未获取到实例 ${s} 的连接地址`);let $=c?.accountName||o.cache?.accountName||"<username>",p=c?.password||"",w=p.length>0,H=Xt($==="<username>"?void 0:$,p,h,b,w),T=await Yl(t,s);return{instanceId:s,host:h,port:b,username:$==="<username>"?void 0:$,passwordKnown:w,connectionString:H,mode:"classic-redis",...T?{publicHost:T.host,publicPort:T.port,publicConnectionString:Xt($==="<username>"?void 0:$,p,T.host,T.port,w)}:{}}}if(!Zt(s))throw Error("cache connect 仅支持 tt-/tk-/r- 开头的实例 ID");let f=await ec(t,s),u=Nn(f?.connectionString)||(o.cache?.host?{host:o.cache.host,port:o.cache.port||6379,url:`redis://${o.cache.host}:${o.cache.port||6379}`,accountName:o.cache.accountName,password:void 0}:null);if(!u?.host)throw Error(`未获取到实例 ${s} 的连接地址`);let l=c?.accountName||u.accountName||o.cache?.accountName,i=c?.password||"",g=i.length>0,y=Xt(l,i,u.host,u.port,g),d=await Yl(t,s);return{instanceId:s,host:u.host,port:u.port,username:l,passwordKnown:g,connectionString:y,mode:"tair-serverless-kv",...d?{publicHost:d.host,publicPort:d.port,publicConnectionString:Xt(l,i,d.host,d.port,g)}:{}}}K();import*as nt from"@alicloud/r-kvstore20150101";ce();var tw="licell_public";async function rc(e,n){let t=k.requireAuth(),o=Oe(t),c=((await o.describeDBInstanceNetInfo(new nt.DescribeDBInstanceNetInfoRequest({instanceId:e}))).body?.netInfoItems?.instanceNetInfo||[]).find((g)=>g.IPType==="Public");if(c?.connectionString)return{host:c.connectionString,port:Number(c.port)||6379};let f=`${e}-pub`.toLowerCase().replace(/[^a-z0-9]/g,"").slice(0,40);try{await o.allocateInstancePublicConnection(new nt.AllocateInstancePublicConnectionRequest({instanceId:e,connectionStringPrefix:f,port:"6379"}))}catch(g){let y=R(g);if(y.includes("NetTypeExists")||y.includes("already exists"))n.message("公网地址已存在,正在获取...");else throw g}let i=((await o.describeDBInstanceNetInfo(new nt.DescribeDBInstanceNetInfoRequest({instanceId:e}))).body?.netInfoItems?.instanceNetInfo||[]).find((g)=>g.IPType==="Public");if(!i?.connectionString)return null;return{host:i.connectionString,port:Number(i.port)||6379}}async function cc(e,n,t){let o=k.requireAuth(),s=Oe(o),r=`${n}/32`;try{await s.modifySecurityIps(new nt.ModifySecurityIpsRequest({instanceId:e,securityIpGroupName:tw,securityIps:r,modifyMode:"Cover"}))}catch(c){t.message(`⚠️ 公网白名单设置失败: ${R(c)}`)}}oe();re();function Ol(e){e.command("cache add","分配 Redis 缓存").option("--type <type>","缓存类型:redis(CI 场景建议显式传入)").option("--instance <instanceId>","绑定已有实例 ID(tt-/tk-/r-),传入后跳过创建").option("--password <password>","绑定已有实例时的访问密码(不传则尝试自动轮换)").option("--username <accountName>","绑定已有实例时指定账号名(可选)").option("--engine-version <version>","旧版 Redis 参数(Tair Serverless KV 模式下不支持)").option("--class <instanceClass>","Tair Serverless KV 规格(如 kvcache.cu.g4b.2)").option("--node-type <type>","旧版 Redis 参数(Tair Serverless KV 模式下不支持)").option("--capacity <mb>","旧版 Redis 参数(Tair Serverless KV 模式下不支持)").option("--vk-name <vkName>","Tair KV 回退模式使用的 vkName(tk- 开头,不传则自动探测)").option("--compute-unit <n>","Tair Serverless KV 计算单元(当前仅支持 1)").option("--zone <zoneId>","可用区(如 cn-hangzhou-b)").option("--vpc <vpcId>","指定 VPC ID").option("--vsw <vSwitchId>","指定 VSwitch ID").option("--security-ip-list <cidrs>","白名单 CIDR(逗号分隔)").action(async(n)=>{await M({commandLabel:"licell cache add",interactiveTTY:P(),requiredCapabilities:["redis","vpc"]},async()=>{j(v.bgGreen(v.black(" \uD83E\uDDE0 Cache Provisioning (Redis) "))),B();let t=P(),o=C(n.type)?.toLowerCase();if(!o){if(!t)throw Error("非交互模式下请传入 --type redis");let u=await ow({message:"选择缓存引擎:",options:[{value:"redis",label:"\uD83D\uDFE5 Tair/Redis (VPC 内网)"}]});if(sw(u))process.exit(0);o=A(u,"缓存类型").toLowerCase()}if(o!=="redis")throw Error("--type 目前仅支持 redis");let s=qe(n.capacity,"capacity"),r=qe(n.computeUnit,"compute-unit"),c=L(),f=await U(c,"正在初始化缓存资源编排...","❌ 缓存拉起失败",()=>nc(c,{instanceId:C(n.instance),existingPassword:C(n.password),accountName:C(n.username),engineVersion:C(n.engineVersion),instanceClass:C(n.class),nodeType:C(n.nodeType),capacityMb:s,vkName:C(n.vkName),computeUnitNum:r,zoneId:C(n.zone),vpcId:C(n.vpc),vSwitchId:C(n.vsw),securityIpList:C(n.securityIpList)}));if(!f)return;if(!a())c.stop(v.green("✅ Redis 缓存已就绪并绑定到本工程内网!"));if(a()){S({stage:"cache.add",type:o,connectionStringMasked:Pn(f)});return}console.log(`
1597
- \uD83D\uDD11 缓存连接串已生成: ${v.cyan(Pn(f))}
1598
- `),q("下次执行 licell deploy 时,将自动作为 process.env.REDIS_URL 注入!")})}),e.command("cache list","查看缓存实例列表").option("--limit <n>","返回数量,默认 20").action(async(n)=>{await M({commandLabel:"licell cache list",interactiveTTY:P(),requiredCapabilities:["redis"]},async()=>{B();let t=Ye(n.limit,20,200),o=L(),s=await U(o,"正在拉取缓存实例列表...","❌ 获取缓存实例列表失败",()=>oc(t));if(!s)return;if(!a())o.stop(v.green(`✅ 共获取 ${s.length} 个实例`));if(a()){S({stage:"cache.list",count:s.length,instances:s});return}if(s.length===0){q("当前地域没有缓存实例");return}for(let r of s)console.log(`${v.cyan(r.instanceId)} mode=${v.gray(r.mode)} status=${v.gray(r.status||"-")} class=${v.gray(r.instanceClass||"-")}`);console.log(""),q("Done.")})}),e.command("cache info <instanceId>","查看缓存实例详情").action(async(n)=>{await M({commandLabel:"licell cache info",interactiveTTY:P(),requiredCapabilities:["redis"]},async()=>{B();let t=A(n,"instanceId"),o=L(),s=await U(o,`正在拉取实例 ${t} 详情...`,"❌ 获取缓存实例详情失败",()=>sc(t));if(!s)return;let r=s.summary;if(!a())o.stop(v.green("✅ 获取成功"));else{S({stage:"cache.info",instanceId:t,detail:s});return}if(console.log(`
1599
- instanceId: ${v.cyan(r.instanceId)}`),console.log(`mode: ${v.cyan(r.mode)}`),console.log(`status: ${v.cyan(r.status||"-")}`),console.log(`class: ${v.cyan(r.instanceClass||"-")}`),r.engineVersion)console.log(`engine: ${v.cyan(r.engineVersion)}`);if(r.host)console.log(`endpoint: ${v.cyan(`${r.host}:${r.port||6379}`)}`);if(console.log(`network: ${v.cyan(`${r.vpcId||"-"} / ${r.vSwitchId||"-"} / ${r.zoneId||"-"}`)}`),s.accountNames.length>0)console.log(`accounts: ${v.cyan(s.accountNames.join(", "))}`);console.log(""),q("Done.")})}),e.command("cache connect [instanceId]","输出缓存连接信息").action(async(n)=>{await M({commandLabel:"licell cache connect",interactiveTTY:P(),requiredCapabilities:["redis"]},async()=>{B();let t=C(n),o=L(),s=await U(o,"正在解析缓存连接信息...","❌ 连接信息解析失败",()=>es(t));if(!s)return;if(!a())o.stop(v.green("✅ 连接信息已生成"));else{S({stage:"cache.connect",instanceId:s.instanceId,connection:s});return}if(console.log(`
1600
- instanceId: ${v.cyan(s.instanceId)}`),console.log(`mode: ${v.cyan(s.mode)}`),console.log(`host: ${v.cyan(s.host)}`),console.log(`port: ${v.cyan(String(s.port))}`),console.log(`username: ${v.cyan(s.username||"<none>")}`),console.log(`password: ${v.cyan(s.passwordKnown?"<known in project>":"<unknown, please provide manually>")}`),console.log(`url: ${v.cyan(s.connectionString)}`),s.publicHost)console.log(""),console.log(v.yellow("── 公网访问 ──")),console.log(`public host: ${v.cyan(s.publicHost)}`),console.log(`public port: ${v.cyan(String(s.publicPort))}`),console.log(`public url: ${v.cyan(s.publicConnectionString)}`);console.log(""),q("Done.")})}),e.command("cache rotate-password","轮换 Redis 密码").option("--instance <instanceId>","指定 Redis 实例 ID,不传则使用当前项目绑定实例").action(async(n)=>{await M({commandLabel:"licell cache rotate-password",interactiveTTY:P(),requiredCapabilities:["redis"]},async()=>{j(v.bgGreen(v.black(" \uD83D\uDD10 Rotate Redis Password "))),B();let t=n.instance?A(n.instance,"实例 ID"):void 0,o=L(),s=await U(o,"正在执行 Redis 密钥轮换...","❌ Redis 密钥轮换失败",()=>tc(o,t));if(!s)return;if(!a())o.stop(v.green("✅ Redis 密钥轮换完成"));if(a()){S({stage:"cache.rotate-password",instanceId:t||null,connectionStringMasked:Pn(s)});return}console.log(`
1601
- \uD83D\uDD11 新连接串: ${v.cyan(Pn(s))}
1602
- `),q("已同步更新 .licell/project.json 的 REDIS_* 环境变量")})}),e.command("cache public-access [instanceId]","开通 Redis 公网访问并添加当前 IP 到白名单").option("--ip <ip>","手动指定公网 IP(不传则自动获取)").action(async(n,t)=>{await M({commandLabel:"licell cache public-access",interactiveTTY:P(),requiredCapabilities:["redis"]},async()=>{let{resolvePublicIp:o}=await Promise.resolve().then(() => (Jr(),Zr));j(v.bgGreen(v.black(" \uD83C\uDF10 Cache Public Access "))),B();let s=C(n),r=L();r.start("正在获取公网 IP...");let c=t.ip?.trim()||await o();r.stop(`公网 IP: ${v.cyan(c)}`);let f=await U(r,"正在解析缓存连接信息...","❌ 连接信息解析失败",()=>es(s));if(!f)return;await U(r,`正在将 ${c}/32 添加到白名单 (licell_public)...`,"❌ 白名单设置失败",()=>cc(f.instanceId,c,r));let u=await U(r,"正在开通公网访问...","❌ 公网访问开通失败",()=>rc(f.instanceId,r));if(!a())r.stop(v.green("✅ 公网访问已开通"));else{S({stage:"cache.public-access",instanceId:f.instanceId,publicIp:c,publicHost:u?.host||null,publicPort:u?.port||null});return}if(console.log(""),console.log(v.yellow("── 内网访问 ──")),console.log(`host: ${v.cyan(f.host)}`),console.log(`port: ${v.cyan(String(f.port))}`),console.log(`url: ${v.cyan(f.connectionString)}`),u){console.log(""),console.log(v.yellow("── 公网访问 ──")),console.log(`host: ${v.cyan(u.host)}`),console.log(`port: ${v.cyan(String(u.port))}`);let l=f.passwordKnown?"<password>":"<password>",i=f.username?`${f.username}:${l}@`:"";console.log(`url: ${v.cyan(`redis://${i}${u.host}:${u.port}`)}`)}else console.log(v.yellow(`
1596
+ 白名单 IP: ${U.cyan(`${c}/32`)} (分组: licell_public)`),q("Done.")})})}import{select as sw,isCancel as ow}from"@clack/prompts";import G from"picocolors";Ae();K();ft();De();Ot();import*as sn from"@alicloud/r-kvstore20150101";import{randomUUID as Ml}from"crypto";Ie();import Kp from"@alicloud/r-kvstore20150101";import*as wn from"@alicloud/openapi-client";import*as Cl from"@alicloud/tea-util";var jp=te(Kp,"@alicloud/r-kvstore20150101"),Wp=te(wn.default,"@alicloud/openapi-client");function Oe(e){return new jp(new wn.Config({accessKeyId:e.ak,accessKeySecret:e.sk,regionId:e.region,endpoint:"r-kvstore.aliyuncs.com",connectTimeout:30000,readTimeout:60000}))}function Qp(e){return new Wp(new wn.Config({accessKeyId:e.ak,accessKeySecret:e.sk,regionId:e.region,endpoint:"r-kvstore.aliyuncs.com"}))}function Dp(e){if(e===null||e===void 0)return;if(typeof e==="boolean")return e?"true":"false";return String(e)}function Vp(e){let n={};for(let[t,s]of Object.entries(e)){let o=Dp(s);if(o===void 0)continue;n[t]=o}return n}async function El(e,n,t){let s=Qp(e),o=new wn.Params({action:n,version:"2015-01-01",protocol:"HTTPS",pathname:"/",method:"POST",authType:"AK",style:"RPC",reqBodyType:"formData",bodyType:"json"}),r=new wn.OpenApiRequest({query:Vp(t)});return s.callApi(o,r,new Cl.RuntimeOptions({readTimeout:20000,connectTimeout:8000}))}ft();ce();De();import*as ke from"@alicloud/r-kvstore20150101";import{randomUUID as Xp}from"crypto";ce();function Nr(e){return["Error","Released","Inactive","Unavailable","Flushing"].includes(e)}function Xe(e,n,t,s){if(!e)return`redis://:${encodeURIComponent(n)}@${t}:${s}`;return`redis://${encodeURIComponent(e)}:${encodeURIComponent(n)}@${t}:${s}`}function Xt(e,n,t,s,o){let r=o?encodeURIComponent(n):"<password>";if(!e)return`redis://:${r}@${t}:${s}`;return`redis://${e==="<username>"?e:encodeURIComponent(e)}:${r}@${t}:${s}`}function Xs(e){if(typeof e!=="object"||e===null)return"";let n="code"in e?String(e.code||""):"";if(n)return n;if("data"in e&&typeof e.data==="object"&&e.data!==null){let t=e.data?.Code;if(t)return String(t)}return""}function Sl(e){return/InvalidInstanceId\.NotFound/i.test(Xs(e)||R(e))}function ql(e){let n=`${Xs(e)} ${R(e)}`;return/NotSupport|Unsupported|InvalidInstanceId|InvalidParameter|OperationDenied|AccessDenied/i.test(n)}function Zs(e){let n=new Set;for(let t of e){if(!t)continue;let s=t.trim();if(s)n.add(s)}return[...n]}function Nn(e){let n=(e||"").trim();if(!n)return null;let t=n.split(",")[0]?.trim();if(!t)return null;let s=/^[a-z][a-z0-9+.-]*:\/\//i.test(t)?t:`redis://${t}`;try{let o=new URL(s),r=o.hostname;if(!r)return null;let c=o.port?Number(o.port):6379;if(!Number.isFinite(c)||c<=0)return null;let f=o.username?decodeURIComponent(o.username):void 0,u=o.password?decodeURIComponent(o.password):void 0,l=o.toString();return{host:r,port:c,accountName:f,password:u,url:l}}catch{return null}}function zn(e){return e.startsWith("r-")}function Zt(e){return e.startsWith("tk-")||e.startsWith("tt-")}function Tt(e,n){return{vpcId:n.vpcId,vswId:n.vswId,sgId:n.sgId??e?.sgId,cidrBlock:n.cidrBlock??e?.cidrBlock}}function zr(e){return{instanceId:e.instanceId||"",mode:"classic-redis",instanceName:e.instanceName,status:e.instanceStatus,instanceClass:e.instanceClass,engineVersion:e.engineVersion,host:e.connectionDomain,port:e.port,zoneId:e.zoneId,vpcId:e.vpcId,vSwitchId:e.vSwitchId}}function Pl(e){return{instanceId:e.instanceId||"",mode:"tair-serverless-kv",instanceName:e.instanceName,status:e.instanceStatus,zoneId:e.zoneId,vpcId:e.vpcId,vSwitchId:e.vSwitchId}}var Rl=1200000,Il=5000,Fl=60000,xl=180000,Js="kvcache.cu.g4b.2",Hl=1;async function Jt(e,n,t){let o=(await e.describeAccounts(new ke.DescribeAccountsRequest({instanceId:n}))).body?.accounts?.account||[],r=o.filter((f)=>f.accountStatus!=="Unavailable");return(r.find((f)=>f.accountType==="Normal")||r[0]||o[0])?.accountName||t||""}async function Ll(e,n,t){try{if((await e.describeServiceLinkedRoleExists(new ke.DescribeServiceLinkedRoleExistsRequest({}))).body?.existsServiceLinkedRole)return;t.message("\uD83D\uDD10 正在初始化 Kvstore 服务关联角色..."),await e.initializeKvstorePermission(new ke.InitializeKvstorePermissionRequest({regionId:n}))}catch(s){t.message(`⚠️ 服务关联角色检查失败 (${R(s)}),将继续尝试创建缓存`)}}async function Al(e,n,t,s,o){let r=s.instanceClass?.trim()||Js,c=`${o||"licell-app"}-redis`,f=xe(),u=s.securityIpList?.trim()||t.cidrBlock||"10.0.0.0/8",i=(await El(e,"CreateTairKVCacheInferInstance",{RegionId:e.region,InstanceName:c,InstanceClass:r,ZoneId:t.zoneId,VpcId:t.vpcId,VSwitchId:t.vswId,ChargeType:"PostPaid",AutoPay:!0,Password:f,SecurityIPList:u,ClientToken:Xp()})).body||{},g=typeof i.InstanceId==="string"?i.InstanceId:typeof i.instanceId==="string"?i.instanceId:"";if(!g)return null;let y=typeof i.ConnectionString==="string"?i.ConnectionString:typeof i.connectionString==="string"?i.connectionString:"",d=Nn(y),m=d?.host||"",h=d?.port||6379,b=d?.accountName||"",$=Oe(e);if(!m){let w=await Ct($,n,[g]);m=w.host,h=w.port,b=w.accountName||b}if(!m)throw Error(`CreateTairKVCacheInferInstance 返回成功但未获取连接地址 (${g})`);let p=Xe(b||void 0,f,m,h);return{instanceId:g,host:m,port:h,accountName:b||void 0,password:f,redisUrl:p}}async function Ns(e,n){let t=[],s=30;for(let o=1;o<=50;o+=1){let r=await e.describeTairKVCacheInferInstances(new ke.DescribeTairKVCacheInferInstancesRequest({regionId:n,pageNumber:o,pageSize:30})),c=r.body?.instances?.tairInferInstanceDTO||[];t.push(...c);let f=r.body?.totalCount||0;if(c.length===0||t.length>=f)break}return t}function Ul(e,n,t){let s=t?.trim();if(s)return s;let o=e.filter((f)=>{if(!(f.instanceId||"").startsWith("tk-"))return!1;let l=f.instanceStatus||"";if(l&&Nr(l))return!1;return!0}),r=o.filter((f)=>{if(f.vpcId&&f.vpcId!==n.vpcId)return!1;if(f.vSwitchId&&f.vSwitchId!==n.vswId)return!1;if(n.zoneId&&f.zoneId&&f.zoneId!==n.zoneId)return!1;return!0}),c=r.length>0?r:o;if(c.length===1)return c[0].instanceId||"";if(c.length>1){let f=c.map((u)=>u.instanceId).filter(Boolean).join(", ");throw Error(`发现多个可用 vkName (${f}),请使用 --vk-name 显式指定`)}return""}async function vl(e,n){try{return(await e.describeTairKVCacheInferInstanceAttribute(new ke.DescribeTairKVCacheInferInstanceAttributeRequest({instanceId:n}))).body?.instances?.DBInstanceAttribute?.[0]}catch(t){if(Sl(t))return null;throw t}}async function Ct(e,n,t,s={}){let o=Zs(t);if(o.length===0)throw Error("未找到可查询的 Tair KVCache 实例 ID");let r=s.waitTimeoutMs||Rl,c=Date.now(),f="Creating",u=0;while(!0){if(Date.now()-c>r)throw Error(`Tair Serverless KV 初始化超时,最后状态: ${f}`);let l=!1;for(let i of o){let g=await vl(e,i);if(!g)continue;l=!0;let y=g.instanceStatus||"Creating";if(f=`${i}:${y}`,Nr(y))throw Error(`Tair KVCache 创建失败,实例状态: ${y} (${i})`);let d=Nn(g.connectionString);if(y==="Normal"&&d?.host)return{...d,sourceInstanceId:i}}if(!l){if(!u)u=Date.now();if(Date.now()-u>=Fl)throw Error(`未查询到可用实例信息,请检查实例 ID 是否正确: ${o.join(", ")}`)}else u=0;n.message(`☕ Tair Serverless KV 初始化中,请稍候... [${f}]`),await ge(Il)}}async function Nt(e,n,t,s){let o=Zs(n);for(let r of o)try{let c=await Jt(e,r,s);if(!c)continue;return await e.resetAccountPassword(new ke.ResetAccountPasswordRequest({instanceId:r,accountName:c,accountPassword:t})),{instanceId:r,accountName:c}}catch{continue}return null}async function zt(e,n,t){let s=Zs(n);for(let o of s)try{return await e.resetTairKVCacheCustomInstancePassword(new ke.ResetTairKVCacheCustomInstancePasswordRequest({instanceId:o,password:t})),{instanceId:o}}catch{continue}return null}async function Et(e,n,t,s){try{await e.modifySecurityIps(new ke.ModifySecurityIpsRequest({instanceId:n,securityIpGroupName:"default",modifyMode:"Append",securityIps:t}))}catch(o){if(J(o)||ql(o)){s.message(`⚠️ 当前实例未应用白名单配置 (${R(o)})`);return}throw o}}async function es(e,n,t){return((await e.describeInstances(new ke.DescribeInstancesRequest({regionId:n.region,instanceIds:t,pageNumber:1,pageSize:30}))).body?.instances?.KVStoreInstance||[]).find((r)=>r.instanceId===t)||null}async function ec(e,n){return vl(e,n)}function Gl(e){let n=Xs(e),t=(()=>{if(typeof e!=="object"||e===null)return"";if("data"in e&&typeof e.data==="object"&&e.data!==null){let o=e.data?.RequestId;if(o)return String(o)}return""})(),s=t?`, requestId=${t}`:"";return`⚠️ 直连 API 创建失败 [${n||"Unknown"}] ${R(e)}${s}`}async function Zp(e,n,t,s,o,r){let c=o.instanceId?.trim()||"";e.message(`\uD83D\uDD17 正在绑定已有 Redis 实例 (${c})...`);let u=(await n.describeInstances(new sn.DescribeInstancesRequest({regionId:t.region,instanceIds:c,pageNumber:1,pageSize:30}))).body?.instances?.KVStoreInstance?.find((h)=>h.instanceId===c),l=u?.connectionDomain||s.cache?.host,i=u?.port||s.cache?.port||6379;if(!l)throw Error(`未查询到 Redis 连接地址,请确认实例 ${c} 可用`);let g=o.accountName?.trim()||s.cache?.accountName||"";if(!g)g=await Jt(n,c,s.cache?.accountName);let y=o.existingPassword?.trim()||"";if(!y){if(!g)throw Error("未查询到 Redis 账号,请使用 --username 指定实例账号,或使用 --password 直接绑定");let h=xe();await n.resetAccountPassword(new sn.ResetAccountPasswordRequest({instanceId:c,accountName:g,accountPassword:h})),y=h}let d=o.securityIpList?.trim()||r.cidrBlock||"10.0.0.0/8";e.message("\uD83D\uDD10 正在配置 Redis 内网白名单..."),await Et(n,c,d,e);let m=Xe(g||void 0,y,l,i);return s.envs={...s.envs,REDIS_URL:m,REDIS_HOST:l,REDIS_PORT:String(i),REDIS_PASSWORD:y,REDIS_USERNAME:g},s.network=Tt(s.network,r),s.cache={type:"redis",instanceId:c,host:l,port:i,accountName:g,mode:"classic-redis"},k.setProject(s),m}async function Jp(e,n,t,s,o){let r=s.instanceId?.trim()||"";e.message(`\uD83D\uDD17 正在绑定已有 Tair Serverless KV 实例 (${r})...`);let c=await Ct(n,e,[r,s.vkName,t.cache?.vkName],{waitTimeoutMs:xl});if(!c.host&&t.cache?.host)c={host:t.cache.host,port:t.cache.port||6379,url:`redis://${t.cache.host}:${t.cache.port||6379}`,sourceInstanceId:r};let f=s.accountName?.trim()||c.accountName||t.cache?.accountName||"",u=s.existingPassword?.trim()||c.password||"";if(!u){e.message("\uD83D\uDD10 未传入 --password,正在自动轮换实例密码...");let g=xe(),y=await Nt(n,[c.sourceInstanceId,s.vkName,r],g,f);if(y)f=y.accountName,u=g;else{if(!await zt(n,[c.sourceInstanceId,r,s.vkName],g))throw Error("未能自动重置已存在实例密码。请使用 --password 传入控制台已设置密码,或先执行 `licell cache rotate-password --instance <id>` 再重试");u=g}}let l=s.securityIpList?.trim()||o.cidrBlock||"10.0.0.0/8";e.message("\uD83D\uDD10 正在配置 Redis 内网白名单..."),await Et(n,c.sourceInstanceId,l,e);let i=Xe(f||void 0,u,c.host,c.port);return t.envs={...t.envs,REDIS_URL:i,REDIS_HOST:c.host,REDIS_PORT:String(c.port),REDIS_PASSWORD:u,REDIS_USERNAME:f},t.network=Tt(t.network,o),t.cache={type:"redis",instanceId:c.sourceInstanceId,host:c.host,port:c.port,accountName:f,vkName:s.vkName?.trim()||t.cache?.vkName||(c.sourceInstanceId.startsWith("tk-")?c.sourceInstanceId:void 0),mode:"tair-serverless-kv"},k.setProject(t),i}async function nc(e,n={}){let t=k.requireAuth(),s=k.getProject();if(n.engineVersion||n.nodeType||n.capacityMb)throw Error("Tair Serverless KV 不支持 --engine-version/--node-type/--capacity 参数");let o=n.zoneId?.trim(),r=n.vpcId?.trim(),c=n.vSwitchId?.trim(),f=await(r||c?(()=>{if(!r||!c)throw Error("自定义网络时需同时提供 --vpc 与 --vsw");if(!o)throw Error("自定义网络时需提供 --zone");return lt({vpcId:r,vswId:c,zoneId:o})})():it({preferredZoneIds:o?[o]:void 0})),u=Oe(t);await Ll(u,t.region,e);let l=n.instanceId?.trim();if(l){if(Zt(l))return Jp(e,u,s,n,f);if(zn(l))return Zp(e,u,t,s,n,f);throw Error("--instance 仅支持 tt-/tk-(Tair)或 r-(经典 Redis)开头的实例 ID")}let i;try{e.message("⚡ 正在通过直连 API 创建 Tair Serverless KV...");let I=await Al(t,e,f,n,s.appName);if(I){let fe=n.securityIpList?.trim()||f.cidrBlock||"10.0.0.0/8";return e.message("\uD83D\uDD10 正在配置 Redis 内网白名单..."),await Et(u,I.instanceId,fe,e),s.envs={...s.envs,REDIS_URL:I.redisUrl,REDIS_HOST:I.host,REDIS_PORT:String(I.port),REDIS_PASSWORD:I.password,REDIS_USERNAME:I.accountName||""},s.network=Tt(s.network,f),s.cache={type:"redis",instanceId:I.instanceId,host:I.host,port:I.port,accountName:I.accountName,mode:"tair-serverless-kv"},k.setProject(s),I.redisUrl}}catch(I){i=I,e.message(Gl(I))}e.message("\uD83D\uDD0E 正在查询可用的 Tair Serverless KV 虚拟集群...");let g=await Ns(u,t.region),y=Ul(g,f,n.vkName);if(!y)return e.message("⚠️ Tair Serverless KV 不可用,正在兜底创建云原生 Redis 社区版..."),nw(e,u,t,s,f,n);let d=n.instanceClass?.trim()||Js,m=n.computeUnitNum||Hl;if(!Number.isInteger(m)||m<=0)throw Error("--compute-unit 必须是正整数");if(m!==1)throw Error("当前阿里云 CreateTairKVCacheVNode 仅支持 --compute-unit 1");let h=`${s.appName||"licell-app"}-redis`;e.message(`⚡ 正在创建 Tair Serverless KV: class=${d}, cu=${m}, vk=${y}`);let b=await u.createTairKVCacheVNode(new sn.CreateTairKVCacheVNodeRequest({regionId:t.region,instanceName:h,instanceClass:d,computeUnitNum:m,zoneId:f.zoneId||o,vSwitchId:f.vswId,vkName:y,clientToken:Ml()})),$=b.body?.instanceId,p=b.body?.vkName||y;if(!$)throw Error("Tair Serverless KV 创建失败:未返回 instanceId");let w=await Ct(u,e,[p,$]),H=w.host,T=w.port,x=w.accountName||s.cache?.accountName||"",E=w.password||"",_=w.url;if(!E){e.message("\uD83D\uDD10 正在设置 Redis 密码...");let I=xe(),fe=await Nt(u,[w.sourceInstanceId,p,$],I,x);if(fe)x=fe.accountName,E=I,_=Xe(x,E,H,T);else{if(!await zt(u,[w.sourceInstanceId,$,p],I))throw Error("未能自动设置 Tair Serverless KV 密码,请在控制台手动设置后重试");E=I,_=Xe(x||void 0,E,H,T)}}let F=n.securityIpList?.trim()||f.cidrBlock||"10.0.0.0/8";return e.message("\uD83D\uDD10 正在配置 Redis 内网白名单..."),await Et(u,w.sourceInstanceId,F,e),s.envs={...s.envs,REDIS_URL:_,REDIS_HOST:H,REDIS_PORT:String(T),REDIS_PASSWORD:E,REDIS_USERNAME:x},s.network=Tt(s.network,f),s.cache={type:"redis",instanceId:w.sourceInstanceId,host:H,port:T,accountName:x,vkName:p,mode:"tair-serverless-kv"},k.setProject(s),_}var Np=600000,zp=5000,ew="redis.master.small.default";async function nw(e,n,t,s,o,r){let c=`${s.appName||"licell-app"}-redis`,f=xe(),u=r.instanceClass?.trim()||ew;e.message(`\uD83D\uDCE6 正在创建云原生 Redis 社区版 (${u}, 按量付费)...`);let l=await n.createInstance(new sn.CreateInstanceRequest({regionId:t.region,instanceType:"Redis",engineVersion:"5.0",instanceClass:u,instanceName:c,chargeType:"PostPaid",nodeType:"double",networkType:"VPC",vpcId:o.vpcId,vSwitchId:o.vswId,zoneId:o.zoneId,password:f,token:Ml()})),i=l.body?.instanceId;if(!i)throw Error("Redis 创建失败:未返回 instanceId");let g=l.body?.connectionDomain||"",y=l.body?.port||6379,d=Date.now();while(!0){if(Date.now()-d>Np)throw Error("Redis 实例创建超时");await ge(zp);let h=(await n.describeInstanceAttribute(new sn.DescribeInstanceAttributeRequest({instanceId:i}))).body?.instances?.DBInstanceAttribute?.[0],b=h?.instanceStatus||"Creating";if(b==="Normal"){let $=h?.connectionDomain||g,p=h?.port||y;if(!$)throw Error("Redis 实例已就绪但未获取到连接地址");let w=r.securityIpList?.trim()||o.cidrBlock||"10.0.0.0/8";e.message("\uD83D\uDD10 正在配置 Redis 内网白名单..."),await Et(n,i,w,e);let H=Xe(void 0,f,$,p);return s.envs={...s.envs,REDIS_URL:H,REDIS_HOST:$,REDIS_PORT:String(p),REDIS_PASSWORD:f,REDIS_USERNAME:""},s.network=Tt(s.network,o),s.cache={type:"redis",instanceId:i,host:$,port:p,accountName:void 0,mode:"classic-redis"},k.setProject(s),H}e.message(`☕ Redis 实例初始化中,请稍候... [${b}]`)}}K();ft();import*as Bl from"@alicloud/r-kvstore20150101";async function tc(e,n){let t=k.requireAuth(),s=k.getProject(),o=Oe(t),r=n||s.cache?.instanceId;if(!r)throw Error("未找到 Redis 实例 ID,请先执行 licell cache add");if(zn(r)){e.message("\uD83D\uDD0E 正在获取 Redis 账号...");let g=await Jt(o,r,s.cache?.accountName);if(!g)throw Error("未找到可用 Redis 账号,无法轮换密码");let y=xe();e.message("\uD83D\uDD10 正在轮换 Redis 密码..."),await o.resetAccountPassword(new Bl.ResetAccountPasswordRequest({instanceId:r,accountName:g,accountPassword:y}));let d=await es(o,t,r),m=d?.connectionDomain||s.cache?.host,h=d?.port||s.cache?.port||6379;if(!m)throw Error("未查询到 Redis 连接地址");let b=Xe(g,y,m,h);return s.envs={...s.envs,REDIS_URL:b,REDIS_HOST:m,REDIS_PORT:String(h),REDIS_PASSWORD:y,REDIS_USERNAME:g},s.cache={...s.cache||{type:"redis",instanceId:r},type:"redis",instanceId:r,host:m,port:h,accountName:g},k.setProject(s),b}e.message("\uD83D\uDD0E 正在解析 Tair Serverless KV 连接地址...");let c=await Ct(o,e,[r,s.cache?.vkName]);if(!c.host&&s.cache?.host)c={host:s.cache.host,port:s.cache.port||6379,url:`redis://${s.cache.host}:${s.cache.port||6379}`,sourceInstanceId:r};let f=xe();e.message("\uD83D\uDD10 正在轮换 Tair Serverless KV 密码...");let u=await Nt(o,[c.sourceInstanceId,s.cache?.vkName,r],f,s.cache?.accountName),l=u?.accountName||s.cache?.accountName||"";if(!u){if(!await zt(o,[c.sourceInstanceId,r,s.cache?.vkName],f))throw Error("轮换密码失败:当前实例不支持自动密码重置")}let i=Xe(l||void 0,f,c.host,c.port);return s.envs={...s.envs,REDIS_URL:i,REDIS_HOST:c.host,REDIS_PORT:String(c.port),REDIS_PASSWORD:f,REDIS_USERNAME:l},s.cache={...s.cache||{type:"redis",instanceId:r},type:"redis",instanceId:c.sourceInstanceId,host:c.host,port:c.port,accountName:l,vkName:s.cache?.vkName,mode:"tair-serverless-kv"},k.setProject(s),i}K();import*as et from"@alicloud/r-kvstore20150101";async function Yl(e,n){try{let o=((await e.describeDBInstanceNetInfo(new et.DescribeDBInstanceNetInfoRequest({instanceId:n}))).body?.netInfoItems?.instanceNetInfo||[]).find((r)=>r.IPType==="Public");if(o?.connectionString)return{host:o.connectionString,port:Number(o.port)||6379}}catch{}return null}async function sc(e=200){let n=k.requireAuth(),t=Oe(n),s=[],o=Math.max(1,Math.min(Math.floor(e),500));for(let r=1;r<=20&&s.length<o;r+=1){let c=await t.describeInstances(new et.DescribeInstancesRequest({regionId:n.region,pageNumber:r,pageSize:50})),f=c.body?.instances?.KVStoreInstance||[];for(let l of f){let i=zr(l);if(!i.instanceId)continue;if(s.push(i),s.length>=o)break}let u=c.body?.totalCount||0;if(f.length===0||u>0&&s.length>=u)break}if(s.length<o){let r=await Ns(t,n.region);for(let c of r){let f=Pl(c);if(!f.instanceId)continue;if(s.some((u)=>u.instanceId===f.instanceId))continue;if(s.push(f),s.length>=o)break}}return s.slice(0,o)}async function oc(e){let n=e.trim();if(!n)throw Error("instanceId 不能为空");let t=k.requireAuth(),s=Oe(t);if(zn(n)){let u=await es(s,t,n);if(!u?.instanceId)throw Error(`未找到 Redis 实例: ${n}`);let i=((await s.describeAccounts(new et.DescribeAccountsRequest({instanceId:n}))).body?.accounts?.account||[]).map((g)=>g.accountName).filter((g)=>typeof g==="string"&&g.length>0);return{summary:zr(u),accountNames:i}}if(!Zt(n))throw Error("cache info 仅支持 tt-/tk-/r- 开头的实例 ID");let o=await ec(s,n);if(!o?.instanceId)throw Error(`未找到 Tair 实例: ${n}`);let r=Nn(o.connectionString),f=((await s.describeAccounts(new et.DescribeAccountsRequest({instanceId:n}))).body?.accounts?.account||[]).map((u)=>u.accountName).filter((u)=>typeof u==="string"&&u.length>0);return{summary:{instanceId:o.instanceId,mode:"tair-serverless-kv",instanceName:o.instanceName,status:o.instanceStatus,instanceClass:o.instanceClass,host:r?.host,port:r?.port,zoneId:o.zoneId,vpcId:o.vpcId,vSwitchId:o.vSwitchId},accountNames:f}}async function zs(e){let n=k.requireAuth(),t=Oe(n),s=k.getProject(),o=e?.trim()||s.cache?.instanceId||"";if(!o)throw Error("未指定缓存实例 ID,且当前项目未绑定缓存实例");let c=s.cache?.instanceId===o?Nn(s.envs?.REDIS_URL):null;if(zn(o)){let m=await es(t,n,o);if(!m?.instanceId)throw Error(`未找到 Redis 实例: ${o}`);let h=m.connectionDomain||s.cache?.host||"",b=m.port||s.cache?.port||6379;if(!h)throw Error(`未获取到实例 ${o} 的连接地址`);let $=c?.accountName||s.cache?.accountName||"<username>",p=c?.password||"",w=p.length>0,H=Xt($==="<username>"?void 0:$,p,h,b,w),T=await Yl(t,o);return{instanceId:o,host:h,port:b,username:$==="<username>"?void 0:$,passwordKnown:w,connectionString:H,mode:"classic-redis",...T?{publicHost:T.host,publicPort:T.port,publicConnectionString:Xt($==="<username>"?void 0:$,p,T.host,T.port,w)}:{}}}if(!Zt(o))throw Error("cache connect 仅支持 tt-/tk-/r- 开头的实例 ID");let f=await ec(t,o),u=Nn(f?.connectionString)||(s.cache?.host?{host:s.cache.host,port:s.cache.port||6379,url:`redis://${s.cache.host}:${s.cache.port||6379}`,accountName:s.cache.accountName,password:void 0}:null);if(!u?.host)throw Error(`未获取到实例 ${o} 的连接地址`);let l=c?.accountName||u.accountName||s.cache?.accountName,i=c?.password||"",g=i.length>0,y=Xt(l,i,u.host,u.port,g),d=await Yl(t,o);return{instanceId:o,host:u.host,port:u.port,username:l,passwordKnown:g,connectionString:y,mode:"tair-serverless-kv",...d?{publicHost:d.host,publicPort:d.port,publicConnectionString:Xt(l,i,d.host,d.port,g)}:{}}}K();import*as nt from"@alicloud/r-kvstore20150101";ce();var tw="licell_public";async function rc(e,n){let t=k.requireAuth(),s=Oe(t),c=((await s.describeDBInstanceNetInfo(new nt.DescribeDBInstanceNetInfoRequest({instanceId:e}))).body?.netInfoItems?.instanceNetInfo||[]).find((g)=>g.IPType==="Public");if(c?.connectionString)return{host:c.connectionString,port:Number(c.port)||6379};let f=`${e}-pub`.toLowerCase().replace(/[^a-z0-9]/g,"").slice(0,40);try{await s.allocateInstancePublicConnection(new nt.AllocateInstancePublicConnectionRequest({instanceId:e,connectionStringPrefix:f,port:"6379"}))}catch(g){let y=R(g);if(y.includes("NetTypeExists")||y.includes("already exists"))n.message("公网地址已存在,正在获取...");else throw g}let i=((await s.describeDBInstanceNetInfo(new nt.DescribeDBInstanceNetInfoRequest({instanceId:e}))).body?.netInfoItems?.instanceNetInfo||[]).find((g)=>g.IPType==="Public");if(!i?.connectionString)return null;return{host:i.connectionString,port:Number(i.port)||6379}}async function cc(e,n,t){let s=k.requireAuth(),o=Oe(s),r=`${n}/32`;try{await o.modifySecurityIps(new nt.ModifySecurityIpsRequest({instanceId:e,securityIpGroupName:tw,securityIps:r,modifyMode:"Cover"}))}catch(c){t.message(`⚠️ 公网白名单设置失败: ${R(c)}`)}}se();re();function Ol(e){e.command("cache add","分配 Redis 缓存").option("--type <type>","缓存类型:redis(CI 场景建议显式传入)").option("--instance <instanceId>","绑定已有实例 ID(tt-/tk-/r-),传入后跳过创建").option("--password <password>","绑定已有实例时的访问密码(不传则尝试自动轮换)").option("--username <accountName>","绑定已有实例时指定账号名(可选)").option("--engine-version <version>","旧版 Redis 参数(Tair Serverless KV 模式下不支持)").option("--class <instanceClass>","Tair Serverless KV 规格(如 kvcache.cu.g4b.2)").option("--node-type <type>","旧版 Redis 参数(Tair Serverless KV 模式下不支持)").option("--capacity <mb>","旧版 Redis 参数(Tair Serverless KV 模式下不支持)").option("--vk-name <vkName>","Tair KV 回退模式使用的 vkName(tk- 开头,不传则自动探测)").option("--compute-unit <n>","Tair Serverless KV 计算单元(当前仅支持 1)").option("--zone <zoneId>","可用区(如 cn-hangzhou-b)").option("--vpc <vpcId>","指定 VPC ID").option("--vsw <vSwitchId>","指定 VSwitch ID").option("--security-ip-list <cidrs>","白名单 CIDR(逗号分隔)").action(async(n)=>{await M({commandLabel:"licell cache add",interactiveTTY:P(),requiredCapabilities:["redis","vpc"]},async()=>{j(G.bgGreen(G.black(" \uD83E\uDDE0 Cache Provisioning (Redis) "))),B();let t=P(),s=C(n.type)?.toLowerCase();if(!s){if(!t)throw Error("非交互模式下请传入 --type redis");let u=await sw({message:"选择缓存引擎:",options:[{value:"redis",label:"\uD83D\uDFE5 Tair/Redis (VPC 内网)"}]});if(ow(u))process.exit(0);s=L(u,"缓存类型").toLowerCase()}if(s!=="redis")throw Error("--type 目前仅支持 redis");let o=qe(n.capacity,"capacity"),r=qe(n.computeUnit,"compute-unit"),c=A(),f=await v(c,"正在初始化缓存资源编排...","❌ 缓存拉起失败",()=>nc(c,{instanceId:C(n.instance),existingPassword:C(n.password),accountName:C(n.username),engineVersion:C(n.engineVersion),instanceClass:C(n.class),nodeType:C(n.nodeType),capacityMb:o,vkName:C(n.vkName),computeUnitNum:r,zoneId:C(n.zone),vpcId:C(n.vpc),vSwitchId:C(n.vsw),securityIpList:C(n.securityIpList)}));if(!f)return;if(!a())c.stop(G.green("✅ Redis 缓存已就绪并绑定到本工程内网!"));if(a()){S({stage:"cache.add",type:s,connectionStringMasked:Pn(f)});return}console.log(`
1597
+ \uD83D\uDD11 缓存连接串已生成: ${G.cyan(Pn(f))}
1598
+ `),q("下次执行 licell deploy 时,将自动作为 process.env.REDIS_URL 注入!")})}),e.command("cache list","查看缓存实例列表").option("--limit <n>","返回数量,默认 20").action(async(n)=>{await M({commandLabel:"licell cache list",interactiveTTY:P(),requiredCapabilities:["redis"]},async()=>{B();let t=Ye(n.limit,20,200),s=A(),o=await v(s,"正在拉取缓存实例列表...","❌ 获取缓存实例列表失败",()=>sc(t));if(!o)return;if(!a())s.stop(G.green(`✅ 共获取 ${o.length} 个实例`));if(a()){S({stage:"cache.list",count:o.length,instances:o});return}if(o.length===0){q("当前地域没有缓存实例");return}for(let r of o)console.log(`${G.cyan(r.instanceId)} mode=${G.gray(r.mode)} status=${G.gray(r.status||"-")} class=${G.gray(r.instanceClass||"-")}`);console.log(""),q("Done.")})}),e.command("cache info <instanceId>","查看缓存实例详情").action(async(n)=>{await M({commandLabel:"licell cache info",interactiveTTY:P(),requiredCapabilities:["redis"]},async()=>{B();let t=L(n,"instanceId"),s=A(),o=await v(s,`正在拉取实例 ${t} 详情...`,"❌ 获取缓存实例详情失败",()=>oc(t));if(!o)return;let r=o.summary;if(!a())s.stop(G.green("✅ 获取成功"));else{S({stage:"cache.info",instanceId:t,detail:o});return}if(console.log(`
1599
+ instanceId: ${G.cyan(r.instanceId)}`),console.log(`mode: ${G.cyan(r.mode)}`),console.log(`status: ${G.cyan(r.status||"-")}`),console.log(`class: ${G.cyan(r.instanceClass||"-")}`),r.engineVersion)console.log(`engine: ${G.cyan(r.engineVersion)}`);if(r.host)console.log(`endpoint: ${G.cyan(`${r.host}:${r.port||6379}`)}`);if(console.log(`network: ${G.cyan(`${r.vpcId||"-"} / ${r.vSwitchId||"-"} / ${r.zoneId||"-"}`)}`),o.accountNames.length>0)console.log(`accounts: ${G.cyan(o.accountNames.join(", "))}`);console.log(""),q("Done.")})}),e.command("cache connect [instanceId]","输出缓存连接信息").action(async(n)=>{await M({commandLabel:"licell cache connect",interactiveTTY:P(),requiredCapabilities:["redis"]},async()=>{B();let t=C(n),s=A(),o=await v(s,"正在解析缓存连接信息...","❌ 连接信息解析失败",()=>zs(t));if(!o)return;if(!a())s.stop(G.green("✅ 连接信息已生成"));else{S({stage:"cache.connect",instanceId:o.instanceId,connection:o});return}if(console.log(`
1600
+ instanceId: ${G.cyan(o.instanceId)}`),console.log(`mode: ${G.cyan(o.mode)}`),console.log(`host: ${G.cyan(o.host)}`),console.log(`port: ${G.cyan(String(o.port))}`),console.log(`username: ${G.cyan(o.username||"<none>")}`),console.log(`password: ${G.cyan(o.passwordKnown?"<known in project>":"<unknown, please provide manually>")}`),console.log(`url: ${G.cyan(o.connectionString)}`),o.publicHost)console.log(""),console.log(G.yellow("── 公网访问 ──")),console.log(`public host: ${G.cyan(o.publicHost)}`),console.log(`public port: ${G.cyan(String(o.publicPort))}`),console.log(`public url: ${G.cyan(o.publicConnectionString)}`);console.log(""),q("Done.")})}),e.command("cache rotate-password","轮换 Redis 密码").option("--instance <instanceId>","指定 Redis 实例 ID,不传则使用当前项目绑定实例").action(async(n)=>{await M({commandLabel:"licell cache rotate-password",interactiveTTY:P(),requiredCapabilities:["redis"]},async()=>{j(G.bgGreen(G.black(" \uD83D\uDD10 Rotate Redis Password "))),B();let t=n.instance?L(n.instance,"实例 ID"):void 0,s=A(),o=await v(s,"正在执行 Redis 密钥轮换...","❌ Redis 密钥轮换失败",()=>tc(s,t));if(!o)return;if(!a())s.stop(G.green("✅ Redis 密钥轮换完成"));if(a()){S({stage:"cache.rotate-password",instanceId:t||null,connectionStringMasked:Pn(o)});return}console.log(`
1601
+ \uD83D\uDD11 新连接串: ${G.cyan(Pn(o))}
1602
+ `),q("已同步更新 .licell/project.json 的 REDIS_* 环境变量")})}),e.command("cache public-access [instanceId]","开通 Redis 公网访问并添加当前 IP 到白名单").option("--ip <ip>","手动指定公网 IP(不传则自动获取)").action(async(n,t)=>{await M({commandLabel:"licell cache public-access",interactiveTTY:P(),requiredCapabilities:["redis"]},async()=>{let{resolvePublicIp:s}=await Promise.resolve().then(() => (Jr(),Zr));j(G.bgGreen(G.black(" \uD83C\uDF10 Cache Public Access "))),B();let o=C(n),r=A();r.start("正在获取公网 IP...");let c=t.ip?.trim()||await s();r.stop(`公网 IP: ${G.cyan(c)}`);let f=await v(r,"正在解析缓存连接信息...","❌ 连接信息解析失败",()=>zs(o));if(!f)return;await v(r,`正在将 ${c}/32 添加到白名单 (licell_public)...`,"❌ 白名单设置失败",()=>cc(f.instanceId,c,r));let u=await v(r,"正在开通公网访问...","❌ 公网访问开通失败",()=>rc(f.instanceId,r));if(!a())r.stop(G.green("✅ 公网访问已开通"));else{S({stage:"cache.public-access",instanceId:f.instanceId,publicIp:c,publicHost:u?.host||null,publicPort:u?.port||null});return}if(console.log(""),console.log(G.yellow("── 内网访问 ──")),console.log(`host: ${G.cyan(f.host)}`),console.log(`port: ${G.cyan(String(f.port))}`),console.log(`url: ${G.cyan(f.connectionString)}`),u){console.log(""),console.log(G.yellow("── 公网访问 ──")),console.log(`host: ${G.cyan(u.host)}`),console.log(`port: ${G.cyan(String(u.port))}`);let l=f.passwordKnown?"<password>":"<password>",i=f.username?`${f.username}:${l}@`:"";console.log(`url: ${G.cyan(`redis://${i}${u.host}:${u.port}`)}`)}else console.log(G.yellow(`
1603
1603
  ⚠️ 公网地址尚未就绪,请稍后通过 cache connect 查看`));console.log(`
1604
- 白名单 IP: ${v.cyan(`${c}/32`)} (分组: licell_public)`),q("Done.")})})}Le();K();import Q from"picocolors";import{mkdirSync as Jl,existsSync as gc,readFileSync as yc,rmSync as gw,writeFileSync as Xl}from"fs";import{join as Hn,resolve as Nl}from"path";import{spawnSync as zl}from"child_process";oe();import{existsSync as no,mkdirSync as rw,readFileSync as cw,readdirSync as Kl,statSync as fw,writeFileSync as uw}from"fs";import{join as jl,resolve as iw}from"path";function Wl(e){let n=(e||"").trim().toLowerCase();if(!n||n==="smoke")return"smoke";if(n==="full")return"full";throw Error("--suite 仅支持 smoke 或 full")}function Ql(e=new Date){let n=(t)=>String(t).padStart(2,"0");return[e.getUTCFullYear(),n(e.getUTCMonth()+1),n(e.getUTCDate()),"-",n(e.getUTCHours()),n(e.getUTCMinutes()),n(e.getUTCSeconds()),"-",String(e.getTime()).slice(-4)].join("")}function fc(e=process.cwd()){return jl(e,".licell","e2e")}function ns(e,n=process.cwd()){return jl(fc(n),`${e}.json`)}function lw(e=process.cwd()){let n=fc(e);if(!no(n))rw(n,{recursive:!0});return n}function ve(e,n=e.projectRoot){lw(n);let t=ns(e.runId,n);return uw(t,JSON.stringify(e,null,2)),t}function uc(e,n=process.cwd()){let t=ns(e,n);if(!no(t))return null;return JSON.parse(cw(t,"utf8"))}function ic(e=process.cwd()){let n=fc(e);if(!no(n))return[];let t=Kl(n).filter((o)=>o.endsWith(".json")).map((o)=>o.slice(0,-5)).filter((o)=>o.length>0);return t.sort((o,s)=>s.localeCompare(o)),t}function Dl(e=process.cwd()){return ic(e)[0]}function Vl(e){if(!no(e))return;if(!fw(e).isDirectory())throw Error(`路径已存在且不是目录: ${e}`);if(Kl(e).length>0)throw Error(`目录非空,请更换 --workspace 或先清理: ${e}`)}function lc(e=process.argv,n=process.execPath,t=process.cwd(),o=no){let s=e[1];if(s&&typeof s==="string"&&!s.startsWith("-")){let r=iw(t,s);if(o(r))return{command:n,prefixArgs:[r]}}return{command:n,prefixArgs:[]}}Ht();ce();re();function _e(){return new Date().toISOString()}function St(e,n){if(n.length===0)return;console.log(Q.bold(e));for(let t of n)console.log(`- ${t}`);console.log("")}function yw(e){let n=[Hn(e,".licell","project.json"),Hn(e,".ali","project.json")];for(let t of n){if(!gc(t))continue;try{let o=JSON.parse(yc(t,"utf8"));if(typeof o.appName==="string"&&o.appName.trim().length>0)return o.appName.trim()}catch{}}return}function dw(e){let n=[Hn(e,".licell","project.json"),Hn(e,".ali","project.json")];for(let t of n){if(!gc(t))continue;try{let s=JSON.parse(yc(t,"utf8")).network;if(!s||typeof s!=="object")continue;let r=typeof s.vpcId==="string"&&s.vpcId.trim().length>0?s.vpcId.trim():void 0,c=typeof s.vswId==="string"&&s.vswId.trim().length>0?s.vswId.trim():void 0,f=typeof s.sgId==="string"&&s.sgId.trim().length>0?s.sgId.trim():void 0;if(!r||!c)continue;return{vpcId:r,vswId:c,sgId:f}}catch{}}return}function eg(e,n,t){let o=[...e.prefixArgs,...n],s=zl(e.command,o,{cwd:t,stdio:"inherit",env:process.env});if(s.status!==0){let r=s.signal?` signal=${s.signal}`:"";throw Error(`命令失败: licell ${n.join(" ")} (exit=${String(s.status)}${r})`)}}function hw(e,n,t){let o=zl(e,n,{cwd:t,stdio:"inherit",env:process.env});if(o.status!==0){let s=o.signal?` signal=${o.signal}`:"";throw Error(`命令失败: ${e} ${n.join(" ")} (exit=${String(o.status)}${s})`)}}function aw(e){return`licell-e2e-${e}`.toLowerCase()}function Zl(e,n){let t=Hn(e,"e2e-static-dist");return Jl(t,{recursive:!0}),Xl(Hn(t,"index.html"),`<!doctype html>
1604
+ 白名单 IP: ${G.cyan(`${c}/32`)} (分组: licell_public)`),q("Done.")})})}Ae();K();import Q from"picocolors";import{mkdirSync as Jl,existsSync as gc,readFileSync as yc,rmSync as gw,writeFileSync as Xl}from"fs";import{join as Hn,resolve as Nl}from"path";import{spawnSync as zl}from"child_process";se();import{existsSync as ns,mkdirSync as rw,readFileSync as cw,readdirSync as Kl,statSync as fw,writeFileSync as uw}from"fs";import{join as jl,resolve as iw}from"path";function Wl(e){let n=(e||"").trim().toLowerCase();if(!n||n==="smoke")return"smoke";if(n==="full")return"full";throw Error("--suite 仅支持 smoke 或 full")}function Ql(e=new Date){let n=(t)=>String(t).padStart(2,"0");return[e.getUTCFullYear(),n(e.getUTCMonth()+1),n(e.getUTCDate()),"-",n(e.getUTCHours()),n(e.getUTCMinutes()),n(e.getUTCSeconds()),"-",String(e.getTime()).slice(-4)].join("")}function fc(e=process.cwd()){return jl(e,".licell","e2e")}function eo(e,n=process.cwd()){return jl(fc(n),`${e}.json`)}function lw(e=process.cwd()){let n=fc(e);if(!ns(n))rw(n,{recursive:!0});return n}function ve(e,n=e.projectRoot){lw(n);let t=eo(e.runId,n);return uw(t,JSON.stringify(e,null,2)),t}function uc(e,n=process.cwd()){let t=eo(e,n);if(!ns(t))return null;return JSON.parse(cw(t,"utf8"))}function ic(e=process.cwd()){let n=fc(e);if(!ns(n))return[];let t=Kl(n).filter((s)=>s.endsWith(".json")).map((s)=>s.slice(0,-5)).filter((s)=>s.length>0);return t.sort((s,o)=>o.localeCompare(s)),t}function Dl(e=process.cwd()){return ic(e)[0]}function Vl(e){if(!ns(e))return;if(!fw(e).isDirectory())throw Error(`路径已存在且不是目录: ${e}`);if(Kl(e).length>0)throw Error(`目录非空,请更换 --workspace 或先清理: ${e}`)}function lc(e=process.argv,n=process.execPath,t=process.cwd(),s=ns){let o=e[1];if(o&&typeof o==="string"&&!o.startsWith("-")){let r=iw(t,o);if(s(r))return{command:n,prefixArgs:[r]}}return{command:n,prefixArgs:[]}}Ht();ce();re();function _e(){return new Date().toISOString()}function St(e,n){if(n.length===0)return;console.log(Q.bold(e));for(let t of n)console.log(`- ${t}`);console.log("")}function yw(e){let n=[Hn(e,".licell","project.json"),Hn(e,".ali","project.json")];for(let t of n){if(!gc(t))continue;try{let s=JSON.parse(yc(t,"utf8"));if(typeof s.appName==="string"&&s.appName.trim().length>0)return s.appName.trim()}catch{}}return}function dw(e){let n=[Hn(e,".licell","project.json"),Hn(e,".ali","project.json")];for(let t of n){if(!gc(t))continue;try{let o=JSON.parse(yc(t,"utf8")).network;if(!o||typeof o!=="object")continue;let r=typeof o.vpcId==="string"&&o.vpcId.trim().length>0?o.vpcId.trim():void 0,c=typeof o.vswId==="string"&&o.vswId.trim().length>0?o.vswId.trim():void 0,f=typeof o.sgId==="string"&&o.sgId.trim().length>0?o.sgId.trim():void 0;if(!r||!c)continue;return{vpcId:r,vswId:c,sgId:f}}catch{}}return}function eg(e,n,t){let s=[...e.prefixArgs,...n],o=zl(e.command,s,{cwd:t,stdio:"inherit",env:process.env});if(o.status!==0){let r=o.signal?` signal=${o.signal}`:"";throw Error(`命令失败: licell ${n.join(" ")} (exit=${String(o.status)}${r})`)}}function hw(e,n,t){let s=zl(e,n,{cwd:t,stdio:"inherit",env:process.env});if(s.status!==0){let o=s.signal?` signal=${s.signal}`:"";throw Error(`命令失败: ${e} ${n.join(" ")} (exit=${String(s.status)}${o})`)}}function aw(e){return`licell-e2e-${e}`.toLowerCase()}function Zl(e,n){let t=Hn(e,"e2e-static-dist");return Jl(t,{recursive:!0}),Xl(Hn(t,"index.html"),`<!doctype html>
1605
1605
  <html>
1606
1606
  <head><meta charset="UTF-8"><title>licell-e2e</title></head>
1607
1607
  <body><h1>licell e2e ${n}</h1></body>
1608
1608
  </html>
1609
1609
  `),Xl(Hn(t,"health.txt"),`ok:${n}
1610
- `),t}function to(e,n){e.steps.push(n),e.updatedAt=_e()}function ne(e,n,t){let o=_e(),s=`licell ${t.join(" ")}`;X({stage:`e2e.${n}`,action:n,status:"start",data:{command:s}});try{eg(e.invocation,t,e.workspaceDir),to(e.manifest,{name:n,command:s,status:"ok",startedAt:o,endedAt:_e()}),ve(e.manifest),X({stage:`e2e.${n}`,action:n,status:"ok"})}catch(r){throw to(e.manifest,{name:n,command:s,status:"failed",startedAt:o,endedAt:_e(),error:R(r)}),ve(e.manifest),X({stage:`e2e.${n}`,action:n,status:"failed",message:R(r)}),r}}function mw(e,n,t,o){if(!n){to(e.manifest,{name:t,command:`licell ${o.join(" ")}`,status:"skipped",startedAt:_e(),endedAt:_e()}),ve(e.manifest);return}ne(e,t,o)}function pw(e,n,t,o){let s=_e(),r=`${t} ${o.join(" ")}`.trim();X({stage:`e2e.${n}`,action:n,status:"start",data:{command:r}});try{hw(t,o,e.workspaceDir),to(e.manifest,{name:n,command:r,status:"ok",startedAt:s,endedAt:_e()}),ve(e.manifest),X({stage:`e2e.${n}`,action:n,status:"ok"})}catch(c){throw to(e.manifest,{name:n,command:r,status:"failed",startedAt:s,endedAt:_e(),error:R(c)}),ve(e.manifest),X({stage:`e2e.${n}`,action:n,status:"failed",message:R(c)}),c}}function ww(e){let n=["fc","oss","rds","redis","logs"];if(e.domain||e.domainSuffix)n.push("dns");if(e.enableCdn)n.push("cdn");if(e.includeStatic)n.push("oss");if(e.useVpc)n.push("vpc");return[...new Set(n)]}function $w(e,n){return`licell-${e}-${n.substring(0,4)}`.toLowerCase()}async function kw(e){let n=process.cwd(),t=P(),o=Wl(C(e.suite)),s=C(e.runId)||Ql(),r=aw(s),c=C(e.runtime)||"nodejs22",f=C(e.target)||"preview",u=Boolean(e.enableVpc),l=C(e.domain),i=C(e.domainSuffix),g=C(e.dbInstance),y=C(e.cacheInstance),d=Boolean(e.skipStatic),m=l?Ao(l):void 0,h=i?Vn(i):void 0;if(m&&h)throw Error("--domain 与 --domain-suffix 不能同时使用");let b=Boolean(e.enableCdn);if(b&&!m&&!h)throw Error("--enable-cdn 需要配合 --domain 或 --domain-suffix");let $=Boolean(e.preview);if($&&!h)throw Error("--preview 需要配合 --domain-suffix");let p=Boolean(e.cleanup),w=Boolean(e.yes),H=Nl(C(e.workspace)||Hn(n,".licell","e2e-work",s));Vl(H),Jl(H,{recursive:!0});let T={runId:s,suite:o,status:"running",createdAt:_e(),updatedAt:_e(),projectRoot:n,workspaceDir:H,target:f,runtime:c,resources:{appName:r,...m?{domain:m}:{},...h?{domainSuffix:h}:{}},steps:[],cleanup:{status:"pending",details:[],errors:[]}},x=ve(T,n),E=lc(),_={invocation:E,workspaceDir:H,manifest:T,state:{hasDeployedApi:!1,hasDeployedStatic:!1}};j(Q.bgBlue(Q.white(" \uD83E\uDDEA Licell E2E Runner "))),console.log(`runId: ${Q.cyan(s)}`),console.log(`suite: ${Q.cyan(o)}`),console.log(`workspace: ${Q.cyan(H)}`),console.log(`manifest: ${Q.cyan(x)}
1611
- `),St("执行计划",[`runtime: ${c}`,`target: ${f}`,`api function: ${r}`,`network: ${u?"vpc(shared licell-vpc)":"public(no-vpc)"}`,...m?[`fixed domain: ${m}`]:[],...h?[`domain suffix: ${h}`]:[],...b?["cdn: enabled"]:[],...$?["preview deploy: enabled"]:[],...o==="full"&&!d?["static deploy: enabled"]:["static deploy: skipped"]]);let F;X({stage:"e2e",action:"run",status:"start",data:{runId:s,suite:o,runtime:c,target:f,workspaceDir:H}});try{await M({commandLabel:"licell e2e run",interactiveTTY:t,requiredCapabilities:ww({domain:m,domainSuffix:h,enableCdn:b,includeStatic:o==="full"&&!d,useVpc:u})},async()=>{ne(_,"init",["init","--runtime",c,"--app",r,"--yes"]);let I=yw(H);if(!I)throw Error("init 成功后未检测到 appName");if(c.startsWith("nodejs"))pw(_,"bun-install","bun",["install"]);if(T.resources.appName=I,!T.resources.domain&&T.resources.domainSuffix)T.resources.domain=`${I}.${T.resources.domainSuffix}`;T.updatedAt=_e(),ve(T,n),St("创建资源",[`fc function: ${I}`,...T.resources.domain?[`domain: ${T.resources.domain}`]:[]]);let fe=["deploy","--type","api","--runtime",c,"--target",f];if(fe.push(u?"--enable-vpc":"--disable-vpc"),m)fe.push("--domain",m);if(h)fe.push("--domain-suffix",h);if(b)fe.push("--enable-cdn");ne(_,"deploy-api",fe),_.state.hasDeployedApi=!0;let Ce=dw(H);if(Ce){if(T.resources.vpcId=Ce.vpcId,T.resources.vswId=Ce.vswId,Ce.sgId)T.resources.sgId=Ce.sgId;ve(T,n)}if(ne(_,"fn-list",["fn","list","--prefix",I,"--limit","20"]),ne(_,"fn-info",["fn","info",I,"--target",f]),ne(_,"fn-invoke",["fn","invoke",I,"--target",f,"--payload",JSON.stringify({runId:s,ping:"pong"})]),ne(_,"env-set",["env","set","LICELL_E2E_RUN_ID",s]),ne(_,"env-list",["env","list","--target",f]),ne(_,"env-pull",["env","pull","--target",f]),ne(_,"env-rm",["env","rm","LICELL_E2E_RUN_ID","--yes"]),ne(_,"release-list",["release","list","--limit","5"]),ne(_,"release-promote",["release","promote","--target",f]),$&&h){let le=["deploy","--type","api","--runtime",c,"--preview"];if(le.push(u?"--enable-vpc":"--disable-vpc"),le.push("--domain-suffix",h),ne(_,"deploy-api-preview",le),o==="full"&&!d){let Je=["deploy","--type","static","--dist",Zl(H,`${s}-preview`),"--preview"];Je.push("--domain-suffix",h),ne(_,"deploy-static-preview",Je)}ne(_,"release-prune-preview",["release","prune","--preview","--keep","2"])}if(ne(_,"logs-once",["logs","--once","--window","180","--lines","200"]),ne(_,"oss-list",["oss","list","--limit","5"]),ne(_,"db-list",["db","list","--limit","5"]),ne(_,"cache-list",["cache","list","--limit","5"]),g)ne(_,"db-info",["db","info",g]),ne(_,"db-connect",["db","connect",g]);if(y)ne(_,"cache-info",["cache","info",y]),ne(_,"cache-connect",["cache","connect",y]);let rn=(()=>{let le=T.resources.domain;if(!le)return;return Qe(le).rootDomain})();if(mw(_,Boolean(rn),"dns-records-list",["dns","records","list",rn||"","--limit","20"]),o==="full"){if(ne(_,"whoami",["whoami"]),!d){let le=k.requireAuth(),cn=Zl(H,s);ne(_,"deploy-static",["deploy","--type","static","--dist",cn]),_.state.hasDeployedStatic=!0,T.resources.staticBucket=$w(I,le.accountId),ve(T,n),St("静态资源",[`oss bucket: ${T.resources.staticBucket}`,`upload prefix: e2e-upload-${s}`]),ne(_,"oss-upload",["oss","upload","--bucket",T.resources.staticBucket,"--source-dir",cn,"--target-dir",`e2e-upload-${s}`]),ne(_,"oss-ls-uploaded",["oss","ls",T.resources.staticBucket,`e2e-upload-${s}`,"--limit","20"])}}}),T.status="succeeded",T.updatedAt=_e(),ve(T,n),St("E2E 结果",[`runId: ${s}`,`status: ${T.status}`,...T.resources.appName?[`fc function: ${T.resources.appName}`]:[],...T.resources.domain?[`domain: ${T.resources.domain}`]:[],...T.resources.staticBucket?[`oss bucket: ${T.resources.staticBucket}`]:[],...T.resources.vpcId?[`vpc: ${T.resources.vpcId}/${T.resources.vswId||"-"}`]:[]]),console.log(Q.green(`✅ E2E run 完成(${s})`)),S({stage:"e2e",runId:s,suite:o,status:T.status,appName:T.resources.appName||null,domain:T.resources.domain||null,staticBucket:T.resources.staticBucket||null,workspaceDir:H})}catch(I){F=I,T.status="failed",T.updatedAt=_e(),T.notes=[...T.notes||[],R(I)],ve(T,n)}if(p){console.log(Q.gray(`
1612
- 自动进入清理阶段...`));try{await ng(T,{yes:w,keepWorkspace:!1,invocation:E,interactiveTTY:t})}catch(I){if(!F)throw I;console.warn(Q.yellow(`⚠️ 自动清理失败: ${R(I)}`))}}else console.log(Q.gray(`可执行清理命令: licell e2e cleanup ${s}`));if(F)throw F;q("Done.")}async function ng(e,n){let t=e.status,o=n.interactiveTTY??P();await Be(`清理 E2E 运行 ${e.runId} 相关云资源`,{yes:n.yes,interactiveTTY:o});let s=n.invocation||lc(),r=[],c=[],f=e.workspaceDir,u=e.resources.appName,l=e.resources.domain,i=e.resources.staticBucket,g=e.resources.vpcId,y=e.resources.vswId,d=(m,h,b)=>{try{eg(s,h,f),c.push(`${m}: ok`)}catch($){let p=R($),w=p.toLowerCase();if((b?.ignoreErrorPatterns||[]).some((T)=>w.includes(T))){c.push(`${m}: skipped (${p})`);return}r.push(`${m}: ${p}`),c.push(`${m}: failed`)}};if(e.cleanup=e.cleanup||{},e.cleanup.attemptedAt=_e(),e.cleanup.status="pending",e.cleanup.details=c,e.cleanup.errors=r,e.updatedAt=_e(),ve(e,e.projectRoot),X({stage:"e2e.cleanup",action:"cleanup",status:"start",data:{runId:e.runId,appName:u||null,domain:l||null,staticBucket:i||null}}),St("清理目标",[...u?[`fc function: ${u}`]:[],...l?[`domain binding: ${l}`]:[],...i?[`oss bucket: ${i}`]:[],...g?[`vpc network: ${g}/${y||"-"} (shared, keep)`]:[],...n.keepWorkspace?["workspace: keep"]:[`workspace: ${f}`]]),await M({commandLabel:"licell e2e cleanup",interactiveTTY:o,requiredCapabilities:["fc",...l?["dns"]:[],...i?["oss"]:[]]},async()=>{if(l)console.log(Q.gray(`清理 domain: ${l}`)),d("domain-rm",["domain","rm",l,"--yes"]);if(u){console.log(Q.gray(`清理 preview 域名: ${u}`)),d("release-prune-preview",["release","prune","--preview","--keep","0","--apply","--yes"],{ignoreErrorPatterns:["not found","no preview"]}),console.log(Q.gray(`清理 function: ${u}`)),d("fn-rm",["fn","rm",u,"--force","--yes"],{ignoreErrorPatterns:["functionnotfound","does not exist","not found"]});let m=`${u}-static-proxy`;console.log(Q.gray(`清理 static proxy function: ${m}`)),d("fn-rm-static-proxy",["fn","rm",m,"--force","--yes"],{ignoreErrorPatterns:["functionnotfound","does not exist","not found"]})}if(i){console.log(Q.gray(`清理 oss bucket: ${i}`));try{let m=await Ni(i);c.push(`oss-bucket-rm: ok (${m.bucket}, objects=${m.deletedObjects}, bucketDeleted=${m.deletedBucket})`),console.log(Q.green(`oss 清理完成: ${m.bucket} (objects=${m.deletedObjects})`))}catch(m){r.push(`oss-bucket-rm: ${R(m)}`),c.push("oss-bucket-rm: failed"),console.warn(Q.yellow(`oss 清理失败: ${R(m)}`))}}if(g)c.push(`vpc-rm: skipped (${g} 为共享网络,e2e 默认不自动删除)`)}),!n.keepWorkspace)try{gw(f,{recursive:!0,force:!0}),c.push("workspace-rm: ok"),console.log(Q.green(`workspace 已清理: ${f}`))}catch(m){r.push(`workspace-rm: ${R(m)}`),c.push("workspace-rm: failed"),console.warn(Q.yellow(`workspace 清理失败: ${R(m)}`))}if(e.cleanup.finishedAt=_e(),e.cleanup.status=r.length>0?"partial":"done",r.length>0)e.status="partial_cleaned";else e.status=t==="failed"?"failed":"cleaned";if(e.updatedAt=_e(),ve(e,e.projectRoot),r.length>0){console.warn(Q.yellow(`⚠️ 清理存在 ${r.length} 个失败项:`));for(let m of r)console.warn(Q.yellow(`- ${m}`))}else St("清理结果",c.map((m)=>m.replace(/^([^:]+): /,"$1 => "))),console.log(Q.green(`✅ 清理完成: ${e.runId}`));S({stage:"e2e.cleanup",runId:e.runId,status:e.cleanup.status||"unknown",details:c,errors:r})}function tg(e){e.command("e2e run","执行固定 E2E 套件(默认 smoke)").option("--suite <suite>","套件:smoke/full(默认 smoke)").option("--run-id <id>","指定 runId(默认自动生成)").option("--runtime <runtime>","部署 runtime(默认 nodejs22)").option("--target <alias>","部署 target alias(默认 preview)").option("--enable-vpc","API 部署启用 VPC(默认关闭,便于无残留清理)").option("--domain <domain>","固定完整域名(可选)").option("--domain-suffix <suffix>","固定域名后缀(可选)").option("--db-instance <instanceId>","full 套件时附加验证 db info/connect(复用已有实例)").option("--cache-instance <instanceId>","full 套件时附加验证 cache info/connect(复用已有实例)").option("--skip-static","full 套件时跳过 static + oss upload 场景").option("--enable-cdn","部署时启用 CDN(需配合 domain/domain-suffix)").option("--preview","测试 preview 部署流程(需配合 --domain-suffix)").option("--cleanup","执行完后自动清理").option("--workspace <dir>","指定 E2E 工作目录(默认 .licell/e2e-work/<runId>)").option("--yes","自动清理时跳过二次确认").action(async(n)=>{try{await kw(n)}catch(t){if(a())he(t,{stage:"e2e"});else console.error(Q.red(R(t)));process.exitCode=1}}),e.command("e2e cleanup [runId]","清理指定 E2E run 产生的资源").option("--manifest <path>","直接指定 manifest 文件路径").option("--keep-workspace","保留本地 workspace 目录").option("--yes","跳过二次确认(危险)").action(async(n,t)=>{try{let o=process.cwd(),s=C(t.manifest),r=null;if(s){let c=Nl(s);if(!gc(c))throw Error(`manifest 不存在: ${c}`);r=JSON.parse(yc(c,"utf8"))}else{let c=C(n)||Dl(o);if(!c)throw Error("未找到任何 e2e manifest,请先执行 `licell e2e run`");if(r=uc(c,o),!r)throw Error(`未找到 runId=${c} 的 manifest`)}j(Q.bgBlue(Q.white(" \uD83E\uDDF9 Licell E2E Cleanup "))),console.log(`runId: ${Q.cyan(r.runId)}`),console.log(`workspace: ${Q.cyan(r.workspaceDir)}`),console.log(`manifest: ${Q.cyan(ns(r.runId,r.projectRoot))}
1613
- `),await ng(r,{yes:Boolean(t.yes),keepWorkspace:Boolean(t.keepWorkspace)}),q("Done.")}catch(o){if(a())he(o,{stage:"e2e.cleanup"});else console.error(Q.red(R(o)));process.exitCode=1}}),e.command("e2e list","查看本项目 e2e 运行记录").action(()=>{let n=ic(process.cwd());if(n.length===0){q("当前项目暂无 e2e 记录");return}for(let t of n){let o=uc(t,process.cwd());if(!o)continue;console.log(`${Q.cyan(t)} suite=${Q.gray(o.suite)} status=${Q.gray(o.status)} workspace=${Q.gray(o.workspaceDir)}`)}console.log(""),q("Done.")})}import W from"picocolors";Le();Ve();oe();K();re();K();Cn();Dn();import*as og from"@alicloud/fc20230330";Ie();import _w,*as sg from"@alicloud/oss20190517";import*as rg from"@alicloud/openapi-client";import*as cg from"@alicloud/tea-util";var bw=te(_w,"@alicloud/oss20190517");function Tw(){let e=k.requireAuth(),n=new bw(new rg.Config({accessKeyId:e.ak,accessKeySecret:e.sk,regionId:e.region,endpoint:`oss-${e.region}.aliyuncs.com`})),t=new cg.RuntimeOptions({connectTimeout:8000,readTimeout:120000});return{auth:e,client:n,runtime:t}}async function Cw(e){let{client:n}=ie(),t=[],o=new RegExp(`^${e}-preview-v\\d+\\.`),s;while(!0){let r=await Y(()=>n.listCustomDomains(new og.ListCustomDomainsRequest({limit:100,nextToken:s}))),c=r.body?.customDomains||[];for(let f of c){let u=f.domainName;if(u&&o.test(u))t.push(u)}if(s=r.body?.nextToken,!s||c.length===0)break}return t}function Ew(e,n){let t=e.match(new RegExp(`^${n}-preview-v(\\d+)\\.`));if(!t)return null;return parseInt(t[1],10)}async function Sw(e,n){try{await Y(()=>e.deleteCustomDomain(n))}catch(t){if(!ue(t))throw t}}async function qw(e,n){let{client:t,runtime:o}=Tw(),s=await Do(e,n,1000),r=0;for(let c of s)try{await Y(()=>t.deleteObjectWithOptions(e,c.name,new sg.DeleteObjectRequest({}),{},o)),r++}catch(f){if(!ue(f))throw f}return r}async function fg(e,n,t){let o=await Cw(e),s=o.map((i)=>({domain:i,version:Ew(i,e)})).filter((i)=>i.version!==null).sort((i,g)=>(g.version||0)-(i.version||0)),r=s.slice(0,n),c=s.slice(n),f={keep:n,totalPreviewDomains:o.length,candidates:c.map((i)=>i.domain),deletedDomains:[],deletedOssPaths:[],failed:[]};if(!t)return f;let u=Qo(e),{client:l}=ie();for(let i of c)try{if(await Sw(l,i.domain),f.deletedDomains.push(i.domain),i.version!==null)for(let g of[i.version,i.version-1]){if(g<=0)continue;let y=`_preview/${g}/`;try{if(await qw(u,y)>0)f.deletedOssPaths.push(y)}catch{}}}catch(g){f.failed.push({domain:i.domain,reason:g instanceof Error?g.message:String(g)})}return f}function ug(e){e.command("release list","查看函数版本列表").option("--limit <n>","返回版本数量,默认 20").action(async(n)=>{await M({commandLabel:"licell release list",interactiveTTY:P(),requiredCapabilities:["fc"]},async()=>{j(W.bgBlue(W.white(" \uD83D\uDCDA Function Versions "))),B();let t=k.getProject();Ae(t);let o=Ye(n.limit,20,100),s=L(),r=await U(s,"正在拉取函数版本列表...","❌ 获取版本列表失败",()=>dt(t.appName,o));if(!r)return;if(!a())s.stop(W.green(`✅ 共获取 ${r.length} 个版本`));if(a()){S({stage:"release.list",appName:t.appName,count:r.length,versions:r});return}if(r.length===0){q("当前函数还没有已发布版本");return}for(let c of r){let f=c.versionId||"unknown",u=c.createdTime||"-",l=c.description||"-";console.log(`${W.cyan(f)} ${W.gray(u)} ${l}`)}q("Done.")})}),e.command("release promote [versionId]","发布并切流到目标别名").option("--target <target>","目标别名,默认 prod").action(async(n,t)=>{await M({commandLabel:"licell release promote",interactiveTTY:P(),requiredCapabilities:["fc"]},async()=>{j(W.bgBlue(W.white(" \uD83D\uDE80 Promote Release "))),B();let o=k.getProject();Ae(o);let s=tn(t.target),r=L(),c=await U(r,`正在准备发布到别名 ${s}...`,"❌ 切流失败",async()=>{let f=n?A(n,"versionId"):"";if(!f){r.message("未指定 versionId,正在发布当前函数代码为新版本...");try{f=await Sn(o.appName,`promote ${s} at ${new Date().toISOString()}`)}catch(u){if(!Uo(u))throw u;r.message("检测到当前代码无变更,复用最新已发布版本..."),f=await vo(o.appName)}}return await qn(o.appName,s,f,`promoted by licell at ${new Date().toISOString()}`),f});if(!c)return;if(!a())r.stop(W.green("✅ 别名切流完成"));if(a()){S({stage:"release.promote",appName:o.appName,target:s,versionId:c});return}console.log(`
1614
- \uD83C\uDFF7️ alias=${W.cyan(s)} -> version=${W.cyan(c)}
1615
- `),q("Done.")})}),e.command("release rollback <versionId>","回滚到指定函数版本").option("--target <target>","目标别名,默认 prod").action(async(n,t)=>{await M({commandLabel:"licell release rollback",interactiveTTY:P(),requiredCapabilities:["fc"]},async()=>{j(W.bgBlue(W.white(" ↩ Rollback Release "))),B();let o=k.getProject();Ae(o);let s=tn(t.target),r=A(n,"versionId"),c=L();if(!await U(c,`正在回滚 ${s} 到版本 ${r}...`,"❌ 回滚失败",async()=>{return await qn(o.appName,s,r,`rollback by licell at ${new Date().toISOString()}`),!0}))return;if(!a())c.stop(W.green("✅ 回滚完成"));if(a()){S({stage:"release.rollback",appName:o.appName,target:s,versionId:r});return}console.log(`
1616
- \uD83C\uDFF7️ alias=${W.cyan(s)} -> version=${W.cyan(r)}
1617
- `),q("Done.")})}),e.command("release prune","清理历史函数版本(默认仅预览)").option("--keep <n>","保留最近 N 个版本,默认 10").option("--apply","执行删除,未传则仅预览").option("--yes","跳过二次确认(危险)").option("--preview","清理预览域名绑定(而非函数版本)").action(async(n)=>{await M({commandLabel:"licell release prune",interactiveTTY:P(),requiredCapabilities:n.preview?["fc","dns"]:["fc"]},async()=>{j(W.bgBlue(W.white(n.preview?" \uD83E\uDDF9 Prune Preview Domains ":" \uD83E\uDDF9 Prune Function Versions "))),B();let t=k.getProject();Ae(t);let o=qe(n.keep,"keep")||(n.preview?3:10),s=Boolean(n.apply);if(n.preview){if(s)await Be(`清理预览域名绑定(保留最近 ${o} 个)`,{yes:Boolean(n.yes)});let f=L(),u=await U(f,s?"正在清理预览域名...":"正在预览可清理的预览域名...","❌ 清理失败",()=>fg(t.appName,o,s));if(!u)return;if(!a())f.stop(W.green(s?"✅ 清理任务完成":"✅ 预览完成"));if(a()){S({stage:"release.prune.preview",appName:t.appName,keepRequested:o,applyRequested:s,...u});return}if(console.log(`
1618
- 保留数量: ${W.cyan(String(u.keep))}`),console.log(`发现预览域名: ${W.cyan(String(u.totalPreviewDomains))}`),console.log(`候选删除: ${W.cyan(String(u.candidates.length))}`),u.candidates.length>0)console.log(`候选: ${u.candidates.join(", ")}`);if(s){if(console.log(`已删除域名绑定: ${W.cyan(String(u.deletedDomains.length))}`),console.log(`已删除 OSS 路径: ${W.cyan(String(u.deletedOssPaths.length))}`),u.failed.length>0){console.log(W.yellow(`删除失败: ${u.failed.length}`));for(let l of u.failed)console.log(W.yellow(`- ${l.domain}: ${l.reason}`))}}else console.log(W.gray(`
1619
- 提示: 加上 --apply 才会执行实际删除`));console.log(""),q("Done.");return}if(s)await Be(`清理函数历史版本(保留最近 ${o} 个)`,{yes:Boolean(n.yes)});let r=L(),c=await U(r,s?"正在清理历史版本...":"正在预览可清理版本...","❌ 清理失败",()=>hr(t.appName,o,s));if(!c)return;if(!a())r.stop(W.green(s?"✅ 清理任务完成":"✅ 预览完成"));if(a()){S({stage:"release.prune",appName:t.appName,keepRequested:o,applyRequested:s,...c});return}if(console.log(`
1620
- 保留数量: ${W.cyan(String(c.keep))}`),console.log(`总发布版本: ${W.cyan(String(c.totalVersions))}`),console.log(`Alias 保护版本: ${W.cyan(String(c.aliasProtectedVersions.length))}`),console.log(`候选删除版本: ${W.cyan(String(c.candidates.length))}`),c.candidates.length>0)console.log(`候选: ${c.candidates.join(", ")}`);if(s){if(console.log(`已删除: ${W.cyan(String(c.deleted.length))}`),c.failed.length>0){console.log(W.yellow(`删除失败: ${c.failed.length}`));for(let f of c.failed)console.log(W.yellow(`- ${f.versionId}: ${f.reason}`))}}else console.log(W.gray(`
1621
- 提示: 加上 --apply 才会执行实际删除`));console.log(""),q("Done.")})})}import sn from"picocolors";Ve();Le();oe();K();re();function ig(e){e.command("domain add <domain>","绑定自定义域名").option("--ssl","自动配置 Let's Encrypt 免费证书开启 HTTPS").option("--ssl-force-renew","配合 --ssl 强制续签证书(忽略到期阈值)").option("--target <target>","将域名路由到指定 FC alias(如 prod/preview)").action(async(n,t)=>{await M({commandLabel:"licell domain add",interactiveTTY:P(),requiredCapabilities:["fc","dns"]},async()=>{j(sn.bgCyan(sn.black(" \uD83C\uDF10 Domain & SSL Configuration ")));let o=await B(),s=A(n,"域名"),r=tn(t.target);if(t.sslForceRenew&&!t.ssl)throw Error("--ssl-force-renew 需要与 --ssl 一起使用");let c=k.getProject();Ae(c);let f=L(),u=await U(f,`正在配置云解析 DNS,将 ${s} 指向应用...`,"❌ 配置流程中断",async()=>{let l=`${o.accountId}.${o.region}.fc.aliyuncs.com`;await _t(s,l,r);try{f.message(`正在确保别名 ${r} 存在...`);let i;try{i=await Sn(c.appName,`domain bind ${r} at ${new Date().toISOString()}`)}catch(g){if(!Uo(g))throw g;i=await vo(c.appName)}await qn(c.appName,r,i,`domain bind by licell at ${new Date().toISOString()}`)}catch{if(!a())console.warn(sn.yellow(`⚠️ 未能自动创建别名 ${r},请先 deploy 后执行 licell release promote`))}if(t.ssl)return f.message("DNS CNAME 配置成功。正在接管 Let's Encrypt 签发流程..."),vi(s,f,{forceRenew:Boolean(t.sslForceRenew)});return`http://${s}`});if(!u)return;if(!a())f.stop(sn.green("✅ 域名绑定与网络平面配置大功告成!"));if(a()){S({stage:"domain.add",domain:s,releaseTarget:r||null,ssl:Boolean(t.ssl),finalUrl:u});return}if(r)console.log(`
1622
- \uD83C\uDFF7️ 域名路由已绑定 alias=${sn.cyan(r)}
1623
- `);q(`\uD83D\uDD17 你的应用现在可通过安全的 ${sn.cyan(sn.underline(u))} 访问`)})}),e.command("domain rm <domain>","解绑自定义域名并清理 DNS CNAME").option("--yes","跳过二次确认(危险)").action(async(n,t)=>{await M({commandLabel:"licell domain rm",interactiveTTY:P(),requiredCapabilities:["fc","dns"]},async()=>{j(sn.bgCyan(sn.black(" \uD83C\uDF10 Domain Removal "))),await B();let o=A(n,"域名").toLowerCase();await Be(`解绑域名 ${o}`,{yes:Boolean(t.yes)});let s=L();if(!await U(s,`正在解绑域名 ${o}...`,"❌ 域名解绑失败",async()=>{return await Ci(o),!0}))return;if(!a())s.stop(sn.green("✅ 域名已解绑并完成 DNS 清理")),q("Done.");else S({stage:"domain.rm",domain:o,removed:!0})})})}import{text as Pw,isCancel as Rw}from"@clack/prompts";import $n from"picocolors";Le();oe();re();function lg(e){e.command("dns records list [domain]","查看域名解析记录").option("--limit <n>","返回数量,默认 100").action(async(n,t)=>{await M({commandLabel:"licell dns records list",interactiveTTY:P(),requiredCapabilities:["dns"]},async()=>{B();let o=n;if(!o){if(!P())throw Error("缺少域名参数,请使用:licell dns records list <domain>");let u=await Pw({message:"请输入要查看的域名:",placeholder:"example.com"});if(Rw(u))process.exit(0);o=A(u,"域名")}let s=A(o,"域名").toLowerCase(),r=Ye(t.limit,100,500),c=L(),f=await U(c,`正在拉取 ${s} 的解析记录...`,"❌ 获取 DNS 记录失败",()=>Ei(s,r));if(!f)return;if(!a())c.stop($n.green(`✅ 共获取 ${f.length} 条记录`));if(a()){S({stage:"dns.records.list",domain:s,count:f.length,records:f});return}if(f.length===0){q("当前域名无解析记录");return}for(let u of f)console.log(`${$n.cyan(u.recordId)} ${$n.gray(u.rr)} ${$n.gray(u.type)} ${$n.gray(u.value)} ttl=${$n.gray(String(u.ttl||"-"))}`);console.log(""),q("Done.")})}),e.command("dns records add <domain>","添加域名解析记录").option("--rr <rr>","主机记录,如 @/www/api").option("--type <type>","记录类型,如 A/CNAME/TXT").option("--value <value>","记录值").option("--ttl <ttl>","TTL 秒,默认 600").option("--line <line>","线路,默认 default").action(async(n,t)=>{await M({commandLabel:"licell dns records add",interactiveTTY:P(),requiredCapabilities:["dns"]},async()=>{B();let o=A(n,"域名").toLowerCase(),s=C(t.rr),r=C(t.type),c=C(t.value);if(!s||!r||!c)throw Error("dns records add 需要提供 --rr --type --value");let f=qe(t.ttl,"ttl"),u=C(t.line)||"default",l=L(),i=await U(l,"正在添加 DNS 记录...","❌ DNS 记录创建失败",()=>Si(o,{rr:s,type:r,value:c,ttl:f,line:u}));if(!i)return;if(!a())l.stop($n.green("✅ DNS 记录已创建"));if(a()){S({stage:"dns.records.add",domain:o,recordId:i,rr:s,type:r,value:c,ttl:f||600,line:u});return}console.log(`
1610
+ `),t}function ts(e,n){e.steps.push(n),e.updatedAt=_e()}function ne(e,n,t){let s=_e(),o=`licell ${t.join(" ")}`;X({stage:`e2e.${n}`,action:n,status:"start",data:{command:o}});try{eg(e.invocation,t,e.workspaceDir),ts(e.manifest,{name:n,command:o,status:"ok",startedAt:s,endedAt:_e()}),ve(e.manifest),X({stage:`e2e.${n}`,action:n,status:"ok"})}catch(r){throw ts(e.manifest,{name:n,command:o,status:"failed",startedAt:s,endedAt:_e(),error:R(r)}),ve(e.manifest),X({stage:`e2e.${n}`,action:n,status:"failed",message:R(r)}),r}}function mw(e,n,t,s){if(!n){ts(e.manifest,{name:t,command:`licell ${s.join(" ")}`,status:"skipped",startedAt:_e(),endedAt:_e()}),ve(e.manifest);return}ne(e,t,s)}function pw(e,n,t,s){let o=_e(),r=`${t} ${s.join(" ")}`.trim();X({stage:`e2e.${n}`,action:n,status:"start",data:{command:r}});try{hw(t,s,e.workspaceDir),ts(e.manifest,{name:n,command:r,status:"ok",startedAt:o,endedAt:_e()}),ve(e.manifest),X({stage:`e2e.${n}`,action:n,status:"ok"})}catch(c){throw ts(e.manifest,{name:n,command:r,status:"failed",startedAt:o,endedAt:_e(),error:R(c)}),ve(e.manifest),X({stage:`e2e.${n}`,action:n,status:"failed",message:R(c)}),c}}function ww(e){let n=["fc","oss","rds","redis","logs"];if(e.domain||e.domainSuffix)n.push("dns");if(e.enableCdn)n.push("cdn");if(e.includeStatic)n.push("oss");if(e.useVpc)n.push("vpc");return[...new Set(n)]}function $w(e,n){return`licell-${e}-${n.substring(0,4)}`.toLowerCase()}async function kw(e){let n=process.cwd(),t=P(),s=Wl(C(e.suite)),o=C(e.runId)||Ql(),r=aw(o),c=C(e.runtime)||"nodejs22",f=C(e.target)||"preview",u=Boolean(e.enableVpc),l=C(e.domain),i=C(e.domainSuffix),g=C(e.dbInstance),y=C(e.cacheInstance),d=Boolean(e.skipStatic),m=l?Hs(l):void 0,h=i?Vn(i):void 0;if(m&&h)throw Error("--domain 与 --domain-suffix 不能同时使用");let b=Boolean(e.enableCdn);if(b&&!m&&!h)throw Error("--enable-cdn 需要配合 --domain 或 --domain-suffix");let $=Boolean(e.preview);if($&&!h)throw Error("--preview 需要配合 --domain-suffix");let p=Boolean(e.cleanup),w=Boolean(e.yes),H=Nl(C(e.workspace)||Hn(n,".licell","e2e-work",o));Vl(H),Jl(H,{recursive:!0});let T={runId:o,suite:s,status:"running",createdAt:_e(),updatedAt:_e(),projectRoot:n,workspaceDir:H,target:f,runtime:c,resources:{appName:r,...m?{domain:m}:{},...h?{domainSuffix:h}:{}},steps:[],cleanup:{status:"pending",details:[],errors:[]}},x=ve(T,n),E=lc(),_={invocation:E,workspaceDir:H,manifest:T,state:{hasDeployedApi:!1,hasDeployedStatic:!1}};j(Q.bgBlue(Q.white(" \uD83E\uDDEA Licell E2E Runner "))),console.log(`runId: ${Q.cyan(o)}`),console.log(`suite: ${Q.cyan(s)}`),console.log(`workspace: ${Q.cyan(H)}`),console.log(`manifest: ${Q.cyan(x)}
1611
+ `),St("执行计划",[`runtime: ${c}`,`target: ${f}`,`api function: ${r}`,`network: ${u?"vpc(shared licell-vpc)":"public(no-vpc)"}`,...m?[`fixed domain: ${m}`]:[],...h?[`domain suffix: ${h}`]:[],...b?["cdn: enabled"]:[],...$?["preview deploy: enabled"]:[],...s==="full"&&!d?["static deploy: enabled"]:["static deploy: skipped"]]);let F;X({stage:"e2e",action:"run",status:"start",data:{runId:o,suite:s,runtime:c,target:f,workspaceDir:H}});try{await M({commandLabel:"licell e2e run",interactiveTTY:t,requiredCapabilities:ww({domain:m,domainSuffix:h,enableCdn:b,includeStatic:s==="full"&&!d,useVpc:u})},async()=>{ne(_,"init",["init","--runtime",c,"--app",r,"--yes"]);let I=yw(H);if(!I)throw Error("init 成功后未检测到 appName");if(c.startsWith("nodejs"))pw(_,"bun-install","bun",["install"]);if(T.resources.appName=I,!T.resources.domain&&T.resources.domainSuffix)T.resources.domain=`${I}.${T.resources.domainSuffix}`;T.updatedAt=_e(),ve(T,n),St("创建资源",[`fc function: ${I}`,...T.resources.domain?[`domain: ${T.resources.domain}`]:[]]);let fe=["deploy","--type","api","--runtime",c,"--target",f];if(fe.push(u?"--enable-vpc":"--disable-vpc"),m)fe.push("--domain",m);if(h)fe.push("--domain-suffix",h);if(b)fe.push("--enable-cdn");ne(_,"deploy-api",fe),_.state.hasDeployedApi=!0;let Ce=dw(H);if(Ce){if(T.resources.vpcId=Ce.vpcId,T.resources.vswId=Ce.vswId,Ce.sgId)T.resources.sgId=Ce.sgId;ve(T,n)}if(ne(_,"fn-list",["fn","list","--prefix",I,"--limit","20"]),ne(_,"fn-info",["fn","info",I,"--target",f]),ne(_,"fn-invoke",["fn","invoke",I,"--target",f,"--payload",JSON.stringify({runId:o,ping:"pong"})]),ne(_,"env-set",["env","set","LICELL_E2E_RUN_ID",o]),ne(_,"env-list",["env","list","--target",f]),ne(_,"env-pull",["env","pull","--target",f]),ne(_,"env-rm",["env","rm","LICELL_E2E_RUN_ID","--yes"]),ne(_,"release-list",["release","list","--limit","5"]),ne(_,"release-promote",["release","promote","--target",f]),$&&h){let le=["deploy","--type","api","--runtime",c,"--preview"];if(le.push(u?"--enable-vpc":"--disable-vpc"),le.push("--domain-suffix",h),ne(_,"deploy-api-preview",le),s==="full"&&!d){let Je=["deploy","--type","static","--dist",Zl(H,`${o}-preview`),"--preview"];Je.push("--domain-suffix",h),ne(_,"deploy-static-preview",Je)}ne(_,"release-prune-preview",["release","prune","--preview","--keep","2"])}if(ne(_,"logs-once",["logs","--once","--window","180","--lines","200"]),ne(_,"oss-list",["oss","list","--limit","5"]),ne(_,"db-list",["db","list","--limit","5"]),ne(_,"cache-list",["cache","list","--limit","5"]),g)ne(_,"db-info",["db","info",g]),ne(_,"db-connect",["db","connect",g]);if(y)ne(_,"cache-info",["cache","info",y]),ne(_,"cache-connect",["cache","connect",y]);let rn=(()=>{let le=T.resources.domain;if(!le)return;return Qe(le).rootDomain})();if(mw(_,Boolean(rn),"dns-records-list",["dns","records","list",rn||"","--limit","20"]),s==="full"){if(ne(_,"whoami",["whoami"]),!d){let le=k.requireAuth(),cn=Zl(H,o);ne(_,"deploy-static",["deploy","--type","static","--dist",cn]),_.state.hasDeployedStatic=!0,T.resources.staticBucket=$w(I,le.accountId),ve(T,n),St("静态资源",[`oss bucket: ${T.resources.staticBucket}`,`upload prefix: e2e-upload-${o}`]),ne(_,"oss-upload",["oss","upload","--bucket",T.resources.staticBucket,"--source-dir",cn,"--target-dir",`e2e-upload-${o}`]),ne(_,"oss-ls-uploaded",["oss","ls",T.resources.staticBucket,`e2e-upload-${o}`,"--limit","20"])}}}),T.status="succeeded",T.updatedAt=_e(),ve(T,n),St("E2E 结果",[`runId: ${o}`,`status: ${T.status}`,...T.resources.appName?[`fc function: ${T.resources.appName}`]:[],...T.resources.domain?[`domain: ${T.resources.domain}`]:[],...T.resources.staticBucket?[`oss bucket: ${T.resources.staticBucket}`]:[],...T.resources.vpcId?[`vpc: ${T.resources.vpcId}/${T.resources.vswId||"-"}`]:[]]),console.log(Q.green(`✅ E2E run 完成(${o})`)),S({stage:"e2e",runId:o,suite:s,status:T.status,appName:T.resources.appName||null,domain:T.resources.domain||null,staticBucket:T.resources.staticBucket||null,workspaceDir:H})}catch(I){F=I,T.status="failed",T.updatedAt=_e(),T.notes=[...T.notes||[],R(I)],ve(T,n)}if(p){console.log(Q.gray(`
1612
+ 自动进入清理阶段...`));try{await ng(T,{yes:w,keepWorkspace:!1,invocation:E,interactiveTTY:t})}catch(I){if(!F)throw I;console.warn(Q.yellow(`⚠️ 自动清理失败: ${R(I)}`))}}else console.log(Q.gray(`可执行清理命令: licell e2e cleanup ${o}`));if(F)throw F;q("Done.")}async function ng(e,n){let t=e.status,s=n.interactiveTTY??P();await Be(`清理 E2E 运行 ${e.runId} 相关云资源`,{yes:n.yes,interactiveTTY:s});let o=n.invocation||lc(),r=[],c=[],f=e.workspaceDir,u=e.resources.appName,l=e.resources.domain,i=e.resources.staticBucket,g=e.resources.vpcId,y=e.resources.vswId,d=(m,h,b)=>{try{eg(o,h,f),c.push(`${m}: ok`)}catch($){let p=R($),w=p.toLowerCase();if((b?.ignoreErrorPatterns||[]).some((T)=>w.includes(T))){c.push(`${m}: skipped (${p})`);return}r.push(`${m}: ${p}`),c.push(`${m}: failed`)}};if(e.cleanup=e.cleanup||{},e.cleanup.attemptedAt=_e(),e.cleanup.status="pending",e.cleanup.details=c,e.cleanup.errors=r,e.updatedAt=_e(),ve(e,e.projectRoot),X({stage:"e2e.cleanup",action:"cleanup",status:"start",data:{runId:e.runId,appName:u||null,domain:l||null,staticBucket:i||null}}),St("清理目标",[...u?[`fc function: ${u}`]:[],...l?[`domain binding: ${l}`]:[],...i?[`oss bucket: ${i}`]:[],...g?[`vpc network: ${g}/${y||"-"} (shared, keep)`]:[],...n.keepWorkspace?["workspace: keep"]:[`workspace: ${f}`]]),await M({commandLabel:"licell e2e cleanup",interactiveTTY:s,requiredCapabilities:["fc",...l?["dns"]:[],...i?["oss"]:[]]},async()=>{if(l)console.log(Q.gray(`清理 domain: ${l}`)),d("domain-rm",["domain","rm",l,"--yes"]);if(u){console.log(Q.gray(`清理 preview 域名: ${u}`)),d("release-prune-preview",["release","prune","--preview","--keep","0","--apply","--yes"],{ignoreErrorPatterns:["not found","no preview"]}),console.log(Q.gray(`清理 function: ${u}`)),d("fn-rm",["fn","rm",u,"--force","--yes"],{ignoreErrorPatterns:["functionnotfound","does not exist","not found"]});let m=`${u}-static-proxy`;console.log(Q.gray(`清理 static proxy function: ${m}`)),d("fn-rm-static-proxy",["fn","rm",m,"--force","--yes"],{ignoreErrorPatterns:["functionnotfound","does not exist","not found"]})}if(i){console.log(Q.gray(`清理 oss bucket: ${i}`));try{let m=await Ni(i);c.push(`oss-bucket-rm: ok (${m.bucket}, objects=${m.deletedObjects}, bucketDeleted=${m.deletedBucket})`),console.log(Q.green(`oss 清理完成: ${m.bucket} (objects=${m.deletedObjects})`))}catch(m){r.push(`oss-bucket-rm: ${R(m)}`),c.push("oss-bucket-rm: failed"),console.warn(Q.yellow(`oss 清理失败: ${R(m)}`))}}if(g)c.push(`vpc-rm: skipped (${g} 为共享网络,e2e 默认不自动删除)`)}),!n.keepWorkspace)try{gw(f,{recursive:!0,force:!0}),c.push("workspace-rm: ok"),console.log(Q.green(`workspace 已清理: ${f}`))}catch(m){r.push(`workspace-rm: ${R(m)}`),c.push("workspace-rm: failed"),console.warn(Q.yellow(`workspace 清理失败: ${R(m)}`))}if(e.cleanup.finishedAt=_e(),e.cleanup.status=r.length>0?"partial":"done",r.length>0)e.status="partial_cleaned";else e.status=t==="failed"?"failed":"cleaned";if(e.updatedAt=_e(),ve(e,e.projectRoot),r.length>0){console.warn(Q.yellow(`⚠️ 清理存在 ${r.length} 个失败项:`));for(let m of r)console.warn(Q.yellow(`- ${m}`))}else St("清理结果",c.map((m)=>m.replace(/^([^:]+): /,"$1 => "))),console.log(Q.green(`✅ 清理完成: ${e.runId}`));S({stage:"e2e.cleanup",runId:e.runId,status:e.cleanup.status||"unknown",details:c,errors:r})}function tg(e){e.command("e2e run","执行固定 E2E 套件(默认 smoke)").option("--suite <suite>","套件:smoke/full(默认 smoke)").option("--run-id <id>","指定 runId(默认自动生成)").option("--runtime <runtime>","部署 runtime(默认 nodejs22)").option("--target <alias>","部署 target alias(默认 preview)").option("--enable-vpc","API 部署启用 VPC(默认关闭,便于无残留清理)").option("--domain <domain>","固定完整域名(可选)").option("--domain-suffix <suffix>","固定域名后缀(可选)").option("--db-instance <instanceId>","full 套件时附加验证 db info/connect(复用已有实例)").option("--cache-instance <instanceId>","full 套件时附加验证 cache info/connect(复用已有实例)").option("--skip-static","full 套件时跳过 static + oss upload 场景").option("--enable-cdn","部署时启用 CDN(需配合 domain/domain-suffix)").option("--preview","测试 preview 部署流程(需配合 --domain-suffix)").option("--cleanup","执行完后自动清理").option("--workspace <dir>","指定 E2E 工作目录(默认 .licell/e2e-work/<runId>)").option("--yes","自动清理时跳过二次确认").action(async(n)=>{try{await kw(n)}catch(t){if(a())he(t,{stage:"e2e"});else console.error(Q.red(R(t)));process.exitCode=1}}),e.command("e2e cleanup [runId]","清理指定 E2E run 产生的资源").option("--manifest <path>","直接指定 manifest 文件路径").option("--keep-workspace","保留本地 workspace 目录").option("--yes","跳过二次确认(危险)").action(async(n,t)=>{try{let s=process.cwd(),o=C(t.manifest),r=null;if(o){let c=Nl(o);if(!gc(c))throw Error(`manifest 不存在: ${c}`);r=JSON.parse(yc(c,"utf8"))}else{let c=C(n)||Dl(s);if(!c)throw Error("未找到任何 e2e manifest,请先执行 `licell e2e run`");if(r=uc(c,s),!r)throw Error(`未找到 runId=${c} 的 manifest`)}j(Q.bgBlue(Q.white(" \uD83E\uDDF9 Licell E2E Cleanup "))),console.log(`runId: ${Q.cyan(r.runId)}`),console.log(`workspace: ${Q.cyan(r.workspaceDir)}`),console.log(`manifest: ${Q.cyan(eo(r.runId,r.projectRoot))}
1613
+ `),await ng(r,{yes:Boolean(t.yes),keepWorkspace:Boolean(t.keepWorkspace)}),q("Done.")}catch(s){if(a())he(s,{stage:"e2e.cleanup"});else console.error(Q.red(R(s)));process.exitCode=1}}),e.command("e2e list","查看本项目 e2e 运行记录").action(()=>{let n=ic(process.cwd());if(n.length===0){q("当前项目暂无 e2e 记录");return}for(let t of n){let s=uc(t,process.cwd());if(!s)continue;console.log(`${Q.cyan(t)} suite=${Q.gray(s.suite)} status=${Q.gray(s.status)} workspace=${Q.gray(s.workspaceDir)}`)}console.log(""),q("Done.")})}import W from"picocolors";Ae();Ve();se();K();re();K();Cn();Dn();import*as sg from"@alicloud/fc20230330";Ie();import _w,*as og from"@alicloud/oss20190517";import*as rg from"@alicloud/openapi-client";import*as cg from"@alicloud/tea-util";var bw=te(_w,"@alicloud/oss20190517");function Tw(){let e=k.requireAuth(),n=new bw(new rg.Config({accessKeyId:e.ak,accessKeySecret:e.sk,regionId:e.region,endpoint:`oss-${e.region}.aliyuncs.com`})),t=new cg.RuntimeOptions({connectTimeout:8000,readTimeout:120000});return{auth:e,client:n,runtime:t}}async function Cw(e){let{client:n}=ie(),t=[],s=new RegExp(`^${e}-preview-v\\d+\\.`),o;while(!0){let r=await Y(()=>n.listCustomDomains(new sg.ListCustomDomainsRequest({limit:100,nextToken:o}))),c=r.body?.customDomains||[];for(let f of c){let u=f.domainName;if(u&&s.test(u))t.push(u)}if(o=r.body?.nextToken,!o||c.length===0)break}return t}function Ew(e,n){let t=e.match(new RegExp(`^${n}-preview-v(\\d+)\\.`));if(!t)return null;return parseInt(t[1],10)}async function Sw(e,n){try{await Y(()=>e.deleteCustomDomain(n))}catch(t){if(!ue(t))throw t}}async function qw(e,n){let{client:t,runtime:s}=Tw(),o=await Qs(e,n,1000),r=0;for(let c of o)try{await Y(()=>t.deleteObjectWithOptions(e,c.name,new og.DeleteObjectRequest({}),{},s)),r++}catch(f){if(!ue(f))throw f}return r}async function fg(e,n,t){let s=await Cw(e),o=s.map((i)=>({domain:i,version:Ew(i,e)})).filter((i)=>i.version!==null).sort((i,g)=>(g.version||0)-(i.version||0)),r=o.slice(0,n),c=o.slice(n),f={keep:n,totalPreviewDomains:s.length,candidates:c.map((i)=>i.domain),deletedDomains:[],deletedOssPaths:[],failed:[]};if(!t)return f;let u=Ws(e),{client:l}=ie();for(let i of c)try{if(await Sw(l,i.domain),f.deletedDomains.push(i.domain),i.version!==null)for(let g of[i.version,i.version-1]){if(g<=0)continue;let y=`_preview/${g}/`;try{if(await qw(u,y)>0)f.deletedOssPaths.push(y)}catch{}}}catch(g){f.failed.push({domain:i.domain,reason:g instanceof Error?g.message:String(g)})}return f}function ug(e){e.command("release list","查看函数版本列表").option("--limit <n>","返回版本数量,默认 20").action(async(n)=>{await M({commandLabel:"licell release list",interactiveTTY:P(),requiredCapabilities:["fc"]},async()=>{j(W.bgBlue(W.white(" \uD83D\uDCDA Function Versions "))),B();let t=k.getProject();Le(t);let s=Ye(n.limit,20,100),o=A(),r=await v(o,"正在拉取函数版本列表...","❌ 获取版本列表失败",()=>dt(t.appName,s));if(!r)return;if(!a())o.stop(W.green(`✅ 共获取 ${r.length} 个版本`));if(a()){S({stage:"release.list",appName:t.appName,count:r.length,versions:r});return}if(r.length===0){q("当前函数还没有已发布版本");return}for(let c of r){let f=c.versionId||"unknown",u=c.createdTime||"-",l=c.description||"-";console.log(`${W.cyan(f)} ${W.gray(u)} ${l}`)}q("Done.")})}),e.command("release promote [versionId]","发布并切流到目标别名").option("--target <target>","目标别名,默认 prod").action(async(n,t)=>{await M({commandLabel:"licell release promote",interactiveTTY:P(),requiredCapabilities:["fc"]},async()=>{j(W.bgBlue(W.white(" \uD83D\uDE80 Promote Release "))),B();let s=k.getProject();Le(s);let o=tn(t.target),r=A(),c=await v(r,`正在准备发布到别名 ${o}...`,"❌ 切流失败",async()=>{let f=n?L(n,"versionId"):"";if(!f){r.message("未指定 versionId,正在发布当前函数代码为新版本...");try{f=await Sn(s.appName,`promote ${o} at ${new Date().toISOString()}`)}catch(u){if(!As(u))throw u;r.message("检测到当前代码无变更,复用最新已发布版本..."),f=await Us(s.appName)}}return await qn(s.appName,o,f,`promoted by licell at ${new Date().toISOString()}`),f});if(!c)return;if(!a())r.stop(W.green("✅ 别名切流完成"));if(a()){S({stage:"release.promote",appName:s.appName,target:o,versionId:c});return}console.log(`
1614
+ \uD83C\uDFF7️ alias=${W.cyan(o)} -> version=${W.cyan(c)}
1615
+ `),q("Done.")})}),e.command("release rollback <versionId>","回滚到指定函数版本").option("--target <target>","目标别名,默认 prod").action(async(n,t)=>{await M({commandLabel:"licell release rollback",interactiveTTY:P(),requiredCapabilities:["fc"]},async()=>{j(W.bgBlue(W.white(" ↩ Rollback Release "))),B();let s=k.getProject();Le(s);let o=tn(t.target),r=L(n,"versionId"),c=A();if(!await v(c,`正在回滚 ${o} 到版本 ${r}...`,"❌ 回滚失败",async()=>{return await qn(s.appName,o,r,`rollback by licell at ${new Date().toISOString()}`),!0}))return;if(!a())c.stop(W.green("✅ 回滚完成"));if(a()){S({stage:"release.rollback",appName:s.appName,target:o,versionId:r});return}console.log(`
1616
+ \uD83C\uDFF7️ alias=${W.cyan(o)} -> version=${W.cyan(r)}
1617
+ `),q("Done.")})}),e.command("release prune","清理历史函数版本(默认仅预览)").option("--keep <n>","保留最近 N 个版本,默认 10").option("--apply","执行删除,未传则仅预览").option("--yes","跳过二次确认(危险)").option("--preview","清理预览域名绑定(而非函数版本)").action(async(n)=>{await M({commandLabel:"licell release prune",interactiveTTY:P(),requiredCapabilities:n.preview?["fc","dns"]:["fc"]},async()=>{j(W.bgBlue(W.white(n.preview?" \uD83E\uDDF9 Prune Preview Domains ":" \uD83E\uDDF9 Prune Function Versions "))),B();let t=k.getProject();Le(t);let s=qe(n.keep,"keep")||(n.preview?3:10),o=Boolean(n.apply);if(n.preview){if(o)await Be(`清理预览域名绑定(保留最近 ${s} 个)`,{yes:Boolean(n.yes)});let f=A(),u=await v(f,o?"正在清理预览域名...":"正在预览可清理的预览域名...","❌ 清理失败",()=>fg(t.appName,s,o));if(!u)return;if(!a())f.stop(W.green(o?"✅ 清理任务完成":"✅ 预览完成"));if(a()){S({stage:"release.prune.preview",appName:t.appName,keepRequested:s,applyRequested:o,...u});return}if(console.log(`
1618
+ 保留数量: ${W.cyan(String(u.keep))}`),console.log(`发现预览域名: ${W.cyan(String(u.totalPreviewDomains))}`),console.log(`候选删除: ${W.cyan(String(u.candidates.length))}`),u.candidates.length>0)console.log(`候选: ${u.candidates.join(", ")}`);if(o){if(console.log(`已删除域名绑定: ${W.cyan(String(u.deletedDomains.length))}`),console.log(`已删除 OSS 路径: ${W.cyan(String(u.deletedOssPaths.length))}`),u.failed.length>0){console.log(W.yellow(`删除失败: ${u.failed.length}`));for(let l of u.failed)console.log(W.yellow(`- ${l.domain}: ${l.reason}`))}}else console.log(W.gray(`
1619
+ 提示: 加上 --apply 才会执行实际删除`));console.log(""),q("Done.");return}if(o)await Be(`清理函数历史版本(保留最近 ${s} 个)`,{yes:Boolean(n.yes)});let r=A(),c=await v(r,o?"正在清理历史版本...":"正在预览可清理版本...","❌ 清理失败",()=>hr(t.appName,s,o));if(!c)return;if(!a())r.stop(W.green(o?"✅ 清理任务完成":"✅ 预览完成"));if(a()){S({stage:"release.prune",appName:t.appName,keepRequested:s,applyRequested:o,...c});return}if(console.log(`
1620
+ 保留数量: ${W.cyan(String(c.keep))}`),console.log(`总发布版本: ${W.cyan(String(c.totalVersions))}`),console.log(`Alias 保护版本: ${W.cyan(String(c.aliasProtectedVersions.length))}`),console.log(`候选删除版本: ${W.cyan(String(c.candidates.length))}`),c.candidates.length>0)console.log(`候选: ${c.candidates.join(", ")}`);if(o){if(console.log(`已删除: ${W.cyan(String(c.deleted.length))}`),c.failed.length>0){console.log(W.yellow(`删除失败: ${c.failed.length}`));for(let f of c.failed)console.log(W.yellow(`- ${f.versionId}: ${f.reason}`))}}else console.log(W.gray(`
1621
+ 提示: 加上 --apply 才会执行实际删除`));console.log(""),q("Done.")})})}import on from"picocolors";Ve();Ae();se();K();re();function ig(e){e.command("domain add <domain>","绑定自定义域名").option("--ssl","自动配置 Let's Encrypt 免费证书开启 HTTPS").option("--ssl-force-renew","配合 --ssl 强制续签证书(忽略到期阈值)").option("--target <target>","将域名路由到指定 FC alias(如 prod/preview)").action(async(n,t)=>{await M({commandLabel:"licell domain add",interactiveTTY:P(),requiredCapabilities:["fc","dns"]},async()=>{j(on.bgCyan(on.black(" \uD83C\uDF10 Domain & SSL Configuration ")));let s=await B(),o=L(n,"域名"),r=tn(t.target);if(t.sslForceRenew&&!t.ssl)throw Error("--ssl-force-renew 需要与 --ssl 一起使用");let c=k.getProject();Le(c);let f=A(),u=await v(f,`正在配置云解析 DNS,将 ${o} 指向应用...`,"❌ 配置流程中断",async()=>{let l=`${s.accountId}.${s.region}.fc.aliyuncs.com`;await _t(o,l,r);try{f.message(`正在确保别名 ${r} 存在...`);let i;try{i=await Sn(c.appName,`domain bind ${r} at ${new Date().toISOString()}`)}catch(g){if(!As(g))throw g;i=await Us(c.appName)}await qn(c.appName,r,i,`domain bind by licell at ${new Date().toISOString()}`)}catch{if(!a())console.warn(on.yellow(`⚠️ 未能自动创建别名 ${r},请先 deploy 后执行 licell release promote`))}if(t.ssl)return f.message("DNS CNAME 配置成功。正在接管 Let's Encrypt 签发流程..."),vi(o,f,{forceRenew:Boolean(t.sslForceRenew)});return`http://${o}`});if(!u)return;if(!a())f.stop(on.green("✅ 域名绑定与网络平面配置大功告成!"));if(a()){S({stage:"domain.add",domain:o,releaseTarget:r||null,ssl:Boolean(t.ssl),finalUrl:u});return}if(r)console.log(`
1622
+ \uD83C\uDFF7️ 域名路由已绑定 alias=${on.cyan(r)}
1623
+ `);q(`\uD83D\uDD17 你的应用现在可通过安全的 ${on.cyan(on.underline(u))} 访问`)})}),e.command("domain rm <domain>","解绑自定义域名并清理 DNS CNAME").option("--yes","跳过二次确认(危险)").action(async(n,t)=>{await M({commandLabel:"licell domain rm",interactiveTTY:P(),requiredCapabilities:["fc","dns"]},async()=>{j(on.bgCyan(on.black(" \uD83C\uDF10 Domain Removal "))),await B();let s=L(n,"域名").toLowerCase();await Be(`解绑域名 ${s}`,{yes:Boolean(t.yes)});let o=A();if(!await v(o,`正在解绑域名 ${s}...`,"❌ 域名解绑失败",async()=>{return await Ci(s),!0}))return;if(!a())o.stop(on.green("✅ 域名已解绑并完成 DNS 清理")),q("Done.");else S({stage:"domain.rm",domain:s,removed:!0})})})}import{text as Pw,isCancel as Rw}from"@clack/prompts";import $n from"picocolors";Ae();se();re();function lg(e){e.command("dns records list [domain]","查看域名解析记录").option("--limit <n>","返回数量,默认 100").action(async(n,t)=>{await M({commandLabel:"licell dns records list",interactiveTTY:P(),requiredCapabilities:["dns"]},async()=>{B();let s=n;if(!s){if(!P())throw Error("缺少域名参数,请使用:licell dns records list <domain>");let u=await Pw({message:"请输入要查看的域名:",placeholder:"example.com"});if(Rw(u))process.exit(0);s=L(u,"域名")}let o=L(s,"域名").toLowerCase(),r=Ye(t.limit,100,500),c=A(),f=await v(c,`正在拉取 ${o} 的解析记录...`,"❌ 获取 DNS 记录失败",()=>Ei(o,r));if(!f)return;if(!a())c.stop($n.green(`✅ 共获取 ${f.length} 条记录`));if(a()){S({stage:"dns.records.list",domain:o,count:f.length,records:f});return}if(f.length===0){q("当前域名无解析记录");return}for(let u of f)console.log(`${$n.cyan(u.recordId)} ${$n.gray(u.rr)} ${$n.gray(u.type)} ${$n.gray(u.value)} ttl=${$n.gray(String(u.ttl||"-"))}`);console.log(""),q("Done.")})}),e.command("dns records add <domain>","添加域名解析记录").option("--rr <rr>","主机记录,如 @/www/api").option("--type <type>","记录类型,如 A/CNAME/TXT").option("--value <value>","记录值").option("--ttl <ttl>","TTL 秒,默认 600").option("--line <line>","线路,默认 default").action(async(n,t)=>{await M({commandLabel:"licell dns records add",interactiveTTY:P(),requiredCapabilities:["dns"]},async()=>{B();let s=L(n,"域名").toLowerCase(),o=C(t.rr),r=C(t.type),c=C(t.value);if(!o||!r||!c)throw Error("dns records add 需要提供 --rr --type --value");let f=qe(t.ttl,"ttl"),u=C(t.line)||"default",l=A(),i=await v(l,"正在添加 DNS 记录...","❌ DNS 记录创建失败",()=>Si(s,{rr:o,type:r,value:c,ttl:f,line:u}));if(!i)return;if(!a())l.stop($n.green("✅ DNS 记录已创建"));if(a()){S({stage:"dns.records.add",domain:s,recordId:i,rr:o,type:r,value:c,ttl:f||600,line:u});return}console.log(`
1624
1624
  recordId: ${$n.cyan(i)}
1625
- `),q("Done.")})}),e.command("dns records rm <recordId>","删除域名解析记录").option("--yes","跳过二次确认(危险)").action(async(n,t)=>{await M({commandLabel:"licell dns records rm",interactiveTTY:P(),requiredCapabilities:["dns"]},async()=>{B();let o=A(n,"recordId");await Be(`删除 DNS 记录 ${o}`,{yes:Boolean(t.yes)});let s=L();if(!await U(s,`正在删除记录 ${o}...`,"❌ DNS 记录删除失败",async()=>{return await qi(o),!0}))return;if(!a())s.stop($n.green("✅ DNS 记录已删除")),q("Done.");else S({stage:"dns.records.rm",recordId:o,removed:!0})})})}import qt from"picocolors";import{writeFileSync as gg}from"fs";Le();Ve();oe();K();re();function yg(e){e.command("env list","查看云端环境变量").option("--target <target>","查看指定 FC alias 的环境变量(如 prod/preview)").option("--show-values","显示完整变量值(默认隐藏)").action(async(n)=>{await M({commandLabel:"licell env list",interactiveTTY:P(),requiredCapabilities:["fc"]},async()=>{B();let t=k.getProject();Ae(t);let o=n.target?tn(n.target):void 0,s=L(),r=await U(s,o?`正在拉取 alias=${o} 的环境变量...`:"正在拉取云端环境变量...","❌ 获取环境变量失败",()=>yt(t.appName,o));if(!r)return;if(!a())s.stop(qt.green(`✅ 共 ${Object.keys(r).length} 个环境变量`));let c=Object.entries(r).sort(([u],[l])=>u.localeCompare(l)),f=Boolean(n.showValues);if(a()){let u=f?Object.fromEntries(c):Object.fromEntries(c.map(([l,i])=>[l,`<hidden:${String(i).length} chars>`]));S({stage:"env.list",qualifier:o||null,count:c.length,showValues:f,envs:u});return}if(c.length===0){q("云端当前无环境变量");return}for(let[u,l]of c){let i=f?l:`<hidden:${String(l).length} chars>`;console.log(`${qt.cyan(u)}=${i}`)}console.log(""),q("Done.")})}),e.command("env set <key> <value>","设置云端环境变量(并同步本地 .licell/project.json)").action(async(n,t)=>{await M({commandLabel:"licell env set",interactiveTTY:P(),requiredCapabilities:["fc"]},async()=>{B();let o=k.getProject();Ae(o);let s=Tr(A(n,"环境变量名")),r=A(t,"环境变量值"),c=L(),f=await U(c,`正在写入环境变量 ${s}...`,"❌ 环境变量写入失败",()=>yr(o.appName,s,r));if(!f)return;if(k.setProject({envs:f},{replaceEnvs:!0}),!a())c.stop(qt.green("✅ 环境变量已写入云端并同步到本地配置")),q("Done.");else S({stage:"env.set",key:s,updatedCount:Object.keys(f).length})})}),e.command("env rm <key>","删除云端环境变量(并同步本地 .licell/project.json)").option("--yes","跳过二次确认(危险)").action(async(n,t)=>{await M({commandLabel:"licell env rm",interactiveTTY:P(),requiredCapabilities:["fc"]},async()=>{B();let o=k.getProject();Ae(o);let s=Tr(A(n,"环境变量名"));await Be(`删除环境变量 ${s}`,{yes:Boolean(t.yes)});let r=L(),c=await U(r,`正在删除环境变量 ${s}...`,"❌ 环境变量删除失败",()=>dr(o.appName,s));if(!c)return;if(k.setProject({envs:c},{replaceEnvs:!0}),!a())r.stop(qt.green("✅ 环境变量已从云端移除(若存在)并同步本地配置")),q("Done.");else S({stage:"env.rm",key:s,updatedCount:Object.keys(c).length})})}),e.command("env pull","拉取云端环境变量").option("--target <target>","从指定 FC alias 拉取环境变量(如 prod/preview)").action(async(n)=>{await M({commandLabel:"licell env pull",interactiveTTY:P(),requiredCapabilities:["fc"]},async()=>{B();let t=k.getProject();Ae(t);let o=n.target?tn(n.target):void 0,s=L(),r=await U(s,o?`正在拉取 alias=${o} 的环境变量...`:"正在拉取云端环境变量...","❌ 环境变量拉取失败",()=>yt(t.appName,o));if(!r)return;k.setProject({envs:r},{replaceEnvs:!0});let c=Object.entries(r);if(c.length===0){try{gg(".env","",{mode:384})}catch(u){throw Error(`写入 .env 文件失败: ${u instanceof Error?u.message:String(u)}`)}if(!a())s.stop(qt.yellow("云端无环境变量,已清空本地 .env"));S({stage:"env.pull",qualifier:o||null,count:0,envFile:".env",emptied:!0});return}let f=c.map(([u,l])=>`${u}="${wi(String(l))}"`).join(`
1626
- `);try{gg(".env",f,{mode:384})}catch(u){throw Error(`写入 .env 文件失败: ${u instanceof Error?u.message:String(u)}`)}if(Xu(),!a())s.stop(qt.green(`✅ 已拉取 ${c.length} 个环境变量并写入 .env`));S({stage:"env.pull",qualifier:o||null,count:c.length,envFile:".env",emptied:!1})})})}import mg from"picocolors";K();De();Ie();ce();import Iw,*as dc from"@alicloud/sls20201230";import*as hg from"@alicloud/openapi-client";import An from"picocolors";var Fw=te(Iw,"@alicloud/sls20201230");function xw(e){return e.replace(/['"\\*?:|\[\]{}()&!^~]/g,"")}function Hw(e){let n=R(e).toLowerCase();return n.includes("projectnotexist")||n.includes("logstorenotexist")}function dg(e,n,t=!1){let o=[];return e.sort((s,r)=>parseInt(s.__time__||"0",10)-parseInt(r.__time__||"0",10)).forEach((s)=>{let r=`${s.__time__||""}|${s.__source__||""}|${s.message||s.content||""}`;if(n?.has(r))return;if(n){if(n.add(r),n.size>5000){let l=[...n];n.clear();for(let i of l.slice(-2500))n.add(i)}}let c=new Date(parseInt(s.__time__||"0",10)*1000).toLocaleTimeString(),f=String(s.message||s.content||JSON.stringify(s)).trim();if(f.toLowerCase().includes("error"))f=An.red(f);let u=`${An.gray(`[${c}]`)} ${f}`;if(o.push(u),!t)console.log(u)}),o}async function ag(e,n={}){let t=k.requireAuth(),o=new Fw(new hg.Config({accessKeyId:t.ak,accessKeySecret:t.sk,endpoint:`${t.region}.log.aliyuncs.com`})),s=`aliyun-fc-${t.region}-${t.accountId}`,r="function-log",c=xw(e),f=n.lineLimit&&n.lineLimit>0?Math.floor(n.lineLimit):1000;if(n.once){let d=n.windowSeconds&&n.windowSeconds>0?Math.floor(n.windowSeconds):120,m=Math.floor(Date.now()/1000),h=Math.max(0,m-d);try{let $=(await o.getLogs(s,"function-log",new dc.GetLogsRequest({from:h,to:m,query:`* and functionName: "${c}"`,line:f}))).body||[];if($.length===0){if(!n.silent)console.log(An.gray(`最近 ${d}s 无日志`));return{mode:"once",logs:[],lines:[]}}let p=dg($,void 0,Boolean(n.silent));return{mode:"once",logs:$,lines:p}}catch(b){if(Hw(b)){if(!n.silent)console.log(An.yellow(`⚠️ 日志服务尚未就绪,已跳过: ${R(b)}`));return{mode:"once",logs:[],lines:[]}}throw b}}if(!n.silent)console.log(An.gray(`
1627
- \uD83D\uDCE1 正在监听云端 [${An.cyan(e)}] 的实时日志流 (Ctrl+C 退出)...
1628
- `));let u=Math.floor(Date.now()/1000)-60,l=new Set,i=0,g=!0,y=()=>{if(g=!1,!n.silent)console.log(An.gray(`
1629
- \uD83D\uDC4B 日志流已断开`));process.exit(0)};process.on("SIGINT",y),process.on("SIGTERM",y);while(g){try{let d=Math.floor(Date.now()/1000);if(d<=u){await ge(1500);continue}let h=(await o.getLogs(s,"function-log",new dc.GetLogsRequest({from:u,to:d,query:`* and functionName: "${c}"`,line:f}))).body||[];if(dg(h,l,Boolean(n.silent)),h.length>0){let b=parseInt(h[h.length-1].__time__||`${u}`,10);if(Number.isFinite(b)&&b>0)u=b}}catch(d){let m=Date.now();if(m-i>1e4){let h=R(d);if(!n.silent)console.log(An.yellow(`⚠️ 日志拉取失败,10 秒后重试: ${h}`));i=m}}await ge(1500)}}Le();oe();K();re();function pg(e){e.command("logs","查看云端日志(默认实时流式)").option("--once","仅拉取一次最近日志并退出").option("--window <seconds>","一次拉取模式的时间窗(默认 120 秒)").option("--lines <n>","每次请求最大日志条数(默认 1000)").action(async(n)=>{await M({commandLabel:"licell logs",interactiveTTY:P(),requiredCapabilities:["fc","logs"]},async()=>{j(mg.bgBlue(mg.white(" \uD83D\uDCE1 Serverless Log Stream "))),B();let t=k.getProject();Ae(t,"当前目录下没有找到绑定的云端项目");let o=a()?!0:Boolean(n.once),s=await ag(t.appName,{once:o,windowSeconds:qe(n.window,"--window"),lineLimit:qe(n.lines,"--lines"),silent:a()});if(a())S({stage:"logs",appName:t.appName,once:o,lines:s&&"lines"in s?s.lines:[],count:s&&"logs"in s?s.logs.length:0})})})}oe();re();import Pt from"picocolors";import{createHash as Aw}from"crypto";import{spawnSync as kg}from"child_process";import{mkdtempSync as Lw,rmSync as Uw,writeFileSync as vw}from"fs";import{join as wg}from"path";import{tmpdir as Gw}from"os";var hc="agents-infrastructure/licell",Mw=/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/;function Bw(e){if(e.scriptUrl)return e.scriptUrl;let n=e.repo||hc;if(!Mw.test(n))throw Error("无效的仓库格式,必须是 owner/repo");if(e.version)return`https://github.com/${n}/releases/download/${e.version}/install.sh`;return`https://github.com/${n}/releases/latest/download/install.sh`}function Yw(e){let n=e.lastIndexOf("/");if(n<0)return null;return`${e.substring(0,n)}/SHA256SUMS.txt`}function Ow(e,n){for(let t of e.split(`
1630
- `)){let o=t.trim();if(!o)continue;let s=o.match(/^([a-f0-9]{64})\s+(.+)$/);if(s&&s[2].trim()===n)return s[1]}return null}function Kw(e,n){return Aw("sha256").update(e,"utf8").digest("hex")===n}function $g(e,n){let t=kg("curl",["-fsSL",e],{encoding:"utf8",maxBuffer:20971520});if(t.status!==0||!t.stdout){let o=(t.stderr||"").trim();throw Error(o?`下载${n}失败: ${o}`:`下载${n}失败`)}return t.stdout}function _g(e){e.command("upgrade","升级到最新 release 版本").option("--version <tag>","指定版本(如 v0.9.6)").option("--repo <owner/repo>",`GitHub 仓库(默认 ${hc})`).option("--script-url <url>","覆盖 install.sh 地址(调试用途,需配合 --skip-checksum)").option("--skip-checksum","跳过 SHA256 完整性校验(不推荐)").option("--dry-run","只输出将使用的安装脚本地址").action(async(n)=>{j(Pt.bgBlue(Pt.white(" ⬆ Licell Upgrade ")));let t=C(n.version),o=C(n.repo)||hc,s=C(n.scriptUrl),r=Boolean(n.skipChecksum);if(s&&!r)throw Error("使用 --script-url 时必须同时指定 --skip-checksum 以确认跳过完整性校验");let c=Bw({repo:o,version:t,scriptUrl:s});if(Boolean(n.dryRun)){if(a())S({stage:"upgrade",dryRun:!0,scriptUrl:c});else console.log(c),q("Done.");return}let f=L();f.start("正在下载升级脚本...");let u=$g(c,"安装脚本");if(!r){let g=Yw(c);if(g){f.message("正在校验脚本完整性...");try{let y=$g(g,"SHA256SUMS"),d=Ow(y,"install.sh");if(d){if(!Kw(u,d))throw Error("install.sh SHA256 校验失败,脚本可能被篡改。如需跳过校验请使用 --skip-checksum")}else if(a())X({stage:"upgrade",action:"checksum",status:"info",message:"SHA256SUMS 未包含 install.sh,已跳过校验"});else console.error(Pt.yellow("⚠️ SHA256SUMS 中未找到 install.sh 条目,跳过校验"))}catch(y){if(y instanceof Error&&y.message.includes("SHA256 校验失败"))throw y;if(a())X({stage:"upgrade",action:"checksum",status:"info",message:"无法下载 SHA256SUMS,已跳过校验"});else console.error(Pt.yellow("⚠️ 无法下载 SHA256SUMS 校验文件,跳过校验"))}}}let l=Lw(wg(Gw(),"licell-upgrade-")),i=wg(l,"install.sh");try{if(vw(i,u,{mode:448}),!a())f.stop(Pt.green("✅ 脚本下载完成,开始安装"));let g=kg("bash",[i],{stdio:a()?"pipe":"inherit",encoding:"utf8",env:{...process.env,LICELL_SKIP_RUN_CHECK:"1"}});if(a()){let y=typeof g.stdout==="string"?g.stdout.trim():"",d=typeof g.stderr==="string"?g.stderr.trim():"";if(y)X({stage:"upgrade.install",action:"stdout",status:"info",message:y});if(d)X({stage:"upgrade.install",action:"stderr",status:"info",message:d})}if(g.status!==0)throw Error(`升级安装失败(exit=${g.status??"unknown"})`);if(a())S({stage:"upgrade",dryRun:!1,scriptUrl:c,checksumSkipped:r});else q(Pt.green("✅ 升级完成"))}finally{Uw(l,{recursive:!0,force:!0})}})}import ro from"picocolors";import{existsSync as ss,readFileSync as Eg,writeFileSync as wc,mkdirSync as Sg}from"fs";import{join as $c,dirname as qg}from"path";import{homedir as Pg}from"os";ce();re();import{spawn as jw}from"child_process";import{createInterface as Ww}from"readline";import{isAbsolute as Qw,resolve as ac}from"path";function ts(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function Dw(e){process.stdout.write(`${JSON.stringify(e)}
1631
- `)}function Tg(e){Dw(e)}function oo(e,n,t,o){Tg({jsonrpc:"2.0",id:e,error:o?{code:n,message:t,data:o}:{code:n,message:t}})}function Rt(e,n){Tg({jsonrpc:"2.0",id:e,result:n})}function Vw(){let e=process.execPath,n=[],t=process.argv[1];if(typeof t==="string"){let o=t.toLowerCase();if(o.endsWith(".js")||o.endsWith(".cjs")||o.endsWith(".mjs")||o.endsWith(".ts"))n.push(t)}return{command:e,baseArgs:n}}function Xw(e,n){if(typeof n!=="string")return e;let t=n.trim();if(!t)return e;let o=Qw(t)?ac(t):ac(e,t),s=ac(e);if(o===s)return o;if(!o.startsWith(`${s}/`))throw Error(`cwd 必须在 projectRoot 内部: cwd=${o}, projectRoot=${s}`);return o}function bg(e){let n="",t=0,o=!1;return{push(s){if(o)return;let r=t+s.length;if(r<=e){n+=s.toString("utf8"),t=r;return}let c=Math.max(0,e-t);if(c>0)n+=s.subarray(0,c).toString("utf8");t=e,o=!0},get(){return{text:n,truncated:o}}}}function D(e){if(typeof e!=="string")return;let n=e.trim();return n.length>0?n:void 0}function Te(e){if(typeof e==="boolean")return e;return}function Ln(e){if(typeof e!=="number")return;if(!Number.isFinite(e))return;return e}function os(e,n){if(e!==!0)throw Error(n)}function Zw(e){for(let n=0;n<e.length;n+=1){let t=e[n];if(t==="--")break;if(t==="--output")return!0;if(t.startsWith("--output="))return!0}return!1}async function Jw(e){if(e.argv.length===0)throw Error("argv 不能为空");if(e.argv[0]==="mcp"||e.argv.includes("mcp"))throw Error("禁止在 MCP 中递归调用 licell mcp(请直接调用其它 licell 命令)");let n=Xw(e.projectRoot,e.cwd),t=typeof e.timeoutMs==="number"?e.timeoutMs:void 0,o=Number.isFinite(t)&&t&&t>0?Math.min(Math.max(1000,Math.floor(t)),1800000):600000,{command:s,baseArgs:r}=Vw(),c=Zw(e.argv)?[...e.argv]:[...e.argv,"--output","json"],f=[...r,...c],u=1048576,l=bg(u),i=bg(u),g={...process.env,CI:"1",NO_COLOR:"1",LICELL_MCP:"1"},y=jw(s,f,{cwd:n,env:g,stdio:["ignore","pipe","pipe"]});y.stdout?.on("data",(p)=>l.push(p)),y.stderr?.on("data",(p)=>i.push(p));let d=!1,m=setTimeout(()=>{d=!0,y.kill("SIGTERM"),setTimeout(()=>y.kill("SIGKILL"),2000).unref()},o);m.unref();let h=await new Promise((p,w)=>{y.on("error",w),y.on("close",(H)=>p(H))}).finally(()=>clearTimeout(m)),b=l.get(),$=i.get();return{exitCode:h,timedOut:d,stdout:b,stderr:$,command:[s,...f].join(" ")}}function Nw(e){let n={exitCode:e.exitCode,timedOut:e.timedOut,command:e.command,stdout:e.stdout.text,stdoutTruncated:e.stdout.truncated,stderr:e.stderr.text,stderrTruncated:e.stderr.truncated,records:Du(e.stdout.text)},t=e.timedOut?`timed out: ${n.command}`:`exit=${n.exitCode??"null"}: ${n.command}`,o=e.stdout.truncated?`
1632
- [stdout truncated]`:"",s=e.stderr.truncated?`
1625
+ `),q("Done.")})}),e.command("dns records rm <recordId>","删除域名解析记录").option("--yes","跳过二次确认(危险)").action(async(n,t)=>{await M({commandLabel:"licell dns records rm",interactiveTTY:P(),requiredCapabilities:["dns"]},async()=>{B();let s=L(n,"recordId");await Be(`删除 DNS 记录 ${s}`,{yes:Boolean(t.yes)});let o=A();if(!await v(o,`正在删除记录 ${s}...`,"❌ DNS 记录删除失败",async()=>{return await qi(s),!0}))return;if(!a())o.stop($n.green("✅ DNS 记录已删除")),q("Done.");else S({stage:"dns.records.rm",recordId:s,removed:!0})})})}import qt from"picocolors";import{writeFileSync as gg}from"fs";Ae();Ve();se();K();re();function yg(e){e.command("env list","查看云端环境变量").option("--target <target>","查看指定 FC alias 的环境变量(如 prod/preview)").option("--show-values","显示完整变量值(默认隐藏)").action(async(n)=>{await M({commandLabel:"licell env list",interactiveTTY:P(),requiredCapabilities:["fc"]},async()=>{B();let t=k.getProject();Le(t);let s=n.target?tn(n.target):void 0,o=A(),r=await v(o,s?`正在拉取 alias=${s} 的环境变量...`:"正在拉取云端环境变量...","❌ 获取环境变量失败",()=>yt(t.appName,s));if(!r)return;if(!a())o.stop(qt.green(`✅ 共 ${Object.keys(r).length} 个环境变量`));let c=Object.entries(r).sort(([u],[l])=>u.localeCompare(l)),f=Boolean(n.showValues);if(a()){let u=f?Object.fromEntries(c):Object.fromEntries(c.map(([l,i])=>[l,`<hidden:${String(i).length} chars>`]));S({stage:"env.list",qualifier:s||null,count:c.length,showValues:f,envs:u});return}if(c.length===0){q("云端当前无环境变量");return}for(let[u,l]of c){let i=f?l:`<hidden:${String(l).length} chars>`;console.log(`${qt.cyan(u)}=${i}`)}console.log(""),q("Done.")})}),e.command("env set <key> <value>","设置云端环境变量(并同步本地 .licell/project.json)").action(async(n,t)=>{await M({commandLabel:"licell env set",interactiveTTY:P(),requiredCapabilities:["fc"]},async()=>{B();let s=k.getProject();Le(s);let o=Tr(L(n,"环境变量名")),r=L(t,"环境变量值"),c=A(),f=await v(c,`正在写入环境变量 ${o}...`,"❌ 环境变量写入失败",()=>yr(s.appName,o,r));if(!f)return;if(k.setProject({envs:f},{replaceEnvs:!0}),!a())c.stop(qt.green("✅ 环境变量已写入云端并同步到本地配置")),q("Done.");else S({stage:"env.set",key:o,updatedCount:Object.keys(f).length})})}),e.command("env rm <key>","删除云端环境变量(并同步本地 .licell/project.json)").option("--yes","跳过二次确认(危险)").action(async(n,t)=>{await M({commandLabel:"licell env rm",interactiveTTY:P(),requiredCapabilities:["fc"]},async()=>{B();let s=k.getProject();Le(s);let o=Tr(L(n,"环境变量名"));await Be(`删除环境变量 ${o}`,{yes:Boolean(t.yes)});let r=A(),c=await v(r,`正在删除环境变量 ${o}...`,"❌ 环境变量删除失败",()=>dr(s.appName,o));if(!c)return;if(k.setProject({envs:c},{replaceEnvs:!0}),!a())r.stop(qt.green("✅ 环境变量已从云端移除(若存在)并同步本地配置")),q("Done.");else S({stage:"env.rm",key:o,updatedCount:Object.keys(c).length})})}),e.command("env pull","拉取云端环境变量").option("--target <target>","从指定 FC alias 拉取环境变量(如 prod/preview)").action(async(n)=>{await M({commandLabel:"licell env pull",interactiveTTY:P(),requiredCapabilities:["fc"]},async()=>{B();let t=k.getProject();Le(t);let s=n.target?tn(n.target):void 0,o=A(),r=await v(o,s?`正在拉取 alias=${s} 的环境变量...`:"正在拉取云端环境变量...","❌ 环境变量拉取失败",()=>yt(t.appName,s));if(!r)return;k.setProject({envs:r},{replaceEnvs:!0});let c=Object.entries(r);if(c.length===0){try{gg(".env","",{mode:384})}catch(u){throw Error(`写入 .env 文件失败: ${u instanceof Error?u.message:String(u)}`)}if(!a())o.stop(qt.yellow("云端无环境变量,已清空本地 .env"));S({stage:"env.pull",qualifier:s||null,count:0,envFile:".env",emptied:!0});return}let f=c.map(([u,l])=>`${u}="${wi(String(l))}"`).join(`
1626
+ `);try{gg(".env",f,{mode:384})}catch(u){throw Error(`写入 .env 文件失败: ${u instanceof Error?u.message:String(u)}`)}if(Xu(),!a())o.stop(qt.green(`✅ 已拉取 ${c.length} 个环境变量并写入 .env`));S({stage:"env.pull",qualifier:s||null,count:c.length,envFile:".env",emptied:!1})})})}import mg from"picocolors";K();De();Ie();ce();import Iw,*as dc from"@alicloud/sls20201230";import*as hg from"@alicloud/openapi-client";import Ln from"picocolors";var Fw=te(Iw,"@alicloud/sls20201230");function xw(e){return e.replace(/['"\\*?:|\[\]{}()&!^~]/g,"")}function Hw(e){let n=R(e).toLowerCase();return n.includes("projectnotexist")||n.includes("logstorenotexist")}function dg(e,n,t=!1){let s=[];return e.sort((o,r)=>parseInt(o.__time__||"0",10)-parseInt(r.__time__||"0",10)).forEach((o)=>{let r=`${o.__time__||""}|${o.__source__||""}|${o.message||o.content||""}`;if(n?.has(r))return;if(n){if(n.add(r),n.size>5000){let l=[...n];n.clear();for(let i of l.slice(-2500))n.add(i)}}let c=new Date(parseInt(o.__time__||"0",10)*1000).toLocaleTimeString(),f=String(o.message||o.content||JSON.stringify(o)).trim();if(f.toLowerCase().includes("error"))f=Ln.red(f);let u=`${Ln.gray(`[${c}]`)} ${f}`;if(s.push(u),!t)console.log(u)}),s}async function ag(e,n={}){let t=k.requireAuth(),s=new Fw(new hg.Config({accessKeyId:t.ak,accessKeySecret:t.sk,endpoint:`${t.region}.log.aliyuncs.com`})),o=`aliyun-fc-${t.region}-${t.accountId}`,r="function-log",c=xw(e),f=n.lineLimit&&n.lineLimit>0?Math.floor(n.lineLimit):1000;if(n.once){let d=n.windowSeconds&&n.windowSeconds>0?Math.floor(n.windowSeconds):120,m=Math.floor(Date.now()/1000),h=Math.max(0,m-d);try{let $=(await s.getLogs(o,"function-log",new dc.GetLogsRequest({from:h,to:m,query:`* and functionName: "${c}"`,line:f}))).body||[];if($.length===0){if(!n.silent)console.log(Ln.gray(`最近 ${d}s 无日志`));return{mode:"once",logs:[],lines:[]}}let p=dg($,void 0,Boolean(n.silent));return{mode:"once",logs:$,lines:p}}catch(b){if(Hw(b)){if(!n.silent)console.log(Ln.yellow(`⚠️ 日志服务尚未就绪,已跳过: ${R(b)}`));return{mode:"once",logs:[],lines:[]}}throw b}}if(!n.silent)console.log(Ln.gray(`
1627
+ \uD83D\uDCE1 正在监听云端 [${Ln.cyan(e)}] 的实时日志流 (Ctrl+C 退出)...
1628
+ `));let u=Math.floor(Date.now()/1000)-60,l=new Set,i=0,g=!0,y=()=>{if(g=!1,!n.silent)console.log(Ln.gray(`
1629
+ \uD83D\uDC4B 日志流已断开`));process.exit(0)};process.on("SIGINT",y),process.on("SIGTERM",y);while(g){try{let d=Math.floor(Date.now()/1000);if(d<=u){await ge(1500);continue}let h=(await s.getLogs(o,"function-log",new dc.GetLogsRequest({from:u,to:d,query:`* and functionName: "${c}"`,line:f}))).body||[];if(dg(h,l,Boolean(n.silent)),h.length>0){let b=parseInt(h[h.length-1].__time__||`${u}`,10);if(Number.isFinite(b)&&b>0)u=b}}catch(d){let m=Date.now();if(m-i>1e4){let h=R(d);if(!n.silent)console.log(Ln.yellow(`⚠️ 日志拉取失败,10 秒后重试: ${h}`));i=m}}await ge(1500)}}Ae();se();K();re();function pg(e){e.command("logs","查看云端日志(默认实时流式)").option("--once","仅拉取一次最近日志并退出").option("--window <seconds>","一次拉取模式的时间窗(默认 120 秒)").option("--lines <n>","每次请求最大日志条数(默认 1000)").action(async(n)=>{await M({commandLabel:"licell logs",interactiveTTY:P(),requiredCapabilities:["fc","logs"]},async()=>{j(mg.bgBlue(mg.white(" \uD83D\uDCE1 Serverless Log Stream "))),B();let t=k.getProject();Le(t,"当前目录下没有找到绑定的云端项目");let s=a()?!0:Boolean(n.once),o=await ag(t.appName,{once:s,windowSeconds:qe(n.window,"--window"),lineLimit:qe(n.lines,"--lines"),silent:a()});if(a())S({stage:"logs",appName:t.appName,once:s,lines:o&&"lines"in o?o.lines:[],count:o&&"logs"in o?o.logs.length:0})})})}se();re();import Pt from"picocolors";import{createHash as Lw}from"crypto";import{spawnSync as kg}from"child_process";import{mkdtempSync as Aw,rmSync as Uw,writeFileSync as vw}from"fs";import{join as wg}from"path";import{tmpdir as Gw}from"os";var hc="agents-infrastructure/licell",Mw=/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/;function Bw(e){if(e.scriptUrl)return e.scriptUrl;let n=e.repo||hc;if(!Mw.test(n))throw Error("无效的仓库格式,必须是 owner/repo");if(e.version)return`https://github.com/${n}/releases/download/${e.version}/install.sh`;return`https://github.com/${n}/releases/latest/download/install.sh`}function Yw(e){let n=e.lastIndexOf("/");if(n<0)return null;return`${e.substring(0,n)}/SHA256SUMS.txt`}function Ow(e,n){for(let t of e.split(`
1630
+ `)){let s=t.trim();if(!s)continue;let o=s.match(/^([a-f0-9]{64})\s+(.+)$/);if(o&&o[2].trim()===n)return o[1]}return null}function Kw(e,n){return Lw("sha256").update(e,"utf8").digest("hex")===n}function $g(e,n){let t=kg("curl",["-fsSL",e],{encoding:"utf8",maxBuffer:20971520});if(t.status!==0||!t.stdout){let s=(t.stderr||"").trim();throw Error(s?`下载${n}失败: ${s}`:`下载${n}失败`)}return t.stdout}function _g(e){e.command("upgrade","升级到最新 release 版本").option("--version <tag>","指定版本(如 v0.9.6)").option("--repo <owner/repo>",`GitHub 仓库(默认 ${hc})`).option("--script-url <url>","覆盖 install.sh 地址(调试用途,需配合 --skip-checksum)").option("--skip-checksum","跳过 SHA256 完整性校验(不推荐)").option("--dry-run","只输出将使用的安装脚本地址").action(async(n)=>{j(Pt.bgBlue(Pt.white(" ⬆ Licell Upgrade ")));let t=C(n.version),s=C(n.repo)||hc,o=C(n.scriptUrl),r=Boolean(n.skipChecksum);if(o&&!r)throw Error("使用 --script-url 时必须同时指定 --skip-checksum 以确认跳过完整性校验");let c=Bw({repo:s,version:t,scriptUrl:o});if(Boolean(n.dryRun)){if(a())S({stage:"upgrade",dryRun:!0,scriptUrl:c});else console.log(c),q("Done.");return}let f=A();f.start("正在下载升级脚本...");let u=$g(c,"安装脚本");if(!r){let g=Yw(c);if(g){f.message("正在校验脚本完整性...");try{let y=$g(g,"SHA256SUMS"),d=Ow(y,"install.sh");if(d){if(!Kw(u,d))throw Error("install.sh SHA256 校验失败,脚本可能被篡改。如需跳过校验请使用 --skip-checksum")}else if(a())X({stage:"upgrade",action:"checksum",status:"info",message:"SHA256SUMS 未包含 install.sh,已跳过校验"});else console.error(Pt.yellow("⚠️ SHA256SUMS 中未找到 install.sh 条目,跳过校验"))}catch(y){if(y instanceof Error&&y.message.includes("SHA256 校验失败"))throw y;if(a())X({stage:"upgrade",action:"checksum",status:"info",message:"无法下载 SHA256SUMS,已跳过校验"});else console.error(Pt.yellow("⚠️ 无法下载 SHA256SUMS 校验文件,跳过校验"))}}}let l=Aw(wg(Gw(),"licell-upgrade-")),i=wg(l,"install.sh");try{if(vw(i,u,{mode:448}),!a())f.stop(Pt.green("✅ 脚本下载完成,开始安装"));let g=kg("bash",[i],{stdio:a()?"pipe":"inherit",encoding:"utf8",env:{...process.env,LICELL_SKIP_RUN_CHECK:"1"}});if(a()){let y=typeof g.stdout==="string"?g.stdout.trim():"",d=typeof g.stderr==="string"?g.stderr.trim():"";if(y)X({stage:"upgrade.install",action:"stdout",status:"info",message:y});if(d)X({stage:"upgrade.install",action:"stderr",status:"info",message:d})}if(g.status!==0)throw Error(`升级安装失败(exit=${g.status??"unknown"})`);if(a())S({stage:"upgrade",dryRun:!1,scriptUrl:c,checksumSkipped:r});else q(Pt.green("✅ 升级完成"))}finally{Uw(l,{recursive:!0,force:!0})}})}import rs from"picocolors";import{existsSync as so,readFileSync as Eg,writeFileSync as wc,mkdirSync as Sg}from"fs";import{join as $c,dirname as qg}from"path";import{homedir as Pg}from"os";ce();re();import{spawn as jw}from"child_process";import{createInterface as Ww}from"readline";import{isAbsolute as Qw,resolve as ac}from"path";function no(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function Dw(e){process.stdout.write(`${JSON.stringify(e)}
1631
+ `)}function Tg(e){Dw(e)}function ss(e,n,t,s){Tg({jsonrpc:"2.0",id:e,error:s?{code:n,message:t,data:s}:{code:n,message:t}})}function Rt(e,n){Tg({jsonrpc:"2.0",id:e,result:n})}function Vw(){let e=process.execPath,n=[],t=process.argv[1];if(typeof t==="string"){let s=t.toLowerCase();if(s.endsWith(".js")||s.endsWith(".cjs")||s.endsWith(".mjs")||s.endsWith(".ts"))n.push(t)}return{command:e,baseArgs:n}}function Xw(e,n){if(typeof n!=="string")return e;let t=n.trim();if(!t)return e;let s=Qw(t)?ac(t):ac(e,t),o=ac(e);if(s===o)return s;if(!s.startsWith(`${o}/`))throw Error(`cwd 必须在 projectRoot 内部: cwd=${s}, projectRoot=${o}`);return s}function bg(e){let n="",t=0,s=!1;return{push(o){if(s)return;let r=t+o.length;if(r<=e){n+=o.toString("utf8"),t=r;return}let c=Math.max(0,e-t);if(c>0)n+=o.subarray(0,c).toString("utf8");t=e,s=!0},get(){return{text:n,truncated:s}}}}function D(e){if(typeof e!=="string")return;let n=e.trim();return n.length>0?n:void 0}function Te(e){if(typeof e==="boolean")return e;return}function An(e){if(typeof e!=="number")return;if(!Number.isFinite(e))return;return e}function to(e,n){if(e!==!0)throw Error(n)}function Zw(e){for(let n=0;n<e.length;n+=1){let t=e[n];if(t==="--")break;if(t==="--output")return!0;if(t.startsWith("--output="))return!0}return!1}async function Jw(e){if(e.argv.length===0)throw Error("argv 不能为空");if(e.argv[0]==="mcp"||e.argv.includes("mcp"))throw Error("禁止在 MCP 中递归调用 licell mcp(请直接调用其它 licell 命令)");let n=Xw(e.projectRoot,e.cwd),t=typeof e.timeoutMs==="number"?e.timeoutMs:void 0,s=Number.isFinite(t)&&t&&t>0?Math.min(Math.max(1000,Math.floor(t)),1800000):600000,{command:o,baseArgs:r}=Vw(),c=Zw(e.argv)?[...e.argv]:[...e.argv,"--output","json"],f=[...r,...c],u=1048576,l=bg(u),i=bg(u),g={...process.env,CI:"1",NO_COLOR:"1",LICELL_MCP:"1"},y=jw(o,f,{cwd:n,env:g,stdio:["ignore","pipe","pipe"]});y.stdout?.on("data",(p)=>l.push(p)),y.stderr?.on("data",(p)=>i.push(p));let d=!1,m=setTimeout(()=>{d=!0,y.kill("SIGTERM"),setTimeout(()=>y.kill("SIGKILL"),2000).unref()},s);m.unref();let h=await new Promise((p,w)=>{y.on("error",w),y.on("close",(H)=>p(H))}).finally(()=>clearTimeout(m)),b=l.get(),$=i.get();return{exitCode:h,timedOut:d,stdout:b,stderr:$,command:[o,...f].join(" ")}}function Nw(e){let n={exitCode:e.exitCode,timedOut:e.timedOut,command:e.command,stdout:e.stdout.text,stdoutTruncated:e.stdout.truncated,stderr:e.stderr.text,stderrTruncated:e.stderr.truncated,records:Du(e.stdout.text)},t=e.timedOut?`timed out: ${n.command}`:`exit=${n.exitCode??"null"}: ${n.command}`,s=e.stdout.truncated?`
1632
+ [stdout truncated]`:"",o=e.stderr.truncated?`
1633
1633
  [stderr truncated]`:"";return{isError:Boolean(e.timedOut||typeof e.exitCode==="number"&&e.exitCode!==0),content:[{type:"text",text:`${t}
1634
1634
 
1635
1635
  [stdout]
1636
- ${n.stdout}${o}
1636
+ ${n.stdout}${s}
1637
1637
 
1638
1638
  [stderr]
1639
- ${n.stderr}${s}`.trim()}],structuredContent:n}}async function mc(e){let n=(d)=>{process.stderr.write(`${d}
1640
- `)},t=process.env.LICELL_MCP_DEBUG==="1"||process.env.LICELL_MCP_DEBUG==="true",o={licell_cli:{name:"licell_cli",title:"Deploy & manage Aliyun services (licell)",description:"Use licell CLI to deploy API/static services to Alibaba Cloud and manage related resources (FC, custom domains, SSL, DNS, CDN, logs, etc.). Returns stdout/stderr.",inputSchema:{type:"object",additionalProperties:!1,properties:{argv:{type:"array",items:{type:"string"},minItems:1,description:'licell arguments, e.g. ["deploy","--type","static","--dist","dist"]'},cwd:{type:"string",description:"Working directory relative to projectRoot (default: projectRoot)"},timeoutMs:{type:"number",description:"Command timeout in milliseconds (default: 600000, max: 1800000)"}},required:["argv"]},annotations:{openWorldHint:!0,destructiveHint:!0}},licell_deploy:{name:"licell_deploy",title:"Deploy service (API/Static) to Aliyun",description:"Deploy current project. API deploys to Function Compute (FC 3.0); Static deploys to OSS hosting. For API, Agent should call licell_fc_deploy_spec + licell_fc_deploy_check before deploy.",inputSchema:{type:"object",additionalProperties:!1,properties:{type:{type:"string",enum:["api","static"],description:"Deployment type."},runtime:{type:"string",description:"API runtime: nodejs20/nodejs22/python3.12/python3.13/docker; Static: static/statis."},entry:{type:"string",description:"API entry file (default depends on runtime)."},dist:{type:"string",description:"Static site directory (default: dist)."},target:{type:"string",description:"FC alias target (e.g. prod/preview). API only."},domain:{type:"string",description:"Full custom domain (e.g. api.example.com). API/Static supported; implies SSL. Static will auto-enable CDN."},domainSuffix:{type:"string",description:"Domain suffix (e.g. example.com) to bind <appName>.<suffix>. API/Static supported."},enableCdn:{type:"boolean",description:"Enable CDN after domain bind (API optional; Static with domain already auto-enables). Implies SSL."},ssl:{type:"boolean",description:"Enable HTTPS (Let's Encrypt). If domain/enableCdn is set, SSL is implied."},sslForceRenew:{type:"boolean",description:"Force renew certificate when SSL enabled."},acrNamespace:{type:"string",description:"ACR namespace for docker runtime."},enableVpc:{type:"boolean",description:"Enable VPC integration (API only)."},disableVpc:{type:"boolean",description:"Disable VPC integration (API only, public mode)."},memory:{type:"number",description:"Memory size (MB)."},vcpu:{type:"number",description:"vCPU cores (e.g. 0.5/1/2)."},instanceConcurrency:{type:"number",description:"Instance concurrency."},timeout:{type:"number",description:"Timeout seconds."},cwd:{type:"string",description:"Working directory relative to projectRoot (default: projectRoot)."},timeoutMs:{type:"number",description:"Command timeout in milliseconds."}},required:["type"]}},licell_fc_deploy_spec:{name:"licell_fc_deploy_spec",title:"Get FC API deploy spec",description:"Return machine-readable FC API runtime specs (handlerContract/eventSchema/responseSchema/examples/validationRules and resource constraints) for agent planning.",inputSchema:{type:"object",additionalProperties:!1,properties:{runtime:{type:"string",description:"Optional runtime filter: nodejs20/nodejs22/python3.12/python3.13/docker."},all:{type:"boolean",description:"Return all runtime specs."},cwd:{type:"string",description:"Working directory relative to projectRoot (default: projectRoot)."},timeoutMs:{type:"number",description:"Command timeout in milliseconds."}}}},licell_fc_deploy_check:{name:"licell_fc_deploy_check",title:"Precheck FC API deploy readiness",description:"Read-only validation before FC API deployment. Returns actionable issues (missing handler, wrong entry, Docker prerequisites, etc.) and does not modify project files.",inputSchema:{type:"object",additionalProperties:!1,properties:{runtime:{type:"string",description:"Runtime to validate (default from project/env or nodejs20)."},entry:{type:"string",description:"Optional entry path override."},dockerDaemon:{type:"boolean",description:"When runtime=docker, also check local Docker daemon availability."},cwd:{type:"string",description:"Working directory relative to projectRoot (default: projectRoot)."},timeoutMs:{type:"number",description:"Command timeout in milliseconds."}}}},licell_init:{name:"licell_init",title:"Initialize licell project",description:"Initialize current directory: write .licell/project.json, and optionally generate scaffold files for supported runtimes.",inputSchema:{type:"object",additionalProperties:!1,properties:{runtime:{type:"string",description:"nodejs20/nodejs22/python3.12/python3.13/docker."},app:{type:"string",description:"appName (FC functionName)."},force:{type:"boolean",description:"Overwrite/generate scaffold in non-empty dir."},yes:{type:"boolean",description:"Non-interactive mode (recommended for MCP). Default true."},cwd:{type:"string",description:"Working directory relative to projectRoot (default: projectRoot)."},timeoutMs:{type:"number",description:"Command timeout in milliseconds."}}}},licell_release_promote:{name:"licell_release_promote",title:"Promote FC release (alias switch)",description:"Publish (if needed) and switch an FC alias (e.g. prod/preview) to a version.",inputSchema:{type:"object",additionalProperties:!1,properties:{versionId:{type:"string",description:"Optional versionId. If omitted, licell will publish current code or reuse latest published."},target:{type:"string",description:"Alias target (default: prod)."},cwd:{type:"string",description:"Working directory relative to projectRoot (default: projectRoot)."},timeoutMs:{type:"number",description:"Command timeout in milliseconds."}}}},licell_release_rollback:{name:"licell_release_rollback",title:"Rollback FC release (alias switch)",description:"Switch an FC alias to a specific versionId.",inputSchema:{type:"object",additionalProperties:!1,properties:{versionId:{type:"string",description:"VersionId to rollback to."},target:{type:"string",description:"Alias target (default: prod)."},cwd:{type:"string",description:"Working directory relative to projectRoot (default: projectRoot)."},timeoutMs:{type:"number",description:"Command timeout in milliseconds."}},required:["versionId"]}},licell_release_prune:{name:"licell_release_prune",title:"Prune FC historical versions (dangerous)",description:"Preview or delete old FC published versions. Destructive when apply=true (requires yes=true).",inputSchema:{type:"object",additionalProperties:!1,properties:{keep:{type:"number",description:"Keep latest N versions (default 10)."},apply:{type:"boolean",description:"If true, perform deletion. If false/omitted, preview only."},yes:{type:"boolean",description:"Required when apply=true (non-interactive double-confirm)."},cwd:{type:"string",description:"Working directory relative to projectRoot (default: projectRoot)."},timeoutMs:{type:"number",description:"Command timeout in milliseconds."}}},annotations:{destructiveHint:!0}},licell_fn_list:{name:"licell_fn_list",title:"List functions",description:"List FC functions in current region.",inputSchema:{type:"object",additionalProperties:!1,properties:{limit:{type:"number",description:"Max items (default 20)."},prefix:{type:"string",description:"Filter by function name prefix."},cwd:{type:"string"},timeoutMs:{type:"number"}}}},licell_fn_info:{name:"licell_fn_info",title:"Get function info",description:"Get FC function details.",inputSchema:{type:"object",additionalProperties:!1,properties:{name:{type:"string",description:"Function name. If omitted, uses project appName."},target:{type:"string",description:"Qualifier alias/version (e.g. prod/preview/1)."},cwd:{type:"string"},timeoutMs:{type:"number"}}}},licell_fn_invoke:{name:"licell_fn_invoke",title:"Invoke function",description:"Invoke FC function synchronously with an optional payload.",inputSchema:{type:"object",additionalProperties:!1,properties:{name:{type:"string",description:"Function name. If omitted, uses project appName."},target:{type:"string",description:"Qualifier alias/version (e.g. prod/preview/1)."},payload:{type:"string",description:"Raw payload text."},payloadJson:{type:"object",description:"JSON payload object (will be JSON.stringify-ed).",additionalProperties:!0},cwd:{type:"string"},timeoutMs:{type:"number"}}}},licell_fn_rm:{name:"licell_fn_rm",title:"Remove function (dangerous)",description:"Delete FC function. Destructive (requires yes=true).",inputSchema:{type:"object",additionalProperties:!1,properties:{name:{type:"string",description:"Function name. If omitted, uses project appName."},force:{type:"boolean",description:"Cascade delete triggers/aliases/versions."},yes:{type:"boolean",description:"Required in non-interactive mode."},cwd:{type:"string"},timeoutMs:{type:"number"}}},annotations:{destructiveHint:!0}},licell_domain_add:{name:"licell_domain_add",title:"Bind custom domain (and optional SSL)",description:"Bind a custom domain to current FC app and optionally enable HTTPS.",inputSchema:{type:"object",additionalProperties:!1,properties:{domain:{type:"string",description:"Full domain, e.g. api.example.com."},ssl:{type:"boolean",description:"Enable HTTPS (Let's Encrypt)."},sslForceRenew:{type:"boolean",description:"Force renew certificate."},target:{type:"string",description:"Route to FC alias (prod/preview)."},cwd:{type:"string"},timeoutMs:{type:"number"}},required:["domain"]}},licell_domain_rm:{name:"licell_domain_rm",title:"Unbind custom domain (dangerous)",description:"Unbind custom domain and cleanup DNS record. Destructive (requires yes=true).",inputSchema:{type:"object",additionalProperties:!1,properties:{domain:{type:"string",description:"Full domain, e.g. api.example.com."},yes:{type:"boolean",description:"Required in non-interactive mode."},cwd:{type:"string"},timeoutMs:{type:"number"}},required:["domain"]},annotations:{destructiveHint:!0}},licell_dns_records_list:{name:"licell_dns_records_list",title:"List DNS records",description:"List DNS records for a domain (Alidns).",inputSchema:{type:"object",additionalProperties:!1,properties:{domain:{type:"string",description:"Root domain, e.g. example.com."},limit:{type:"number",description:"Max items (default 100)."},cwd:{type:"string"},timeoutMs:{type:"number"}},required:["domain"]}},licell_dns_records_add:{name:"licell_dns_records_add",title:"Add DNS record",description:"Add a DNS record (Alidns).",inputSchema:{type:"object",additionalProperties:!1,properties:{domain:{type:"string",description:"Root domain, e.g. example.com."},rr:{type:"string",description:"RR host, e.g. @/www/api."},type:{type:"string",description:"Record type, e.g. A/CNAME/TXT."},value:{type:"string",description:"Record value."},ttl:{type:"number",description:"TTL seconds (default 600)."},line:{type:"string",description:"Line (default: default)."},cwd:{type:"string"},timeoutMs:{type:"number"}},required:["domain","rr","type","value"]}},licell_dns_records_rm:{name:"licell_dns_records_rm",title:"Remove DNS record (dangerous)",description:"Remove a DNS record by recordId. Destructive (requires yes=true).",inputSchema:{type:"object",additionalProperties:!1,properties:{recordId:{type:"string",description:"RecordId from list."},yes:{type:"boolean",description:"Required in non-interactive mode."},cwd:{type:"string"},timeoutMs:{type:"number"}},required:["recordId"]},annotations:{destructiveHint:!0}}},s=["2025-03-26","2025-06-18","2025-11-25"],r=!1,c=s[s.length-1],f=Ww({input:process.stdin,crlfDelay:1/0}),u=new Set,l=(d)=>{u.add(d),d.finally(()=>u.delete(d)).catch(()=>{})},i=async(d)=>{if(t)n(`[mcp] <= notification ${d.method}`);if(d.method==="notifications/initialized")return;if(d.method==="exit")process.exit(0)},g=async(d)=>{let{id:m,method:h}=d;if(t)n(`[mcp] <= request ${h} id=${m}`);if(!r&&h!=="initialize"&&h!=="ping"){oo(m,-32002,"Server not initialized. Call 'initialize' first.");return}if(h==="ping"){Rt(m,{});return}if(h==="initialize"){let b=ts(d.params)?d.params:{};c=(typeof b.protocolVersion==="string"?b.protocolVersion:"")||c,r=!0,Rt(m,{protocolVersion:c,capabilities:{tools:{}},serverInfo:{name:"licell",version:e.serverVersion},instructions:`This MCP server is for deploying and managing services on Alibaba Cloud via licell, scoped to projectRoot.
1641
- Destructive commands require explicit --yes in non-interactive mode.`});return}if(h==="shutdown"){Rt(m,null);return}if(h==="tools/list"){let b=[];for(let $ of Object.values(o)){let p={name:$.name,title:$.title,description:$.description,inputSchema:$.inputSchema};if("annotations"in $&&$.annotations)p.annotations=$.annotations;if(c<"2025-03-26")p.input_schema=$.inputSchema;b.push(p)}Rt(m,{tools:b});return}if(h==="tools/call"){let b=ts(d.params)?d.params:{},$=typeof b.name==="string"?b.name:"",p=ts(b.arguments)?b.arguments:{};try{let w=null,H=p.cwd,T=p.timeoutMs;if($==="licell_cli"){let E=p.argv;if(!Array.isArray(E)||E.some((_)=>typeof _!=="string"||_.trim()===""))throw Error("Invalid arguments: argv must be a non-empty string[]");w=E.map((_)=>_.trim())}else if($==="licell_deploy"){let E=D(p.type);if(E!=="api"&&E!=="static")throw Error('type must be "api" or "static"');w=["deploy","--type",E];let _=D(p.runtime),F=D(p.entry),I=D(p.dist),fe=D(p.target),Ce=D(p.domain),rn=D(p.domainSuffix),le=D(p.acrNamespace),cn=Te(p.enableCdn),Je=Te(p.ssl),fn=Te(p.sslForceRenew),ot=Te(p.enableVpc),It=Te(p.disableVpc),qc=Ln(p.memory),Pc=Ln(p.vcpu),Rc=Ln(p.instanceConcurrency),Ic=Ln(p.timeout);if(_)w.push("--runtime",_);if(F)w.push("--entry",F);if(I)w.push("--dist",I);if(fe)w.push("--target",fe);if(Ce)w.push("--domain",Ce);if(rn)w.push("--domain-suffix",rn);if(cn)w.push("--enable-cdn");if(Je)w.push("--ssl");if(fn)w.push("--ssl-force-renew");if(le)w.push("--acr-namespace",le);if(ot)w.push("--enable-vpc");if(It)w.push("--disable-vpc");if(qc!==void 0)w.push("--memory",String(qc));if(Pc!==void 0)w.push("--vcpu",String(Pc));if(Rc!==void 0)w.push("--instance-concurrency",String(Rc));if(Ic!==void 0)w.push("--timeout",String(Ic))}else if($==="licell_fc_deploy_spec"){w=["deploy","spec"];let E=D(p.runtime),_=Te(p.all);if(E)w.push(E);if(_)w.push("--all")}else if($==="licell_fc_deploy_check"){w=["deploy","check"];let E=D(p.runtime),_=D(p.entry),F=Te(p.dockerDaemon);if(E)w.push("--runtime",E);if(_)w.push("--entry",_);if(F)w.push("--docker-daemon")}else if($==="licell_init"){w=["init"];let E=D(p.runtime),_=D(p.app),F=Te(p.force),I=Te(p.yes);if(E)w.push("--runtime",E);if(_)w.push("--app",_);if(F)w.push("--force");if(I!==!1)w.push("--yes")}else if($==="licell_release_promote"){let E=D(p.versionId),_=D(p.target);if(w=["release","promote"],E)w.push(E);if(_)w.push("--target",_)}else if($==="licell_release_rollback"){let E=D(p.versionId);if(!E)throw Error("versionId is required");let _=D(p.target);if(w=["release","rollback",E],_)w.push("--target",_)}else if($==="licell_release_prune"){let E=Ln(p.keep),_=Te(p.apply),F=Te(p.yes);if(w=["release","prune"],E!==void 0)w.push("--keep",String(E));if(_)os(F,"apply=true is destructive; set yes=true to confirm"),w.push("--apply","--yes")}else if($==="licell_fn_list"){w=["fn","list"];let E=Ln(p.limit),_=D(p.prefix);if(E!==void 0)w.push("--limit",String(E));if(_)w.push("--prefix",_)}else if($==="licell_fn_info"){w=["fn","info"];let E=D(p.name),_=D(p.target);if(E)w.push(E);if(_)w.push("--target",_)}else if($==="licell_fn_invoke"){w=["fn","invoke"];let E=D(p.name),_=D(p.target),F=D(p.payload),I=p.payloadJson;if(F&&I!==void 0)throw Error("Provide only one of payload or payloadJson");if(E)w.push(E);if(_)w.push("--target",_);if(F)w.push("--payload",F);if(I!==void 0)w.push("--payload",JSON.stringify(I))}else if($==="licell_fn_rm"){w=["fn","rm"];let E=D(p.name),_=Te(p.force),F=Te(p.yes);if(os(F,"fn rm is destructive; set yes=true to confirm"),E)w.push(E);if(_)w.push("--force");w.push("--yes")}else if($==="licell_domain_add"){let E=D(p.domain);if(!E)throw Error("domain is required");let _=D(p.target),F=Te(p.ssl),I=Te(p.sslForceRenew);if(w=["domain","add",E],F)w.push("--ssl");if(I)w.push("--ssl-force-renew");if(_)w.push("--target",_)}else if($==="licell_domain_rm"){let E=D(p.domain);if(!E)throw Error("domain is required");let _=Te(p.yes);os(_,"domain rm is destructive; set yes=true to confirm"),w=["domain","rm",E,"--yes"]}else if($==="licell_dns_records_list"){let E=D(p.domain);if(!E)throw Error("domain is required");let _=Ln(p.limit);if(w=["dns","records","list",E],_!==void 0)w.push("--limit",String(_))}else if($==="licell_dns_records_add"){let E=D(p.domain),_=D(p.rr),F=D(p.type),I=D(p.value);if(!E||!_||!F||!I)throw Error("domain, rr, type, value are required");let fe=Ln(p.ttl),Ce=D(p.line);if(w=["dns","records","add",E,"--rr",_,"--type",F,"--value",I],fe!==void 0)w.push("--ttl",String(fe));if(Ce)w.push("--line",Ce)}else if($==="licell_dns_records_rm"){let E=D(p.recordId);if(!E)throw Error("recordId is required");let _=Te(p.yes);os(_,"dns records rm is destructive; set yes=true to confirm"),w=["dns","records","rm",E,"--yes"]}else throw Error(`Unknown tool: ${$}`);if(!w)throw Error(`Unknown tool: ${$}`);if(t)n(`[mcp] tool ${$} starting...`);let x=await Jw({projectRoot:e.projectRoot,argv:w,cwd:H,timeoutMs:T});if(t)n(`[mcp] tool ${$} done (exit=${x.exitCode}, timedOut=${x.timedOut})`);Rt(m,Nw(x))}catch(w){Rt(m,{isError:!0,content:[{type:"text",text:R(w)}]})}return}oo(m,-32601,`Method not found: ${h}`)},y=(d)=>{if(!ts(d)||d.jsonrpc!=="2.0"){oo(null,-32600,"Invalid Request");return}if(typeof d.method!=="string"){oo(d.id??null,-32600,"Invalid Request");return}let{method:m,id:h}=d;if(h!==void 0&&h!==null){l(g(d).catch(($)=>n(`[mcp] request handler error: ${R($)}`)));return}l(i(d).catch(($)=>n(`[mcp] notification handler error: ${R($)}`)))};if(f.on("line",(d)=>{let m=d.trim();if(!m)return;let h;try{h=JSON.parse(m)}catch(b){oo(null,-32700,"Parse error",{message:R(b)});return}if(Array.isArray(h)){for(let b of h)y(b);return}y(h)}),n(`[mcp] server started: ${e.serverTitle} (projectRoot=${e.projectRoot}, protocol=${c})`),await new Promise((d)=>f.once("close",d)),u.size>0)await Promise.race([Promise.allSettled([...u]).then(()=>{return}),new Promise((d)=>setTimeout(d,2000))])}Le();oe();import{existsSync as zw,readFileSync as e$}from"fs";import{resolve as pc}from"path";var __dirname="/Users/wyatt/work/licell/src/utils",n$=process.env.LICELL_VERSION;function so(e){if(typeof e!=="string")return null;let n=e.trim();return n.length>0?n:null}function t$(){let e=[];if(typeof __dirname==="string"&&__dirname.length>0)e.push(pc(__dirname,"../../package.json")),e.push(pc(__dirname,"../package.json"));e.push(pc(process.cwd(),"package.json"));for(let n of e){if(!zw(n))continue;try{let t=JSON.parse(e$(n,"utf-8")),o=so(typeof t.version==="string"?t.version:null);if(o)return o}catch{}}return null}function tt(e){let n=e?.env??process.env,t=so(n.LICELL_VERSION);if(t)return t;let o=so(e?.bundledVersion??n$);if(o)return o;let s=so(n.npm_package_version);if(s)return s;let c=Object.prototype.hasOwnProperty.call(e??{},"packageVersion")?e?.packageVersion??null:t$(),f=so(c);if(f)return f;return"dev"}re();var Cg=["fc","dns","oss","rds","redis","cdn","vpc","cr","logs"];function Rg(e){if(!ss(e))return null;return JSON.parse(Eg(e,"utf8"))}function rs(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function Ig(e,n){wc(e,`${JSON.stringify(n,null,2)}
1642
- `,{encoding:"utf8"})}function Fg(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function o$(e,n){let t=new RegExp(`^[ \\t]*${Fg(n)}[ \\t]*=[ \\t]*\\[([\\s\\S]*?)\\][ \\t]*$`,"m"),o=e.match(t);if(!o)return null;return o[1].split(",").map((c)=>c.trim()).filter(Boolean).map((c)=>{let f=c.match(/^"(.*)"$/);return f?f[1]:c})}function s$(e,n){let t=new RegExp(`^[ \\t]*\\[${Fg(n)}\\][ \\t]*$`,"m"),o=e.match(t);if(!o||o.index===void 0)return null;let s=o.index,r=e.indexOf(`
1643
- `,s),c=r===-1?e.length:r+1,f=e.slice(c).match(/^[ \t]*\[[^\]]+\][ \t]*$/m),u=f&&f.index!==void 0?c+f.index:e.length;return{start:s,end:u}}function r$(e){let n=e.match(/^[ \t]*command[ \t]*=[ \t]*"([^"]+)"[ \t]*$/m);if(!n||n[1]!=="licell")return!1;let t=o$(e,"args");if(!t||t.length!==2)return!1;return t[0]==="mcp"&&t[1]==="serve"}function c$(e){return`[mcp_servers.${e}]
1639
+ ${n.stderr}${o}`.trim()}],structuredContent:n}}async function mc(e){let n=(d)=>{process.stderr.write(`${d}
1640
+ `)},t=process.env.LICELL_MCP_DEBUG==="1"||process.env.LICELL_MCP_DEBUG==="true",s={licell_cli:{name:"licell_cli",title:"Deploy & manage Aliyun services (licell)",description:"Use licell CLI to deploy API/static services to Alibaba Cloud and manage related resources (FC, custom domains, SSL, DNS, CDN, logs, etc.). Returns stdout/stderr.",inputSchema:{type:"object",additionalProperties:!1,properties:{argv:{type:"array",items:{type:"string"},minItems:1,description:'licell arguments, e.g. ["deploy","--type","static","--dist","dist"]'},cwd:{type:"string",description:"Working directory relative to projectRoot (default: projectRoot)"},timeoutMs:{type:"number",description:"Command timeout in milliseconds (default: 600000, max: 1800000)"}},required:["argv"]},annotations:{openWorldHint:!0,destructiveHint:!0}},licell_deploy:{name:"licell_deploy",title:"Deploy service (API/Static) to Aliyun",description:"Deploy current project. API deploys to Function Compute (FC 3.0); Static deploys to OSS hosting. For API, Agent should call licell_fc_deploy_spec + licell_fc_deploy_check before deploy.",inputSchema:{type:"object",additionalProperties:!1,properties:{type:{type:"string",enum:["api","static"],description:"Deployment type."},runtime:{type:"string",description:"API runtime: nodejs20/nodejs22/python3.12/python3.13/docker; Static: static/statis."},entry:{type:"string",description:"API entry file (default depends on runtime)."},dist:{type:"string",description:"Static site directory (default: dist)."},target:{type:"string",description:"FC alias target (e.g. prod/preview). API only."},domain:{type:"string",description:"Full custom domain (e.g. api.example.com). API/Static supported; implies SSL. Static will auto-enable CDN."},domainSuffix:{type:"string",description:"Domain suffix (e.g. example.com) to bind <appName>.<suffix>. API/Static supported."},enableCdn:{type:"boolean",description:"Enable CDN after domain bind (API optional; Static with domain already auto-enables). Implies SSL."},ssl:{type:"boolean",description:"Enable HTTPS (Let's Encrypt). If domain/enableCdn is set, SSL is implied."},sslForceRenew:{type:"boolean",description:"Force renew certificate when SSL enabled."},acrNamespace:{type:"string",description:"ACR namespace for docker runtime."},enableVpc:{type:"boolean",description:"Enable VPC integration (API only)."},disableVpc:{type:"boolean",description:"Disable VPC integration (API only, public mode)."},memory:{type:"number",description:"Memory size (MB)."},vcpu:{type:"number",description:"vCPU cores (e.g. 0.5/1/2)."},instanceConcurrency:{type:"number",description:"Instance concurrency."},timeout:{type:"number",description:"Timeout seconds."},cwd:{type:"string",description:"Working directory relative to projectRoot (default: projectRoot)."},timeoutMs:{type:"number",description:"Command timeout in milliseconds."}},required:["type"]}},licell_fc_deploy_spec:{name:"licell_fc_deploy_spec",title:"Get FC API deploy spec",description:"Return machine-readable FC API runtime specs (handlerContract/eventSchema/responseSchema/examples/validationRules and resource constraints) for agent planning.",inputSchema:{type:"object",additionalProperties:!1,properties:{runtime:{type:"string",description:"Optional runtime filter: nodejs20/nodejs22/python3.12/python3.13/docker."},all:{type:"boolean",description:"Return all runtime specs."},cwd:{type:"string",description:"Working directory relative to projectRoot (default: projectRoot)."},timeoutMs:{type:"number",description:"Command timeout in milliseconds."}}}},licell_fc_deploy_check:{name:"licell_fc_deploy_check",title:"Precheck FC API deploy readiness",description:"Read-only validation before FC API deployment. Returns actionable issues (missing handler, wrong entry, Docker prerequisites, etc.) and does not modify project files.",inputSchema:{type:"object",additionalProperties:!1,properties:{runtime:{type:"string",description:"Runtime to validate (default from project/env or nodejs20)."},entry:{type:"string",description:"Optional entry path override."},dockerDaemon:{type:"boolean",description:"When runtime=docker, also check local Docker daemon availability."},cwd:{type:"string",description:"Working directory relative to projectRoot (default: projectRoot)."},timeoutMs:{type:"number",description:"Command timeout in milliseconds."}}}},licell_init:{name:"licell_init",title:"Initialize licell project",description:"Initialize current directory: write .licell/project.json, and optionally generate scaffold files for supported runtimes.",inputSchema:{type:"object",additionalProperties:!1,properties:{runtime:{type:"string",description:"nodejs20/nodejs22/python3.12/python3.13/docker."},app:{type:"string",description:"appName (FC functionName)."},force:{type:"boolean",description:"Overwrite/generate scaffold in non-empty dir."},yes:{type:"boolean",description:"Non-interactive mode (recommended for MCP). Default true."},cwd:{type:"string",description:"Working directory relative to projectRoot (default: projectRoot)."},timeoutMs:{type:"number",description:"Command timeout in milliseconds."}}}},licell_release_promote:{name:"licell_release_promote",title:"Promote FC release (alias switch)",description:"Publish (if needed) and switch an FC alias (e.g. prod/preview) to a version.",inputSchema:{type:"object",additionalProperties:!1,properties:{versionId:{type:"string",description:"Optional versionId. If omitted, licell will publish current code or reuse latest published."},target:{type:"string",description:"Alias target (default: prod)."},cwd:{type:"string",description:"Working directory relative to projectRoot (default: projectRoot)."},timeoutMs:{type:"number",description:"Command timeout in milliseconds."}}}},licell_release_rollback:{name:"licell_release_rollback",title:"Rollback FC release (alias switch)",description:"Switch an FC alias to a specific versionId.",inputSchema:{type:"object",additionalProperties:!1,properties:{versionId:{type:"string",description:"VersionId to rollback to."},target:{type:"string",description:"Alias target (default: prod)."},cwd:{type:"string",description:"Working directory relative to projectRoot (default: projectRoot)."},timeoutMs:{type:"number",description:"Command timeout in milliseconds."}},required:["versionId"]}},licell_release_prune:{name:"licell_release_prune",title:"Prune FC historical versions (dangerous)",description:"Preview or delete old FC published versions. Destructive when apply=true (requires yes=true).",inputSchema:{type:"object",additionalProperties:!1,properties:{keep:{type:"number",description:"Keep latest N versions (default 10)."},apply:{type:"boolean",description:"If true, perform deletion. If false/omitted, preview only."},yes:{type:"boolean",description:"Required when apply=true (non-interactive double-confirm)."},cwd:{type:"string",description:"Working directory relative to projectRoot (default: projectRoot)."},timeoutMs:{type:"number",description:"Command timeout in milliseconds."}}},annotations:{destructiveHint:!0}},licell_fn_list:{name:"licell_fn_list",title:"List functions",description:"List FC functions in current region.",inputSchema:{type:"object",additionalProperties:!1,properties:{limit:{type:"number",description:"Max items (default 20)."},prefix:{type:"string",description:"Filter by function name prefix."},cwd:{type:"string"},timeoutMs:{type:"number"}}}},licell_fn_info:{name:"licell_fn_info",title:"Get function info",description:"Get FC function details.",inputSchema:{type:"object",additionalProperties:!1,properties:{name:{type:"string",description:"Function name. If omitted, uses project appName."},target:{type:"string",description:"Qualifier alias/version (e.g. prod/preview/1)."},cwd:{type:"string"},timeoutMs:{type:"number"}}}},licell_fn_invoke:{name:"licell_fn_invoke",title:"Invoke function",description:"Invoke FC function synchronously with an optional payload.",inputSchema:{type:"object",additionalProperties:!1,properties:{name:{type:"string",description:"Function name. If omitted, uses project appName."},target:{type:"string",description:"Qualifier alias/version (e.g. prod/preview/1)."},payload:{type:"string",description:"Raw payload text."},payloadJson:{type:"object",description:"JSON payload object (will be JSON.stringify-ed).",additionalProperties:!0},cwd:{type:"string"},timeoutMs:{type:"number"}}}},licell_fn_rm:{name:"licell_fn_rm",title:"Remove function (dangerous)",description:"Delete FC function. Destructive (requires yes=true).",inputSchema:{type:"object",additionalProperties:!1,properties:{name:{type:"string",description:"Function name. If omitted, uses project appName."},force:{type:"boolean",description:"Cascade delete triggers/aliases/versions."},yes:{type:"boolean",description:"Required in non-interactive mode."},cwd:{type:"string"},timeoutMs:{type:"number"}}},annotations:{destructiveHint:!0}},licell_domain_add:{name:"licell_domain_add",title:"Bind custom domain (and optional SSL)",description:"Bind a custom domain to current FC app and optionally enable HTTPS.",inputSchema:{type:"object",additionalProperties:!1,properties:{domain:{type:"string",description:"Full domain, e.g. api.example.com."},ssl:{type:"boolean",description:"Enable HTTPS (Let's Encrypt)."},sslForceRenew:{type:"boolean",description:"Force renew certificate."},target:{type:"string",description:"Route to FC alias (prod/preview)."},cwd:{type:"string"},timeoutMs:{type:"number"}},required:["domain"]}},licell_domain_rm:{name:"licell_domain_rm",title:"Unbind custom domain (dangerous)",description:"Unbind custom domain and cleanup DNS record. Destructive (requires yes=true).",inputSchema:{type:"object",additionalProperties:!1,properties:{domain:{type:"string",description:"Full domain, e.g. api.example.com."},yes:{type:"boolean",description:"Required in non-interactive mode."},cwd:{type:"string"},timeoutMs:{type:"number"}},required:["domain"]},annotations:{destructiveHint:!0}},licell_dns_records_list:{name:"licell_dns_records_list",title:"List DNS records",description:"List DNS records for a domain (Alidns).",inputSchema:{type:"object",additionalProperties:!1,properties:{domain:{type:"string",description:"Root domain, e.g. example.com."},limit:{type:"number",description:"Max items (default 100)."},cwd:{type:"string"},timeoutMs:{type:"number"}},required:["domain"]}},licell_dns_records_add:{name:"licell_dns_records_add",title:"Add DNS record",description:"Add a DNS record (Alidns).",inputSchema:{type:"object",additionalProperties:!1,properties:{domain:{type:"string",description:"Root domain, e.g. example.com."},rr:{type:"string",description:"RR host, e.g. @/www/api."},type:{type:"string",description:"Record type, e.g. A/CNAME/TXT."},value:{type:"string",description:"Record value."},ttl:{type:"number",description:"TTL seconds (default 600)."},line:{type:"string",description:"Line (default: default)."},cwd:{type:"string"},timeoutMs:{type:"number"}},required:["domain","rr","type","value"]}},licell_dns_records_rm:{name:"licell_dns_records_rm",title:"Remove DNS record (dangerous)",description:"Remove a DNS record by recordId. Destructive (requires yes=true).",inputSchema:{type:"object",additionalProperties:!1,properties:{recordId:{type:"string",description:"RecordId from list."},yes:{type:"boolean",description:"Required in non-interactive mode."},cwd:{type:"string"},timeoutMs:{type:"number"}},required:["recordId"]},annotations:{destructiveHint:!0}}},o=["2025-03-26","2025-06-18","2025-11-25"],r=!1,c=o[o.length-1],f=Ww({input:process.stdin,crlfDelay:1/0}),u=new Set,l=(d)=>{u.add(d),d.finally(()=>u.delete(d)).catch(()=>{})},i=async(d)=>{if(t)n(`[mcp] <= notification ${d.method}`);if(d.method==="notifications/initialized")return;if(d.method==="exit")process.exit(0)},g=async(d)=>{let{id:m,method:h}=d;if(t)n(`[mcp] <= request ${h} id=${m}`);if(!r&&h!=="initialize"&&h!=="ping"){ss(m,-32002,"Server not initialized. Call 'initialize' first.");return}if(h==="ping"){Rt(m,{});return}if(h==="initialize"){let b=no(d.params)?d.params:{};c=(typeof b.protocolVersion==="string"?b.protocolVersion:"")||c,r=!0,Rt(m,{protocolVersion:c,capabilities:{tools:{}},serverInfo:{name:"licell",version:e.serverVersion},instructions:`This MCP server is for deploying and managing services on Alibaba Cloud via licell, scoped to projectRoot.
1641
+ Destructive commands require explicit --yes in non-interactive mode.`});return}if(h==="shutdown"){Rt(m,null);return}if(h==="tools/list"){let b=[];for(let $ of Object.values(s)){let p={name:$.name,title:$.title,description:$.description,inputSchema:$.inputSchema};if("annotations"in $&&$.annotations)p.annotations=$.annotations;if(c<"2025-03-26")p.input_schema=$.inputSchema;b.push(p)}Rt(m,{tools:b});return}if(h==="tools/call"){let b=no(d.params)?d.params:{},$=typeof b.name==="string"?b.name:"",p=no(b.arguments)?b.arguments:{};try{let w=null,H=p.cwd,T=p.timeoutMs;if($==="licell_cli"){let E=p.argv;if(!Array.isArray(E)||E.some((_)=>typeof _!=="string"||_.trim()===""))throw Error("Invalid arguments: argv must be a non-empty string[]");w=E.map((_)=>_.trim())}else if($==="licell_deploy"){let E=D(p.type);if(E!=="api"&&E!=="static")throw Error('type must be "api" or "static"');w=["deploy","--type",E];let _=D(p.runtime),F=D(p.entry),I=D(p.dist),fe=D(p.target),Ce=D(p.domain),rn=D(p.domainSuffix),le=D(p.acrNamespace),cn=Te(p.enableCdn),Je=Te(p.ssl),fn=Te(p.sslForceRenew),st=Te(p.enableVpc),It=Te(p.disableVpc),qc=An(p.memory),Pc=An(p.vcpu),Rc=An(p.instanceConcurrency),Ic=An(p.timeout);if(_)w.push("--runtime",_);if(F)w.push("--entry",F);if(I)w.push("--dist",I);if(fe)w.push("--target",fe);if(Ce)w.push("--domain",Ce);if(rn)w.push("--domain-suffix",rn);if(cn)w.push("--enable-cdn");if(Je)w.push("--ssl");if(fn)w.push("--ssl-force-renew");if(le)w.push("--acr-namespace",le);if(st)w.push("--enable-vpc");if(It)w.push("--disable-vpc");if(qc!==void 0)w.push("--memory",String(qc));if(Pc!==void 0)w.push("--vcpu",String(Pc));if(Rc!==void 0)w.push("--instance-concurrency",String(Rc));if(Ic!==void 0)w.push("--timeout",String(Ic))}else if($==="licell_fc_deploy_spec"){w=["deploy","spec"];let E=D(p.runtime),_=Te(p.all);if(E)w.push(E);if(_)w.push("--all")}else if($==="licell_fc_deploy_check"){w=["deploy","check"];let E=D(p.runtime),_=D(p.entry),F=Te(p.dockerDaemon);if(E)w.push("--runtime",E);if(_)w.push("--entry",_);if(F)w.push("--docker-daemon")}else if($==="licell_init"){w=["init"];let E=D(p.runtime),_=D(p.app),F=Te(p.force),I=Te(p.yes);if(E)w.push("--runtime",E);if(_)w.push("--app",_);if(F)w.push("--force");if(I!==!1)w.push("--yes")}else if($==="licell_release_promote"){let E=D(p.versionId),_=D(p.target);if(w=["release","promote"],E)w.push(E);if(_)w.push("--target",_)}else if($==="licell_release_rollback"){let E=D(p.versionId);if(!E)throw Error("versionId is required");let _=D(p.target);if(w=["release","rollback",E],_)w.push("--target",_)}else if($==="licell_release_prune"){let E=An(p.keep),_=Te(p.apply),F=Te(p.yes);if(w=["release","prune"],E!==void 0)w.push("--keep",String(E));if(_)to(F,"apply=true is destructive; set yes=true to confirm"),w.push("--apply","--yes")}else if($==="licell_fn_list"){w=["fn","list"];let E=An(p.limit),_=D(p.prefix);if(E!==void 0)w.push("--limit",String(E));if(_)w.push("--prefix",_)}else if($==="licell_fn_info"){w=["fn","info"];let E=D(p.name),_=D(p.target);if(E)w.push(E);if(_)w.push("--target",_)}else if($==="licell_fn_invoke"){w=["fn","invoke"];let E=D(p.name),_=D(p.target),F=D(p.payload),I=p.payloadJson;if(F&&I!==void 0)throw Error("Provide only one of payload or payloadJson");if(E)w.push(E);if(_)w.push("--target",_);if(F)w.push("--payload",F);if(I!==void 0)w.push("--payload",JSON.stringify(I))}else if($==="licell_fn_rm"){w=["fn","rm"];let E=D(p.name),_=Te(p.force),F=Te(p.yes);if(to(F,"fn rm is destructive; set yes=true to confirm"),E)w.push(E);if(_)w.push("--force");w.push("--yes")}else if($==="licell_domain_add"){let E=D(p.domain);if(!E)throw Error("domain is required");let _=D(p.target),F=Te(p.ssl),I=Te(p.sslForceRenew);if(w=["domain","add",E],F)w.push("--ssl");if(I)w.push("--ssl-force-renew");if(_)w.push("--target",_)}else if($==="licell_domain_rm"){let E=D(p.domain);if(!E)throw Error("domain is required");let _=Te(p.yes);to(_,"domain rm is destructive; set yes=true to confirm"),w=["domain","rm",E,"--yes"]}else if($==="licell_dns_records_list"){let E=D(p.domain);if(!E)throw Error("domain is required");let _=An(p.limit);if(w=["dns","records","list",E],_!==void 0)w.push("--limit",String(_))}else if($==="licell_dns_records_add"){let E=D(p.domain),_=D(p.rr),F=D(p.type),I=D(p.value);if(!E||!_||!F||!I)throw Error("domain, rr, type, value are required");let fe=An(p.ttl),Ce=D(p.line);if(w=["dns","records","add",E,"--rr",_,"--type",F,"--value",I],fe!==void 0)w.push("--ttl",String(fe));if(Ce)w.push("--line",Ce)}else if($==="licell_dns_records_rm"){let E=D(p.recordId);if(!E)throw Error("recordId is required");let _=Te(p.yes);to(_,"dns records rm is destructive; set yes=true to confirm"),w=["dns","records","rm",E,"--yes"]}else throw Error(`Unknown tool: ${$}`);if(!w)throw Error(`Unknown tool: ${$}`);if(t)n(`[mcp] tool ${$} starting...`);let x=await Jw({projectRoot:e.projectRoot,argv:w,cwd:H,timeoutMs:T});if(t)n(`[mcp] tool ${$} done (exit=${x.exitCode}, timedOut=${x.timedOut})`);Rt(m,Nw(x))}catch(w){Rt(m,{isError:!0,content:[{type:"text",text:R(w)}]})}return}ss(m,-32601,`Method not found: ${h}`)},y=(d)=>{if(!no(d)||d.jsonrpc!=="2.0"){ss(null,-32600,"Invalid Request");return}if(typeof d.method!=="string"){ss(d.id??null,-32600,"Invalid Request");return}let{method:m,id:h}=d;if(h!==void 0&&h!==null){l(g(d).catch(($)=>n(`[mcp] request handler error: ${R($)}`)));return}l(i(d).catch(($)=>n(`[mcp] notification handler error: ${R($)}`)))};if(f.on("line",(d)=>{let m=d.trim();if(!m)return;let h;try{h=JSON.parse(m)}catch(b){ss(null,-32700,"Parse error",{message:R(b)});return}if(Array.isArray(h)){for(let b of h)y(b);return}y(h)}),n(`[mcp] server started: ${e.serverTitle} (projectRoot=${e.projectRoot}, protocol=${c})`),await new Promise((d)=>f.once("close",d)),u.size>0)await Promise.race([Promise.allSettled([...u]).then(()=>{return}),new Promise((d)=>setTimeout(d,2000))])}Ae();se();import{existsSync as zw,readFileSync as e$}from"fs";import{resolve as pc}from"path";var __dirname="/Users/wyatt/work/licell/src/utils",n$=process.env.LICELL_VERSION;function os(e){if(typeof e!=="string")return null;let n=e.trim();return n.length>0?n:null}function t$(){let e=[];if(typeof __dirname==="string"&&__dirname.length>0)e.push(pc(__dirname,"../../package.json")),e.push(pc(__dirname,"../package.json"));e.push(pc(process.cwd(),"package.json"));for(let n of e){if(!zw(n))continue;try{let t=JSON.parse(e$(n,"utf-8")),s=os(typeof t.version==="string"?t.version:null);if(s)return s}catch{}}return null}function tt(e){let n=e?.env??process.env,t=os(n.LICELL_VERSION);if(t)return t;let s=os(e?.bundledVersion??n$);if(s)return s;let o=os(n.npm_package_version);if(o)return o;let c=Object.prototype.hasOwnProperty.call(e??{},"packageVersion")?e?.packageVersion??null:t$(),f=os(c);if(f)return f;return"dev"}re();var Cg=["fc","dns","oss","rds","redis","cdn","vpc","cr","logs"];function Rg(e){if(!so(e))return null;return JSON.parse(Eg(e,"utf8"))}function oo(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function Ig(e,n){wc(e,`${JSON.stringify(n,null,2)}
1642
+ `,{encoding:"utf8"})}function Fg(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function s$(e,n){let t=new RegExp(`^[ \\t]*${Fg(n)}[ \\t]*=[ \\t]*\\[([\\s\\S]*?)\\][ \\t]*$`,"m"),s=e.match(t);if(!s)return null;return s[1].split(",").map((c)=>c.trim()).filter(Boolean).map((c)=>{let f=c.match(/^"(.*)"$/);return f?f[1]:c})}function o$(e,n){let t=new RegExp(`^[ \\t]*\\[${Fg(n)}\\][ \\t]*$`,"m"),s=e.match(t);if(!s||s.index===void 0)return null;let o=s.index,r=e.indexOf(`
1643
+ `,o),c=r===-1?e.length:r+1,f=e.slice(c).match(/^[ \t]*\[[^\]]+\][ \t]*$/m),u=f&&f.index!==void 0?c+f.index:e.length;return{start:o,end:u}}function r$(e){let n=e.match(/^[ \t]*command[ \t]*=[ \t]*"([^"]+)"[ \t]*$/m);if(!n||n[1]!=="licell")return!1;let t=s$(e,"args");if(!t||t.length!==2)return!1;return t[0]==="mcp"&&t[1]==="serve"}function c$(e){return`[mcp_servers.${e}]
1644
1644
  command = "licell"
1645
1645
  args = ["mcp", "serve"]
1646
- `}function cs(e){let n=$c(e.projectRoot,".mcp.json"),t=Rg(n),o=rs(t)?{...t}:{},r={...rs(o.mcpServers)?o.mcpServers:{}},c={command:"licell",args:["mcp","serve"]},f=r[e.serverName];if(JSON.stringify(f)===JSON.stringify(c))return{configPath:n,updated:!1};return r[e.serverName]=c,o.mcpServers=r,Ig(n,o),{configPath:n,updated:!0}}function xg(e){let n=e?.serverName||"licell",t=$c(Pg(),".claude","settings.local.json"),o=qg(t);if(!ss(o))Sg(o,{recursive:!0});let s=Rg(t),r=rs(s)?{...s}:{},f={...rs(r.mcpServers)?r.mcpServers:{}},u={command:"licell",args:["mcp","serve"]},l=f[n];if(JSON.stringify(l)===JSON.stringify(u))return{configPath:t,updated:!1};return f[n]=u,r.mcpServers=f,Ig(t,r),{configPath:t,updated:!0}}function Hg(e){let n=e?.serverName||"licell",t=$c(Pg(),".codex","config.toml"),o=qg(t);if(!ss(o))Sg(o,{recursive:!0});let s=ss(t)?Eg(t,"utf8"):"",r=`mcp_servers.${n}`,c=c$(n),f=s$(s,r);if(f){let i=s.slice(f.start,f.end);if(r$(i))return{configPath:t,updated:!1};let g=`${s.slice(0,f.start)}${c}${s.slice(f.end)}`;return wc(t,g,"utf8"),{configPath:t,updated:!0}}let u=s.trimEnd(),l=u.length>0?`${u}
1646
+ `}function ro(e){let n=$c(e.projectRoot,".mcp.json"),t=Rg(n),s=oo(t)?{...t}:{},r={...oo(s.mcpServers)?s.mcpServers:{}},c={command:"licell",args:["mcp","serve"]},f=r[e.serverName];if(JSON.stringify(f)===JSON.stringify(c))return{configPath:n,updated:!1};return r[e.serverName]=c,s.mcpServers=r,Ig(n,s),{configPath:n,updated:!0}}function xg(e){let n=e?.serverName||"licell",t=$c(Pg(),".claude","settings.local.json"),s=qg(t);if(!so(s))Sg(s,{recursive:!0});let o=Rg(t),r=oo(o)?{...o}:{},f={...oo(r.mcpServers)?r.mcpServers:{}},u={command:"licell",args:["mcp","serve"]},l=f[n];if(JSON.stringify(l)===JSON.stringify(u))return{configPath:t,updated:!1};return f[n]=u,r.mcpServers=f,Ig(t,r),{configPath:t,updated:!0}}function Hg(e){let n=e?.serverName||"licell",t=$c(Pg(),".codex","config.toml"),s=qg(t);if(!so(s))Sg(s,{recursive:!0});let o=so(t)?Eg(t,"utf8"):"",r=`mcp_servers.${n}`,c=c$(n),f=o$(o,r);if(f){let i=o.slice(f.start,f.end);if(r$(i))return{configPath:t,updated:!1};let g=`${o.slice(0,f.start)}${c}${o.slice(f.end)}`;return wc(t,g,"utf8"),{configPath:t,updated:!0}}let u=o.trimEnd(),l=u.length>0?`${u}
1647
1647
 
1648
- ${c}`:c;return wc(t,l,"utf8"),{configPath:t,updated:!0}}function Ag(e){e.command("mcp [action]","MCP:让 Agent 通过 licell 执行部署/发布/运维(初始化 .mcp.json 或启动 stdio server)").option("--project-root <path>","项目根目录(默认当前目录)").option("--server-name <name>","写入 .mcp.json 的 server 名称(默认 licell)").action(async(n,t)=>{let o=(n||"").trim().toLowerCase(),s=typeof t.projectRoot==="string"&&t.projectRoot.trim()?t.projectRoot.trim():process.cwd(),r=typeof t.serverName==="string"&&t.serverName.trim()?t.serverName.trim():"licell",c=P();if(!o){await pt({commandLabel:"licell mcp",interactiveTTY:c}),await wt({commandLabel:"licell mcp",interactiveTTY:c,requiredCapabilities:Cg});let{configPath:f,updated:u}=cs({projectRoot:s,serverName:r});if(a()){S({stage:"mcp",action:"init",configPath:f,updated:u,next:"run `licell mcp serve` without --output json to start stdio server"});return}console.log(ro.green(`✅ MCP 配置已就绪: ${f}${u?" (updated)":""}`)),console.log(ro.gray('现在启动 MCP 服务(stdio)。用于 Claude Code/Cursor 等客户端时,请在 .mcp.json 中使用 args: ["mcp","serve"]。')),console.log(ro.gray("提示:删除/清理类命令在 MCP 非交互模式下仍需要显式传 --yes。")),console.log(""),await mc({projectRoot:s,serverVersion:tt(),serverTitle:`licell ${tt()}`});return}if(o==="init"){let{configPath:f,updated:u}=cs({projectRoot:s,serverName:r});if(a())S({stage:"mcp",action:"init",configPath:f,updated:u});else console.log(ro.green(`✅ 已写入 MCP 配置: ${f}${u?"":" (no changes)"}`)),console.log(ro.gray("下一步:在支持 MCP 的客户端中启用该项目的 .mcp.json(例如 Claude Code)。"));return}if(o==="serve"){if(a())throw Error("mcp serve 使用 stdio JSON-RPC 协议,不支持 --output json");await pt({commandLabel:"licell mcp serve",interactiveTTY:c}),await wt({commandLabel:"licell mcp serve",interactiveTTY:c,requiredCapabilities:Cg}),await mc({projectRoot:s,serverVersion:tt(),serverTitle:`licell ${tt()}`});return}throw Error(`未知 mcp action: ${n||""}(支持: init / serve)`)})}var fs=["-h","--help","-v","--version","--output"],f$=["login","logout","whoami","switch","init","deploy","fn","oss","db","cache","e2e","release","domain","dns","env","logs","upgrade","mcp","auth","completion"],Lg={auth:["repair"],deploy:["spec","check"],fn:["list","info","invoke","rm"],oss:["list","info","ls","upload","bucket"],db:["add","list","info","connect"],cache:["add","list","info","connect","rotate-password"],e2e:["run","cleanup","list"],release:["list","promote","rollback","prune"],domain:["add","rm"],dns:["records"],env:["list","set","rm","pull"],mcp:["init","serve"],completion:["bash","zsh"]},Ug={"dns records":["list","add","rm"]},kc={login:["--account-id","--ak","--sk","--region","--bootstrap-ram","--bootstrap-user","--bootstrap-policy"],"auth repair":["--account-id","--ak","--sk","--region","--bootstrap-user","--bootstrap-policy"],switch:["--region"],init:["--runtime","--app","--force","--yes"],deploy:["--type","--entry","--dist","--runtime","--target","--domain","--domain-suffix","--enable-cdn","--ssl","--ssl-force-renew","--acr-namespace","--enable-vpc","--disable-vpc","--memory","--vcpu","--instance-concurrency","--timeout"],"deploy spec":["--all"],"deploy check":["--runtime","--entry","--docker-daemon"],"fn list":["--limit","--prefix"],"fn info":["--target"],"fn invoke":["--target","--payload","--file"],"fn rm":["--force","--yes"],"oss list":["--limit"],"oss ls":["--limit"],"oss upload":["--bucket","--source-dir","--target-dir"],"oss bucket":["--bucket","--source-dir","--target-dir"],"db add":["--type","--engine-version","--category","--class","--storage","--storage-type","--min-rcu","--max-rcu","--auto-pause","--zone","--zone-slave1","--zone-slave2","--vpc","--vsw","--security-ip-list","--description"],"db list":["--limit"],"db public-access":["--ip"],"cache add":["--type","--instance","--password","--username","--engine-version","--class","--node-type","--capacity","--vk-name","--compute-unit","--zone","--vpc","--vsw","--security-ip-list"],"cache list":["--limit"],"cache rotate-password":["--instance"],"cache public-access":["--ip"],"e2e run":["--suite","--run-id","--runtime","--target","--enable-vpc","--domain","--domain-suffix","--db-instance","--cache-instance","--skip-static","--enable-cdn","--cleanup","--workspace","--yes"],"e2e cleanup":["--manifest","--keep-workspace","--yes"],"release list":["--limit"],"release promote":["--target"],"release rollback":["--target"],"release prune":["--keep","--apply","--yes"],"domain add":["--ssl","--ssl-force-renew","--target"],"domain rm":["--yes"],"dns records list":["--limit"],"dns records add":["--rr","--type","--value","--ttl","--line"],"dns records rm":["--yes"],"env list":["--target","--show-values"],"env rm":["--yes"],"env pull":["--target"],mcp:["--project-root","--server-name"],upgrade:["--version","--repo","--script-url","--skip-checksum","--dry-run"],completion:["--engine"]};function co(e){return[...new Set(e)]}function u$(e){if(!e)return[];let n=e.trim();if(!n)return[];return n.split(/\s+/).filter(Boolean)}function i$(e){let n=e[0];if(!n||n.startsWith("-"))return[];let t=Lg[n];if(!t)return[n];let o=e[1];if(!o||o.startsWith("-")||!t.includes(o))return[n];let s=`${n} ${o}`,r=Ug[s];if(!r)return[n,o];let c=e[2];if(!c||c.startsWith("-")||!r.includes(c))return[n,o];return[n,o,c]}function l$(e){if(e.length===0)return co([...f$,...fs]);if(e.length===1){let t=e[0];return co([...Lg[t]||[],...kc[t]||[],...fs])}if(e.length===2){let t=`${e[0]} ${e[1]}`;return co([...Ug[t]||[],...kc[t]||[],...fs])}let n=`${e[0]} ${e[1]} ${e[2]}`;return co([...kc[n]||[],...fs])}function vg(e){let n=u$(e.compWords),t=Number(e.compCword);if(!Number.isFinite(t))t=n.length-1;let o=[...n];if(o[0]==="licell")o.shift(),t-=1;if(t<0)t=0;let s=e.compCur??o[t]??"",r=o.slice(0,Math.max(0,Math.min(t,o.length))),c=i$(r),f=l$(c),u=s.startsWith("-"),l=f.filter((i)=>{if(u&&!i.startsWith("-"))return!1;return i.startsWith(s)});return co(l).sort()}function Gg(e){let n=(e||"").trim().toLowerCase();if(n==="bash"||n==="zsh")return n;throw Error("shell 仅支持 bash 或 zsh")}function Mg(e){let n=(e||"").toLowerCase();if(n.includes("zsh"))return"zsh";if(n.includes("bash"))return"bash";return}function Bg(e){if(e==="bash")return`# bash completion for licell
1648
+ ${c}`:c;return wc(t,l,"utf8"),{configPath:t,updated:!0}}function Lg(e){e.command("mcp [action]","MCP:让 Agent 通过 licell 执行部署/发布/运维(初始化 .mcp.json 或启动 stdio server)").option("--project-root <path>","项目根目录(默认当前目录)").option("--server-name <name>","写入 .mcp.json 的 server 名称(默认 licell)").action(async(n,t)=>{let s=(n||"").trim().toLowerCase(),o=typeof t.projectRoot==="string"&&t.projectRoot.trim()?t.projectRoot.trim():process.cwd(),r=typeof t.serverName==="string"&&t.serverName.trim()?t.serverName.trim():"licell",c=P();if(!s){await pt({commandLabel:"licell mcp",interactiveTTY:c}),await wt({commandLabel:"licell mcp",interactiveTTY:c,requiredCapabilities:Cg});let{configPath:f,updated:u}=ro({projectRoot:o,serverName:r});if(a()){S({stage:"mcp",action:"init",configPath:f,updated:u,next:"run `licell mcp serve` without --output json to start stdio server"});return}console.log(rs.green(`✅ MCP 配置已就绪: ${f}${u?" (updated)":""}`)),console.log(rs.gray('现在启动 MCP 服务(stdio)。用于 Claude Code/Cursor 等客户端时,请在 .mcp.json 中使用 args: ["mcp","serve"]。')),console.log(rs.gray("提示:删除/清理类命令在 MCP 非交互模式下仍需要显式传 --yes。")),console.log(""),await mc({projectRoot:o,serverVersion:tt(),serverTitle:`licell ${tt()}`});return}if(s==="init"){let{configPath:f,updated:u}=ro({projectRoot:o,serverName:r});if(a())S({stage:"mcp",action:"init",configPath:f,updated:u});else console.log(rs.green(`✅ 已写入 MCP 配置: ${f}${u?"":" (no changes)"}`)),console.log(rs.gray("下一步:在支持 MCP 的客户端中启用该项目的 .mcp.json(例如 Claude Code)。"));return}if(s==="serve"){if(a())throw Error("mcp serve 使用 stdio JSON-RPC 协议,不支持 --output json");await pt({commandLabel:"licell mcp serve",interactiveTTY:c}),await wt({commandLabel:"licell mcp serve",interactiveTTY:c,requiredCapabilities:Cg}),await mc({projectRoot:o,serverVersion:tt(),serverTitle:`licell ${tt()}`});return}throw Error(`未知 mcp action: ${n||""}(支持: init / serve)`)})}var co=["-h","--help","-v","--version","--output"],f$=["login","logout","whoami","switch","init","deploy","fn","oss","db","cache","e2e","release","domain","dns","env","logs","upgrade","mcp","auth","completion"],Ag={auth:["repair"],deploy:["spec","check"],fn:["list","info","invoke","rm"],oss:["list","info","ls","upload","bucket"],db:["add","list","info","connect"],cache:["add","list","info","connect","rotate-password"],e2e:["run","cleanup","list"],release:["list","promote","rollback","prune"],domain:["add","rm"],dns:["records"],env:["list","set","rm","pull"],mcp:["init","serve"],completion:["bash","zsh"]},Ug={"dns records":["list","add","rm"]},kc={login:["--account-id","--ak","--sk","--region","--bootstrap-ram","--bootstrap-user","--bootstrap-policy"],"auth repair":["--account-id","--ak","--sk","--region","--bootstrap-user","--bootstrap-policy"],switch:["--region"],init:["--runtime","--app","--force","--yes"],deploy:["--type","--entry","--dist","--runtime","--target","--domain","--domain-suffix","--enable-cdn","--ssl","--ssl-force-renew","--acr-namespace","--enable-vpc","--disable-vpc","--memory","--vcpu","--instance-concurrency","--timeout"],"deploy spec":["--all"],"deploy check":["--runtime","--entry","--docker-daemon"],"fn list":["--limit","--prefix"],"fn info":["--target"],"fn invoke":["--target","--payload","--file"],"fn rm":["--force","--yes"],"oss list":["--limit"],"oss ls":["--limit"],"oss upload":["--bucket","--source-dir","--target-dir"],"oss bucket":["--bucket","--source-dir","--target-dir"],"db add":["--type","--engine-version","--category","--class","--storage","--storage-type","--min-rcu","--max-rcu","--auto-pause","--zone","--zone-slave1","--zone-slave2","--vpc","--vsw","--security-ip-list","--description"],"db list":["--limit"],"db public-access":["--ip"],"cache add":["--type","--instance","--password","--username","--engine-version","--class","--node-type","--capacity","--vk-name","--compute-unit","--zone","--vpc","--vsw","--security-ip-list"],"cache list":["--limit"],"cache rotate-password":["--instance"],"cache public-access":["--ip"],"e2e run":["--suite","--run-id","--runtime","--target","--enable-vpc","--domain","--domain-suffix","--db-instance","--cache-instance","--skip-static","--enable-cdn","--cleanup","--workspace","--yes"],"e2e cleanup":["--manifest","--keep-workspace","--yes"],"release list":["--limit"],"release promote":["--target"],"release rollback":["--target"],"release prune":["--keep","--apply","--yes"],"domain add":["--ssl","--ssl-force-renew","--target"],"domain rm":["--yes"],"dns records list":["--limit"],"dns records add":["--rr","--type","--value","--ttl","--line"],"dns records rm":["--yes"],"env list":["--target","--show-values"],"env rm":["--yes"],"env pull":["--target"],mcp:["--project-root","--server-name"],upgrade:["--version","--repo","--script-url","--skip-checksum","--dry-run"],completion:["--engine"]};function cs(e){return[...new Set(e)]}function u$(e){if(!e)return[];let n=e.trim();if(!n)return[];return n.split(/\s+/).filter(Boolean)}function i$(e){let n=e[0];if(!n||n.startsWith("-"))return[];let t=Ag[n];if(!t)return[n];let s=e[1];if(!s||s.startsWith("-")||!t.includes(s))return[n];let o=`${n} ${s}`,r=Ug[o];if(!r)return[n,s];let c=e[2];if(!c||c.startsWith("-")||!r.includes(c))return[n,s];return[n,s,c]}function l$(e){if(e.length===0)return cs([...f$,...co]);if(e.length===1){let t=e[0];return cs([...Ag[t]||[],...kc[t]||[],...co])}if(e.length===2){let t=`${e[0]} ${e[1]}`;return cs([...Ug[t]||[],...kc[t]||[],...co])}let n=`${e[0]} ${e[1]} ${e[2]}`;return cs([...kc[n]||[],...co])}function vg(e){let n=u$(e.compWords),t=Number(e.compCword);if(!Number.isFinite(t))t=n.length-1;let s=[...n];if(s[0]==="licell")s.shift(),t-=1;if(t<0)t=0;let o=e.compCur??s[t]??"",r=s.slice(0,Math.max(0,Math.min(t,s.length))),c=i$(r),f=l$(c),u=o.startsWith("-"),l=f.filter((i)=>{if(u&&!i.startsWith("-"))return!1;return i.startsWith(o)});return cs(l).sort()}function Gg(e){let n=(e||"").trim().toLowerCase();if(n==="bash"||n==="zsh")return n;throw Error("shell 仅支持 bash 或 zsh")}function Mg(e){let n=(e||"").toLowerCase();if(n.includes("zsh"))return"zsh";if(n.includes("bash"))return"bash";return}function Bg(e){if(e==="bash")return`# bash completion for licell
1649
1649
  _licell_completion() {
1650
1650
  local cur
1651
1651
  cur="\${COMP_WORDS[COMP_CWORD]}"
@@ -1673,7 +1673,7 @@ _licell_completion() {
1673
1673
  compdef _licell_completion licell
1674
1674
  `}re();function Yg(e){e.command("completion [shell]","输出 shell 补全脚本(bash/zsh)").option("--engine","内部补全引擎(供 shell completion 调用)").action((n,t)=>{if(t.engine){let c=vg({compWords:process.env.COMP_WORDS,compCword:process.env.COMP_CWORD,compCur:process.env.COMP_CUR});if(a()){S({stage:"completion.engine",count:c.length,candidates:c});return}if(c.length>0)process.stdout.write(`${c.join(`
1675
1675
  `)}
1676
- `);return}let o=Mg(process.env.SHELL),s=Gg(n||o||"bash"),r=Bg(s);if(a())S({stage:"completion.script",shell:s,script:r});else process.stdout.write(r)})}oe();ce();re();import{select as m$,isCancel as p$}from"@clack/prompts";import Un from"picocolors";import{existsSync as bc,mkdirSync as g$,readFileSync as Og,writeFileSync as us}from"fs";import{dirname as y$,join as fo,isAbsolute as d$}from"path";import{homedir as h$}from"os";var _c="- licell: Deploy and manage Alibaba Cloud Serverless applications using the licell CLI. Covers deploy, release, functions, env vars, domains, DNS, logs, OSS, database, cache, and MCP. (file: .claude/skills/licell/SKILL.md)";function Kg(){return`---
1676
+ `);return}let s=Mg(process.env.SHELL),o=Gg(n||s||"bash"),r=Bg(o);if(a())S({stage:"completion.script",shell:o,script:r});else process.stdout.write(r)})}se();ce();re();import{select as m$,isCancel as p$}from"@clack/prompts";import Un from"picocolors";import{existsSync as bc,mkdirSync as g$,readFileSync as Og,writeFileSync as fo}from"fs";import{dirname as y$,join as fs,isAbsolute as d$}from"path";import{homedir as h$}from"os";var _c="- licell: Deploy and manage Alibaba Cloud Serverless applications using the licell CLI. Covers deploy, release, functions, env vars, domains, DNS, logs, OSS, database, cache, and MCP. (file: .claude/skills/licell/SKILL.md)";function Kg(){return`---
1677
1677
  name: licell
1678
1678
  description: >-
1679
1679
  Deploy and manage Alibaba Cloud Serverless applications using the licell CLI.
@@ -1703,22 +1703,22 @@ licell release promote --target prod # 发布到生产
1703
1703
  \`\`\`
1704
1704
 
1705
1705
  <!-- PLACEHOLDER_COMMAND_REFERENCE -->
1706
- `}function jg(){return"## Command Reference\n\n### Deploy (`licell deploy`)\n\n一键打包部署到阿里云函数计算。\n\n| 选项 | 说明 |\n|------|------|\n| `--type <type>` | 部署类型:`api` 或 `static` |\n| `--entry <entry>` | API 入口文件(Node 默认 src/index.ts;Python 默认 src/main.py) |\n| `--dist <dist>` | 静态站点目录(默认 dist) |\n| `--runtime <runtime>` | 运行时:nodejs20/nodejs22/python3.12/python3.13/docker/static |\n| `--target <target>` | 部署后自动发布到指定 alias(如 prod/preview) |\n| `--domain <domain>` | 绑定完整自定义域名(如 api.example.com) |\n| `--domain-suffix <suffix>` | 自动绑定子域名后缀(如 example.com) |\n| `--enable-cdn` | 接入 CDN 并将 DNS CNAME 切到 CDN |\n| `--ssl` | 启用 HTTPS(Let's Encrypt 自动证书) |\n| `--enable-vpc` / `--disable-vpc` | 启用/禁用 VPC 接入 |\n| `--memory <mb>` | 函数内存(MB,默认 512) |\n| `--vcpu <n>` | vCPU 核数(默认 0.5) |\n| `--instance-concurrency <n>` | 单实例并发数 |\n| `--timeout <seconds>` | 函数超时(秒,默认 30) |\n| `--acr-namespace <ns>` | Docker 部署的 ACR 命名空间(默认 licell) |\n\n### FC API Spec & Precheck(Agent 推荐)\n\n| 命令 | 说明 |\n|------|------|\n| `licell deploy spec [runtime]` | 查看 FC API runtime 规格(entry/handler/资源约束) |\n| `licell deploy spec --all` | 查看全部 runtime 规格 |\n| `licell deploy check --runtime <runtime> --entry <entry>` | 部署前本地预检(缺少 handler/入口错误会提前报出) |\n| `licell deploy check --runtime docker --docker-daemon` | Docker 预检并检测本机 Docker daemon |\n\n**Agent 最佳实践:**\n\n```bash\n# 1) 先读规格(知道该 runtime 的硬性要求)\nlicell deploy spec nodejs22\n\n# 2) 再做预检(拿到可执行修复建议)\nlicell deploy check --runtime nodejs22 --entry src/index.ts\n\n# 3) 预检通过后再部署\nlicell deploy --type api --runtime nodejs22 --entry src/index.ts --target preview\n```\n\n`deploy spec --output json` 包含 `handlerContract`、`eventSchema`、`responseSchema`、`examples`、`validationRules`,可直接供 Agent 规划与校验。\n\n**常见用法:**\n\n```bash\n# API 部署到 preview\nlicell deploy --type api --target preview\n\n# 静态站点部署\nlicell deploy --type static --dist dist --domain-suffix example.com\n\n# Docker 部署\nlicell deploy --type api --runtime docker --target preview\n```\n\n### Release Management (`licell release`)\n\n| 命令 | 说明 |\n|------|------|\n| `licell release list [--limit <n>]` | 查看函数版本列表 |\n| `licell release promote [versionId] --target <alias>` | 发布并切流到目标别名(默认 prod) |\n| `licell release prune --keep <n> [--apply]` | 清理历史版本(不传 --apply 仅预览) |\n\n### Function Management (`licell fn`)\n\n| 命令 | 说明 |\n|------|------|\n| `licell fn list [--limit <n>] [--prefix <p>]` | 查看函数列表 |\n| `licell fn info [name] [--target <alias>]` | 查看函数详情 |\n| `licell fn invoke [name] [--target <alias>] [--payload <json>]` | 调用云端函数 |\n| `licell fn rm <name> [--force] [--yes]` | 删除函数(--force 级联删除触发器/alias/版本) |\n\n### Environment Variables (`licell env`)\n\n| 命令 | 说明 |\n|------|------|\n| `licell env list [--target <alias>] [--show-values]` | 查看云端环境变量 |\n| `licell env set <key> <value>` | 设置环境变量(同步本地配置) |\n| `licell env rm <key> [--yes]` | 删除环境变量 |\n| `licell env pull [--target <alias>]` | 拉取云端环境变量到本地 |\n\n### Domain (`licell domain`)\n\n| 命令 | 说明 |\n|------|------|\n| `licell domain add <domain> [--ssl] [--target <alias>]` | 绑定自定义域名 |\n| `licell domain rm <domain> [--yes]` | 解绑域名并清理 DNS |\n\n### DNS Records (`licell dns`)\n\n| 命令 | 说明 |\n|------|------|\n| `licell dns records list [domain] [--limit <n>]` | 查看解析记录 |\n| `licell dns records add <domain> --rr <rr> --type <type> --value <val>` | 添加解析记录 |\n| `licell dns records rm <recordId> [--yes]` | 删除解析记录 |\n\n### Logs (`licell logs`)\n\n| 选项 | 说明 |\n|------|------|\n| `--once` | 仅拉取一次最近日志并退出 |\n| `--window <seconds>` | 一次拉取的时间窗(默认 120 秒) |\n| `--lines <n>` | 每次最大日志条数(默认 1000) |\n\n### OSS (`licell oss`)\n\n| 命令 | 说明 |\n|------|------|\n| `licell oss list [--limit <n>]` | 查看 Bucket 列表 |\n| `licell oss info <bucket>` | 查看 Bucket 详情 |\n| `licell oss objects [bucket] [--prefix <p>] [--limit <n>]` | 查看对象列表 |\n| `licell oss upload [bucket] --source-dir <dir> [--target-dir <dir>]` | 上传目录到 OSS |\n\n### Database (`licell db`)\n\n| 命令 | 说明 |\n|------|------|\n| `licell db add --type <postgres\\|mysql>` | 分配 Serverless 数据库 |\n| `licell db list [--limit <n>]` | 查看数据库实例列表 |\n| `licell db info [instanceId]` | 查看实例详情 |\n| `licell db connect [instanceId]` | 输出连接信息 |\n\n`db add` 高级选项:`--engine-version`、`--class`、`--storage`、`--min-rcu`、`--max-rcu`、`--auto-pause`、`--zone`、`--vpc`、`--vsw`、`--security-ip-list`\n\n### Cache (`licell cache`)\n\n| 命令 | 说明 |\n|------|------|\n| `licell cache add --type redis` | 分配 Redis/Tair 缓存 |\n| `licell cache list [--limit <n>]` | 查看实例列表 |\n| `licell cache info [instanceId]` | 查看实例详情 |\n| `licell cache connect [instanceId]` | 输出连接信息 |\n| `licell cache rotate-password [--instance <id>]` | 轮换 Redis 密码 |\n\n`cache add` 高级选项:`--instance`、`--password`、`--class`、`--zone`、`--vpc`、`--vsw`、`--security-ip-list`\n\n### MCP Server (`licell mcp`)\n\n| 命令 | 说明 |\n|------|------|\n| `licell mcp` | 初始化 .mcp.json 并启动 stdio server |\n| `licell mcp init` | 仅写入 .mcp.json 配置 |\n| `licell mcp serve` | 启动 MCP stdio server |\n\n### Auth (`licell login` / `licell whoami` / `licell switch`)\n\n| 命令 | 说明 |\n|------|------|\n| `licell login` | 配置阿里云凭证(交互式或 --ak/--sk/--account-id) |\n| `licell login --bootstrap-ram` | 自动创建最小权限 RAM 用户 |\n| `licell whoami` | 查看当前登录信息 |\n| `licell switch --region <region>` | 切换默认 region |\n\n## Common Patterns\n\n### 典型部署流程\n\n```bash\n# 1. 部署到 preview 环境\nlicell deploy --type api --target preview\n\n# 2. 验证 preview 环境\nlicell fn invoke --target preview --payload '{\"path\":\"/health\"}'\n\n# 3. 发布到生产\nlicell release promote --target prod\n\n# 4. 如有问题,回滚\nlicell release rollback --target prod\n```\n\n### 环境变量管理\n\n```bash\nlicell env set DATABASE_URL \"postgresql://...\"\nlicell env set REDIS_URL \"redis://...\"\nlicell deploy --type api --target preview # 重新部署使变量生效\n```\n\n### 自定义域名 + HTTPS + CDN\n\n```bash\nlicell deploy --type api --target prod --domain api.example.com --ssl --enable-cdn\n```\n\n### 数据库 + 缓存 + VPC 全栈部署\n\n```bash\nlicell db add --type postgres\nlicell cache add --type redis\nlicell deploy --type api --target preview --enable-vpc\n```\n\n## Error Handling\n\n- 认证失败:运行 `licell login` 重新配置凭证\n- 部署失败:检查 `licell logs --once` 查看错误日志\n- 域名冲突:使用 `licell domain rm <domain>` 清理后重试\n- 版本清理:`licell release prune --keep 5 --apply` 清理旧版本\n- 破坏性操作(rm/prune)需要 `--yes` 跳过确认,或在交互模式下手动确认\n"}function a$(e){let n=h$();if(e==="claude")return fo(n,".claude","skills","licell");return fo(n,".agents","skills","licell")}function is(e){let n=Kg().replace(`<!-- PLACEHOLDER_COMMAND_REFERENCE -->
1706
+ `}function jg(){return"## Command Reference\n\n### Deploy (`licell deploy`)\n\n一键打包部署到阿里云函数计算。\n\n| 选项 | 说明 |\n|------|------|\n| `--type <type>` | 部署类型:`api` 或 `static` |\n| `--entry <entry>` | API 入口文件(Node 默认 src/index.ts;Python 默认 src/main.py) |\n| `--dist <dist>` | 静态站点目录(默认 dist) |\n| `--runtime <runtime>` | 运行时:nodejs20/nodejs22/python3.12/python3.13/docker/static |\n| `--target <target>` | 部署后自动发布到指定 alias(如 prod/preview) |\n| `--domain <domain>` | 绑定完整自定义域名(如 api.example.com) |\n| `--domain-suffix <suffix>` | 自动绑定子域名后缀(如 example.com) |\n| `--enable-cdn` | 接入 CDN 并将 DNS CNAME 切到 CDN |\n| `--ssl` | 启用 HTTPS(Let's Encrypt 自动证书) |\n| `--enable-vpc` / `--disable-vpc` | 启用/禁用 VPC 接入 |\n| `--memory <mb>` | 函数内存(MB,默认 512) |\n| `--vcpu <n>` | vCPU 核数(默认 0.5) |\n| `--instance-concurrency <n>` | 单实例并发数 |\n| `--timeout <seconds>` | 函数超时(秒,默认 30) |\n| `--acr-namespace <ns>` | Docker 部署的 ACR 命名空间(默认 licell) |\n\n### FC API Spec & Precheck(Agent 推荐)\n\n| 命令 | 说明 |\n|------|------|\n| `licell deploy spec [runtime]` | 查看 FC API runtime 规格(entry/handler/资源约束) |\n| `licell deploy spec --all` | 查看全部 runtime 规格 |\n| `licell deploy check --runtime <runtime> --entry <entry>` | 部署前本地预检(缺少 handler/入口错误会提前报出) |\n| `licell deploy check --runtime docker --docker-daemon` | Docker 预检并检测本机 Docker daemon |\n\n**Agent 最佳实践:**\n\n```bash\n# 1) 先读规格(知道该 runtime 的硬性要求)\nlicell deploy spec nodejs22\n\n# 2) 再做预检(拿到可执行修复建议)\nlicell deploy check --runtime nodejs22 --entry src/index.ts\n\n# 3) 预检通过后再部署\nlicell deploy --type api --runtime nodejs22 --entry src/index.ts --target preview\n```\n\n`deploy spec --output json` 包含 `handlerContract`、`eventSchema`、`responseSchema`、`examples`、`validationRules`,可直接供 Agent 规划与校验。\n\n**常见用法:**\n\n```bash\n# API 部署到 preview\nlicell deploy --type api --target preview\n\n# 静态站点部署\nlicell deploy --type static --dist dist --domain-suffix example.com\n\n# Docker 部署\nlicell deploy --type api --runtime docker --target preview\n```\n\n### Release Management (`licell release`)\n\n| 命令 | 说明 |\n|------|------|\n| `licell release list [--limit <n>]` | 查看函数版本列表 |\n| `licell release promote [versionId] --target <alias>` | 发布并切流到目标别名(默认 prod) |\n| `licell release prune --keep <n> [--apply]` | 清理历史版本(不传 --apply 仅预览) |\n\n### Function Management (`licell fn`)\n\n| 命令 | 说明 |\n|------|------|\n| `licell fn list [--limit <n>] [--prefix <p>]` | 查看函数列表 |\n| `licell fn info [name] [--target <alias>]` | 查看函数详情 |\n| `licell fn invoke [name] [--target <alias>] [--payload <json>]` | 调用云端函数 |\n| `licell fn rm <name> [--force] [--yes]` | 删除函数(--force 级联删除触发器/alias/版本) |\n\n### Environment Variables (`licell env`)\n\n| 命令 | 说明 |\n|------|------|\n| `licell env list [--target <alias>] [--show-values]` | 查看云端环境变量 |\n| `licell env set <key> <value>` | 设置环境变量(同步本地配置) |\n| `licell env rm <key> [--yes]` | 删除环境变量 |\n| `licell env pull [--target <alias>]` | 拉取云端环境变量到本地 |\n\n### Domain (`licell domain`)\n\n| 命令 | 说明 |\n|------|------|\n| `licell domain add <domain> [--ssl] [--target <alias>]` | 绑定自定义域名 |\n| `licell domain rm <domain> [--yes]` | 解绑域名并清理 DNS |\n\n### DNS Records (`licell dns`)\n\n| 命令 | 说明 |\n|------|------|\n| `licell dns records list [domain] [--limit <n>]` | 查看解析记录 |\n| `licell dns records add <domain> --rr <rr> --type <type> --value <val>` | 添加解析记录 |\n| `licell dns records rm <recordId> [--yes]` | 删除解析记录 |\n\n### Logs (`licell logs`)\n\n| 选项 | 说明 |\n|------|------|\n| `--once` | 仅拉取一次最近日志并退出 |\n| `--window <seconds>` | 一次拉取的时间窗(默认 120 秒) |\n| `--lines <n>` | 每次最大日志条数(默认 1000) |\n\n### OSS (`licell oss`)\n\n| 命令 | 说明 |\n|------|------|\n| `licell oss list [--limit <n>]` | 查看 Bucket 列表 |\n| `licell oss info <bucket>` | 查看 Bucket 详情 |\n| `licell oss objects [bucket] [--prefix <p>] [--limit <n>]` | 查看对象列表 |\n| `licell oss upload [bucket] --source-dir <dir> [--target-dir <dir>]` | 上传目录到 OSS |\n\n### Database (`licell db`)\n\n| 命令 | 说明 |\n|------|------|\n| `licell db add --type <postgres\\|mysql>` | 分配 Serverless 数据库 |\n| `licell db list [--limit <n>]` | 查看数据库实例列表 |\n| `licell db info [instanceId]` | 查看实例详情 |\n| `licell db connect [instanceId]` | 输出连接信息 |\n\n`db add` 高级选项:`--engine-version`、`--class`、`--storage`、`--min-rcu`、`--max-rcu`、`--auto-pause`、`--zone`、`--vpc`、`--vsw`、`--security-ip-list`\n\n### Cache (`licell cache`)\n\n| 命令 | 说明 |\n|------|------|\n| `licell cache add --type redis` | 分配 Redis/Tair 缓存 |\n| `licell cache list [--limit <n>]` | 查看实例列表 |\n| `licell cache info [instanceId]` | 查看实例详情 |\n| `licell cache connect [instanceId]` | 输出连接信息 |\n| `licell cache rotate-password [--instance <id>]` | 轮换 Redis 密码 |\n\n`cache add` 高级选项:`--instance`、`--password`、`--class`、`--zone`、`--vpc`、`--vsw`、`--security-ip-list`\n\n### MCP Server (`licell mcp`)\n\n| 命令 | 说明 |\n|------|------|\n| `licell mcp` | 初始化 .mcp.json 并启动 stdio server |\n| `licell mcp init` | 仅写入 .mcp.json 配置 |\n| `licell mcp serve` | 启动 MCP stdio server |\n\n### Auth (`licell login` / `licell whoami` / `licell switch`)\n\n| 命令 | 说明 |\n|------|------|\n| `licell login` | 配置阿里云凭证(交互式或 --ak/--sk/--account-id) |\n| `licell login --bootstrap-ram` | 自动创建最小权限 RAM 用户 |\n| `licell whoami` | 查看当前登录信息 |\n| `licell switch --region <region>` | 切换默认 region |\n\n## Common Patterns\n\n### 典型部署流程\n\n```bash\n# 1. 部署到 preview 环境\nlicell deploy --type api --target preview\n\n# 2. 验证 preview 环境\nlicell fn invoke --target preview --payload '{\"path\":\"/health\"}'\n\n# 3. 发布到生产\nlicell release promote --target prod\n\n# 4. 如有问题,回滚\nlicell release rollback --target prod\n```\n\n### 环境变量管理\n\n```bash\nlicell env set DATABASE_URL \"postgresql://...\"\nlicell env set REDIS_URL \"redis://...\"\nlicell deploy --type api --target preview # 重新部署使变量生效\n```\n\n### 自定义域名 + HTTPS + CDN\n\n```bash\nlicell deploy --type api --target prod --domain api.example.com --ssl --enable-cdn\n```\n\n### 数据库 + 缓存 + VPC 全栈部署\n\n```bash\nlicell db add --type postgres\nlicell cache add --type redis\nlicell deploy --type api --target preview --enable-vpc\n```\n\n## Error Handling\n\n- 认证失败:运行 `licell login` 重新配置凭证\n- 部署失败:检查 `licell logs --once` 查看错误日志\n- 域名冲突:使用 `licell domain rm <domain>` 清理后重试\n- 版本清理:`licell release prune --keep 5 --apply` 清理旧版本\n- 破坏性操作(rm/prune)需要 `--yes` 跳过确认,或在交互模式下手动确认\n"}function a$(e){let n=h$();if(e==="claude")return fs(n,".claude","skills","licell");return fs(n,".agents","skills","licell")}function uo(e){let n=Kg().replace(`<!-- PLACEHOLDER_COMMAND_REFERENCE -->
1707
1707
  `,"")+jg();if(e==="claude")return[{path:".claude/skills/licell/SKILL.md",content:n}];return[{path:"codex.md",content:n}]}function Wg(e){let n=Kg().replace(`<!-- PLACEHOLDER_COMMAND_REFERENCE -->
1708
- `,"")+jg(),t=a$(e);return[{path:fo(t,"SKILL.md"),content:n}]}function ls(e){let n=fo(e,"AGENTS.md");if(!bc(n)){let c=`# AGENTS.md
1708
+ `,"")+jg(),t=a$(e);return[{path:fs(t,"SKILL.md"),content:n}]}function io(e){let n=fs(e,"AGENTS.md");if(!bc(n)){let c=`# AGENTS.md
1709
1709
 
1710
1710
  ## Available Skills
1711
1711
 
1712
1712
  ${_c}
1713
- `;return us(n,c,"utf8"),{filePath:n,updated:!0}}let t=Og(n,"utf8");if(t.includes(".claude/skills/licell/SKILL.md"))return{filePath:n,updated:!1};let s=/^(#{2,3}\s+Available\s+[Ss]kills\s*)$/m.exec(t);if(s&&s.index!==void 0){let c=s.index+s[0].length,f=`${t.slice(0,c)}
1714
- ${_c}${t.slice(c)}`;return us(n,f,"utf8"),{filePath:n,updated:!0}}let r=`${t.trimEnd()}
1713
+ `;return fo(n,c,"utf8"),{filePath:n,updated:!0}}let t=Og(n,"utf8");if(t.includes(".claude/skills/licell/SKILL.md"))return{filePath:n,updated:!1};let o=/^(#{2,3}\s+Available\s+[Ss]kills\s*)$/m.exec(t);if(o&&o.index!==void 0){let c=o.index+o[0].length,f=`${t.slice(0,c)}
1714
+ ${_c}${t.slice(c)}`;return fo(n,f,"utf8"),{filePath:n,updated:!0}}let r=`${t.trimEnd()}
1715
1715
 
1716
1716
  ## Available Skills
1717
1717
 
1718
1718
  ${_c}
1719
- `;return us(n,r,"utf8"),{filePath:n,updated:!0}}function gs(e,n,t=!1){let o=[],s=[];for(let r of n){let c=d$(r.path)?r.path:fo(e,r.path),f=y$(c);if(!bc(f))g$(f,{recursive:!0});if(bc(c)){if(Og(c,"utf8")===r.content){s.push(r.path);continue}if(!t)throw Error(`文件已存在且内容不同: ${r.path}(使用 --force 覆盖)`)}us(c,r.content,"utf8"),o.push(r.path)}return{written:o,skipped:s}}var w$=new Set(["claude","codex"]);function Qg(e){e.command("skills init [agent]","为 AI Agent 生成 licell skills(claude / codex)").option("--project-root <path>","目标项目目录(默认当前目录)").option("--force","覆盖已有文件").action(async(n,t)=>{if(!a())j(Un.bgBlue(Un.white(" \uD83D\uDEE0 Licell Skills Init ")));else X({stage:"skills",action:"skills init",status:"start"});let o=P(),s=typeof t.projectRoot==="string"&&t.projectRoot.trim()?t.projectRoot.trim():process.cwd();try{let r;if(n&&w$.has(n))r=n;else if(n)throw Error(`不支持的 agent: ${n}(支持: claude / codex)`);else if(o){let g=await m$({message:"选择目标 Agent:",options:[{value:"claude",label:"Claude Code (.claude/skills/ + AGENTS.md)"},{value:"codex",label:"OpenAI Codex (codex.md + AGENTS.md)"}]});if(p$(g)){if(a())throw Error("操作已取消");process.exit(0)}r=g}else throw Error("非交互模式下必须指定 agent 参数(claude / codex)");let c=L();c.start(`正在生成 ${r} skills...`);let f=is(r),{written:u,skipped:l}=gs(s,f,Boolean(t.force)),i=ls(s);if(c.stop(Un.green("✅ Skills 生成完成")),console.log(`agent: ${Un.cyan(r)}`),u.length>0){console.log(`
1719
+ `;return fo(n,r,"utf8"),{filePath:n,updated:!0}}function lo(e,n,t=!1){let s=[],o=[];for(let r of n){let c=d$(r.path)?r.path:fs(e,r.path),f=y$(c);if(!bc(f))g$(f,{recursive:!0});if(bc(c)){if(Og(c,"utf8")===r.content){o.push(r.path);continue}if(!t)throw Error(`文件已存在且内容不同: ${r.path}(使用 --force 覆盖)`)}fo(c,r.content,"utf8"),s.push(r.path)}return{written:s,skipped:o}}var w$=new Set(["claude","codex"]);function Qg(e){e.command("skills init [agent]","为 AI Agent 生成 licell skills(claude / codex)").option("--project-root <path>","目标项目目录(默认当前目录)").option("--force","覆盖已有文件").action(async(n,t)=>{if(!a())j(Un.bgBlue(Un.white(" \uD83D\uDEE0 Licell Skills Init ")));else X({stage:"skills",action:"skills init",status:"start"});let s=P(),o=typeof t.projectRoot==="string"&&t.projectRoot.trim()?t.projectRoot.trim():process.cwd();try{let r;if(n&&w$.has(n))r=n;else if(n)throw Error(`不支持的 agent: ${n}(支持: claude / codex)`);else if(s){let g=await m$({message:"选择目标 Agent:",options:[{value:"claude",label:"Claude Code (.claude/skills/ + AGENTS.md)"},{value:"codex",label:"OpenAI Codex (codex.md + AGENTS.md)"}]});if(p$(g)){if(a())throw Error("操作已取消");process.exit(0)}r=g}else throw Error("非交互模式下必须指定 agent 参数(claude / codex)");let c=A();c.start(`正在生成 ${r} skills...`);let f=uo(r),{written:u,skipped:l}=lo(o,f,Boolean(t.force)),i=io(o);if(c.stop(Un.green("✅ Skills 生成完成")),console.log(`agent: ${Un.cyan(r)}`),u.length>0){console.log(`
1720
1720
  已写入文件:`);for(let g of u)console.log(` ${Un.green("+")} ${g}`)}if(l.length>0){console.log(`
1721
- 已跳过(内容相同):`);for(let g of l)console.log(` ${Un.gray("=")} ${g}`)}if(i.updated)console.log(` ${Un.green("+")} AGENTS.md`);else console.log(` ${Un.gray("=")} AGENTS.md(已包含 licell 条目)`);if(a())S({stage:"skills",agent:r,projectRoot:s,writtenFiles:u,skippedFiles:l,agentsMdUpdated:i.updated});else q("Done.")}catch(r){if(a())he(r,{stage:"skills"});else console.error(R(r));process.exitCode=1}})}oe();ce();re();import{select as Dg,confirm as $$,isCancel as Tc}from"@clack/prompts";import Ze from"picocolors";var k$=new Set(["claude","codex"]);async function Cc(e={}){let n=P(),t=a(),o=typeof e.projectRoot==="string"&&e.projectRoot.trim()?e.projectRoot.trim():process.cwd();try{let s=()=>{if(t)S({stage:"setup",cancelled:!0});else q("已取消")},r;if(e.agent&&k$.has(e.agent))r=e.agent;else if(e.agent)throw Error(`不支持的 agent: ${e.agent}(支持: claude / codex)`);else if(n){let h=await Dg({message:"选择目标 Agent:",options:[{value:"claude",label:"Claude Code"},{value:"codex",label:"OpenAI Codex"}]});if(Tc(h)){s();return}r=h}else throw Error("非交互模式下必须指定 --agent 参数(claude / codex)");let c;if(e.global)c="global";else if(n){let h=await Dg({message:"配置范围:",options:[{value:"global",label:"全局(所有项目生效)"},{value:"project",label:"当前项目"}]});if(Tc(h)){s();return}c=h}else c="global";let f=L();if(!t)f.start("正在生成 Skills...");let u=c==="global"?Wg(r):is(r),l=c==="global"?"":o,{written:i,skipped:g}=gs(l,u,Boolean(e.force));if(c==="project")ls(o);if(!t)f.stop(Ze.green("✅ Skills 生成完成"));if(!t&&i.length>0)for(let h of i)console.log(` ${Ze.green("+")} ${h}`);if(!t&&g.length>0)for(let h of g)console.log(` ${Ze.gray("=")} ${h}(已存在)`);let y=!1,d=null,m=null;if(n){let h=await $$({message:"是否配置 MCP(让 Agent 能调用 licell)?"});if(Tc(h)){s();return}y=h===!0}if(y)if(c==="global"&&r==="claude"){let{configPath:h,updated:b}=xg();if(d=h,m=b,!t)console.log(` ${b?Ze.green("+"):Ze.gray("=")} ${h}${b?"":"(已存在)"}`)}else if(c==="global"&&r==="codex"){let{configPath:h,updated:b}=Hg();if(d=h,m=b,!t)console.log(` ${b?Ze.green("+"):Ze.gray("=")} ${h}${b?"":"(已存在)"}`)}else{let{configPath:h,updated:b}=cs({projectRoot:o,serverName:"licell"});if(d=h,m=b,!t)console.log(` ${b?Ze.green("+"):Ze.gray("=")} ${h}${b?"":"(已存在)"}`)}if(t)S({stage:"setup",agent:r,scope:c,projectRoot:o,writtenFiles:i,skippedFiles:g,mcpConfigured:y,mcpConfigPath:d,mcpConfigUpdated:m});else q("Done.")}catch(s){if(t)he(s,{stage:"setup"});else console.error(R(s));process.exitCode=1}}function Vg(e){e.command("setup","安装后引导:配置 AI Agent Skills 和 MCP").option("--agent <agent>","目标 Agent(claude / codex)").option("--global","全局配置(所有项目生效)").option("--project-root <path>","项目目录(默认当前目录)").option("--force","覆盖已有文件").action(async(n)=>{if(!a())j(Ze.bgBlue(Ze.white(" \uD83D\uDEE0 Licell Setup ")));else X({stage:"setup",action:"setup",status:"start"});await Cc(n)})}K();oe();oe();re();import vn from"picocolors";function Xg(e){e.command("config domain [suffix]","查看或设置全局默认域名后缀").option("--unset","清除已设置的全局域名后缀").action((n,t)=>{let o=k.getGlobalConfig();if(t.unset){if(k.setGlobalConfig({domainSuffix:void 0}),a())S({stage:"config.domain",domainSuffix:null,action:"unset"});else j(vn.bgMagenta(vn.white(" ⚙ Config "))),q(vn.green("已清除全局域名后缀"));return}let s=C(n);if(!s){let c=o.domainSuffix||null;if(a())S({stage:"config.domain",domainSuffix:c});else if(c)console.log(`全局域名后缀: ${vn.cyan(c)}`);else console.log(vn.gray("未设置全局域名后缀。用法: licell config domain <suffix>"));return}let r=Vn(s);if(k.setGlobalConfig({domainSuffix:r}),a())S({stage:"config.domain",domainSuffix:r,action:"set"});else j(vn.bgMagenta(vn.white(" ⚙ Config "))),console.log(`全局域名后缀已设置为: ${vn.cyan(r)}`),q("后续 deploy/domain 命令将自动使用此域名后缀")})}import{existsSync as Jg,readFileSync as _$,writeFileSync as b$,mkdirSync as T$}from"fs";import{homedir as C$}from"os";import{join as Ng}from"path";import uo from"picocolors";var Ec=Ng(C$(),".licell-cli"),Sc=Ng(Ec,"update-check.json"),E$=43200000,S$=2000,q$="https://api.github.com/repos/agents-infrastructure/licell/releases/latest";function Zg(e,n){let t=e.split(".").map(Number),o=n.split(".").map(Number),s=Math.max(t.length,o.length);for(let r=0;r<s;r++){let c=t[r]??0,f=o[r]??0;if(c!==f)return c-f}return 0}function P$(){try{if(!Jg(Sc))return null;let e=JSON.parse(_$(Sc,"utf-8"));if(typeof e.latestVersion!=="string"||typeof e.checkedAt!=="number")return null;return e}catch{return null}}function R$(e){try{if(!Jg(Ec))T$(Ec,{recursive:!0});b$(Sc,JSON.stringify(e),"utf-8")}catch{}}async function I$(){try{let e=new AbortController,n=setTimeout(()=>e.abort(),S$),t=await fetch(q$,{signal:e.signal,headers:{Accept:"application/vnd.github+json"}});if(clearTimeout(n),!t.ok)return null;let o=await t.json();if(typeof o.tag_name!=="string")return null;return o.tag_name.replace(/^v/,"")}catch{return null}}async function zg(e){if(!e||e==="dev")return null;let n=P$();if(n&&Date.now()-n.checkedAt<E$){if(Zg(n.latestVersion,e)>0)return{currentVersion:e,latestVersion:n.latestVersion};return null}let t=await I$();if(!t)return null;if(R$({latestVersion:t,checkedAt:Date.now()}),Zg(t,e)>0)return{currentVersion:e,latestVersion:t};return null}function ey(e){let n=`新版本可用: ${e.currentVersion} → ${e.latestVersion}`,t="licell upgrade",o=Math.max(n.length,18),s=(r)=>r+" ".repeat(o-r.replace(/\x1b\[[0-9;]*m/g,"").length);console.error(""),console.error(uo.yellow(`╭${"─".repeat(o+4)}╮`)),console.error(uo.yellow(`│ ${s(n)} │`)),console.error(uo.yellow(`│ ${s(`运行 ${uo.bold("licell upgrade")} 升级`)} │`)),console.error(uo.yellow(`╰${"─".repeat(o+4)}╯`))}ce();K();oe();Go();import{confirm as ny,isCancel as ty}from"@clack/prompts";import Gn from"picocolors";ce();async function oy(){console.log(Gn.cyan(`
1721
+ 已跳过(内容相同):`);for(let g of l)console.log(` ${Un.gray("=")} ${g}`)}if(i.updated)console.log(` ${Un.green("+")} AGENTS.md`);else console.log(` ${Un.gray("=")} AGENTS.md(已包含 licell 条目)`);if(a())S({stage:"skills",agent:r,projectRoot:o,writtenFiles:u,skippedFiles:l,agentsMdUpdated:i.updated});else q("Done.")}catch(r){if(a())he(r,{stage:"skills"});else console.error(R(r));process.exitCode=1}})}se();ce();re();import{select as Dg,confirm as $$,isCancel as Tc}from"@clack/prompts";import Ze from"picocolors";var k$=new Set(["claude","codex"]);async function Cc(e={}){let n=P(),t=a(),s=typeof e.projectRoot==="string"&&e.projectRoot.trim()?e.projectRoot.trim():process.cwd();try{let o=()=>{if(t)S({stage:"setup",cancelled:!0});else q("已取消")},r;if(e.agent&&k$.has(e.agent))r=e.agent;else if(e.agent)throw Error(`不支持的 agent: ${e.agent}(支持: claude / codex)`);else if(n){let h=await Dg({message:"选择目标 Agent:",options:[{value:"claude",label:"Claude Code"},{value:"codex",label:"OpenAI Codex"}]});if(Tc(h)){o();return}r=h}else throw Error("非交互模式下必须指定 --agent 参数(claude / codex)");let c;if(e.global)c="global";else if(n){let h=await Dg({message:"配置范围:",options:[{value:"global",label:"全局(所有项目生效)"},{value:"project",label:"当前项目"}]});if(Tc(h)){o();return}c=h}else c="global";let f=A();if(!t)f.start("正在生成 Skills...");let u=c==="global"?Wg(r):uo(r),l=c==="global"?"":s,{written:i,skipped:g}=lo(l,u,Boolean(e.force));if(c==="project")io(s);if(!t)f.stop(Ze.green("✅ Skills 生成完成"));if(!t&&i.length>0)for(let h of i)console.log(` ${Ze.green("+")} ${h}`);if(!t&&g.length>0)for(let h of g)console.log(` ${Ze.gray("=")} ${h}(已存在)`);let y=!1,d=null,m=null;if(n){let h=await $$({message:"是否配置 MCP(让 Agent 能调用 licell)?"});if(Tc(h)){o();return}y=h===!0}if(y)if(c==="global"&&r==="claude"){let{configPath:h,updated:b}=xg();if(d=h,m=b,!t)console.log(` ${b?Ze.green("+"):Ze.gray("=")} ${h}${b?"":"(已存在)"}`)}else if(c==="global"&&r==="codex"){let{configPath:h,updated:b}=Hg();if(d=h,m=b,!t)console.log(` ${b?Ze.green("+"):Ze.gray("=")} ${h}${b?"":"(已存在)"}`)}else{let{configPath:h,updated:b}=ro({projectRoot:s,serverName:"licell"});if(d=h,m=b,!t)console.log(` ${b?Ze.green("+"):Ze.gray("=")} ${h}${b?"":"(已存在)"}`)}if(t)S({stage:"setup",agent:r,scope:c,projectRoot:s,writtenFiles:i,skippedFiles:g,mcpConfigured:y,mcpConfigPath:d,mcpConfigUpdated:m});else q("Done.")}catch(o){if(t)he(o,{stage:"setup"});else console.error(R(o));process.exitCode=1}}function Vg(e){e.command("setup","安装后引导:配置 AI Agent Skills 和 MCP").option("--agent <agent>","目标 Agent(claude / codex)").option("--global","全局配置(所有项目生效)").option("--project-root <path>","项目目录(默认当前目录)").option("--force","覆盖已有文件").action(async(n)=>{if(!a())j(Ze.bgBlue(Ze.white(" \uD83D\uDEE0 Licell Setup ")));else X({stage:"setup",action:"setup",status:"start"});await Cc(n)})}K();se();se();re();import vn from"picocolors";function Xg(e){e.command("config domain [suffix]","查看或设置全局默认域名后缀").option("--unset","清除已设置的全局域名后缀").action((n,t)=>{let s=k.getGlobalConfig();if(t.unset){if(k.setGlobalConfig({domainSuffix:void 0}),a())S({stage:"config.domain",domainSuffix:null,action:"unset"});else j(vn.bgMagenta(vn.white(" ⚙ Config "))),q(vn.green("已清除全局域名后缀"));return}let o=C(n);if(!o){let c=s.domainSuffix||null;if(a())S({stage:"config.domain",domainSuffix:c});else if(c)console.log(`全局域名后缀: ${vn.cyan(c)}`);else console.log(vn.gray("未设置全局域名后缀。用法: licell config domain <suffix>"));return}let r=Vn(o);if(k.setGlobalConfig({domainSuffix:r}),a())S({stage:"config.domain",domainSuffix:r,action:"set"});else j(vn.bgMagenta(vn.white(" ⚙ Config "))),console.log(`全局域名后缀已设置为: ${vn.cyan(r)}`),q("后续 deploy/domain 命令将自动使用此域名后缀")})}import{existsSync as Jg,readFileSync as _$,writeFileSync as b$,mkdirSync as T$}from"fs";import{homedir as C$}from"os";import{join as Ng}from"path";import us from"picocolors";var Ec=Ng(C$(),".licell-cli"),Sc=Ng(Ec,"update-check.json"),E$=43200000,S$=2000,q$="https://api.github.com/repos/agents-infrastructure/licell/releases/latest";function Zg(e,n){let t=e.split(".").map(Number),s=n.split(".").map(Number),o=Math.max(t.length,s.length);for(let r=0;r<o;r++){let c=t[r]??0,f=s[r]??0;if(c!==f)return c-f}return 0}function P$(){try{if(!Jg(Sc))return null;let e=JSON.parse(_$(Sc,"utf-8"));if(typeof e.latestVersion!=="string"||typeof e.checkedAt!=="number")return null;return e}catch{return null}}function R$(e){try{if(!Jg(Ec))T$(Ec,{recursive:!0});b$(Sc,JSON.stringify(e),"utf-8")}catch{}}async function I$(){try{let e=new AbortController,n=setTimeout(()=>e.abort(),S$),t=await fetch(q$,{signal:e.signal,headers:{Accept:"application/vnd.github+json"}});if(clearTimeout(n),!t.ok)return null;let s=await t.json();if(typeof s.tag_name!=="string")return null;return s.tag_name.replace(/^v/,"")}catch{return null}}async function zg(e){if(!e||e==="dev")return null;let n=P$();if(n&&Date.now()-n.checkedAt<E$){if(Zg(n.latestVersion,e)>0)return{currentVersion:e,latestVersion:n.latestVersion};return null}let t=await I$();if(!t)return null;if(R$({latestVersion:t,checkedAt:Date.now()}),Zg(t,e)>0)return{currentVersion:e,latestVersion:t};return null}function ey(e){let n=`新版本可用: ${e.currentVersion} → ${e.latestVersion}`,t="licell upgrade",s=Math.max(n.length,18),o=(r)=>r+" ".repeat(s-r.replace(/\x1b\[[0-9;]*m/g,"").length);console.error(""),console.error(us.yellow(`╭${"─".repeat(s+4)}╮`)),console.error(us.yellow(`│ ${o(n)} │`)),console.error(us.yellow(`│ ${o(`运行 ${us.bold("licell upgrade")} 升级`)} │`)),console.error(us.yellow(`╰${"─".repeat(s+4)}╯`))}ce();K();se();vs();import{confirm as ny,isCancel as ty}from"@clack/prompts";import Gn from"picocolors";ce();async function sy(){console.log(Gn.cyan(`
1722
1722
  \uD83D\uDC4B 欢迎使用 Licell CLI!`)),console.log(Gn.gray(`检测到您尚未配置登录信息。本向导将协助您完成初始设置。
1723
- `));let e=await ny({message:"是否现在配置阿里云登录凭证?(支持全自动高权限转最小权限)",initialValue:!0});if(ty(e)){console.log(Gn.gray("已取消初始化向导。稍后可通过 `licell login` 重新配置。"));return}if(e)try{await Bo()}catch(t){console.error(Gn.red(`
1724
- ❌ 登录配置失败: ${R(t)}`)),console.log(Gn.gray("您可以稍后通过 `licell login` 重试登录。"))}else console.log(Gn.gray("跳过登录配置。您可以通过 `licell login` 随时进行配置。"));console.log();let n=await ny({message:"是否配置 AI Agent Skills 和 MCP(推荐,让 AI 更好地使用 licell)?",initialValue:!0});if(ty(n)){console.log(Gn.gray("已完成向导。稍后可通过 `licell setup` 重新配置 AI 助手。"));return}if(n)await Cc();else console.log(Gn.gray("跳过 AI 助手配置。您可以通过 `licell setup` 随时配置。"))}re();var ry=tt(),ee=F$("licell");ee.version(ry);ee.option("--output <mode>","输出格式:text|json(json 更适合 Agent/MCP 解析)",{default:"text"});Rr(ee);mi(ee);ul(ee);il(ee);gl(ee);Tl(ee);Ol(ee);tg(ee);ug(ee);ig(ee);lg(ee);yg(ee);pg(ee);_g(ee);Ag(ee);Yg(ee);Qg(ee);Vg(ee);Xg(ee);ee.help();ee.on("command:*",()=>{let e=ee.args.join(" ");if(a())he(Error(`未知命令: ${e}`),{stage:"parse",details:{command:e}}),process.exit(1);console.error(`未知命令: ${e}`),ee.outputHelp(),process.exit(1)});var cy=Lc(process.argv),lo=cy;try{let e=Bu(cy);lo=e.argv,Yu(e.mode,lo),ju()}catch(e){let n=R(e);console.error(io.red(n)),process.exit(1)}function x$(e){let n=R(e),t=n.match(/missing required args for command `(.+?)`/),o=t||typeof e==="object"&&e!==null&&"name"in e&&String(e.name||"")==="CACError";if(a())he(e,{stage:o?"parse":"runtime",...t?{details:{usage:t[1]}}:{}}),process.exit(1);if(t)console.error(io.red("命令参数不完整。")),console.error(io.gray(`用法: licell ${t[1]}`)),ee.outputHelp(),process.exit(1);console.error(io.red(n)),process.exit(1)}var sy=!1;function fy(e,n){if(sy)return;if(sy=!0,a())he(e,{stage:n});else console.error(io.red(R(e)));process.exit(1)}process.on("unhandledRejection",(e)=>{fy(e,"unhandled_rejection")});process.on("uncaughtException",(e)=>{fy(e,"uncaught_exception")});process.once("beforeExit",(e)=>{if(e!==0)return;if(!a())return;if(Wu()||Qu())return;S({stage:"runtime",completed:!0})});var H$=lo.some((e)=>e==="upgrade"),A$=!a()&&!H$?zg(ry).catch(()=>null):Promise.resolve(null);Promise.resolve().then(async()=>{if(lo.length<=2){if(Ou()==="json")S({stage:"help",help:"请执行 licell <command> --help 查看命令说明"}),process.exit(0);if(P()&&!a()&&!k.getAuth())await oy();else ee.outputHelp();process.exit(0)}}).then(()=>ee.parse(lo)).then(async()=>{let e=await A$;if(e)ey(e)}).catch(x$);
1723
+ `));let e=await ny({message:"是否现在配置阿里云登录凭证?(支持全自动高权限转最小权限)",initialValue:!0});if(ty(e)){console.log(Gn.gray("已取消初始化向导。稍后可通过 `licell login` 重新配置。"));return}if(e)try{await Ms()}catch(t){console.error(Gn.red(`
1724
+ ❌ 登录配置失败: ${R(t)}`)),console.log(Gn.gray("您可以稍后通过 `licell login` 重试登录。"))}else console.log(Gn.gray("跳过登录配置。您可以通过 `licell login` 随时进行配置。"));console.log();let n=await ny({message:"是否配置 AI Agent Skills 和 MCP(推荐,让 AI 更好地使用 licell)?",initialValue:!0});if(ty(n)){console.log(Gn.gray("已完成向导。稍后可通过 `licell setup` 重新配置 AI 助手。"));return}if(n)await Cc();else console.log(Gn.gray("跳过 AI 助手配置。您可以通过 `licell setup` 随时配置。"))}re();var ry=tt(),ee=F$("licell");ee.version(ry);ee.option("--output <mode>","输出格式:text|json(json 更适合 Agent/MCP 解析)",{default:"text"});Rr(ee);mi(ee);ul(ee);il(ee);gl(ee);Tl(ee);Ol(ee);tg(ee);ug(ee);ig(ee);lg(ee);yg(ee);pg(ee);_g(ee);Lg(ee);Yg(ee);Qg(ee);Vg(ee);Xg(ee);ee.help();ee.on("command:*",()=>{let e=ee.args.join(" ");if(a())he(Error(`未知命令: ${e}`),{stage:"parse",details:{command:e}}),process.exit(1);console.error(`未知命令: ${e}`),ee.outputHelp(),process.exit(1)});var cy=Ac(process.argv),ls=cy;try{let e=Bu(cy);ls=e.argv,Yu(e.mode,ls),ju()}catch(e){let n=R(e);console.error(is.red(n)),process.exit(1)}function x$(e){let n=R(e),t=n.match(/missing required args for command `(.+?)`/),s=t||typeof e==="object"&&e!==null&&"name"in e&&String(e.name||"")==="CACError";if(a())he(e,{stage:s?"parse":"runtime",...t?{details:{usage:t[1]}}:{}}),process.exit(1);if(t)console.error(is.red("命令参数不完整。")),console.error(is.gray(`用法: licell ${t[1]}`)),ee.outputHelp(),process.exit(1);console.error(is.red(n)),process.exit(1)}var oy=!1;function fy(e,n){if(oy)return;if(oy=!0,a())he(e,{stage:n});else console.error(is.red(R(e)));process.exit(1)}process.on("unhandledRejection",(e)=>{fy(e,"unhandled_rejection")});process.on("uncaughtException",(e)=>{fy(e,"uncaught_exception")});process.once("beforeExit",(e)=>{if(e!==0)return;if(!a())return;if(Wu()||Qu())return;S({stage:"runtime",completed:!0})});var H$=ls.some((e)=>e==="upgrade"),L$=!a()&&!H$?zg(ry).catch(()=>null):Promise.resolve(null);Promise.resolve().then(async()=>{if(ls.length<=2){if(Ou()==="json")S({stage:"help",help:"请执行 licell <command> --help 查看命令说明"}),process.exit(0);if(P()&&!a()&&!k.getAuth())await sy();else ee.outputHelp();process.exit(0)}}).then(()=>ee.parse(ls)).then(async()=>{let e=await L$;if(e)ey(e)}).catch(x$);