@giteeteam/apps-cli 1.1.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.js +8 -8
- package/package.json +5 -5
- package/dist/templates/skill/SKILL.md +0 -38
- package/dist/templates/skill/guides/01-overview.md +0 -47
- package/dist/templates/skill/guides/02-extension-points.md +0 -75
- package/dist/templates/skill/guides/03-api-routing-rules.md +0 -67
- package/dist/templates/skill/guides/04-deploy-and-subscribe.md +0 -80
- package/dist/templates/skill/guides/05-agent-prompt-template.md +0 -32
- package/dist/templates/skill/guides/06-data-access-and-tables.md +0 -60
- package/dist/templates/skill/references/README.md +0 -12
- package/templates/skill/SKILL.md +0 -38
- package/templates/skill/guides/01-overview.md +0 -47
- package/templates/skill/guides/02-extension-points.md +0 -75
- package/templates/skill/guides/03-api-routing-rules.md +0 -67
- package/templates/skill/guides/04-deploy-and-subscribe.md +0 -80
- package/templates/skill/guides/05-agent-prompt-template.md +0 -32
- package/templates/skill/guides/06-data-access-and-tables.md +0 -60
- package/templates/skill/references/README.md +0 -12
package/README.md
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
"use strict";var
|
|
3
|
-
`);let{pass:
|
|
4
|
-
The output file of the build is ${H.default.bold.green(g)}`)}};var pt=async e=>{let{input:t="functions",output:r="package",prod:n,dev:o,copy:s=[],zip:i,ui:a,rspack:p}=e;await ct({outputDir:de.default.resolve(r),inputDir:de.default.resolve(t),prod:n??!o,filesToCopy:[...B,...s],zip:i,ui:!!a,rspackConfig:p}).catch(()=>{})};var bt=c(require("fs")),kt=c(require("path")),J=c(require("chalk"));var lt=c(require("path")),mt=c(require("lowdb")),ut=c(require("lowdb/adapters/FileSync"));var ge,ye=()=>{if(!ge){let e=lt.default.join(__dirname,"user.json"),t=new ut.default(e);ge=(0,mt.default)(t)}return ge},ft=()=>{let e=ye(),t=e.get("version").value();(!t||t!==E)&&e.defaults({version:E,user:{}}).write()},he=e=>{ft(),ye().set("user",e).write()},T=()=>(ft(),ye().get("user").value());var gt=c(require("fs")),yt=c(require("path")),ht=c(require("form-data"));var dt=c(require("axios"));var k={GET:"GET",POST:"POST",PUT:"PUT",DELETE:"DELETE"},S=async({url:e,method:t,data:r,headers:n})=>{let{domain:o,cookie:s}=T();return(await(0,dt.default)({method:t,baseURL:o,url:e,data:r,headers:{...n,cookie:s}})).data};var vt=async(e,t)=>{let r=new ht.default;return r.append("environmentKey",t),r.append("package",gt.default.createReadStream(yt.default.resolve(e))),S({method:k.POST,url:"/apps/api/v1/apps/deploy",data:r,headers:{...r.getHeaders()}})},W=async({tenants:e,environmentKey:t,appKey:r,versionKey:n})=>S({method:k.POST,url:"/apps/api/v1/apps/install/batch",data:{tenants:e,environmentKey:t,appKey:r,versionKey:n,subscriptionSource:"manual"}});var wt=c(require("chalk")),Dr=wt.default.bold.red,f={error:e=>{console.info(Dr(e))}};var Q=(n=>(n.DEVELOPMENT="development",n.STAGING="staging",n.PRODUCTION="production",n))(Q||{}),Cr=e=>e?(Array.isArray(e)?e:[e]).flatMap(r=>r.split(",")).map(r=>r.trim()).filter(Boolean):[],Rr=e=>{let t=kt.default.basename(e,".zip"),r=t.lastIndexOf("_");return r<=0||r===t.length-1?null:{appKey:t.slice(0,r),versionKey:t.slice(r+1)}},Pt=async(e,t)=>{let{domain:r}=T(),{environment:n="development",subscribe:o,appKey:s,versionKey:i}=t,a=Cr(o);if(!Object.values(Q).includes(n)){f.error("Only support development, staging, production environment.");return}if(!r){f.error("Please login in first!");return}if(!bt.default.existsSync(e)){f.error(`File:${e} is not exist`);return}if(console.info(`Deploying to ${J.default.bold.green(r)}`),await d({start:"start deploy",error:"deploy failed",succeed:"deploy success"},()=>vt(e,n)).catch(()=>{}),a.length===0)return;let p=Rr(e),m=s||(p==null?void 0:p.appKey),l=i||(p==null?void 0:p.versionKey);if(!m||!l){f.error("subscribe needs appKey and versionKey. please pass --app-key and --version-key, or use zip name <appKey>_<versionKey>.zip");return}console.info(`Subscribing app ${J.default.bold.green(m)}:${J.default.bold.green(l)}`),await d({start:"start subscribe",error:"subscribe failed",succeed:"subscribe success"},()=>W({tenants:a,environmentKey:n,appKey:m,versionKey:l})).catch(()=>{})};var N=c(require("path")),M=c(require("fs-extra")),ke=c(require("inquirer"));var xt=c(require("readline")),Et=require("chalk"),Ot=c(require("execa")),Ar=e=>{if(!Et.supportsColor){e.write("\r");return}xt.default.cursorTo(e,0)},jr=(e,t)=>{let r=Math.min(Math.max(e/t,0),1),n=` ${e}/${t}`,o=Math.max(0,process.stderr.columns-n.length-3),s=Math.min(t,o),i=Math.round(s*r),a="#".repeat(i),p="-".repeat(s-i);Ar(process.stderr),process.stderr.write(`[${a}${p}]${n}`)},_r=(e,t,r,n)=>new Promise((o,s)=>{var a;let i=(0,Ot.default)(e,t,{cwd:r,stdio:["inherit","inherit","pipe"],env:n});(a=i.stderr)==null||a.on("data",p=>{let m=p.toString();if(/warning/.test(m))return;let l=m.match(/\[.*] (\d+)\/(\d+)/);if(l){jr(parseFloat(l[1]),parseFloat(l[2]));return}process.stderr.write(p)}),i.on("close",p=>{if(p!==0){s(new Error(`command failed: ${e} ${t.join(" ")}`));return}o()})}),Tt={executeCommand:_r};var ve=c(require("fs-extra")),we=c(require("simple-git")),St=async e=>{await(0,we.default)(e).init()},Y=async(e,t,r)=>{await ve.default.pathExists(t)||await ve.default.mkdir(t);let n=(0,we.default)(t),o=[];r!=null&&r.branch&&o.push("--branch",r.branch),r!=null&&r.singleBranch&&o.push("--single-branch"),await n.clone(e,t,o)};var P=c(require("path")),A=c(require("fs-extra"));var X=c(require("fs-extra")),Dt=process.env.APPS_TEMPLATE_REPO||"https://github.com/moriahq/apps-template.git",be=(r=>(r.micro="micro",r.uikit="uikit",r))(be||{}),Z=async(e,t)=>{if(!await X.default.pathExists(e))return;let r=await X.default.readFile(e,{encoding:"utf-8"});await X.default.writeFile(e,r.replace(/{{(\w+)}}/g,(n,o)=>typeof t[o]=="string"?t[o]:n))};var $r=async()=>{let e=[P.default.resolve(__dirname,"templates/skill"),P.default.resolve(__dirname,"../templates/skill")];for(let t of e)if(await A.default.pathExists(t))return t;return""},Ct=async(e,t)=>{let r=await A.default.readdir(e,{withFileTypes:!0});return(await Promise.all(r.map(async o=>{let s=P.default.join(e,o.name);return o.isDirectory()?Ct(s,t):o.isFile()?[P.default.relative(t,s)]:[]}))).flat()},$=async(e,t)=>{let{appName:r,appKey:n,overwrite:o=!1}=t,s=await $r();if(!s||!await A.default.pathExists(s))throw new Error(`skill templates not found: ${s}`);let i=await Ct(s,s);for(let a of i){let p=P.default.join(s,a),m=a==="SKILL.md"||a.startsWith("skill/")?a:P.default.join("skill",a),l=P.default.join(e,m);!o&&await A.default.pathExists(l)||(await A.default.ensureDir(P.default.dirname(l)),await A.default.copy(p,l),l.endsWith(".md")&&await Z(l,{appName:r,appKey:n}))}};var Nr=async()=>(await ke.default.createPromptModule()({type:"input",name:"App Name",message:"Please enter an app name",validate:r=>!!r}))["App Name"],Ir=async()=>(await ke.default.createPromptModule()({type:"input",name:"App key",message:"Please enter a valid app key",validate:r=>/^[a-z][a-z0-9_]*$/.test(r)?!0:(console.info(""),console.info("only supports letters, numbers, underscores and starts with letters"),!1)}))["App key"],Mr=()=>{if(Number(E.split(".")[0])>=1)return"v1"},Fr=async(e,t)=>{let r=Mr();if(!r){await d({start:"Start clone repository",error:"Clone repository failed!",succeed:"Clone repository success!"},()=>Y(e,t));return}try{await d({start:`Start clone repository (branch: ${r})`,error:"Clone repository failed!",succeed:"Clone repository success!"},()=>Y(e,t,{branch:r,singleBranch:!0}))}catch{console.info(`Template branch '${r}' not found, fallback to default branch.`),await d({start:"Start clone repository (default branch)",error:"Clone repository failed!",succeed:"Clone repository success!"},()=>Y(e,t))}},Ur=async e=>{await d({start:"Start install dependencies",error:"Install dependencies failed!",succeed:"Install dependencies success!"},()=>Tt.executeCommand("pnpm",["install"],e))},Rt=async e=>{let{cwd:t}=e,r="micro",n=await Nr(),o=await Ir(),s=N.default.join(t,o);if(await M.default.pathExists(s)){console.error(`Project path already exists: ${s}`);return}await Fr(Dt,s),await M.default.copy(N.default.join(s,r),s),await Promise.all(Object.values(be).map(a=>M.default.remove(N.default.join(s,a)))),await M.default.remove(N.default.join(s,"./.git"));let i=["./manifest.yml","./package.json","./rspack.config.js","./public/index.html","./src/App.tsx","./src/index.tsx","./static/App.tsx","./static/index.tsx","./SKILL.md"];await Promise.all(i.map(a=>Z(N.default.join(s,a),{appName:n,appKey:o}))),await $(s,{appName:n,appKey:o}),await Ur(s),await St(s),console.info("Project init success!"),console.info("Project path:"+s)};var At=async()=>{let e=process.cwd();await Rt({cwd:e})};var ee=c(require("inquirer"));var Pe=c(require("axios"));var jt=["/api/gateway/login","/login"],Kr=e=>{var n;let t=(n=e==null?void 0:e.response)==null?void 0:n.status,r=e==null?void 0:e.code;return[404,405,502,503,504].includes(t)||["ECONNREFUSED","ENOTFOUND","ECONNABORTED"].includes(r)},_t=async({baseUrl:e,username:t,password:r})=>{var o;let n;for(let s of jt)try{let i=await(0,Pe.default)({method:k.POST,url:`${e}${s}`,data:{username:t,password:r}});return console.info(`Login endpoint: ${s}`),{cookie:(o=i.headers)==null?void 0:o["set-cookie"]}}catch(i){if(n=i,!Kr(i))return console.error(`Login error (${s}):`,i==null?void 0:i.message),{cookie:""}}return console.error(`Login error: all endpoints failed (${jt.join(", ")})`,(n==null?void 0:n.message)||""),{cookie:""}},Lt=async e=>{try{return await(0,Pe.default)({method:k.GET,url:`${e}/apps/api/v1/tenants`}),!1}catch{return!0}};var $t=e=>{try{return new URL(e).origin}catch{return e}};var Nt=c(require("crypto"));var It=e=>{try{let t=Nt.default.createCipheriv(Le,_e,je),r=t.update(e,"utf8","base64");return r+=t.final("base64"),r.toString()}catch(t){return console.error(`Encryption error: ${t}`),null}};var Br=async()=>(await ee.default.createPromptModule()({type:"input",name:"Domain",message:"Please enter the domain address",validate:r=>{try{return new URL(r),!0}catch{return"Please enter a valid domain name"}}})).Domain,Gr=async()=>(await ee.default.createPromptModule()({type:"input",name:"Username",message:"Please enter username",validate:r=>!!r})).Username,Vr=async()=>(await ee.default.createPromptModule()({type:"password",name:"Password",message:"Please enter password",validate:r=>!!r})).Password,Mt=async({domain:e,username:t,password:r})=>{if(e||(e=await Br()),e=$t(e),await Lt(e)){t||(t=await Gr()),r||(r=await Vr());let{cookie:o}=await _t({baseUrl:e,username:t,password:It(r)||r});o?(he({domain:e,username:t,cookie:o}),console.info("Login success!")):console.error("Login failed!")}else he({domain:e}),console.info("Login success!")};var Ft=async e=>{await Mt(e)};var Ut=c(require("chalk"));var qr=e=>e?(Array.isArray(e)?e:[e]).flatMap(r=>r.split(",")).map(r=>r.trim()).filter(Boolean):[],Kt=async(e,t,r)=>{let{domain:n}=T(),{environment:o="development",tenants:s}=r,i=qr(s);if(!n){f.error("Please login in first!");return}if(!Object.values(Q).includes(o)){f.error("Only support development, staging, production environment.");return}if(!e||!t){f.error("Please provide appKey and versionKey.");return}if(i.length===0){f.error("Please provide subscribe tenants by -t/--tenants.");return}console.info(`Subscribing to ${Ut.default.bold.green(n)}`),await d({start:"start subscribe",error:"subscribe failed",succeed:"subscribe success"},()=>W({tenants:i,environmentKey:o,appKey:e,versionKey:t})).catch(()=>{})};var te=c(require("path")),re=c(require("fs-extra"));var zr=async e=>{var a,p,m,l;let t=te.default.join(e,"manifest.yml"),r=te.default.basename(e),n=r;if(!await re.default.pathExists(t))return{appName:r,appKey:n};let o=await re.default.readFile(t,"utf8"),s=((p=(a=o.match(/^\s*name:\s*(.+)\s*$/m))==null?void 0:a[1])==null?void 0:p.trim())||r,i=((l=(m=o.match(/^\s*key:\s*(.+)\s*$/m))==null?void 0:m[1])==null?void 0:l.trim())||n;return{appName:s,appKey:i}},Bt=async e=>{let t=te.default.resolve(e||process.cwd());if(!await re.default.pathExists(t)){f.error(`project path not found: ${t}`);return}let{appName:r,appKey:n}=await zr(t);await $(t,{appName:r,appKey:n,overwrite:!0}),console.info("sync-skill finished."),console.info(`Project path: ${t}`)};var ie=c(require("path"));var tr=c(require("path")),se=c(require("chalk")),rr=c(require("debug")),Ee=c(require("fs-extra"));var Gt=async()=>{var t;return((t=(await S({method:k.GET,url:"/apps/api/v1/config"}).catch(r=>{var n,o;console.info(`getRuntimeConfig error ${((o=(n=r==null?void 0:r.response)==null?void 0:n.data)==null?void 0:o.message)||(r==null?void 0:r.message)}`)})).data)==null?void 0:t.runtime)||{}};var ne=require("child_process"),F=class{constructor(t,r){this.imageName=t,this.containerName=r}async downloadImage(){console.info(`Downloading image(${this.imageName}) ...`),await this.execPromise(`docker pull ${this.imageName}`),console.info(`Download image(${this.imageName}) success!`)}async runContainer({port:t,env:r,volume:n=[]}){await this.removeContainer();let o=Object.keys(r).filter(a=>r[a]!==void 0).map(a=>`-e ${a}=${r[a]}`).join(" "),s=n==null?void 0:n.map(a=>`-v ${a}`).join(" "),i=`docker run -d -p ${t}:${t} ${o} ${s} --name ${this.containerName} ${this.imageName}`;await this.execPromise(i)}async removeContainer(){await this.execPromise(`docker rm -f ${this.containerName}`).catch(()=>{})}logs(){let t=(0,ne.spawn)("docker",["logs","-f",this.containerName]);t.stdout.on("data",r=>{console.info(`${r}`)}),t.stderr.on("data",r=>{console.info(`${r}`)})}execPromise(t){return new Promise((r,n)=>{(0,ne.exec)(t,(o,s,i)=>{if(o)return n(o);r({stdout:s,stderr:i})})})}};var Vt=c(require("localtunnel"));var qt=async({appKey:e,runtimeHost:t,resources:r,resourcePort:n})=>{r!=null&&r.length&&r.forEach(o=>{o.path=`http://127.0.0.1:${n}`}),await S({method:k.POST,url:`/apps/api/v1/apps/tunnel/${e}`,data:{resources:r,runtimeHost:t}}).catch(o=>{var s,i;console.info(`addTunnel error ${((i=(s=o==null?void 0:o.response)==null?void 0:s.data)==null?void 0:i.message)||(o==null?void 0:o.message)}`)})},zt=async e=>{await S({method:"DELETE",url:`/apps/api/v1/apps/tunnel/${e}`}).catch(()=>{})},Ht=async e=>{var t,r;try{return(await(0,Vt.default)({port:e,host:((t=process.env)==null?void 0:t.TUNNEL_HOST)==="OFFICIAL"?void 0:((r=process.env)==null?void 0:r.TUNNEL_HOST)||"http://tunnel.gitee.work:1777"})).url}catch(n){return console.info("runTunnel error",n==null?void 0:n.message),""}};var xe=c(require("path")),Wt=require("@rspack/core"),Jt=require("@rspack/dev-middleware"),Qt=c(require("cors")),Yt=c(require("express"));var oe=async({appKey:e,rspackConfig:t})=>{let n=(await V())({environment:"development",appKey:e});if(t){let p=require(xe.default.resolve(t));n=I(n,p)}let o=(0,Wt.rspack)(n),s=(0,Yt.default)(),i=Number(process.env.UI_PORT)||8e3,a=(0,Jt.devMiddleware)(o);s.use((0,Qt.default)()),s.use(a),s.get("*",(p,m)=>{var l,u;(u=o.outputFileSystem)==null||u.readFile(xe.default.join(((l=n.output)==null?void 0:l.path)||"","index.html"),(g,v)=>{if(g||!v){m.sendStatus(404);return}m.send(v.toString())})}),s.listen(i,"0.0.0.0",()=>{console.info(`The React app is listening on port ${i}!
|
|
5
|
-
`)})};var
|
|
6
|
-
`);let{domain:
|
|
7
|
-
`).replace(/import\s+\{\s*merge\s*\}\s+from\s+['"]webpack-merge['"];?/g,"import merge from 'deepmerge';").replace(/const\s+\{\s*merge\s*\}\s*=\s*require\((['"])webpack-merge\1\);?/g,"const merge = require('deepmerge');").replace(/(['"])webpack-merge\1/g,"$1deepmerge$1").replace(/^.*(rspack-bundle-analyzer|webpack-bundle-analyzer).*\n?/gm,"").replace(/^.*ANALYZER_PACKAGE.*\n?/gm,"").replace(/(['"])webpack\1/g,"$1@rspack/core$1");a!==i&&(await y.default.writeFile(s,a),t=!0)}return t},
|
|
8
|
-
`),!s&&!i&&!a&&!p&&!
|
|
9
|
-
`);var h=new
|
|
2
|
+
"use strict";var lr=Object.create;var se=Object.defineProperty;var mr=Object.getOwnPropertyDescriptor;var ur=Object.getOwnPropertyNames;var fr=Object.getPrototypeOf,dr=Object.prototype.hasOwnProperty;var gr=(e,t)=>()=>(e&&(t=e(e=0)),t);var yr=(e,t)=>{for(var r in t)se(e,r,{get:t[r],enumerable:!0})},hr=(e,t,r,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of ur(t))!dr.call(e,n)&&n!==r&&se(e,n,{get:()=>t[n],enumerable:!(o=mr(t,n))||o.enumerable});return e};var c=(e,t,r)=>(r=e!=null?lr(fr(e)):{},hr(t||!e||!e.__esModule?se(r,"default",{value:e,enumerable:!0}):r,e));var Be={};yr(Be,{default:()=>Er});function Er({environment:e,appKey:t,distOutputPath:r="dist"}){let o=e==="production",n=e==="development",s=Pr(),i=Ue.default.existsSync(D.default.resolve("locales")),a=S=>S?{filename:"js/[name].[chunkhash].min.js",path:D.default.resolve(r),publicPath:"./",library:{type:"umd",name:t},clean:!0}:{filename:"main.js",path:D.default.resolve(r),publicPath:"/",library:{type:"umd",name:t}},p=({resourcePath:S},M,v)=>v===t||/\.global\.(css|less)$/.test(S)||/node_modules/.test(S)?v:`${v}__${(0,Ge.default)(`${S}${v}`,{algorithm:"md5"}).slice(0,8)}`,l={loader:"@ecomfe/class-names-loader",options:{classNamesModule:require.resolve("classnames")}},m=o?_.rspack.CssExtractRspackPlugin.loader:"style-loader",d={loader:"postcss-loader",options:{postcssOptions:{plugins:[Ke.default]}}},g={loader:"css-loader",options:{modules:{getLocalIdent:p},importLoaders:1}},k={loader:"less-loader",options:{lessOptions:{javascriptEnabled:!0,modifyVars:{"ant-prefix":t}}}};return{mode:e,entry:"./src/index.tsx",output:a(o),devtool:o?!1:"source-map",resolve:{alias:{"@":D.default.join(process.cwd(),"src/")},extensions:[".js",".ts",".jsx",".tsx",".css",".less"]},optimization:{minimize:o},plugins:[new _.rspack.DefinePlugin({"process.env":JSON.stringify(process.env),"process.env.appKey":JSON.stringify(t)}),new _.rspack.HtmlRspackPlugin({template:D.default.resolve("public/index.html"),filename:"index.html",inject:!0,appKey:t}),o&&new _.rspack.CssExtractRspackPlugin({filename:"[name].[contenthash].css",chunkFilename:"[name].[contenthash].chunk.css"}),n&&new _.rspack.HotModuleReplacementPlugin,n&&new Fe.default({overlay:!1})].filter(Boolean),module:{rules:[{oneOf:[{test:/\.(js|mjs|jsx|ts|tsx)$/,exclude:/node_modules/,loader:"builtin:swc-loader",options:{sourceMaps:xr,jsc:{parser:{syntax:"typescript",tsx:!0},transform:{react:{runtime:"automatic",development:!o,refresh:!o}}},env:{targets:"chrome >= 72",mode:"usage",coreJs:"3.38"}},type:"javascript/auto"},s&&i&&{test:/locales/,loader:s,include:[D.default.resolve("locales")]},{test:/\.css$/,use:[l,m,"css-loader",d]},{test:/\.less$/,use:[l,m,g,d,k]},{test:/\.(png|jpg|gif)$/i,type:"asset",parser:{dataUrlCondition:{maxSize:2400}},generator:{filename:"[name]_[hash][ext]"}},{test:/\.svg$/i,use:["@svgr/webpack"]}]}]}}}var Ue,D,_,Fe,Ke,Ge,xr,Pr,ze=gr(()=>{"use strict";Ue=c(require("fs")),D=c(require("path")),_=require("@rspack/core"),Fe=c(require("@rspack/plugin-react-refresh")),Ke=c(require("autoprefixer")),Ge=c(require("hasha")),xr=process.env.GENERATE_SOURCEMAP==="true",Pr=()=>{try{return require.resolve("@alienfast/i18next-loader")}catch{return null}}});var ls=require("dotenv/config"),ke=c(require("chalk")),sr=require("commander");var x="2.0.0";var le=c(require("path"));var F=["manifest.yml","locales"],Te=Buffer.from(process.env.ONE_AUTH_IV||"ABCDEFGHIJKLM_iv","utf-8"),De=Buffer.from(process.env.ONE_AUTH_SECRET_KEY||"ABCDEFGHIJKL_key","utf-8"),Ce=process.env.ONE_AUTH_ALGORITHM||"aes-128-cbc";var V=c(require("chalk")),rt=c(require("fs-extra"));var _e=c(require("fs")),ae=c(require("path")),Le=c(require("@rollup/plugin-commonjs")),je=require("rollup");var A=require("fs"),b=require("path"),Re=require("esbuild"),br={treeShaking:!0,format:"esm",loader:"tsx"};var wr=({source:e,dirPath:t,mode:r})=>{let o="";if(r==="development")try{return o=require.resolve(e+"/src/index.ts",{paths:[t]}),o}catch{}try{return o=require.resolve(e,{paths:[t]}),o}catch{}return o};function ie({mode:e,...t}){return t={...br,...t},{name:"esbuild",resolveId(r,o,n){if(!o)return r;let s=(0,b.dirname)(o),i;if((0,A.existsSync)(i=(0,b.resolve)(s,r,"index.ts")))return i;if((0,A.existsSync)(i=(0,b.resolve)(s,r,"index.tsx")))return i;if((0,A.existsSync)(i=(0,b.resolve)(s,r+".ts")))return i;if((0,A.existsSync)(i=(0,b.resolve)(s,r+".tsx")))return i;{let a=wr({source:r,dirPath:s,mode:e});if((0,A.existsSync)(a))return a;throw console.error({source:r,importer:o,options:n}),i=(0,b.relative)(".",(0,b.resolve)(o,r)),new Error(`ENOENT: ${i.replace(/\\/g,"/")}`)}},async transform(r,o){t.sourcefile=o,t.loader=o.endsWith(".json")?"json":t.loader;let{code:n,map:s}=await(0,Re.transform)(r,t);return s?{code:n,map:s}:n}}}function kr(e,t,r="development"){let o=[(0,Le.default)()];o.push(ie({mode:r,sourcemap:r==="development",minify:r!=="development",target:"es2018",jsx:"transform",jsxFactory:"AppsUI.createElement",jsxFragment:"AppsUI.Fragment"}));let n={input:e,plugins:o},s={file:t,format:"es",sourcemap:r==="development"};return{inputOptions:n,outputOptions:s}}async function K(e){let{fileName:t,inputDir:r,outputDir:o,mode:n="development"}=e,s=ae.default.join(r,`${t}.ts`);try{await _e.default.promises.access(s)}catch{console.error(`${s} is not exist`);return}let i=ae.default.join(o,`${t}.js`),{inputOptions:a,outputOptions:p}=kr(s,i,n);await(await(0,je.rollup)(a)).write(p)}var $e=c(require("ora")),u=async(e,t)=>{var o,n;let r=(0,$e.default)(e.start).start();try{let s=await t();return r.succeed(e.succeed),s}catch(s){throw r.fail(`${e.error}: ${((n=(o=s==null?void 0:s.response)==null?void 0:o.data)==null?void 0:n.message)||s.message}`),s}};var Ne=c(require("path")),Ie=require("@giteeteam/apps-manifest"),Me=c(require("fs-extra")),P=async()=>{let e=Ne.default.resolve("manifest.yml"),t=await Me.default.readFile(e);return new Ie.Manifest(t.toString())};var qe=c(require("path")),He=require("@rspack/core");var G=async()=>(await Promise.resolve().then(()=>(ze(),Be))).default;var Ve=e=>Object.prototype.toString.call(e)==="[object Object]",$=(e,t)=>{if(t===void 0)return e;if(Array.isArray(e)&&Array.isArray(t))return[...e,...t];if(Ve(e)&&Ve(t)){let r={...e};for(let[o,n]of Object.entries(t))r[o]=o in r?$(r[o],n):n;return r}return t};var Je=async({appKey:e,rspackConfig:t})=>{let r=await G();return new Promise((o,n)=>{let s=r({environment:"production",appKey:e});if(t){let a=require(qe.default.resolve(t));s=$(s,a)}(0,He.rspack)(s).run((a,p)=>{if(a)return console.error(a),n(a);if(console.info(p==null?void 0:p.toString()),p!=null&&p.hasErrors())return n(new Error("build-ui-error"));o()})})};var We=c(require("path")),Qe=c(require("archiver")),ce=c(require("fs-extra")),Ye=async(e,t)=>{let r=We.default.resolve(t);return await ce.default.remove(r),new Promise((o,n)=>{let s=ce.default.createWriteStream(r),i=(0,Qe.default)("zip",{zlib:{level:9}});s.on("close",function(){o()}),i.on("warning",function(a){console.info(a),a.code==="ENOENT"||n(a)}),i.on("error",function(a){console.info(a),n(a)}),i.pipe(s),i.directory(e,!1),i.finalize()})};var Xe=async e=>{let{manifest:t,mode:r,outputDir:o,inputDir:n}=e,i=t.getFunctionFiles().map(async a=>{await K({inputDir:n,fileName:a,outputDir:o,mode:r})});await Promise.all(i)};var z=c(require("path")),et=c(require("fs-extra"));var pe=c(require("path")),Ze=c(require("fs-extra")),B=async(e,t)=>{for(let r of t)try{let o=pe.default.resolve(r),n=pe.default.join(e,r);await Ze.default.copy(o,n)}catch(o){if(o.code==="ENOENT"&&r==="locales")continue;throw o}};var tt=async({outputDir:e,manifest:t,filesToCopy:r})=>{let{resources:o}=t;if(o)for(let{key:n,path:s}of o){let i=z.default.resolve(z.default.join("./",s)),a=z.default.join(e,n);await et.default.copy(i,a)}await B(e,r)};var ot=async({outputDir:e,inputDir:t,prod:r,filesToCopy:o,zip:n,ui:s,rspackConfig:i})=>{let a=await P(),{version:p,key:l}=a.app;if(!l){console.error("manifest app key is required");return}if(!p){console.error("manifest app version is required");return}console.info(`App key: ${V.default.bold.green(l)}`),console.info(`App version: ${V.default.bold.green(p)}
|
|
3
|
+
`);let{pass:m,errors:d}=a.validate();if(!m){if(d!=null&&d.length){let{message:g,path:k}=d[0];console.error(`Manifest valid error: Path '${k}', ${g}`)}else console.error("Manifest valid error");return}if(await rt.default.emptyDir(e),s&&await u({start:"start build ui",error:"build ui failed",succeed:"build ui success"},()=>Je({appKey:l,rspackConfig:i})),await u({start:"start bundle",error:"bundle failed",succeed:"bundle success"},()=>Xe({outputDir:e,inputDir:t,manifest:a,mode:r?"production":"development"})),await u({start:"start package",error:"package failed",succeed:"package success"},()=>tt({outputDir:e,manifest:a,filesToCopy:o})),n){let g=`${l}_${p}.zip`;await u({start:"start archive",error:"archive failed",succeed:"archive success"},()=>Ye(e,g)),console.info(`
|
|
4
|
+
The output file of the build is ${V.default.bold.green(g)}`)}};var nt=async e=>{let{input:t="functions",output:r="package",prod:o,dev:n,copy:s=[],zip:i,ui:a,rspack:p}=e;await ot({outputDir:le.default.resolve(r),inputDir:le.default.resolve(t),prod:o??!n,filesToCopy:[...F,...s],zip:i,ui:!!a,rspackConfig:p}).catch(()=>{})};var gt=c(require("fs")),yt=c(require("path")),H=c(require("chalk"));var st=c(require("path")),it=c(require("lowdb")),at=c(require("lowdb/adapters/FileSync"));var me,ue=()=>{if(!me){let e=st.default.join(__dirname,"user.json"),t=new at.default(e);me=(0,it.default)(t)}return me},ct=()=>{let e=ue(),t=e.get("version").value();(!t||t!==x)&&e.defaults({version:x,user:{}}).write()},fe=e=>{ct(),ue().set("user",e).write()},E=()=>(ct(),ue().get("user").value());var lt=c(require("fs")),mt=c(require("path")),ut=c(require("form-data"));var pt=c(require("axios"));var w={GET:"GET",POST:"POST",PUT:"PUT",DELETE:"DELETE"},O=async({url:e,method:t,data:r,headers:o})=>{let{domain:n,cookie:s}=E();return(await(0,pt.default)({method:t,baseURL:n,url:e,data:r,headers:{...o,cookie:s}})).data};var ft=async(e,t)=>{let r=new ut.default;return r.append("environmentKey",t),r.append("package",lt.default.createReadStream(mt.default.resolve(e))),O({method:w.POST,url:"/apps/api/v1/apps/deploy",data:r,headers:{...r.getHeaders()}})},q=async({tenants:e,environmentKey:t,appKey:r,versionKey:o})=>O({method:w.POST,url:"/apps/api/v1/apps/install/batch",data:{tenants:e,environmentKey:t,appKey:r,versionKey:o,subscriptionSource:"manual"}});var dt=c(require("chalk")),Or=dt.default.bold.red,f={error:e=>{console.info(Or(e))}};var J=(o=>(o.DEVELOPMENT="development",o.STAGING="staging",o.PRODUCTION="production",o))(J||{}),Sr=e=>e?(Array.isArray(e)?e:[e]).flatMap(r=>r.split(",")).map(r=>r.trim()).filter(Boolean):[],Tr=e=>{let t=yt.default.basename(e,".zip"),r=t.lastIndexOf("_");return r<=0||r===t.length-1?null:{appKey:t.slice(0,r),versionKey:t.slice(r+1)}},ht=async(e,t)=>{let{domain:r}=E(),{environment:o="development",subscribe:n,appKey:s,versionKey:i}=t,a=Sr(n);if(!Object.values(J).includes(o)){f.error("Only support development, staging, production environment.");return}if(!r){f.error("Please login in first!");return}if(!gt.default.existsSync(e)){f.error(`File:${e} is not exist`);return}if(console.info(`Deploying to ${H.default.bold.green(r)}`),await u({start:"start deploy",error:"deploy failed",succeed:"deploy success"},()=>ft(e,o)).catch(()=>{}),a.length===0)return;let p=Tr(e),l=s||(p==null?void 0:p.appKey),m=i||(p==null?void 0:p.versionKey);if(!l||!m){f.error("subscribe needs appKey and versionKey. please pass --app-key and --version-key, or use zip name <appKey>_<versionKey>.zip");return}console.info(`Subscribing app ${H.default.bold.green(l)}:${H.default.bold.green(m)}`),await u({start:"start subscribe",error:"subscribe failed",succeed:"subscribe success"},()=>q({tenants:a,environmentKey:o,appKey:l,versionKey:m})).catch(()=>{})};var C=c(require("path")),L=c(require("fs-extra")),Y=c(require("inquirer"));var vt=c(require("readline")),bt=require("chalk"),wt=c(require("execa")),Dr=e=>{if(!bt.supportsColor){e.write("\r");return}vt.default.cursorTo(e,0)},Cr=(e,t)=>{let r=Math.min(Math.max(e/t,0),1),o=` ${e}/${t}`,n=Math.max(0,process.stderr.columns-o.length-3),s=Math.min(t,n),i=Math.round(s*r),a="#".repeat(i),p="-".repeat(s-i);Dr(process.stderr),process.stderr.write(`[${a}${p}]${o}`)},Rr=(e,t,r,o)=>new Promise((n,s)=>{var a;let i=(0,wt.default)(e,t,{cwd:r,stdio:["inherit","inherit","pipe"],env:o});(a=i.stderr)==null||a.on("data",p=>{let l=p.toString();if(/warning/.test(l))return;let m=l.match(/\[.*] (\d+)\/(\d+)/);if(m){Cr(parseFloat(m[1]),parseFloat(m[2]));return}process.stderr.write(p)}),i.on("close",p=>{if(p!==0){s(new Error(`command failed: ${e} ${t.join(" ")}`));return}n()})}),N={executeCommand:Rr};var de=c(require("fs-extra")),ge=c(require("simple-git")),kt=async e=>{await(0,ge.default)(e).init()},W=async(e,t,r)=>{await de.default.pathExists(t)||await de.default.mkdir(t);let o=(0,ge.default)(t),n=[];r!=null&&r.branch&&n.push("--branch",r.branch),r!=null&&r.singleBranch&&n.push("--single-branch"),await o.clone(e,t,n)};var Q=c(require("fs-extra")),xt=process.env.APPS_TEMPLATE_REPO||"https://github.com/moriahq/apps-template.git",ye=(r=>(r.micro="micro",r.uikit="uikit",r))(ye||{}),Pt=async(e,t)=>{if(!await Q.default.pathExists(e))return;let r=await Q.default.readFile(e,{encoding:"utf-8"});await Q.default.writeFile(e,r.replace(/{{(\w+)}}/g,(o,n)=>typeof t[n]=="string"?t[n]:o))};var _r=async()=>(await Y.default.createPromptModule()({type:"input",name:"App Name",message:"Please enter an app name",validate:r=>!!r}))["App Name"],Lr=async()=>(await Y.default.createPromptModule()({type:"input",name:"App key",message:"Please enter a valid app key",validate:r=>/^[a-z][a-z0-9_]*$/.test(r)?!0:(console.info(""),console.info("only supports letters, numbers, underscores and starts with letters"),!1)}))["App key"],jr=async()=>(await Y.default.createPromptModule()({type:"confirm",name:"Init Skill",message:"Initialize Gitee Team app skill?",default:!0}))["Init Skill"],$r=()=>{if(Number(x.split(".")[0])>=1)return"v1"},Nr=async(e,t)=>{let r=$r();if(!r){await u({start:"Start clone repository",error:"Clone repository failed!",succeed:"Clone repository success!"},()=>W(e,t));return}try{await u({start:`Start clone repository (branch: ${r})`,error:"Clone repository failed!",succeed:"Clone repository success!"},()=>W(e,t,{branch:r,singleBranch:!0}))}catch{console.info(`Template branch '${r}' not found, fallback to default branch.`),await u({start:"Start clone repository (default branch)",error:"Clone repository failed!",succeed:"Clone repository success!"},()=>W(e,t))}},Ir=async e=>{await u({start:"Start install dependencies",error:"Install dependencies failed!",succeed:"Install dependencies success!"},()=>N.executeCommand("pnpm",["install"],e))},Mr=async e=>{await u({start:"Start initialize skill",error:"Initialize skill failed!",succeed:"Initialize skill success!"},()=>N.executeCommand("npx",["-y","skills","add","moriahq/giteeteam-skills"],e))},Et=async e=>{let{cwd:t}=e,r="micro",o=await _r(),n=await Lr(),s=await jr(),i=C.default.join(t,n);if(await L.default.pathExists(i)){console.error(`Project path already exists: ${i}`);return}await Nr(xt,i),await L.default.copy(C.default.join(i,r),i),await Promise.all(Object.values(ye).map(p=>L.default.remove(C.default.join(i,p)))),await L.default.remove(C.default.join(i,"./.git")),await L.default.remove(C.default.join(i,"./SKILL.md"));let a=["./manifest.yml","./package.json","./rspack.config.js","./public/index.html","./src/App.tsx","./src/index.tsx","./static/App.tsx","./static/index.tsx"];await Promise.all(a.map(p=>Pt(C.default.join(i,p),{appName:o,appKey:n}))),await Ir(i),await kt(i),s&&await Mr(i),console.info("Project init success!"),console.info("Project path:"+i)};var Ot=async()=>{let e=process.cwd();await Et({cwd:e})};var X=c(require("inquirer"));var he=c(require("axios"));var St=["/api/gateway/login","/login"],Ur=e=>{var o;let t=(o=e==null?void 0:e.response)==null?void 0:o.status,r=e==null?void 0:e.code;return[404,405,502,503,504].includes(t)||["ECONNREFUSED","ENOTFOUND","ECONNABORTED"].includes(r)},Tt=async({baseUrl:e,username:t,password:r})=>{var n;let o;for(let s of St)try{let i=await(0,he.default)({method:w.POST,url:`${e}${s}`,data:{username:t,password:r}});return console.info(`Login endpoint: ${s}`),{cookie:(n=i.headers)==null?void 0:n["set-cookie"]}}catch(i){if(o=i,!Ur(i))return console.error(`Login error (${s}):`,i==null?void 0:i.message),{cookie:""}}return console.error(`Login error: all endpoints failed (${St.join(", ")})`,(o==null?void 0:o.message)||""),{cookie:""}},Dt=async e=>{try{return await(0,he.default)({method:w.GET,url:`${e}/apps/api/v1/tenants`}),!1}catch{return!0}};var Ct=e=>{try{return new URL(e).origin}catch{return e}};var Rt=c(require("crypto"));var At=e=>{try{let t=Rt.default.createCipheriv(Ce,De,Te),r=t.update(e,"utf8","base64");return r+=t.final("base64"),r.toString()}catch(t){return console.error(`Encryption error: ${t}`),null}};var Fr=async()=>(await X.default.createPromptModule()({type:"input",name:"Domain",message:"Please enter the domain address",validate:r=>{try{return new URL(r),!0}catch{return"Please enter a valid domain name"}}})).Domain,Kr=async()=>(await X.default.createPromptModule()({type:"input",name:"Username",message:"Please enter username",validate:r=>!!r})).Username,Gr=async()=>(await X.default.createPromptModule()({type:"password",name:"Password",message:"Please enter password",validate:r=>!!r})).Password,_t=async({domain:e,username:t,password:r})=>{if(e||(e=await Fr()),e=Ct(e),await Dt(e)){t||(t=await Kr()),r||(r=await Gr());let{cookie:n}=await Tt({baseUrl:e,username:t,password:At(r)||r});n?(fe({domain:e,username:t,cookie:n}),console.info("Login success!")):console.error("Login failed!")}else fe({domain:e}),console.info("Login success!")};var Lt=async e=>{await _t(e)};var jt=c(require("chalk"));var Br=e=>e?(Array.isArray(e)?e:[e]).flatMap(r=>r.split(",")).map(r=>r.trim()).filter(Boolean):[],$t=async(e,t,r)=>{let{domain:o}=E(),{environment:n="development",tenants:s}=r,i=Br(s);if(!o){f.error("Please login in first!");return}if(!Object.values(J).includes(n)){f.error("Only support development, staging, production environment.");return}if(!e||!t){f.error("Please provide appKey and versionKey.");return}if(i.length===0){f.error("Please provide subscribe tenants by -t/--tenants.");return}console.info(`Subscribing to ${jt.default.bold.green(o)}`),await u({start:"start subscribe",error:"subscribe failed",succeed:"subscribe success"},()=>q({tenants:i,environmentKey:n,appKey:e,versionKey:t})).catch(()=>{})};var Nt=c(require("path")),It=c(require("fs-extra"));var Mt=async e=>{let t=Nt.default.resolve(e||process.cwd());if(!await It.default.pathExists(t)){f.error(`project path not found: ${t}`);return}await u({start:"Start sync skill",error:"Sync skill failed!",succeed:"Sync skill success!"},()=>N.executeCommand("npx",["-y","skills","add","moriahq/giteeteam-skills"],t)),console.info("sync-skill finished."),console.info(`Project path: ${t}`)};var re=c(require("path"));var Xt=c(require("path")),te=c(require("chalk")),Zt=c(require("debug")),be=c(require("fs-extra"));var Ut=async()=>{var t;return((t=(await O({method:w.GET,url:"/apps/api/v1/config"}).catch(r=>{var o,n;console.info(`getRuntimeConfig error ${((n=(o=r==null?void 0:r.response)==null?void 0:o.data)==null?void 0:n.message)||(r==null?void 0:r.message)}`)})).data)==null?void 0:t.runtime)||{}};var Z=require("child_process"),I=class{constructor(t,r){this.imageName=t,this.containerName=r}async downloadImage(){console.info(`Downloading image(${this.imageName}) ...`),await this.execPromise(`docker pull ${this.imageName}`),console.info(`Download image(${this.imageName}) success!`)}async runContainer({port:t,env:r,volume:o=[]}){await this.removeContainer();let n=Object.keys(r).filter(a=>r[a]!==void 0).map(a=>`-e ${a}=${r[a]}`).join(" "),s=o==null?void 0:o.map(a=>`-v ${a}`).join(" "),i=`docker run -d -p ${t}:${t} ${n} ${s} --name ${this.containerName} ${this.imageName}`;await this.execPromise(i)}async removeContainer(){await this.execPromise(`docker rm -f ${this.containerName}`).catch(()=>{})}logs(){let t=(0,Z.spawn)("docker",["logs","-f",this.containerName]);t.stdout.on("data",r=>{console.info(`${r}`)}),t.stderr.on("data",r=>{console.info(`${r}`)})}execPromise(t){return new Promise((r,o)=>{(0,Z.exec)(t,(n,s,i)=>{if(n)return o(n);r({stdout:s,stderr:i})})})}};var Ft=c(require("localtunnel"));var Kt=async({appKey:e,runtimeHost:t,resources:r,resourcePort:o})=>{r!=null&&r.length&&r.forEach(n=>{n.path=`http://127.0.0.1:${o}`}),await O({method:w.POST,url:`/apps/api/v1/apps/tunnel/${e}`,data:{resources:r,runtimeHost:t}}).catch(n=>{var s,i;console.info(`addTunnel error ${((i=(s=n==null?void 0:n.response)==null?void 0:s.data)==null?void 0:i.message)||(n==null?void 0:n.message)}`)})},Gt=async e=>{await O({method:"DELETE",url:`/apps/api/v1/apps/tunnel/${e}`}).catch(()=>{})},Bt=async e=>{var t,r;try{return(await(0,Ft.default)({port:e,host:((t=process.env)==null?void 0:t.TUNNEL_HOST)==="OFFICIAL"?void 0:((r=process.env)==null?void 0:r.TUNNEL_HOST)||"http://tunnel.gitee.work:1777"})).url}catch(o){return console.info("runTunnel error",o==null?void 0:o.message),""}};var ve=c(require("path")),zt=require("@rspack/core"),Vt=require("@rspack/dev-middleware"),qt=c(require("cors")),Ht=c(require("express")),Jt=require("http-proxy-middleware");function zr(e,t){var o;let r=(o=t==null?void 0:t.devServer)==null?void 0:o.proxy;if(!(!Array.isArray(r)||r.length===0))for(let n of r){if(!n||!n.target)continue;let s=n.context&&!n.pathFilter?{...n,pathFilter:n.context}:n;e.use((0,Jt.createProxyMiddleware)(s))}}var ee=async({appKey:e,rspackConfig:t})=>{let o=(await G())({environment:"development",appKey:e});if(t){let p=require(ve.default.resolve(t));o=$(o,p)}let n=(0,zt.rspack)(o),s=(0,Ht.default)(),i=Number(process.env.UI_PORT)||8e3,a=(0,Vt.devMiddleware)(n);s.use((0,qt.default)()),zr(s,o),s.use(a),s.get("*",(p,l)=>{var m,d;(d=n.outputFileSystem)==null||d.readFile(ve.default.join(((m=o.output)==null?void 0:m.path)||"","index.html"),(g,k)=>{if(g||!k){l.sendStatus(404);return}l.send(k.toString())})}),s.listen(i,"0.0.0.0",()=>{console.info(`The React app is listening on port ${i}!
|
|
5
|
+
`)})};var Qt=c(require("chokidar"));var Wt=(e,t,r)=>{for(let o of e)K({inputDir:t,outputDir:r,fileName:o,mode:"development"})},Yt=async e=>{let{inputDir:t,outputDir:r,watchFilePaths:o}=e,s=(await P()).getFunctionFiles();Wt(s,t,r);let i,a=[t,...o],p=Qt.default.watch(a);console.info(`watch: ${a.join(", ")}`),p.on("change",l=>{clearTimeout(i),i=setTimeout(()=>{console.info(`${l} has change`),Wt(s,t,r)},1e3)})};var Vr=(0,Zt.default)("cli:tunnel"),qr=async()=>{let e=await Ut(),t=process.env.RUNTIME_IMAGE||e.image||"docker-hub.gitee.work/gitee-prod/proxima-apps-runtime-server:4.4.0-alpha-86",r=Number(process.env.CONTAINER_PORT)||8455,o=process.env.CONTAINER_NAME||"cli-dev-apps-runtime-server",n=process.env.PULL_IMAGE!=="false",s={PORT:r,APPS_DIR:"/usr/src/app/apps",LRU_TTL:1,FS_MODE:"local",DEBUG:process.env.DEBUG||"runtime*",LOG_LEVEL:process.env.LOG_LEVEL,APPS_DEFAULT_LOCALE:process.env.APPS_DEFAULT_LOCALE||"zh-CN",PARSE_PUBLIC_SERVER_URL:process.env.PARSE_PUBLIC_SERVER_URL||e.parsePublicServerUrl,PARSE_SERVER_MASTER_KEY:process.env.PARSE_SERVER_MASTER_KEY||e.parseServerMasterKey,PROXIMA_CORE_URL:process.env.PROXIMA_CORE_URL||e.proximaCoreUrl,APPS_SERVER_URL:process.env.APPS_SERVER_URL||e.appsServerUrl,APPS_PARSE_SERVER_URL:process.env.APPS_PARSE_SERVER_URL||e.appsParseServerUrl,PGSQL_CLIENT_HOST:process.env.PGSQL_CLIENT_HOST,PGSQL_CLIENT_PORT:process.env.PGSQL_CLIENT_PORT,PGSQL_CLIENT_USER:process.env.PGSQL_CLIENT_USER,PGSQL_CLIENT_PASSWORD:process.env.PGSQL_CLIENT_PASSWORD,APPS_RUNTIME_IOREDIS_OPTIONS:process.env.APPS_RUNTIME_IOREDIS_OPTIONS?`'${process.env.APPS_RUNTIME_IOREDIS_OPTIONS}'`:void 0};return{runtimeImage:t,containerPort:r,containerName:o,containerEnv:s,pullImage:n}},er=async e=>{var Ee,Oe,Se;let{inputDir:t,outputDir:r,server:o,filesToCopy:n,resourcePort:s,ui:i,rspackConfig:a,noProxy:p,watchFilePaths:l=[]}=e;await be.default.emptyDir(r);let m=await P(),d=m.app.key,g=m.app.version,k=m.getFunctionFiles(),S=m.resources||[];if(!d){f.error("appKey is required!");return}if(!g){f.error("version is required!");return}console.info(`App key: ${te.default.bold.green(d)}`),console.info(`App version: ${te.default.bold.green(g)}
|
|
6
|
+
`);let{domain:M}=E();if(!M){f.error("Please login in first!");return}console.info(`Tunneling to ${te.default.bold.green(M)}`);let v=[],oe=async R=>{R instanceof Error&&console.error(R);try{await Promise.all(v.map(T=>T()))}catch(T){console.error("other error:",T)}process.exit()};process.on("SIGINT",oe),process.on("uncaughtException",oe),process.on("unhandledRejection",oe),i&&ee({appKey:d,rspackConfig:a});let ir=`${`/${d}/development/${g}`}/server-side`,xe=Xt.default.join(r,ir);await B(xe,n),await Yt({inputDir:t,outputDir:xe,watchFilePaths:l}),v.push(()=>be.default.remove(r));let ne="",Pe;if(o&&k.length>0){let{runtimeImage:R,containerPort:T,containerEnv:ar,containerName:cr,pullImage:pr}=await qr();Pe=T;let U=new I(R,cr);pr&&await U.downloadImage(),await U.runContainer({port:T,env:ar,volume:[`${r}:/usr/src/app/apps`]}),v.push(()=>U.removeContainer()),p||(ne=await u({start:"start tunnel",error:"tunnel failed",succeed:"tunnel success"},()=>Bt(T)),Vr("runtimeHost",ne)),U.logs()}if(p){console.info(`\u4EE3\u7406\u5173\u95ED \u274C\uFF1A\u8BF7\u628A\u8BF7\u6C42\u6307\u5411localhost:${Pe}`);return}if(await Kt({appKey:d,runtimeHost:ne,resources:S,resourcePort:s}),v.push(()=>Gt(d)),o&&((Se=(Oe=(Ee=m.getManifest())==null?void 0:Ee.modules)==null?void 0:Oe.webtrigger)!=null&&Se.length)){console.info("You can use this url for test:");let R=`${M}/apps/api/v1/:tenant/apps/${d}/environments/development/webtriggers/:webtriggerKey`;console.info(`POST ${R}`)}};var tr=async e=>{let{copy:t=[],input:r="functions",output:o="cache",server:n=!0,resourcePort:s,ui:i,rspack:a,proxy:p,watch:l=[]}=e;await er({outputDir:re.default.resolve(o),inputDir:re.default.resolve(r),filesToCopy:[...F,...t],server:n,resourcePort:Number(s)||8e3,ui:!!i,rspackConfig:a,noProxy:!p,watchFilePaths:l.map(m=>re.default.resolve(m))})};var Hr=async({rspackConfig:e})=>{let t=await P(),{key:r}=t.app;if(!r){f.error("manifest app key is required");return}ee({appKey:r,rspackConfig:e})},rr=Hr;var or=async e=>{let{rspack:t}=e;await rr({rspackConfig:t})};var j=c(require("path")),y=c(require("fs-extra"));var Jr=["@ant-design/compatible","@giteeteam/apps-team-theme","webpack","webpack-cli","webpack-dev-server","webpack-merge","webpack-bundle-analyzer"],Wr={"@rspack/core":"^2.0.0","@rspack/plugin-react-refresh":"^1.4.2",deepmerge:"^4.3.1"},Qr=["webpack.config.js","webpack.config.cjs","webpack.config.mjs","webpack.config.ts"],we=e=>Object.fromEntries(Object.entries(e).sort(([t],[r])=>t.localeCompare(r))),Yr=e=>{let t=e.peerDependencies;if(!t||typeof t!="object")return!1;let r=!1;for(let o of["@ant-design/compatible","@giteeteam/apps-team-theme"])o in t&&(delete t[o],r=!0);return r&&(e.peerDependencies=we(t)),r},Xr=e=>{let t=["dependencies","devDependencies"],r=!1;for(let o of t){let n=e[o];if(!(!n||typeof n!="object"))for(let s of Jr)s in n&&(delete n[s],r=!0)}(!e.dependencies||typeof e.dependencies!="object")&&(e.dependencies={},r=!0);for(let[o,n]of Object.entries(Wr))e.dependencies[o]||(e.dependencies[o]=n,r=!0);return e.dependencies&&typeof e.dependencies=="object"&&(e.dependencies=we(e.dependencies)),e.devDependencies&&typeof e.devDependencies=="object"&&(e.devDependencies=we(e.devDependencies)),r},Zr=e=>{if(!e.scripts||typeof e.scripts!="object")return!1;let t=!1;for(let[r,o]of Object.entries(e.scripts)){let n=o;n=n.replace(/\byarn\b/g,"pnpm"),n=n.replace(/\bwebpack-dev-server\b/g,"rspack serve"),n=n.replace(/\bwebpack serve\b/g,"rspack serve"),n=n.replace(/\bwebpack\b/g,"rspack"),n=n.replace(/--webpack\b/g,"--rspack"),n=n.replace(/(^|\s)-w(\s+)/g,"$1-r$2"),n=n.replace(/webpack\.config\.(js|cjs|mjs|ts)\b/g,"rspack.config.$1"),n!==o&&(e.scripts[r]=n,t=!0)}return t},eo=async e=>{let t=j.default.join(e,"yarn.lock");return await y.default.pathExists(t)?(await y.default.remove(t),!0):!1},to=async e=>{let t=!1;for(let r of Qr){let o=j.default.join(e,r);if(!await y.default.pathExists(o))continue;let n=r.replace(/^webpack\.config/,"rspack.config"),s=j.default.join(e,n);await y.default.pathExists(s)||(await y.default.move(o,s),t=!0);let i=await y.default.readFile(s,"utf8"),a=i.replace(/webpack配置/g,"rspack\u914D\u7F6E").replace(/import\s+\{\s*themeConfig\s*\}\s+from\s+['"]@giteeteam\/apps-team-theme['"];?\n?/g,"").replace(/import\s+\{\s*convertLegacyToken\s*\}\s+from\s+['"]@ant-design\/compatible['"];?\n?/g,"").replace(/const\s+mapToken\s*=\s*defaultAlgorithm\(\{\s*\.\.\.defaultSeed\s*,\s*\.\.\.themeConfig\.token\s*\}\);?/g,"const mapToken = defaultAlgorithm({ ...defaultSeed });").replace(/const\s+v4Token\s*=\s*convertLegacyToken\(\{\s*\.\.\.mapToken\s*\}\);?\n?/g,"").replace(/\n([ \t]*)\.\.\.v4Token,\n/g,`
|
|
7
|
+
`).replace(/import\s+\{\s*merge\s*\}\s+from\s+['"]webpack-merge['"];?/g,"import merge from 'deepmerge';").replace(/const\s+\{\s*merge\s*\}\s*=\s*require\((['"])webpack-merge\1\);?/g,"const merge = require('deepmerge');").replace(/(['"])webpack-merge\1/g,"$1deepmerge$1").replace(/^.*(rspack-bundle-analyzer|webpack-bundle-analyzer).*\n?/gm,"").replace(/^.*ANALYZER_PACKAGE.*\n?/gm,"").replace(/(['"])webpack\1/g,"$1@rspack/core$1");a!==i&&(await y.default.writeFile(s,a),t=!0)}return t},nr=async e=>{let t=j.default.resolve(e||process.cwd()),r=j.default.join(t,"package.json");if(!await y.default.pathExists(r)){f.error(`package.json not found in ${t}`);return}let o=await y.default.readFile(r,"utf8"),n=JSON.parse(o),s=Xr(n),i=Yr(n),a=Zr(n),p=await eo(t),l=await to(t);if((s||i||a)&&await y.default.writeFile(r,`${JSON.stringify(n,null,2)}
|
|
8
|
+
`),!s&&!i&&!a&&!p&&!l){console.info("Nothing to upgrade. Project already uses rspack settings.");return}console.info("upgrade-v1 finished."),console.info(`Project path: ${t}`),console.info("Please run: pnpm install")};var ro=`v${x}`;console.info(`GiteeTeam Apps Cli ${ke.default.bold.green(ro)}`);console.info(`Node.js ${ke.default.bold.green(process.version)}
|
|
9
|
+
`);var h=new sr.Command;h.version(x,"-v -V --vers").description("Giteeteam Apps Cli");h.command("build").option("--prod","build in production").option("--dev","build in development").option("--no-zip","no archive").option("-c --copy [letters...]","copy files").option("-i --input [dir]","input dir").option("-o --output [dir]","output dir").option("--ui","build ui").option("-r --rspack [file]","build ui rspack config file").action(nt);h.command("init").action(Ot);h.command("sync-skill [projectPath]").description("sync builtin skill files to project").action(Mt);h.command("upgrade-v1 [projectPath]").description("upgrade webpack project to rspack").action(nr);h.command("login").option("--domain [domain]","login domain").option("-u --username [username]","login username").option("-p --password [password]","login user password").action(Lt);h.command("tunnel").option("-c --copy [letters...]","copy files").option("-i --input [dir]","input dir").option("-o --output [dir]","output dir,default cache").option("--no-server","no server").option("--resourcePort [number]","resource port").option("--ui","build ui").option("-r --rspack [file]","build ui rspack config file").option("--no-proxy","no proxy").option("-w --watch [paths...]","watch files change").action(tr);h.command("deploy [filename]").option("-e --environment [environment]","app environment").option("-s --subscribe [tenants...]","subscribe tenants after deploy").option("--app-key [appKey]","app key used for subscribe").option("--version-key [versionKey]","version key used for subscribe").action(ht);h.command("subscribe <appKey> <versionKey>").option("-t --tenants [tenants...]","subscribe tenants, support comma split").option("-e --environment [environment]","app environment").action($t);h.command("ui-dev").option("-r --rspack [file]","build ui rspack config file").action(or);h.parse(process.argv);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@giteeteam/apps-cli",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Giteeteam Apps cli",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript",
|
|
@@ -13,11 +13,10 @@
|
|
|
13
13
|
"license": "ISC",
|
|
14
14
|
"main": "./dist/index.js",
|
|
15
15
|
"files": [
|
|
16
|
-
"dist"
|
|
17
|
-
"templates/skill"
|
|
16
|
+
"dist"
|
|
18
17
|
],
|
|
19
18
|
"scripts": {
|
|
20
|
-
"build": "rm -rf dist && etsc
|
|
19
|
+
"build": "rm -rf dist && etsc",
|
|
21
20
|
"lint": "npx eslint . --quiet --fix",
|
|
22
21
|
"type-check": "tsc --pretty --noEmit",
|
|
23
22
|
"precommit": "pnpm i && lint-staged && pnpm type-check",
|
|
@@ -40,10 +39,10 @@
|
|
|
40
39
|
},
|
|
41
40
|
"dependencies": {
|
|
42
41
|
"@giteeteam/apps-manifest": "0.8.7",
|
|
42
|
+
"@rollup/plugin-commonjs": "^29.0.2",
|
|
43
43
|
"@rspack/core": "^2.0.0",
|
|
44
44
|
"@rspack/dev-middleware": "^2.0.1",
|
|
45
45
|
"@rspack/plugin-react-refresh": "^1.4.2",
|
|
46
|
-
"@rollup/plugin-commonjs": "^29.0.2",
|
|
47
46
|
"archiver": "^5.3.1",
|
|
48
47
|
"autoprefixer": "^10.4.20",
|
|
49
48
|
"axios": "^1.13.6",
|
|
@@ -59,6 +58,7 @@
|
|
|
59
58
|
"form-data": "^4.0.0",
|
|
60
59
|
"fs-extra": "^11.2.0",
|
|
61
60
|
"hasha": "^5.2.2",
|
|
61
|
+
"http-proxy-middleware": "^3.0.5",
|
|
62
62
|
"inquirer": "^8.2.6",
|
|
63
63
|
"localtunnel": "2.0.2",
|
|
64
64
|
"lowdb": "1.0.0",
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
# {{appName}} 插件开发 Skill
|
|
2
|
-
|
|
3
|
-
这个 Skill 面向插件研发与 AI 代理协作,目标是让 Agent 在当前插件仓库内稳定完成:
|
|
4
|
-
|
|
5
|
-
- 功能实现(前端模块、trigger、webtrigger)
|
|
6
|
-
- API 适配(`/forge/` 到运行时路径的转换)
|
|
7
|
-
- 打包、发布与订阅(CLI 一体化)
|
|
8
|
-
|
|
9
|
-
## 文档导航
|
|
10
|
-
|
|
11
|
-
- `skill/guides/01-overview.md`:开发总览、交付标准与执行节奏
|
|
12
|
-
- `skill/guides/02-extension-points.md`:manifest 扩展点与声明约束
|
|
13
|
-
- `skill/guides/03-api-routing-rules.md`:`/forge/` 路径转换与 `isInOne` 规则
|
|
14
|
-
- `skill/guides/04-deploy-and-subscribe.md`:CLI 登录、构建、部署、订阅
|
|
15
|
-
- `skill/guides/05-agent-prompt-template.md`:给 Agent 的标准任务模板
|
|
16
|
-
- `skill/guides/06-data-access-and-tables.md`:数据表设计与 Parse 访问规范
|
|
17
|
-
- `skill/references/README.md`:平台文档来源与使用策略
|
|
18
|
-
|
|
19
|
-
## 推荐执行顺序
|
|
20
|
-
|
|
21
|
-
1. 阅读 `01-overview` 和 `02-extension-points`,明确能力边界和 manifest 变更。
|
|
22
|
-
2. 查接口前先看 `references/README.md`,仅使用指定来源检索原始路径。
|
|
23
|
-
3. 按 `03-api-routing-rules` 完成前后端路径转换与 API 封装。
|
|
24
|
-
4. 涉及数据表时,执行 `06-data-access-and-tables` 中的建模与评审清单。
|
|
25
|
-
5. 需要发布验证时,执行 `04-deploy-and-subscribe`。
|
|
26
|
-
6. 让 Agent 批量产出代码时,使用 `05-agent-prompt-template`。
|
|
27
|
-
|
|
28
|
-
## 文档来源约束
|
|
29
|
-
|
|
30
|
-
- 接口事实来源:`https://team.gitee.cn/llms-full-team-api.txt`
|
|
31
|
-
- 插件开发规范来源:`https://team.gitee.cn/llms-full-developer.txt`
|
|
32
|
-
- 数据表结构来源:`https://team.gitee.cn/llms-full-table-schema.txt`
|
|
33
|
-
- 禁止用其它页面(包括历史笔记、二手转述)替代以上三类事实来源
|
|
34
|
-
|
|
35
|
-
## 项目标识
|
|
36
|
-
|
|
37
|
-
- 插件名称:`{{appName}}`
|
|
38
|
-
- 插件 Key:`{{appKey}}`
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
# 插件开发总览
|
|
2
|
-
|
|
3
|
-
## 目标
|
|
4
|
-
|
|
5
|
-
在保持可运行最小骨架的前提下,快速迭代插件能力,并保证以下交付完整:
|
|
6
|
-
|
|
7
|
-
- `manifest.yml` 能力声明完整且可校验
|
|
8
|
-
- 前端入口与路由可运行
|
|
9
|
-
- 后端 trigger/webtrigger 代码可调用
|
|
10
|
-
- 打包产物可通过 CLI 上传发布并订阅到目标租户
|
|
11
|
-
|
|
12
|
-
## 默认技术约束
|
|
13
|
-
|
|
14
|
-
- 前端:`React + React Router + Ant Design`
|
|
15
|
-
- 后端:`trigger/*.ts` 与 `functions/*.ts` 按 manifest 声明
|
|
16
|
-
- 构建:`giteeteam-apps` + `rspack`(通过 `rspack.config.js` 扩展)
|
|
17
|
-
- 发布:有代码改动并准备发布时,先递增 `manifest.app.version`
|
|
18
|
-
|
|
19
|
-
## 标准执行流程
|
|
20
|
-
|
|
21
|
-
1. 明确本次模块范围:`itemPanel/workspacePage/adminPage/webtrigger/trigger`。
|
|
22
|
-
2. 核对 manifest 变更:模块声明、resolver/function 映射、资源声明。
|
|
23
|
-
3. 检索接口原始路径:按 `SKILL.md` 的文档来源约束执行(优先 `https://team.gitee.cn/llms-full-team-api.txt`)。
|
|
24
|
-
4. 进行路径转换:前后端分别按 `03-api-routing-rules` 落地。
|
|
25
|
-
5. 若涉及数据表:按 `06-data-access-and-tables` 完成建模与访问封装。
|
|
26
|
-
6. 构建并发布验证:按 `04-deploy-and-subscribe` 执行 CLI 流程。
|
|
27
|
-
|
|
28
|
-
## 接口检索流程(强制)
|
|
29
|
-
|
|
30
|
-
1. 文档来源约束遵循 `SKILL.md` 的“文档来源约束”。
|
|
31
|
-
2. 先定位“请求链接”原文,再提取 `/forge/...` 原始路径。
|
|
32
|
-
3. 前端接口使用 `isInOne` 做环境分支;后端接口不使用该分支。
|
|
33
|
-
4. 提交说明写清楚“原始路径”和“转换后路径”。
|
|
34
|
-
|
|
35
|
-
## 完成标准(DoD)
|
|
36
|
-
|
|
37
|
-
1. manifest、前端、后端三者声明一致。
|
|
38
|
-
2. 新增接口已集中到 API 层,不在页面里散落拼接路径。
|
|
39
|
-
3. 新增数据结构有字段语义与演进说明(如涉及表)。
|
|
40
|
-
4. 发布命令可执行,订阅命令可执行。
|
|
41
|
-
|
|
42
|
-
## 目录建议
|
|
43
|
-
|
|
44
|
-
- `manifest.yml`:插件能力声明
|
|
45
|
-
- `src/`:前端代码
|
|
46
|
-
- `functions/` 或 `trigger/`:后端逻辑
|
|
47
|
-
- `skill/`:AI 协作规范、平台参考、发布流程
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
# 插件扩展点速查
|
|
2
|
-
|
|
3
|
-
以下扩展点与字段定义建议以平台文档为准;实现前先对照文档确认字段合法性。
|
|
4
|
-
|
|
5
|
-
## 常见模块
|
|
6
|
-
|
|
7
|
-
- `adminPage`
|
|
8
|
-
- `workspacePage`
|
|
9
|
-
- `itemPanel`
|
|
10
|
-
- `webtrigger`
|
|
11
|
-
- `trigger`
|
|
12
|
-
- `function`
|
|
13
|
-
- `resources`
|
|
14
|
-
- `tables`
|
|
15
|
-
|
|
16
|
-
## 实现要求
|
|
17
|
-
|
|
18
|
-
1. 每新增一个可执行逻辑,都要在 `modules.function` 声明 handler。
|
|
19
|
-
2. 每新增一个前端页面模块,都要在 `resources` 中声明静态资源路径。
|
|
20
|
-
3. 需要前端调用后端函数时,在页面模块(如 `itemPanel`)配置 `resolver.function`,并在函数中使用 `Resolver` 定义 method。
|
|
21
|
-
4. 需要插件表时,先在 `manifest.tables` 定义结构,再在函数中通过 `getParseQuery/getParseObject` 访问。
|
|
22
|
-
5. `webtrigger` 与 `trigger` 的 `key/function` 必须一一对应。
|
|
23
|
-
6. 修改模块定义后,优先执行构建并检查 manifest 校验报错。
|
|
24
|
-
7. 对已发布应用,禁止直接修改已有字段类型;使用新增字段 + 数据迁移方式演进。
|
|
25
|
-
|
|
26
|
-
## invoke 关键约束(Micro)
|
|
27
|
-
|
|
28
|
-
1. `Micro` 前端使用 `invoke` 时,SDK 会读取 `module.resolver.function` 来定位后端函数 key。
|
|
29
|
-
2. 因此页面模块必须写成:
|
|
30
|
-
|
|
31
|
-
```yaml
|
|
32
|
-
resolver:
|
|
33
|
-
function: <resolver-function-key>
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
3. 如果只写顶层 `function: xxx`(未配置 `resolver`),常见报错:
|
|
37
|
-
`Cannot read properties of undefined (reading 'function')`。
|
|
38
|
-
|
|
39
|
-
## 常见自检
|
|
40
|
-
|
|
41
|
-
1. 页面模块是否有对应资源声明。
|
|
42
|
-
2. resolver 的 function key 与 `modules.function` 是否一致。
|
|
43
|
-
3. trigger/webtrigger 的入参与返回结构是否和调用方约定一致。
|
|
44
|
-
4. 表结构是否包含查询高频字段的索引。
|
|
45
|
-
5. 若 `invoke` 报 `reading 'function'`,优先检查当前页面模块是否配置了 `resolver.function`。
|
|
46
|
-
|
|
47
|
-
## item 接入点弹层挂载(共性问题)
|
|
48
|
-
|
|
49
|
-
`itemPanel` 场景中,`Select/DatePicker/Cascader` 等弹层组件经常被父容器裁剪,表现为下拉显示不全或被遮挡。
|
|
50
|
-
这是弹层挂载容器与 item 详情容器层级不一致导致的共性问题。
|
|
51
|
-
|
|
52
|
-
建议在弹窗或表单内显式指定 `getPopupContainer`,挂载到当前弹窗内的独立 DOM 节点,而不是依赖默认挂载:
|
|
53
|
-
|
|
54
|
-
```tsx
|
|
55
|
-
const popupRef = useRef<HTMLDivElement | null>(null);
|
|
56
|
-
const getPopupContainer = useCallback(() => popupRef.current || document.body, []);
|
|
57
|
-
|
|
58
|
-
<Modal
|
|
59
|
-
open={open}
|
|
60
|
-
modalRender={node => (
|
|
61
|
-
<div>
|
|
62
|
-
{node}
|
|
63
|
-
<div ref={popupRef} />
|
|
64
|
-
</div>
|
|
65
|
-
)}
|
|
66
|
-
>
|
|
67
|
-
<Select getPopupContainer={getPopupContainer} />
|
|
68
|
-
</Modal>
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
自检顺序建议:
|
|
72
|
-
|
|
73
|
-
1. 先确认数据是否已正确拉取(排除“无数据”误判为“下拉显示不全”)。
|
|
74
|
-
2. 再确认弹层是否挂在当前 `itemPanel` 可视容器内。
|
|
75
|
-
3. 若仍异常,检查父容器是否存在 `overflow: hidden`、`transform`、`z-index` 冲突。
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
# /forge/ 接口转换规则
|
|
2
|
-
|
|
3
|
-
接口来源遵循 `SKILL.md` 的“文档来源约束”。`/forge/` 原始路径应从 `https://team.gitee.cn/llms-full-team-api.txt` 检索,路径按调用场景转换。
|
|
4
|
-
|
|
5
|
-
## 规则总览
|
|
6
|
-
|
|
7
|
-
1. 文档原始路径:`/forge/xxx`
|
|
8
|
-
2. 前端默认路径:`/api/team/parse/xxx`
|
|
9
|
-
3. 后端默认路径:`/parse/xxx`
|
|
10
|
-
4. 前端允许按 `isInOne` 做环境分支;后端不做该分支。
|
|
11
|
-
|
|
12
|
-
## 前端调用(浏览器)
|
|
13
|
-
|
|
14
|
-
当文档给出:
|
|
15
|
-
|
|
16
|
-
- `/forge/xxx`
|
|
17
|
-
|
|
18
|
-
前端实际请求改为:
|
|
19
|
-
|
|
20
|
-
- `/api/team/parse/xxx`
|
|
21
|
-
|
|
22
|
-
示例:
|
|
23
|
-
|
|
24
|
-
- 文档 `/forge/api/search` => 前端 `/api/team/parse/api/search`
|
|
25
|
-
|
|
26
|
-
环境分支(仅前端,按 `isInOne`):
|
|
27
|
-
|
|
28
|
-
- 集成环境(`isInOne === true`)优先:`/parse/xxx`
|
|
29
|
-
- 开发环境(`isInOne === false`)优先:`/api/team/parse/xxx`
|
|
30
|
-
- 为兼容网关差异,可保留另一条路径作为回退
|
|
31
|
-
|
|
32
|
-
## Trigger 调用(服务端)
|
|
33
|
-
|
|
34
|
-
当在 trigger/webtrigger 中调用同类接口时:
|
|
35
|
-
|
|
36
|
-
- 使用 `/parse/xxx`,或走项目内 parse client 对应能力
|
|
37
|
-
- 后端不使用 `isInOne` 做路径分支
|
|
38
|
-
|
|
39
|
-
示例:
|
|
40
|
-
|
|
41
|
-
- 文档 `/forge/api/search` => 后端 `/parse/api/search`
|
|
42
|
-
|
|
43
|
-
## `isInOne` 判断示例
|
|
44
|
-
|
|
45
|
-
```ts
|
|
46
|
-
export const isInOne = (gateway: string): boolean => {
|
|
47
|
-
if (!gateway) return false;
|
|
48
|
-
try {
|
|
49
|
-
const gatewayURL = new URL(gateway);
|
|
50
|
-
return gatewayURL.pathname.startsWith('/api');
|
|
51
|
-
} catch {
|
|
52
|
-
return false;
|
|
53
|
-
}
|
|
54
|
-
};
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
示例输入:
|
|
58
|
-
|
|
59
|
-
- `https://osc.gitee.work/api/team` => `true`(集成环境)
|
|
60
|
-
- `https://osc.gitee.work` => `false`(开发环境)
|
|
61
|
-
- `''` 或非法 URL => `false`
|
|
62
|
-
|
|
63
|
-
## 统一约束
|
|
64
|
-
|
|
65
|
-
1. 不要在前端直接请求裸 `/forge/*`。
|
|
66
|
-
2. 抽一层 API client(例如 `src/services/api.ts`)统一做路径转换。
|
|
67
|
-
3. 在提交说明中标注“原始路径 -> 实际路径”的映射关系。
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
# 插件发布与订阅(CLI)
|
|
2
|
-
|
|
3
|
-
以下流程通过 `giteeteam-apps` 完成登录、构建、部署和租户订阅。
|
|
4
|
-
|
|
5
|
-
## 0) 登录(必须)
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
giteeteam-apps login --domain http://<host> -u <username> -p <password>
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
## 1) 构建产物
|
|
12
|
-
|
|
13
|
-
先更新版本号(必须):
|
|
14
|
-
|
|
15
|
-
```yaml
|
|
16
|
-
# manifest.yml
|
|
17
|
-
app:
|
|
18
|
-
version: '<new-version>'
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
```bash
|
|
22
|
-
giteeteam-apps build --prod
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
## 2) 部署到目标环境
|
|
26
|
-
|
|
27
|
-
```bash
|
|
28
|
-
giteeteam-apps deploy <zip-file> -e production
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
常见环境值:`development`、`staging`、`production`(默认 `development`)。
|
|
32
|
-
|
|
33
|
-
在当前仓库建议使用显式命令(避免 `package.json` 中未传文件名导致失败):
|
|
34
|
-
|
|
35
|
-
```bash
|
|
36
|
-
pnpm exec giteeteam-apps deploy <appKey>_<versionKey>.zip -e development
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
如果发布/订阅请求失败,可按以下顺序重试:
|
|
40
|
-
|
|
41
|
-
1. 先正常执行命令。
|
|
42
|
-
2. 失败后使用提权执行(允许无沙箱网络)。
|
|
43
|
-
3. 提权后仍失败,再补全 `NO_PROXY/no_proxy` 重试。
|
|
44
|
-
|
|
45
|
-
`NO_PROXY/no_proxy` 示例:
|
|
46
|
-
|
|
47
|
-
```bash
|
|
48
|
-
NO_PROXY=localhost,127.0.0.1,<domain>,<target-host> \
|
|
49
|
-
no_proxy=localhost,127.0.0.1,<domain>,<target-host> \
|
|
50
|
-
pnpm exec giteeteam-apps deploy <zip-file> -e <environment>
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
## 3) 订阅到租户
|
|
54
|
-
|
|
55
|
-
方式 A:独立订阅
|
|
56
|
-
|
|
57
|
-
```bash
|
|
58
|
-
giteeteam-apps subscribe <appKey> <versionKey> -t osc -e production
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
方式 B:部署后自动订阅
|
|
62
|
-
|
|
63
|
-
```bash
|
|
64
|
-
giteeteam-apps deploy <zip-file> -e production --subscribe osc --app-key <appKey> --version-key <versionKey>
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
## 参数说明
|
|
68
|
-
|
|
69
|
-
1. `<zip-file>` 使用 `build` 产出的压缩包路径。
|
|
70
|
-
2. `-t/--tenants` 与 `--subscribe` 支持逗号分隔,例如 `osc,test`。
|
|
71
|
-
3. 若 zip 命名为 `<appKey>_<versionKey>.zip`,`deploy --subscribe` 可省略 `--app-key/--version-key`。
|
|
72
|
-
4. 发布订阅流程不需要再手动调用 `curl`。
|
|
73
|
-
5. 有代码改动并准备发布时,必须递增 `manifest.yml` 中的 `app.version`。
|
|
74
|
-
6. 默认重试顺序:正常请求 -> 提权请求 -> 提权 + `NO_PROXY/no_proxy`。
|
|
75
|
-
|
|
76
|
-
## 故障排查
|
|
77
|
-
|
|
78
|
-
1. `503` 或 `socket hang up`:优先检查环境服务状态,再重试 deploy。
|
|
79
|
-
2. `Please login in first!`:先执行 `giteeteam-apps login`。
|
|
80
|
-
3. 订阅失败且提示缺少 app/version:补充 `--app-key` 与 `--version-key`。
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
# 给 Agent 的任务模板
|
|
2
|
-
|
|
3
|
-
```text
|
|
4
|
-
请在当前仓库实现/修改插件能力。
|
|
5
|
-
插件名称:{{appName}}
|
|
6
|
-
插件 key:{{appKey}}
|
|
7
|
-
目标模块:<itemPanel/workspacePage/adminPage/webtrigger/trigger>
|
|
8
|
-
核心功能:
|
|
9
|
-
1. <功能 1>
|
|
10
|
-
2. <功能 2>
|
|
11
|
-
3. <功能 3>
|
|
12
|
-
|
|
13
|
-
约束:
|
|
14
|
-
- 接口检索仅使用 `https://team.gitee.cn/llms-full-team-api.txt`
|
|
15
|
-
- 插件开发规范仅使用 `https://team.gitee.cn/llms-full-developer.txt`
|
|
16
|
-
- 数据表结构仅使用 `https://team.gitee.cn/llms-full-table-schema.txt`
|
|
17
|
-
- 前端接口使用 `isInOne` 环境判断并执行 `/forge/` 转换;后端不使用该判断
|
|
18
|
-
- 路径映射必须满足:前端 `/forge/xxx` => `/api/team/parse/xxx`;后端 `/forge/xxx` => `/parse/xxx`
|
|
19
|
-
- 涉及数据表访问时:`getParseQuery(false, 'Item')` 查核心表,`getParseQuery(true, 'xxx')` 查插件表
|
|
20
|
-
- 默认采用 `manifest.tables + getParseQuery/getParseObject`;`storage.entities` 仅用于 Storage API
|
|
21
|
-
- 已发布应用禁止直接修改已有字段类型;只能新增字段并给出迁移方案
|
|
22
|
-
- `manifest.yml` 定义完整且可校验
|
|
23
|
-
- Micro 页面若使用 `invoke`,对应模块必须配置 `resolver.function`(不能只写顶层 `function`)
|
|
24
|
-
- 需要发布时必须递增 `manifest.app.version`
|
|
25
|
-
- trigger 与 webtrigger 的 function 声明完整
|
|
26
|
-
- 发布/订阅失败时按顺序处理:先正常请求,再提权,最后提权 + `NO_PROXY/no_proxy`
|
|
27
|
-
|
|
28
|
-
输出要求:
|
|
29
|
-
- 列出修改文件
|
|
30
|
-
- 列出“原始接口路径 -> 实际调用路径”映射
|
|
31
|
-
- 列出验证步骤与结果
|
|
32
|
-
```
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
# 数据访问与表设计
|
|
2
|
-
|
|
3
|
-
## 目标
|
|
4
|
-
|
|
5
|
-
在插件中实现“核心表 + 插件表”的统一访问方式,并支持资源负载等场景。
|
|
6
|
-
|
|
7
|
-
## 文档来源
|
|
8
|
-
|
|
9
|
-
1. 表结构与字段定义:`https://team.gitee.cn/llms-full-table-schema.txt`。
|
|
10
|
-
2. 插件开发规范与能力边界:`https://team.gitee.cn/llms-full-developer.txt`。
|
|
11
|
-
3. 接口路径与调用约定:`https://team.gitee.cn/llms-full-team-api.txt`。
|
|
12
|
-
|
|
13
|
-
## 核心规则
|
|
14
|
-
|
|
15
|
-
1. `getParseQuery(false, 'Item')` / `getParseObject(false, 'Item')`:访问 Team 核心表。
|
|
16
|
-
2. `getParseQuery(true, 'Resource')` / `getParseObject(true, 'Resource')`:访问插件自有表。
|
|
17
|
-
3. 默认采用 `manifest.tables + getParseQuery/getParseObject`。
|
|
18
|
-
4. `manifest.storage.entities` 仅在使用 Storage API(`storage.entity(...)`)时启用,不与 `getParseQuery` 混用。
|
|
19
|
-
|
|
20
|
-
## 建表前置设计(高优先级)
|
|
21
|
-
|
|
22
|
-
1. 建表前先定义字段语义、关联关系、索引、扩展方向。
|
|
23
|
-
2. 应用发布后,不直接修改已有字段 `type`。
|
|
24
|
-
3. 字段演进采用“新增字段 + 数据迁移 + 切换读写”。
|
|
25
|
-
4. 变更说明必须包含迁移方案、回滚方案和影响范围。
|
|
26
|
-
5. Team 应用中心没有“洗表/重建历史数据”的能力,改类型会带来兼容风险和历史数据不可用风险。
|
|
27
|
-
|
|
28
|
-
## 表定义建议(资源负载场景)
|
|
29
|
-
|
|
30
|
-
- `Resource`
|
|
31
|
-
- 字段:`name`(String), `type`(String: human|hardware), `workspaceKey`(String), `description`(String)
|
|
32
|
-
- 索引:`type`、`workspaceKey`
|
|
33
|
-
|
|
34
|
-
- `ResourceBooking`
|
|
35
|
-
- 字段:`resource`(Pointer->Resource), `itemId`(String), `workspaceKey`(String), `startDate`(Date), `endDate`(Date), `initiatedBy`(Pointer->_User)
|
|
36
|
-
- 索引:`resource,startDate,endDate`、`itemId,workspaceKey`
|
|
37
|
-
|
|
38
|
-
## 推荐实现方式
|
|
39
|
-
|
|
40
|
-
1. 前端使用 `invoke` 调 resolver method,不在前端堆叠 Parse 细节。
|
|
41
|
-
2. resolver 中统一封装 CRUD(如 `getParseQuery/getParseObject/saveAllObject/deleteParseObject`)。
|
|
42
|
-
3. ItemPanel 默认以 `itemId/workspaceKey` 作为数据作用域。
|
|
43
|
-
4. 先确定一套数据访问模型并长期保持一致。
|
|
44
|
-
|
|
45
|
-
## 提交前评审清单
|
|
46
|
-
|
|
47
|
-
1. 字段命名、类型、必填、默认值是否匹配业务语义。
|
|
48
|
-
2. 关联字段是否使用正确的 `Pointer/Relation` 与 `targetClass`。
|
|
49
|
-
3. 高频查询条件是否有对应索引。
|
|
50
|
-
4. 数据隔离维度是否明确(如 `itemId/workspaceKey/tenant`)。
|
|
51
|
-
5. 前后端读写路径是否统一且可追踪。
|
|
52
|
-
6. 演进方案是否可通过“新增字段 + 迁移”落地。
|
|
53
|
-
|
|
54
|
-
## 常见问题
|
|
55
|
-
|
|
56
|
-
1. 只声明 `modules.function`,但页面模块未配置 `resolver`,导致 `invoke` 无法命中。
|
|
57
|
-
2. 未声明 `manifest.tables` 就访问插件表,导致运行期风险。
|
|
58
|
-
3. 发布时 `deploy` 未传 zip 文件名导致命令失败。
|
|
59
|
-
4. 部署返回 `503` 或 `socket hang up` 时,优先排查环境状态,不要先改表结构。
|
|
60
|
-
5. 环境不可用时至少完成两项本地验证:构建通过 + lint 通过,确保 manifest 与代码链路可构建。
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
# 参考资料
|
|
2
|
-
|
|
3
|
-
- `https://team.gitee.cn/llms-full-developer.txt`:插件开发手册
|
|
4
|
-
- `https://team.gitee.cn/llms-full-team-api.txt`:接口文档
|
|
5
|
-
- `https://team.gitee.cn/llms-full-table-schema.txt`:数据表 schema 文档
|
|
6
|
-
|
|
7
|
-
## 使用策略
|
|
8
|
-
|
|
9
|
-
1. 接口路径、参数、返回结构,仅以 `https://team.gitee.cn/llms-full-team-api.txt` 为准。
|
|
10
|
-
2. manifest、模块扩展点、开发规范,仅以 `https://team.gitee.cn/llms-full-developer.txt` 为准。
|
|
11
|
-
3. 表结构、字段定义、索引建议,仅以 `https://team.gitee.cn/llms-full-table-schema.txt` 为准。
|
|
12
|
-
4. 先在对应子文档中搜索原文,再落地实现;不要混用二手资料。
|
package/templates/skill/SKILL.md
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
# {{appName}} 插件开发 Skill
|
|
2
|
-
|
|
3
|
-
这个 Skill 面向插件研发与 AI 代理协作,目标是让 Agent 在当前插件仓库内稳定完成:
|
|
4
|
-
|
|
5
|
-
- 功能实现(前端模块、trigger、webtrigger)
|
|
6
|
-
- API 适配(`/forge/` 到运行时路径的转换)
|
|
7
|
-
- 打包、发布与订阅(CLI 一体化)
|
|
8
|
-
|
|
9
|
-
## 文档导航
|
|
10
|
-
|
|
11
|
-
- `skill/guides/01-overview.md`:开发总览、交付标准与执行节奏
|
|
12
|
-
- `skill/guides/02-extension-points.md`:manifest 扩展点与声明约束
|
|
13
|
-
- `skill/guides/03-api-routing-rules.md`:`/forge/` 路径转换与 `isInOne` 规则
|
|
14
|
-
- `skill/guides/04-deploy-and-subscribe.md`:CLI 登录、构建、部署、订阅
|
|
15
|
-
- `skill/guides/05-agent-prompt-template.md`:给 Agent 的标准任务模板
|
|
16
|
-
- `skill/guides/06-data-access-and-tables.md`:数据表设计与 Parse 访问规范
|
|
17
|
-
- `skill/references/README.md`:平台文档来源与使用策略
|
|
18
|
-
|
|
19
|
-
## 推荐执行顺序
|
|
20
|
-
|
|
21
|
-
1. 阅读 `01-overview` 和 `02-extension-points`,明确能力边界和 manifest 变更。
|
|
22
|
-
2. 查接口前先看 `references/README.md`,仅使用指定来源检索原始路径。
|
|
23
|
-
3. 按 `03-api-routing-rules` 完成前后端路径转换与 API 封装。
|
|
24
|
-
4. 涉及数据表时,执行 `06-data-access-and-tables` 中的建模与评审清单。
|
|
25
|
-
5. 需要发布验证时,执行 `04-deploy-and-subscribe`。
|
|
26
|
-
6. 让 Agent 批量产出代码时,使用 `05-agent-prompt-template`。
|
|
27
|
-
|
|
28
|
-
## 文档来源约束
|
|
29
|
-
|
|
30
|
-
- 接口事实来源:`https://team.gitee.cn/llms-full-team-api.txt`
|
|
31
|
-
- 插件开发规范来源:`https://team.gitee.cn/llms-full-developer.txt`
|
|
32
|
-
- 数据表结构来源:`https://team.gitee.cn/llms-full-table-schema.txt`
|
|
33
|
-
- 禁止用其它页面(包括历史笔记、二手转述)替代以上三类事实来源
|
|
34
|
-
|
|
35
|
-
## 项目标识
|
|
36
|
-
|
|
37
|
-
- 插件名称:`{{appName}}`
|
|
38
|
-
- 插件 Key:`{{appKey}}`
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
# 插件开发总览
|
|
2
|
-
|
|
3
|
-
## 目标
|
|
4
|
-
|
|
5
|
-
在保持可运行最小骨架的前提下,快速迭代插件能力,并保证以下交付完整:
|
|
6
|
-
|
|
7
|
-
- `manifest.yml` 能力声明完整且可校验
|
|
8
|
-
- 前端入口与路由可运行
|
|
9
|
-
- 后端 trigger/webtrigger 代码可调用
|
|
10
|
-
- 打包产物可通过 CLI 上传发布并订阅到目标租户
|
|
11
|
-
|
|
12
|
-
## 默认技术约束
|
|
13
|
-
|
|
14
|
-
- 前端:`React + React Router + Ant Design`
|
|
15
|
-
- 后端:`trigger/*.ts` 与 `functions/*.ts` 按 manifest 声明
|
|
16
|
-
- 构建:`giteeteam-apps` + `rspack`(通过 `rspack.config.js` 扩展)
|
|
17
|
-
- 发布:有代码改动并准备发布时,先递增 `manifest.app.version`
|
|
18
|
-
|
|
19
|
-
## 标准执行流程
|
|
20
|
-
|
|
21
|
-
1. 明确本次模块范围:`itemPanel/workspacePage/adminPage/webtrigger/trigger`。
|
|
22
|
-
2. 核对 manifest 变更:模块声明、resolver/function 映射、资源声明。
|
|
23
|
-
3. 检索接口原始路径:按 `SKILL.md` 的文档来源约束执行(优先 `https://team.gitee.cn/llms-full-team-api.txt`)。
|
|
24
|
-
4. 进行路径转换:前后端分别按 `03-api-routing-rules` 落地。
|
|
25
|
-
5. 若涉及数据表:按 `06-data-access-and-tables` 完成建模与访问封装。
|
|
26
|
-
6. 构建并发布验证:按 `04-deploy-and-subscribe` 执行 CLI 流程。
|
|
27
|
-
|
|
28
|
-
## 接口检索流程(强制)
|
|
29
|
-
|
|
30
|
-
1. 文档来源约束遵循 `SKILL.md` 的“文档来源约束”。
|
|
31
|
-
2. 先定位“请求链接”原文,再提取 `/forge/...` 原始路径。
|
|
32
|
-
3. 前端接口使用 `isInOne` 做环境分支;后端接口不使用该分支。
|
|
33
|
-
4. 提交说明写清楚“原始路径”和“转换后路径”。
|
|
34
|
-
|
|
35
|
-
## 完成标准(DoD)
|
|
36
|
-
|
|
37
|
-
1. manifest、前端、后端三者声明一致。
|
|
38
|
-
2. 新增接口已集中到 API 层,不在页面里散落拼接路径。
|
|
39
|
-
3. 新增数据结构有字段语义与演进说明(如涉及表)。
|
|
40
|
-
4. 发布命令可执行,订阅命令可执行。
|
|
41
|
-
|
|
42
|
-
## 目录建议
|
|
43
|
-
|
|
44
|
-
- `manifest.yml`:插件能力声明
|
|
45
|
-
- `src/`:前端代码
|
|
46
|
-
- `functions/` 或 `trigger/`:后端逻辑
|
|
47
|
-
- `skill/`:AI 协作规范、平台参考、发布流程
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
# 插件扩展点速查
|
|
2
|
-
|
|
3
|
-
以下扩展点与字段定义建议以平台文档为准;实现前先对照文档确认字段合法性。
|
|
4
|
-
|
|
5
|
-
## 常见模块
|
|
6
|
-
|
|
7
|
-
- `adminPage`
|
|
8
|
-
- `workspacePage`
|
|
9
|
-
- `itemPanel`
|
|
10
|
-
- `webtrigger`
|
|
11
|
-
- `trigger`
|
|
12
|
-
- `function`
|
|
13
|
-
- `resources`
|
|
14
|
-
- `tables`
|
|
15
|
-
|
|
16
|
-
## 实现要求
|
|
17
|
-
|
|
18
|
-
1. 每新增一个可执行逻辑,都要在 `modules.function` 声明 handler。
|
|
19
|
-
2. 每新增一个前端页面模块,都要在 `resources` 中声明静态资源路径。
|
|
20
|
-
3. 需要前端调用后端函数时,在页面模块(如 `itemPanel`)配置 `resolver.function`,并在函数中使用 `Resolver` 定义 method。
|
|
21
|
-
4. 需要插件表时,先在 `manifest.tables` 定义结构,再在函数中通过 `getParseQuery/getParseObject` 访问。
|
|
22
|
-
5. `webtrigger` 与 `trigger` 的 `key/function` 必须一一对应。
|
|
23
|
-
6. 修改模块定义后,优先执行构建并检查 manifest 校验报错。
|
|
24
|
-
7. 对已发布应用,禁止直接修改已有字段类型;使用新增字段 + 数据迁移方式演进。
|
|
25
|
-
|
|
26
|
-
## invoke 关键约束(Micro)
|
|
27
|
-
|
|
28
|
-
1. `Micro` 前端使用 `invoke` 时,SDK 会读取 `module.resolver.function` 来定位后端函数 key。
|
|
29
|
-
2. 因此页面模块必须写成:
|
|
30
|
-
|
|
31
|
-
```yaml
|
|
32
|
-
resolver:
|
|
33
|
-
function: <resolver-function-key>
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
3. 如果只写顶层 `function: xxx`(未配置 `resolver`),常见报错:
|
|
37
|
-
`Cannot read properties of undefined (reading 'function')`。
|
|
38
|
-
|
|
39
|
-
## 常见自检
|
|
40
|
-
|
|
41
|
-
1. 页面模块是否有对应资源声明。
|
|
42
|
-
2. resolver 的 function key 与 `modules.function` 是否一致。
|
|
43
|
-
3. trigger/webtrigger 的入参与返回结构是否和调用方约定一致。
|
|
44
|
-
4. 表结构是否包含查询高频字段的索引。
|
|
45
|
-
5. 若 `invoke` 报 `reading 'function'`,优先检查当前页面模块是否配置了 `resolver.function`。
|
|
46
|
-
|
|
47
|
-
## item 接入点弹层挂载(共性问题)
|
|
48
|
-
|
|
49
|
-
`itemPanel` 场景中,`Select/DatePicker/Cascader` 等弹层组件经常被父容器裁剪,表现为下拉显示不全或被遮挡。
|
|
50
|
-
这是弹层挂载容器与 item 详情容器层级不一致导致的共性问题。
|
|
51
|
-
|
|
52
|
-
建议在弹窗或表单内显式指定 `getPopupContainer`,挂载到当前弹窗内的独立 DOM 节点,而不是依赖默认挂载:
|
|
53
|
-
|
|
54
|
-
```tsx
|
|
55
|
-
const popupRef = useRef<HTMLDivElement | null>(null);
|
|
56
|
-
const getPopupContainer = useCallback(() => popupRef.current || document.body, []);
|
|
57
|
-
|
|
58
|
-
<Modal
|
|
59
|
-
open={open}
|
|
60
|
-
modalRender={node => (
|
|
61
|
-
<div>
|
|
62
|
-
{node}
|
|
63
|
-
<div ref={popupRef} />
|
|
64
|
-
</div>
|
|
65
|
-
)}
|
|
66
|
-
>
|
|
67
|
-
<Select getPopupContainer={getPopupContainer} />
|
|
68
|
-
</Modal>
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
自检顺序建议:
|
|
72
|
-
|
|
73
|
-
1. 先确认数据是否已正确拉取(排除“无数据”误判为“下拉显示不全”)。
|
|
74
|
-
2. 再确认弹层是否挂在当前 `itemPanel` 可视容器内。
|
|
75
|
-
3. 若仍异常,检查父容器是否存在 `overflow: hidden`、`transform`、`z-index` 冲突。
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
# /forge/ 接口转换规则
|
|
2
|
-
|
|
3
|
-
接口来源遵循 `SKILL.md` 的“文档来源约束”。`/forge/` 原始路径应从 `https://team.gitee.cn/llms-full-team-api.txt` 检索,路径按调用场景转换。
|
|
4
|
-
|
|
5
|
-
## 规则总览
|
|
6
|
-
|
|
7
|
-
1. 文档原始路径:`/forge/xxx`
|
|
8
|
-
2. 前端默认路径:`/api/team/parse/xxx`
|
|
9
|
-
3. 后端默认路径:`/parse/xxx`
|
|
10
|
-
4. 前端允许按 `isInOne` 做环境分支;后端不做该分支。
|
|
11
|
-
|
|
12
|
-
## 前端调用(浏览器)
|
|
13
|
-
|
|
14
|
-
当文档给出:
|
|
15
|
-
|
|
16
|
-
- `/forge/xxx`
|
|
17
|
-
|
|
18
|
-
前端实际请求改为:
|
|
19
|
-
|
|
20
|
-
- `/api/team/parse/xxx`
|
|
21
|
-
|
|
22
|
-
示例:
|
|
23
|
-
|
|
24
|
-
- 文档 `/forge/api/search` => 前端 `/api/team/parse/api/search`
|
|
25
|
-
|
|
26
|
-
环境分支(仅前端,按 `isInOne`):
|
|
27
|
-
|
|
28
|
-
- 集成环境(`isInOne === true`)优先:`/parse/xxx`
|
|
29
|
-
- 开发环境(`isInOne === false`)优先:`/api/team/parse/xxx`
|
|
30
|
-
- 为兼容网关差异,可保留另一条路径作为回退
|
|
31
|
-
|
|
32
|
-
## Trigger 调用(服务端)
|
|
33
|
-
|
|
34
|
-
当在 trigger/webtrigger 中调用同类接口时:
|
|
35
|
-
|
|
36
|
-
- 使用 `/parse/xxx`,或走项目内 parse client 对应能力
|
|
37
|
-
- 后端不使用 `isInOne` 做路径分支
|
|
38
|
-
|
|
39
|
-
示例:
|
|
40
|
-
|
|
41
|
-
- 文档 `/forge/api/search` => 后端 `/parse/api/search`
|
|
42
|
-
|
|
43
|
-
## `isInOne` 判断示例
|
|
44
|
-
|
|
45
|
-
```ts
|
|
46
|
-
export const isInOne = (gateway: string): boolean => {
|
|
47
|
-
if (!gateway) return false;
|
|
48
|
-
try {
|
|
49
|
-
const gatewayURL = new URL(gateway);
|
|
50
|
-
return gatewayURL.pathname.startsWith('/api');
|
|
51
|
-
} catch {
|
|
52
|
-
return false;
|
|
53
|
-
}
|
|
54
|
-
};
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
示例输入:
|
|
58
|
-
|
|
59
|
-
- `https://osc.gitee.work/api/team` => `true`(集成环境)
|
|
60
|
-
- `https://osc.gitee.work` => `false`(开发环境)
|
|
61
|
-
- `''` 或非法 URL => `false`
|
|
62
|
-
|
|
63
|
-
## 统一约束
|
|
64
|
-
|
|
65
|
-
1. 不要在前端直接请求裸 `/forge/*`。
|
|
66
|
-
2. 抽一层 API client(例如 `src/services/api.ts`)统一做路径转换。
|
|
67
|
-
3. 在提交说明中标注“原始路径 -> 实际路径”的映射关系。
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
# 插件发布与订阅(CLI)
|
|
2
|
-
|
|
3
|
-
以下流程通过 `giteeteam-apps` 完成登录、构建、部署和租户订阅。
|
|
4
|
-
|
|
5
|
-
## 0) 登录(必须)
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
giteeteam-apps login --domain http://<host> -u <username> -p <password>
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
## 1) 构建产物
|
|
12
|
-
|
|
13
|
-
先更新版本号(必须):
|
|
14
|
-
|
|
15
|
-
```yaml
|
|
16
|
-
# manifest.yml
|
|
17
|
-
app:
|
|
18
|
-
version: '<new-version>'
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
```bash
|
|
22
|
-
giteeteam-apps build --prod
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
## 2) 部署到目标环境
|
|
26
|
-
|
|
27
|
-
```bash
|
|
28
|
-
giteeteam-apps deploy <zip-file> -e production
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
常见环境值:`development`、`staging`、`production`(默认 `development`)。
|
|
32
|
-
|
|
33
|
-
在当前仓库建议使用显式命令(避免 `package.json` 中未传文件名导致失败):
|
|
34
|
-
|
|
35
|
-
```bash
|
|
36
|
-
pnpm exec giteeteam-apps deploy <appKey>_<versionKey>.zip -e development
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
如果发布/订阅请求失败,可按以下顺序重试:
|
|
40
|
-
|
|
41
|
-
1. 先正常执行命令。
|
|
42
|
-
2. 失败后使用提权执行(允许无沙箱网络)。
|
|
43
|
-
3. 提权后仍失败,再补全 `NO_PROXY/no_proxy` 重试。
|
|
44
|
-
|
|
45
|
-
`NO_PROXY/no_proxy` 示例:
|
|
46
|
-
|
|
47
|
-
```bash
|
|
48
|
-
NO_PROXY=localhost,127.0.0.1,<domain>,<target-host> \
|
|
49
|
-
no_proxy=localhost,127.0.0.1,<domain>,<target-host> \
|
|
50
|
-
pnpm exec giteeteam-apps deploy <zip-file> -e <environment>
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
## 3) 订阅到租户
|
|
54
|
-
|
|
55
|
-
方式 A:独立订阅
|
|
56
|
-
|
|
57
|
-
```bash
|
|
58
|
-
giteeteam-apps subscribe <appKey> <versionKey> -t osc -e production
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
方式 B:部署后自动订阅
|
|
62
|
-
|
|
63
|
-
```bash
|
|
64
|
-
giteeteam-apps deploy <zip-file> -e production --subscribe osc --app-key <appKey> --version-key <versionKey>
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
## 参数说明
|
|
68
|
-
|
|
69
|
-
1. `<zip-file>` 使用 `build` 产出的压缩包路径。
|
|
70
|
-
2. `-t/--tenants` 与 `--subscribe` 支持逗号分隔,例如 `osc,test`。
|
|
71
|
-
3. 若 zip 命名为 `<appKey>_<versionKey>.zip`,`deploy --subscribe` 可省略 `--app-key/--version-key`。
|
|
72
|
-
4. 发布订阅流程不需要再手动调用 `curl`。
|
|
73
|
-
5. 有代码改动并准备发布时,必须递增 `manifest.yml` 中的 `app.version`。
|
|
74
|
-
6. 默认重试顺序:正常请求 -> 提权请求 -> 提权 + `NO_PROXY/no_proxy`。
|
|
75
|
-
|
|
76
|
-
## 故障排查
|
|
77
|
-
|
|
78
|
-
1. `503` 或 `socket hang up`:优先检查环境服务状态,再重试 deploy。
|
|
79
|
-
2. `Please login in first!`:先执行 `giteeteam-apps login`。
|
|
80
|
-
3. 订阅失败且提示缺少 app/version:补充 `--app-key` 与 `--version-key`。
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
# 给 Agent 的任务模板
|
|
2
|
-
|
|
3
|
-
```text
|
|
4
|
-
请在当前仓库实现/修改插件能力。
|
|
5
|
-
插件名称:{{appName}}
|
|
6
|
-
插件 key:{{appKey}}
|
|
7
|
-
目标模块:<itemPanel/workspacePage/adminPage/webtrigger/trigger>
|
|
8
|
-
核心功能:
|
|
9
|
-
1. <功能 1>
|
|
10
|
-
2. <功能 2>
|
|
11
|
-
3. <功能 3>
|
|
12
|
-
|
|
13
|
-
约束:
|
|
14
|
-
- 接口检索仅使用 `https://team.gitee.cn/llms-full-team-api.txt`
|
|
15
|
-
- 插件开发规范仅使用 `https://team.gitee.cn/llms-full-developer.txt`
|
|
16
|
-
- 数据表结构仅使用 `https://team.gitee.cn/llms-full-table-schema.txt`
|
|
17
|
-
- 前端接口使用 `isInOne` 环境判断并执行 `/forge/` 转换;后端不使用该判断
|
|
18
|
-
- 路径映射必须满足:前端 `/forge/xxx` => `/api/team/parse/xxx`;后端 `/forge/xxx` => `/parse/xxx`
|
|
19
|
-
- 涉及数据表访问时:`getParseQuery(false, 'Item')` 查核心表,`getParseQuery(true, 'xxx')` 查插件表
|
|
20
|
-
- 默认采用 `manifest.tables + getParseQuery/getParseObject`;`storage.entities` 仅用于 Storage API
|
|
21
|
-
- 已发布应用禁止直接修改已有字段类型;只能新增字段并给出迁移方案
|
|
22
|
-
- `manifest.yml` 定义完整且可校验
|
|
23
|
-
- Micro 页面若使用 `invoke`,对应模块必须配置 `resolver.function`(不能只写顶层 `function`)
|
|
24
|
-
- 需要发布时必须递增 `manifest.app.version`
|
|
25
|
-
- trigger 与 webtrigger 的 function 声明完整
|
|
26
|
-
- 发布/订阅失败时按顺序处理:先正常请求,再提权,最后提权 + `NO_PROXY/no_proxy`
|
|
27
|
-
|
|
28
|
-
输出要求:
|
|
29
|
-
- 列出修改文件
|
|
30
|
-
- 列出“原始接口路径 -> 实际调用路径”映射
|
|
31
|
-
- 列出验证步骤与结果
|
|
32
|
-
```
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
# 数据访问与表设计
|
|
2
|
-
|
|
3
|
-
## 目标
|
|
4
|
-
|
|
5
|
-
在插件中实现“核心表 + 插件表”的统一访问方式,并支持资源负载等场景。
|
|
6
|
-
|
|
7
|
-
## 文档来源
|
|
8
|
-
|
|
9
|
-
1. 表结构与字段定义:`https://team.gitee.cn/llms-full-table-schema.txt`。
|
|
10
|
-
2. 插件开发规范与能力边界:`https://team.gitee.cn/llms-full-developer.txt`。
|
|
11
|
-
3. 接口路径与调用约定:`https://team.gitee.cn/llms-full-team-api.txt`。
|
|
12
|
-
|
|
13
|
-
## 核心规则
|
|
14
|
-
|
|
15
|
-
1. `getParseQuery(false, 'Item')` / `getParseObject(false, 'Item')`:访问 Team 核心表。
|
|
16
|
-
2. `getParseQuery(true, 'Resource')` / `getParseObject(true, 'Resource')`:访问插件自有表。
|
|
17
|
-
3. 默认采用 `manifest.tables + getParseQuery/getParseObject`。
|
|
18
|
-
4. `manifest.storage.entities` 仅在使用 Storage API(`storage.entity(...)`)时启用,不与 `getParseQuery` 混用。
|
|
19
|
-
|
|
20
|
-
## 建表前置设计(高优先级)
|
|
21
|
-
|
|
22
|
-
1. 建表前先定义字段语义、关联关系、索引、扩展方向。
|
|
23
|
-
2. 应用发布后,不直接修改已有字段 `type`。
|
|
24
|
-
3. 字段演进采用“新增字段 + 数据迁移 + 切换读写”。
|
|
25
|
-
4. 变更说明必须包含迁移方案、回滚方案和影响范围。
|
|
26
|
-
5. Team 应用中心没有“洗表/重建历史数据”的能力,改类型会带来兼容风险和历史数据不可用风险。
|
|
27
|
-
|
|
28
|
-
## 表定义建议(资源负载场景)
|
|
29
|
-
|
|
30
|
-
- `Resource`
|
|
31
|
-
- 字段:`name`(String), `type`(String: human|hardware), `workspaceKey`(String), `description`(String)
|
|
32
|
-
- 索引:`type`、`workspaceKey`
|
|
33
|
-
|
|
34
|
-
- `ResourceBooking`
|
|
35
|
-
- 字段:`resource`(Pointer->Resource), `itemId`(String), `workspaceKey`(String), `startDate`(Date), `endDate`(Date), `initiatedBy`(Pointer->_User)
|
|
36
|
-
- 索引:`resource,startDate,endDate`、`itemId,workspaceKey`
|
|
37
|
-
|
|
38
|
-
## 推荐实现方式
|
|
39
|
-
|
|
40
|
-
1. 前端使用 `invoke` 调 resolver method,不在前端堆叠 Parse 细节。
|
|
41
|
-
2. resolver 中统一封装 CRUD(如 `getParseQuery/getParseObject/saveAllObject/deleteParseObject`)。
|
|
42
|
-
3. ItemPanel 默认以 `itemId/workspaceKey` 作为数据作用域。
|
|
43
|
-
4. 先确定一套数据访问模型并长期保持一致。
|
|
44
|
-
|
|
45
|
-
## 提交前评审清单
|
|
46
|
-
|
|
47
|
-
1. 字段命名、类型、必填、默认值是否匹配业务语义。
|
|
48
|
-
2. 关联字段是否使用正确的 `Pointer/Relation` 与 `targetClass`。
|
|
49
|
-
3. 高频查询条件是否有对应索引。
|
|
50
|
-
4. 数据隔离维度是否明确(如 `itemId/workspaceKey/tenant`)。
|
|
51
|
-
5. 前后端读写路径是否统一且可追踪。
|
|
52
|
-
6. 演进方案是否可通过“新增字段 + 迁移”落地。
|
|
53
|
-
|
|
54
|
-
## 常见问题
|
|
55
|
-
|
|
56
|
-
1. 只声明 `modules.function`,但页面模块未配置 `resolver`,导致 `invoke` 无法命中。
|
|
57
|
-
2. 未声明 `manifest.tables` 就访问插件表,导致运行期风险。
|
|
58
|
-
3. 发布时 `deploy` 未传 zip 文件名导致命令失败。
|
|
59
|
-
4. 部署返回 `503` 或 `socket hang up` 时,优先排查环境状态,不要先改表结构。
|
|
60
|
-
5. 环境不可用时至少完成两项本地验证:构建通过 + lint 通过,确保 manifest 与代码链路可构建。
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
# 参考资料
|
|
2
|
-
|
|
3
|
-
- `https://team.gitee.cn/llms-full-developer.txt`:插件开发手册
|
|
4
|
-
- `https://team.gitee.cn/llms-full-team-api.txt`:接口文档
|
|
5
|
-
- `https://team.gitee.cn/llms-full-table-schema.txt`:数据表 schema 文档
|
|
6
|
-
|
|
7
|
-
## 使用策略
|
|
8
|
-
|
|
9
|
-
1. 接口路径、参数、返回结构,仅以 `https://team.gitee.cn/llms-full-team-api.txt` 为准。
|
|
10
|
-
2. manifest、模块扩展点、开发规范,仅以 `https://team.gitee.cn/llms-full-developer.txt` 为准。
|
|
11
|
-
3. 表结构、字段定义、索引建议,仅以 `https://team.gitee.cn/llms-full-table-schema.txt` 为准。
|
|
12
|
-
4. 先在对应子文档中搜索原文,再落地实现;不要混用二手资料。
|