nuwax-file-server 1.2.9 → 1.3.0-beta.4

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/dist/cli.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{Command as ee}from"commander";import{createRequire as te}from"module";import f from"path";import U from"os";import c from"fs-extra";import{spawn as E}from"cross-spawn";import V from"tree-kill";import{fileURLToPath as q}from"url";import{execFileSync as R}from"child_process";import M from"http";import{createRequire as G}from"module";var I=f.dirname(q(import.meta.url)),H=G(import.meta.url),n={name:"nuwax-file-server",pidDir:f.join(U.tmpdir(),"nuwax-file-server"),pidFileName:"server.pid",lockFileName:"start.lock",defaultStopTimeout:3e4,defaultStartTimeout:3e4,staleLockTimeout:12e4,checkInterval:500};function $(){return f.join(n.pidDir,n.pidFileName)}function F(){return f.join(n.pidDir,n.lockFileName)}function J(){let e=[f.join(I,"..","server.js"),f.join(I,"server.js")];for(let t of e)if(c.existsSync(t))return t;return e[0]}function W(){let e=["../../package.json","../package.json"];for(let t of e)try{let o=H(t);if(o?.version)return o.version}catch{}return"unknown"}function x(){try{let e=$();if(!c.existsSync(e))return null;let t=c.readFileSync(e,"utf8"),o=JSON.parse(t);return!o||typeof o.pid!="number"?null:o}catch(e){return e.code!=="ENOENT"&&console.error(`Read PID file failed: ${e.message}`),null}}function B(e){try{let t=$();c.ensureDirSync(n.pidDir),c.writeFileSync(t,JSON.stringify(e,null,2)),console.debug(`PID file written: ${t}`)}catch(t){throw console.error(`Write PID file failed: ${t.message}`),t}}function v(){try{let e=$();c.existsSync(e)&&(c.removeSync(e),console.debug(`PID file deleted: ${e}`))}catch(e){console.error(`Delete PID file failed: ${e.message}`)}}function g(e){try{return process.kill(e,0),!0}catch(t){return t.code!=="ESRCH"}}function K(e){try{return k()?R("powershell",["-NoProfile","-Command",`(Get-CimInstance Win32_Process -Filter "ProcessId = ${e}").CommandLine`],{encoding:"utf8",stdio:["ignore","pipe","ignore"]}).trim():R("ps",["-p",String(e),"-o","command="],{encoding:"utf8",stdio:["ignore","pipe","ignore"]}).trim()}catch{return""}}function X(e){if(!g(e))return!1;let t=K(e);if(!t)return!1;let o=t.toLowerCase(),s=f.basename(f.join(I,"..","server.js")).toLowerCase(),i=n.name.toLowerCase();return o.includes(s)||o.includes(i)}function z(){c.ensureDirSync(n.pidDir);let e=F(),t=`${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2,10)}`,o=JSON.stringify({pid:process.pid,token:t,createdAt:new Date().toISOString()},null,2);try{let s=c.openSync(e,"wx");return c.writeSync(s,o),{fd:s,token:t}}catch(s){if(s.code!=="EEXIST")throw s;try{let i=c.readFileSync(e,"utf8"),r=JSON.parse(i),a=Number(r?.pid),u=r?.createdAt?new Date(r.createdAt).getTime():0,l=Date.now()-u,C=Number.isFinite(a)&&a>0&&g(a),p=!u||Number.isNaN(l)||l>n.staleLockTimeout;if(!C||p){c.removeSync(e);let S=c.openSync(e,"wx");return c.writeSync(S,o),console.warn("Detected stale start lock, auto cleaned"),{fd:S,token:t}}}catch{c.removeSync(e);let r=c.openSync(e,"wx");return c.writeSync(r,o),console.warn("Detected invalid start lock, auto cleaned"),{fd:r,token:t}}throw s}}function Q(e){let t=e?.fd,o=e?.token;try{t!=null&&c.closeSync(t)}catch{}try{let s=F();if(c.existsSync(s)){let i=!1;if(!o)i=!0;else try{let r=c.readFileSync(s,"utf8");i=JSON.parse(r)?.token===o}catch{i=!0}i&&c.removeSync(s)}}catch{}}function k(){return process.platform==="win32"}async function Y(e=n.defaultStopTimeout){let t=x();return t?X(t.pid)?(console.log(`Existing service process detected (PID: ${t.pid}), stopping before start...`),await h(t.pid,!1)&&await D(t.pid,e)?(v(),{success:!0,message:`Existing process ${t.pid} stopped gracefully`}):(console.warn(`Graceful stop timeout or failed for PID ${t.pid}, force stop...`),await h(t.pid,!0)?await D(t.pid,e)?(v(),{success:!0,message:`Existing process ${t.pid} stopped forcibly`}):{success:!1,message:`Existing process ${t.pid} did not exit after force stop`}:{success:!1,message:`Failed to stop existing process ${t.pid}`})):(console.log(`Found stale PID file (PID: ${t.pid}), clean it...`),v(),{success:!0,message:"Stale PID file cleaned"}):{success:!0,message:"No existing service process"}}async function h(e,t=!1){return new Promise(o=>{if(!g(e)){console.debug(`Process ${e} does not exist, already stopped`),o(!0);return}let s=t?"SIGKILL":"SIGTERM",i=k()?"taskkill":"tree-kill";if(console.debug(`Use ${i} to stop process ${e} (signal: ${s})`),k()){let r=t?["/F","/PID",String(e)]:["/PID",String(e)],a=E("taskkill",r,{stdio:["ignore","pipe","pipe"],windowsHide:!0}),u="";a.stdout.on("data",l=>{u+=l.toString()}),a.stderr.on("data",l=>{u+=l.toString()}),a.on("error",l=>{console.error(`Stop process failed: ${l.message}`),o(!1)}),a.on("close",l=>{l===0?(console.debug(`Process ${e} stopped`),o(!0)):(console.warn(`taskkill exit code: ${l}, output: ${u}`),t?o(!1):h(e,!0).then(o))})}else V(e,s,r=>{r?r.code==="ESRCH"?(console.debug(`Process ${e} does not exist`),o(!0)):(console.error(`Stop process failed: ${r.message}`),o(!1)):(console.debug(`Process ${e} stopped`),o(!0))})})}async function D(e,t=n.defaultStopTimeout){let o=Date.now();for(;g(e);){if(Date.now()-o>t)return console.warn(`Wait for process ${e} to stop timeout (${t}ms)`),!1;await new Promise(i=>setTimeout(i,n.checkInterval))}let s=Date.now()-o;return console.debug(`Process ${e} stopped after ${s}ms`),!0}async function Z(e,t=n.defaultStartTimeout){let o=Number(e);if(!Number.isFinite(o)||o<=0)return!1;let s=Date.now();for(;Date.now()-s<=t;){if(await new Promise(r=>{let a=M.get({host:"127.0.0.1",port:o,path:"/health",timeout:Math.min(2e3,n.checkInterval*4)},u=>{r(u.statusCode>=200&&u.statusCode<300),u.resume()});a.on("timeout",()=>{a.destroy(),r(!1)}),a.on("error",()=>r(!1))}))return!0;await new Promise(r=>setTimeout(r,n.checkInterval))}return!1}async function N(e={}){let{env:t,port:o,config:s}=e,i=null;console.log(`Start service ${n.name}...`);try{i=z()}catch(r){return r.code==="EEXIST"?{success:!1,pid:null,message:"Another start operation is in progress, please retry later"}:{success:!1,pid:null,message:`Acquire start lock failed: ${r.message}`}}try{let r=Number(e.timeout)||n.defaultStopTimeout,a=await Y(r);if(!a.success)return{success:!1,pid:null,message:`Service start blocked: ${a.message}`};let u={...process.env};t&&(u.NODE_ENV=t,console.log(`Environment: ${t}`)),o&&(u.PORT=o,console.log(`Port: ${o}`)),s&&(u.CONFIG_FILE=s,console.log(`Configuration file: ${s}`));let l=J(),p=E("node",[l,...[]],{env:u,stdio:["pipe","pipe","pipe"],detached:!0,cwd:process.cwd()});if(p.stdout.on("data",m=>{process.stdout.write(m)}),p.stderr.on("data",m=>{process.stderr.write(m)}),p.on("error",m=>{console.error(`Start service failed: ${m.message}`)}),await new Promise(m=>setTimeout(m,2e3)),!g(p.pid))return{success:!1,pid:null,message:"Service start failed"};let S={pid:p.pid,startedAt:new Date().toISOString(),env:t||process.env.NODE_ENV||"production",port:o||process.env.PORT||"60000",version:W(),platform:process.platform};B(S);let P=Number(e.startTimeout)||n.defaultStartTimeout;return await Z(S.port,P)?(p.unref(),console.log(`Service started (PID: ${p.pid})`),console.log(`Service address: http://localhost:${S.port}`),{success:!0,pid:p.pid,message:"Service started successfully"}):(console.error(`Service health check timeout (${P}ms), stop failed instance...`),await h(p.pid,!0),v(),{success:!1,pid:null,message:`Service health check timeout (${P}ms)`})}finally{Q(i)}}async function T(e={}){let{force:t=!1,timeout:o=n.defaultStopTimeout}=e;console.log(`Stop service ${n.name}...`);let s=x();if(!s)return{success:!1,message:"Service not found"};if(!g(s.pid))return console.log("Service process has stopped, clean PID file..."),v(),{success:!0,message:"Service has stopped (process has exited)"};if(!await h(s.pid,t))return{success:!1,message:"Stop service failed"};let r=await D(s.pid,o);return v(),r?{success:!0,message:"Service has stopped"}:{success:!1,message:"Service stop timeout"}}async function O(e={}){console.log(`Restart service ${n.name}...`);let t=await T(e);!t.success&&t.message!=="Service not found"&&console.warn(`Stop service failed: ${t.message}`),await new Promise(s=>setTimeout(s,2e3));let o=await N(e);return o.success?{success:!0,pid:o.pid,message:"Service has restarted"}:{success:!1,pid:null,message:`Restart failed: ${o.message}`}}function _(){let e=x();return e?g(e.pid)?{running:!0,pidInfo:e,message:"Service running"}:{running:!1,pidInfo:e,message:"Service process does not exist"}:{running:!1,pidInfo:null,message:"Service not running"}}function L(e){try{let t=new Date(e),o=new Date;if(isNaN(t.getTime()))return"unknown";let s=Math.floor((o-t)/1e3),i=Math.floor(s/3600),r=Math.floor(s%3600/60),a=s%60;return i>0?`${i} hours ${r} minutes ${a} seconds`:r>0?`${r} minutes ${a} seconds`:`${a} seconds`}catch{return"unknown"}}var Pe=te(import.meta.url),A="1.2.9",d=new ee,w={reset:"\x1B[0m",red:"\x1B[31m",green:"\x1B[32m",yellow:"\x1B[33m",blue:"\x1B[34m",cyan:"\x1B[36m"};function j(e){console.error(`${w.red}ERROR: ${e}${w.reset}`)}function b(e){console.log(`${w.green}${e}${w.reset}`)}function oe(e){console.log(`${w.blue}${e}${w.reset}`)}function y(e){return async(...t)=>{try{let o=await e(...t);o&&o.success===!1?(j(o.message||"Command failed"),process.exitCode=1):process.exitCode=0}catch(o){j(o?.message||"Command failed with unexpected error"),process.exitCode=1}}}async function se(e){let t=await N({env:e.env,port:e.port,config:e.config,timeout:Number(e.timeout),startTimeout:Number(e.startTimeout)});return t.success&&b(`Service started (PID: ${t.pid})`),t}async function re(e){let t=await T({force:e.force,timeout:Number(e.timeout)});return t.success&&b(t.message||"Service stopped"),t}async function ne(e){let t=await O({env:e.env,port:e.port,config:e.config,timeout:Number(e.timeout),startTimeout:Number(e.startTimeout)});return t.success&&b(t.message||"Service restarted"),t}function ie(){let e=_();return oe(`${n.name} service status:`),console.log(""),console.log(` Service name: ${n.name}`),console.log(` Running status: ${e.running?"Running":"Stopped"}`),console.log(` Message: ${e.message}`),console.log(` PID file: ${$()}`),e.pidInfo&&(console.log(` Process ID: ${e.pidInfo.pid}`),console.log(` Environment: ${e.pidInfo.env||"Unknown"}`),console.log(` Port: ${e.pidInfo.port||"Unknown"}`),console.log(` Version: ${e.pidInfo.version||A}`),console.log(` Platform: ${e.pidInfo.platform||process.platform}`),console.log(` Started at: ${e.pidInfo.startedAt||"Unknown"}`),console.log(` Uptime: ${L(e.pidInfo.startedAt)}`)),console.log(""),{success:e.running,message:e.message}}function ce(){d.name("nuwax-file-server").description("Cross-platform file service deployment tool, supporting start/stop/restart/status").version(A,"-v, --version","Display version number").helpOption("-h, --help","Display help information"),d.command("start").allowUnknownOption().description("Start service").option("--env <environment>","\u73AF\u5883: development|production|test","production").option("--port <port>","Service port").option("--config <path>","Custom configuration file path").option("--timeout <ms>","\u505C\u6B62\u65E7\u8FDB\u7A0B\u7B49\u5F85\u8D85\u65F6\uFF08\u6BEB\u79D2\uFF09",`${n.defaultStopTimeout}`).option("--start-timeout <ms>","\u542F\u52A8\u5065\u5EB7\u68C0\u67E5\u8D85\u65F6\uFF08\u6BEB\u79D2\uFF09",`${n.defaultStartTimeout}`).action(y(se)),d.command("stop").description("Stop service").option("--force","Force stop").option("--timeout <ms>","\u505C\u6B62\u670D\u52A1\u7B49\u5F85\u8D85\u65F6\uFF08\u6BEB\u79D2\uFF09",`${n.defaultStopTimeout}`).action(y(re)),d.command("restart").allowUnknownOption().description("Restart service").option("--env <environment>","\u73AF\u5883: development|production|test","production").option("--port <port>","Service port").option("--config <path>","Custom configuration file path").option("--timeout <ms>","\u505C\u6B62\u65E7\u8FDB\u7A0B\u7B49\u5F85\u8D85\u65F6\uFF08\u6BEB\u79D2\uFF09",`${n.defaultStopTimeout}`).option("--start-timeout <ms>","\u542F\u52A8\u5065\u5EB7\u68C0\u67E5\u8D85\u65F6\uFF08\u6BEB\u79D2\uFF09",`${n.defaultStartTimeout}`).action(y(ne)),d.command("status").description("View service status").action(y(ie)),d.command("help").description("Display help information").action(()=>{d.outputHelp()}),d.parse(process.argv),process.argv.slice(2).length||(d.outputHelp(),process.exitCode=0)}ce();
2
+ import{Command as ee}from"commander";import{createRequire as te}from"module";import f from"path";import U from"os";import c from"fs-extra";import{spawn as E}from"cross-spawn";import V from"tree-kill";import{fileURLToPath as q}from"url";import{execFileSync as R}from"child_process";import M from"http";import{createRequire as G}from"module";var I=f.dirname(q(import.meta.url)),H=G(import.meta.url),n={name:"nuwax-file-server",pidDir:f.join(U.tmpdir(),"nuwax-file-server"),pidFileName:"server.pid",lockFileName:"start.lock",defaultStopTimeout:3e4,defaultStartTimeout:3e4,staleLockTimeout:12e4,checkInterval:500};function $(){return f.join(n.pidDir,n.pidFileName)}function F(){return f.join(n.pidDir,n.lockFileName)}function J(){let e=[f.join(I,"..","server.js"),f.join(I,"server.js")];for(let t of e)if(c.existsSync(t))return t;return e[0]}function W(){let e=["../../package.json","../package.json"];for(let t of e)try{let o=H(t);if(o?.version)return o.version}catch{}return"unknown"}function x(){try{let e=$();if(!c.existsSync(e))return null;let t=c.readFileSync(e,"utf8"),o=JSON.parse(t);return!o||typeof o.pid!="number"?null:o}catch(e){return e.code!=="ENOENT"&&console.error(`Read PID file failed: ${e.message}`),null}}function B(e){try{let t=$();c.ensureDirSync(n.pidDir),c.writeFileSync(t,JSON.stringify(e,null,2)),console.debug(`PID file written: ${t}`)}catch(t){throw console.error(`Write PID file failed: ${t.message}`),t}}function v(){try{let e=$();c.existsSync(e)&&(c.removeSync(e),console.debug(`PID file deleted: ${e}`))}catch(e){console.error(`Delete PID file failed: ${e.message}`)}}function g(e){try{return process.kill(e,0),!0}catch(t){return t.code!=="ESRCH"}}function K(e){try{return k()?R("powershell",["-NoProfile","-Command",`(Get-CimInstance Win32_Process -Filter "ProcessId = ${e}").CommandLine`],{encoding:"utf8",stdio:["ignore","pipe","ignore"]}).trim():R("ps",["-p",String(e),"-o","command="],{encoding:"utf8",stdio:["ignore","pipe","ignore"]}).trim()}catch{return""}}function X(e){if(!g(e))return!1;let t=K(e);if(!t)return!1;let o=t.toLowerCase(),s=f.basename(f.join(I,"..","server.js")).toLowerCase(),i=n.name.toLowerCase();return o.includes(s)||o.includes(i)}function z(){c.ensureDirSync(n.pidDir);let e=F(),t=`${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2,10)}`,o=JSON.stringify({pid:process.pid,token:t,createdAt:new Date().toISOString()},null,2);try{let s=c.openSync(e,"wx");return c.writeSync(s,o),{fd:s,token:t}}catch(s){if(s.code!=="EEXIST")throw s;try{let i=c.readFileSync(e,"utf8"),r=JSON.parse(i),a=Number(r?.pid),u=r?.createdAt?new Date(r.createdAt).getTime():0,l=Date.now()-u,C=Number.isFinite(a)&&a>0&&g(a),p=!u||Number.isNaN(l)||l>n.staleLockTimeout;if(!C||p){c.removeSync(e);let S=c.openSync(e,"wx");return c.writeSync(S,o),console.warn("Detected stale start lock, auto cleaned"),{fd:S,token:t}}}catch{c.removeSync(e);let r=c.openSync(e,"wx");return c.writeSync(r,o),console.warn("Detected invalid start lock, auto cleaned"),{fd:r,token:t}}throw s}}function Q(e){let t=e?.fd,o=e?.token;try{t!=null&&c.closeSync(t)}catch{}try{let s=F();if(c.existsSync(s)){let i=!1;if(!o)i=!0;else try{let r=c.readFileSync(s,"utf8");i=JSON.parse(r)?.token===o}catch{i=!0}i&&c.removeSync(s)}}catch{}}function k(){return process.platform==="win32"}async function Y(e=n.defaultStopTimeout){let t=x();return t?X(t.pid)?(console.log(`Existing service process detected (PID: ${t.pid}), stopping before start...`),await h(t.pid,!1)&&await D(t.pid,e)?(v(),{success:!0,message:`Existing process ${t.pid} stopped gracefully`}):(console.warn(`Graceful stop timeout or failed for PID ${t.pid}, force stop...`),await h(t.pid,!0)?await D(t.pid,e)?(v(),{success:!0,message:`Existing process ${t.pid} stopped forcibly`}):{success:!1,message:`Existing process ${t.pid} did not exit after force stop`}:{success:!1,message:`Failed to stop existing process ${t.pid}`})):(console.log(`Found stale PID file (PID: ${t.pid}), clean it...`),v(),{success:!0,message:"Stale PID file cleaned"}):{success:!0,message:"No existing service process"}}async function h(e,t=!1){return new Promise(o=>{if(!g(e)){console.debug(`Process ${e} does not exist, already stopped`),o(!0);return}let s=t?"SIGKILL":"SIGTERM",i=k()?"taskkill":"tree-kill";if(console.debug(`Use ${i} to stop process ${e} (signal: ${s})`),k()){let r=t?["/F","/PID",String(e)]:["/PID",String(e)],a=E("taskkill",r,{stdio:["ignore","pipe","pipe"],windowsHide:!0}),u="";a.stdout.on("data",l=>{u+=l.toString()}),a.stderr.on("data",l=>{u+=l.toString()}),a.on("error",l=>{console.error(`Stop process failed: ${l.message}`),o(!1)}),a.on("close",l=>{l===0?(console.debug(`Process ${e} stopped`),o(!0)):(console.warn(`taskkill exit code: ${l}, output: ${u}`),t?o(!1):h(e,!0).then(o))})}else V(e,s,r=>{r?r.code==="ESRCH"?(console.debug(`Process ${e} does not exist`),o(!0)):(console.error(`Stop process failed: ${r.message}`),o(!1)):(console.debug(`Process ${e} stopped`),o(!0))})})}async function D(e,t=n.defaultStopTimeout){let o=Date.now();for(;g(e);){if(Date.now()-o>t)return console.warn(`Wait for process ${e} to stop timeout (${t}ms)`),!1;await new Promise(i=>setTimeout(i,n.checkInterval))}let s=Date.now()-o;return console.debug(`Process ${e} stopped after ${s}ms`),!0}async function Z(e,t=n.defaultStartTimeout){let o=Number(e);if(!Number.isFinite(o)||o<=0)return!1;let s=Date.now();for(;Date.now()-s<=t;){if(await new Promise(r=>{let a=M.get({host:"127.0.0.1",port:o,path:"/health",timeout:Math.min(2e3,n.checkInterval*4)},u=>{r(u.statusCode>=200&&u.statusCode<300),u.resume()});a.on("timeout",()=>{a.destroy(),r(!1)}),a.on("error",()=>r(!1))}))return!0;await new Promise(r=>setTimeout(r,n.checkInterval))}return!1}async function N(e={}){let{env:t,port:o,config:s}=e,i=null;console.log(`Start service ${n.name}...`);try{i=z()}catch(r){return r.code==="EEXIST"?{success:!1,pid:null,message:"Another start operation is in progress, please retry later"}:{success:!1,pid:null,message:`Acquire start lock failed: ${r.message}`}}try{let r=Number(e.timeout)||n.defaultStopTimeout,a=await Y(r);if(!a.success)return{success:!1,pid:null,message:`Service start blocked: ${a.message}`};let u={...process.env};t&&(u.NODE_ENV=t,console.log(`Environment: ${t}`)),o&&(u.PORT=o,console.log(`Port: ${o}`)),s&&(u.CONFIG_FILE=s,console.log(`Configuration file: ${s}`));let l=J(),p=E("node",[l,...[]],{env:u,stdio:["pipe","pipe","pipe"],detached:!0,cwd:process.cwd()});if(p.stdout.on("data",m=>{process.stdout.write(m)}),p.stderr.on("data",m=>{process.stderr.write(m)}),p.on("error",m=>{console.error(`Start service failed: ${m.message}`)}),await new Promise(m=>setTimeout(m,2e3)),!g(p.pid))return{success:!1,pid:null,message:"Service start failed"};let S={pid:p.pid,startedAt:new Date().toISOString(),env:t||process.env.NODE_ENV||"production",port:o||process.env.PORT||"60000",version:W(),platform:process.platform};B(S);let P=Number(e.startTimeout)||n.defaultStartTimeout;return await Z(S.port,P)?(p.unref(),console.log(`Service started (PID: ${p.pid})`),console.log(`Service address: http://localhost:${S.port}`),{success:!0,pid:p.pid,message:"Service started successfully"}):(console.error(`Service health check timeout (${P}ms), stop failed instance...`),await h(p.pid,!0),v(),{success:!1,pid:null,message:`Service health check timeout (${P}ms)`})}finally{Q(i)}}async function T(e={}){let{force:t=!1,timeout:o=n.defaultStopTimeout}=e;console.log(`Stop service ${n.name}...`);let s=x();if(!s)return{success:!1,message:"Service not found"};if(!g(s.pid))return console.log("Service process has stopped, clean PID file..."),v(),{success:!0,message:"Service has stopped (process has exited)"};if(!await h(s.pid,t))return{success:!1,message:"Stop service failed"};let r=await D(s.pid,o);return v(),r?{success:!0,message:"Service has stopped"}:{success:!1,message:"Service stop timeout"}}async function O(e={}){console.log(`Restart service ${n.name}...`);let t=await T(e);!t.success&&t.message!=="Service not found"&&console.warn(`Stop service failed: ${t.message}`),await new Promise(s=>setTimeout(s,2e3));let o=await N(e);return o.success?{success:!0,pid:o.pid,message:"Service has restarted"}:{success:!1,pid:null,message:`Restart failed: ${o.message}`}}function _(){let e=x();return e?g(e.pid)?{running:!0,pidInfo:e,message:"Service running"}:{running:!1,pidInfo:e,message:"Service process does not exist"}:{running:!1,pidInfo:null,message:"Service not running"}}function L(e){try{let t=new Date(e),o=new Date;if(isNaN(t.getTime()))return"unknown";let s=Math.floor((o-t)/1e3),i=Math.floor(s/3600),r=Math.floor(s%3600/60),a=s%60;return i>0?`${i} hours ${r} minutes ${a} seconds`:r>0?`${r} minutes ${a} seconds`:`${a} seconds`}catch{return"unknown"}}var Pe=te(import.meta.url),A="1.3.0-beta.4",d=new ee,w={reset:"\x1B[0m",red:"\x1B[31m",green:"\x1B[32m",yellow:"\x1B[33m",blue:"\x1B[34m",cyan:"\x1B[36m"};function j(e){console.error(`${w.red}ERROR: ${e}${w.reset}`)}function b(e){console.log(`${w.green}${e}${w.reset}`)}function oe(e){console.log(`${w.blue}${e}${w.reset}`)}function y(e){return async(...t)=>{try{let o=await e(...t);o&&o.success===!1?(j(o.message||"Command failed"),process.exitCode=1):process.exitCode=0}catch(o){j(o?.message||"Command failed with unexpected error"),process.exitCode=1}}}async function se(e){let t=await N({env:e.env,port:e.port,config:e.config,timeout:Number(e.timeout),startTimeout:Number(e.startTimeout)});return t.success&&b(`Service started (PID: ${t.pid})`),t}async function re(e){let t=await T({force:e.force,timeout:Number(e.timeout)});return t.success&&b(t.message||"Service stopped"),t}async function ne(e){let t=await O({env:e.env,port:e.port,config:e.config,timeout:Number(e.timeout),startTimeout:Number(e.startTimeout)});return t.success&&b(t.message||"Service restarted"),t}function ie(){let e=_();return oe(`${n.name} service status:`),console.log(""),console.log(` Service name: ${n.name}`),console.log(` Running status: ${e.running?"Running":"Stopped"}`),console.log(` Message: ${e.message}`),console.log(` PID file: ${$()}`),e.pidInfo&&(console.log(` Process ID: ${e.pidInfo.pid}`),console.log(` Environment: ${e.pidInfo.env||"Unknown"}`),console.log(` Port: ${e.pidInfo.port||"Unknown"}`),console.log(` Version: ${e.pidInfo.version||A}`),console.log(` Platform: ${e.pidInfo.platform||process.platform}`),console.log(` Started at: ${e.pidInfo.startedAt||"Unknown"}`),console.log(` Uptime: ${L(e.pidInfo.startedAt)}`)),console.log(""),{success:e.running,message:e.message}}function ce(){d.name("nuwax-file-server").description("Cross-platform file service deployment tool, supporting start/stop/restart/status").version(A,"-v, --version","Display version number").helpOption("-h, --help","Display help information"),d.command("start").allowUnknownOption().description("Start service").option("--env <environment>","\u73AF\u5883: development|production|test","production").option("--port <port>","Service port").option("--config <path>","Custom configuration file path").option("--timeout <ms>","\u505C\u6B62\u65E7\u8FDB\u7A0B\u7B49\u5F85\u8D85\u65F6\uFF08\u6BEB\u79D2\uFF09",`${n.defaultStopTimeout}`).option("--start-timeout <ms>","\u542F\u52A8\u5065\u5EB7\u68C0\u67E5\u8D85\u65F6\uFF08\u6BEB\u79D2\uFF09",`${n.defaultStartTimeout}`).action(y(se)),d.command("stop").description("Stop service").option("--force","Force stop").option("--timeout <ms>","\u505C\u6B62\u670D\u52A1\u7B49\u5F85\u8D85\u65F6\uFF08\u6BEB\u79D2\uFF09",`${n.defaultStopTimeout}`).action(y(re)),d.command("restart").allowUnknownOption().description("Restart service").option("--env <environment>","\u73AF\u5883: development|production|test","production").option("--port <port>","Service port").option("--config <path>","Custom configuration file path").option("--timeout <ms>","\u505C\u6B62\u65E7\u8FDB\u7A0B\u7B49\u5F85\u8D85\u65F6\uFF08\u6BEB\u79D2\uFF09",`${n.defaultStopTimeout}`).option("--start-timeout <ms>","\u542F\u52A8\u5065\u5EB7\u68C0\u67E5\u8D85\u65F6\uFF08\u6BEB\u79D2\uFF09",`${n.defaultStartTimeout}`).action(y(ne)),d.command("status").description("View service status").action(y(ie)),d.command("help").description("Display help information").action(()=>{d.outputHelp()}),d.parse(process.argv),process.argv.slice(2).length||(d.outputHelp(),process.exitCode=0)}ce();
@@ -1 +1 @@
1
- import{log as s}from"../utils/log/logUtils.js";import P from"../appConfig/index.js";import u from"path";import i from"fs";import{extractZip as T}from"../utils/common/zipUtils.js";import"../utils/build/startDevUtils.js";import"../utils/build/restartDevUtils.js";import{stopDevServer as M}from"../utils/build/stopDevUtils.js";import{ValidationError as h,BusinessError as B,SystemError as R,FileError as b,ResourceError as _}from"../utils/error/errorHandler.js";import{sanitizeSensitivePaths as C}from"../utils/common/sensitiveUtils.js";import{removeNodeModules as Y}from"../utils/buildDependency/dependencyManager.js";import{backupProjectToZip as j,copyDirectoryFiltered as I}from"../utils/project/backupUtils.js";import{createPnpmNpmrc as W}from"../utils/common/npmrcUtils.js";import{resolveProjectPath as v}from"../utils/common/projectPathUtils.js";import{ensurePrimaryAgentDirs as ee,syncAgents as te}from"../utils/common/AgentWorkspaceUtils.js";async function L(e,t="react",l={}){const a=Date.now();if(!e)throw new h("Project ID cannot be empty",{field:"projectId"});const r=["react","vue3"];if(!r.includes(t))throw new h("Template type is invalid, only supports react or vue3",{field:"templateType",templateType:t,allowedValues:r});const c={react:P.INIT_PROJECT_NAME_REACT||"react-vite-template",vue3:P.INIT_PROJECT_NAME_VUE3||"vue3-vite-template"}[t],w=v(e,l);if(i.existsSync(w))throw new B(`Project directory ${e} already exists`,{projectId:e,projectPath:w});try{i.mkdirSync(w,{recursive:!0}),s(e,"INFO",`Project directory created successfully: ${w}`,{projectId:e});const f=P.INIT_PROJECT_DIR,y=u.join(f,`${c}.zip`),m=u.join(f,c);if(s(e,"DEBUG","Start checking template directory",{templateDir:m,templateZipPath:y}),!i.existsSync(m)){if(!i.existsSync(y))throw s(e,"ERROR",`Initialization template does not exist: ${y}`,{projectId:e,templateZipPath:y}),new _("Initialization template does not exist",{});if(s(e,"INFO",`Template directory does not exist, starting to unzip template: ${y}`,{projectId:e,templateZipPath:y}),await T(y,m),s(e,"INFO","Template unzip completed",{projectId:e}),!i.existsSync(m))throw new R("Template unzip directory still does not exist",{})}s(e,"DEBUG","Start copying template content to project directory",{templateDir:m,projectPath:w});const d=await i.promises.readdir(m,{withFileTypes:!0});for(const D of d){const n=u.join(m,D.name),p=u.join(w,D.name);D.isDirectory()?(await i.promises.mkdir(p,{recursive:!0}),await I(n,p)):D.isFile()&&(await i.promises.mkdir(u.dirname(p),{recursive:!0}),await i.promises.copyFile(n,p))}return s(e,"DEBUG","Start creating .npmrc configuration file",{projectPath:w}),await W(w,e),s(e,"INFO",`Project ${e} initialized successfully`,{projectId:e,elapsedMs:Date.now()-a}),{success:!0,message:`Project ${e} created successfully`,projectPath:w}}catch(f){throw s(e,"ERROR",`Project ${e} initialization failed: ${f.message}`,{projectId:e,elapsedMs:Date.now()-a}),new R(`Project ${e} initialization failed: ${f.message}`,{projectId:e,projectPath:w,originalError:f.message})}}async function ie(e){const t=await i.promises.readdir(e,{withFileTypes:!0}),l=P.TOP_LEVEL_NOISE_PATTERNS,a=t.filter(r=>{const o=r.name;return o.startsWith(".")?!1:!l.some(c=>c.endsWith("*")?o.startsWith(c.slice(0,-1)):o===c)});if(a.length===1&&a[0].isDirectory()){const r=u.join(e,a[0].name),o=u.join(e,"..",`temp_${Date.now()}`);await i.promises.rename(r,o);const c=await i.promises.readdir(o);for(const w of c){const f=u.join(o,w),y=u.join(e,w);await i.promises.rename(f,y)}await i.promises.rmdir(o)}}async function A(e,t={}){if(!e)throw new h("Project ID cannot be empty",{field:"projectId"});const l=v(e,t);if(i.existsSync(l))try{s(e,"INFO",`Start cleaning project directory: ${l}`,{projectId:e}),await i.promises.rm(l,{recursive:!0,force:!0}),s(e,"INFO",`Project directory cleaned up: ${l}`,{projectId:e})}catch(a){throw s(e,"ERROR",`Failed to clean project directory: ${a.message}`,{projectId:e,projectPath:l,originalError:a.message}),new R(`Failed to clean project directory: ${a.message}`,{projectId:e,projectPath:l,originalError:a.message})}else s(e,"INFO",`Project directory does not exist, no need to clean: ${l}`,{projectId:e})}function se(e){if(!i.existsSync(e))return!0;try{return i.readdirSync(e).filter(a=>!a.startsWith(".")&&a!=="node_modules").length===0}catch(t){const l=u.basename(e);return s(l,"ERROR",`Failed to check if directory is empty: ${t.message}`,{dirPath:e}),!0}}async function G(e,t,l,a,r,o,c={}){const w=Date.now(),f=v(e,c);try{if(se(f))s(e,"INFO","Project directory is empty, directly deploying new project",{projectId:e});else{s(e,"INFO","Project directory is not empty, starting to backup current version",{projectId:e});const m=parseInt(a)-1,d=u.join(P.UPLOAD_PROJECT_DIR,e),D=u.join(d,`${e}-v${m}.zip`);if(i.existsSync(D))s(e,"INFO",`Backup file already exists, skipping backup: ${D}`,{projectId:e});else try{await $(e,m,c),s(e,"INFO",`Current version backed up: ${D}`,{projectId:e})}catch(n){throw s(e,"ERROR",`Failed to backup current version: ${n.message}`,{projectId:e}),new R(`Failed to backup current version: ${n.message}`,{projectId:e,originalError:n.message})}if(r&&!isNaN(Number(r))){const n=Number(r);s(e,"INFO",`Stopping old version dev server, PID: ${n}`,{projectId:e});try{await M(l,e,n,{strict:!0}),s(e,"INFO","Old version dev server stopped",{projectId:e})}catch(p){s(e,"WARN",`Failed to stop old version dev server: ${p.message}`,{projectId:e,pid:n})}}i.existsSync(f)&&(s(e,"INFO",`Cleaning project directory: ${f}`,{projectId:e}),await i.promises.rm(f,{recursive:!0,force:!0}))}return i.mkdirSync(f,{recursive:!0}),s(e,"INFO",`Project directory created successfully: ${f}`,{projectId:e}),s(e,"DEBUG","Start extracting zip file to project directory",{projectId:e,zipFilePath:t}),await T(t,f),s(e,"DEBUG","Zip file extracted successfully",{projectId:e}),s(e,"DEBUG","Check and remove top level folder",{projectId:e}),await ie(f),s(e,"DEBUG","Check and remove node_modules folder",{projectId:e}),await Y(f),s(e,"DEBUG","Start creating .npmrc configuration file",{projectId:e}),await W(f,e),s(e,"INFO",`Project ${e} uploaded successfully`,{projectId:e,codeVersion:a,elapsedMs:Date.now()-w}),{success:!0,message:`Project ${e} uploaded successfully`,projectId:e,codeVersion:a}}catch(y){s(e,"ERROR",`Failed to upload project: ${y.message}`,{projectId:e,elapsedMs:Date.now()-w});try{await A(e,c),s(e,"INFO","Failed to upload project, project directory cleaned up",{projectId:e})}catch(m){s(e,"ERROR",`Failed to clean project directory: ${m.message}`,{projectId:e,originalError:m.message})}throw y.isOperational?y:new R(`Failed to upload project: ${y.message}`,{projectId:e,projectPath:f,zipFilePath:t,originalError:y.message})}}async function $(e,t,l={}){if(!e)throw new h("Project ID cannot be empty",{field:"projectId"});if(t==null)throw new h("codeVersion cannot be empty",{field:"codeVersion"});const a=Number(t);if(!Number.isFinite(a))throw new h("codeVersion must be a number",{field:"codeVersion"});const r=v(e,l);if(!i.existsSync(r))throw new _("Project does not exist",{projectId:e});const o=u.join(P.UPLOAD_PROJECT_DIR,e);i.existsSync(o)||i.mkdirSync(o,{recursive:!0});const c=`${e}-v${a}.zip`,w=u.join(o,c);return s(e,"DEBUG","Start backing up project to zip",{projectId:e,versionNum:a,outZipPath:w}),await j(e,r,w)}async function J(e,t,l){if(!e)throw new h("Project ID cannot be empty",{field:"projectId"});if(!t)throw new h("codeVersion cannot be empty",{field:"codeVersion"});if(!l)throw new h("Please upload a zip file",{field:"zipFile"});const a=u.join(P.UPLOAD_PROJECT_DIR,e);i.existsSync(a)||i.mkdirSync(a,{recursive:!0});const r=l.path,o=u.join(a,`${e}-v${t}.zip`);try{return i.renameSync(r,o),s(e,"INFO","File saved successfully",{projectId:e,codeVersion:t,filePath:o}),{success:!0,filePath:o}}catch(c){if(s(e,"ERROR","Failed to move file",{projectId:e,codeVersion:t,error:c.message}),i.existsSync(r))try{i.unlinkSync(r)}catch(w){s(e,"ERROR","Failed to clean temporary file",{projectId:e,error:w.message})}throw new R("Failed to save file",{projectId:e,codeVersion:t,originalError:c.message})}}function re(e){if(!e)return[];if(Array.isArray(e))return e.map(t=>typeof t=="string"?t.trim():"").filter(Boolean);if(typeof e=="string"){const t=e.trim();if(!t)return[];try{const l=JSON.parse(t);if(Array.isArray(l))return l.map(a=>typeof a=="string"?a.trim():"").filter(Boolean)}catch{}return[t]}return[]}async function Z(e){i.existsSync(e)&&await i.promises.rm(e,{recursive:!0,force:!0})}async function V(e,t){try{await i.promises.rename(e,t)}catch(l){if(l.code==="EXDEV"){async function a(r,o){if((await i.promises.lstat(r)).isDirectory()){await i.promises.mkdir(o,{recursive:!0});const w=await i.promises.readdir(r);for(const f of w)await a(u.join(r,f),u.join(o,f))}else await i.promises.copyFile(r,o)}await a(e,t),await i.promises.rm(e,{recursive:!0,force:!0})}else throw l}}async function ae(e,t){const l=u.join(e,t);if(i.existsSync(l)&&(await i.promises.lstat(l)).isDirectory())return l;const a=await i.promises.readdir(e,{withFileTypes:!0});for(const r of a){if(!r.isDirectory())continue;const o=u.join(e,r.name,t);if(i.existsSync(o)&&(await i.promises.lstat(o)).isDirectory())return o}return null}async function ne(e,t,l){let a;try{a=await fetch(e)}catch(w){throw new b(`Failed to download skill zip from url: ${e}`,{url:e,reason:w.message})}if(!a.ok)throw new b(`Failed to download skill zip from url: ${e}`,{url:e,status:a.status,statusText:a.statusText});const r=a.headers.get("content-type")||"";r&&!r.includes("zip")&&!r.includes("octet-stream")&&s(l,"WARN","Downloaded skill url content-type is not typical zip",{url:e,contentType:r});const o=await a.arrayBuffer(),c=Buffer.from(o);await i.promises.writeFile(t,c)}async function X(e,t,l,a={}){const r=String(e),o=Date.now(),c=re(l),w=[],f=[];if(!e)throw new h("Project ID cannot be empty",{field:"projectId"});if(!t&&c.length===0)throw new h("file or skillUrls cannot both be empty",{field:"file|skillUrls"});if(t){if(!t.path)throw new h("Uploaded file has no valid path",{field:"file.path"});if(u.extname(t.originalname||t.filename||"").toLowerCase()!==".zip")throw new h("Only zip files are supported",{field:"file",originalName:t?.originalname})}const y=v(String(e),a);if(!i.existsSync(y))throw new h("Project does not exist",{field:"projectId"});const{skillsDir:m,agentTypes:d}=await ee(y),D=u.join(P.UPLOAD_PROJECT_DIR,"temp");try{await i.promises.mkdir(m,{recursive:!0}),await i.promises.mkdir(D,{recursive:!0});const n=new Set;if(t){const k=u.join(D,`project_skill_push_${Date.now()}_${Math.round(Math.random()*1e6)}`);f.push(k),await i.promises.mkdir(k,{recursive:!0}),await T(t.path,k);const N=await ae(k,"skills");if(!N)s(r,"WARN","skills directory not found in uploaded zip",{projectId:e,extractRoot:k});else{const E=(await i.promises.readdir(N,{withFileTypes:!0})).filter(F=>F.isDirectory()&&!F.name.startsWith("."));for(const F of E){const S=u.join(N,F.name),x=u.join(m,F.name);i.existsSync(x)&&await Z(x),await V(S,x),n.add(F.name)}}}for(let k=0;k<c.length;k+=1){const N=c[k],O=u.join(D,`project_skill_url_${Date.now()}_${k}_${Math.round(Math.random()*1e6)}.zip`);w.push(O);const E=u.join(D,`project_skill_url_extract_${Date.now()}_${k}_${Math.round(Math.random()*1e6)}`);f.push(E),await i.promises.mkdir(E,{recursive:!0}),await ne(N,O,r),await T(O,E);const S=(await i.promises.readdir(E,{withFileTypes:!0})).filter(g=>g.isDirectory()&&!g.name.startsWith(".")),U=S.find(g=>g.name==="skills")?(await i.promises.readdir(u.join(E,"skills"),{withFileTypes:!0})).filter(g=>g.isDirectory()&&!g.name.startsWith(".")).map(g=>({name:g.name,sourcePath:u.join(E,"skills",g.name)})):S.map(g=>({name:g.name,sourcePath:u.join(E,g.name)}));if(U.length===0){s(r,"WARN","No skill directory found after extracting skill url zip",{projectId:e,skillUrl:N,extractRoot:E});continue}for(const g of U){const z=u.join(m,g.name);i.existsSync(z)&&await Z(z),await V(g.sourcePath,z),n.add(g.name)}}const p=Array.from(n),Q=p.length>0?`Pushed ${p.length} skills: ${p.join(", ")}`:"No valid skill directories found in file or skillUrls";return s(r,"INFO","Push skills to project workspace completed",{projectId:e,updatedSkills:p,skillUrlsCount:c.length,hasFile:!!t,agentTypes:d,elapsedMs:Date.now()-o}),await te(y),{message:Q,projectPath:y,updatedSkills:p}}catch(n){throw s(r,"ERROR","Push skills to project workspace failed",{projectId:e,error:n.message,elapsedMs:Date.now()-o}),n instanceof h||n instanceof b||n instanceof B||n instanceof R?n:new R(`Failed to push skills to project workspace: ${n.message}`,{projectId:e})}finally{for(const n of f)try{i.existsSync(n)&&await i.promises.rm(n,{recursive:!0,force:!0})}catch(p){s(r,"WARN","Failed to clean extracted skill temp dir",{extractRoot:n,error:p.message})}for(const n of w)try{i.existsSync(n)&&await i.promises.unlink(n)}catch(p){s(r,"WARN","Failed to clean downloaded skill zip",{zipPath:n,error:p.message})}try{t?.path&&i.existsSync(t.path)&&await i.promises.unlink(t.path)}catch(n){s(r,"WARN","Failed to clean uploaded skill zip",{tempZipPath:t?.path,error:n.message})}}}async function q(e,t,l,a={}){const r=Date.now();if(!e)throw new h("Project ID cannot be empty",{field:"projectId"});const o=null;try{if(t&&!isNaN(Number(t))){const m=Number(t);s(o,"INFO",`[delete-project] Stopping development server, PID: ${m}`,{projectId:e,pid:m});try{await M(l,e,m,{strict:!0}),s(o,"INFO","[delete-project] Development server stopped",{projectId:e})}catch(d){s(o,"WARN",`[delete-project] Failed to stop development server: ${d.message}`,{projectId:e,pid:m})}}const c=[u.join(P.UPLOAD_PROJECT_DIR,e),v(e,a),u.join(P.DIST_TARGET_DIR,e),u.join(P.LOG_BASE_DIR,e)],w=[],f=[];for(const m of c)if(i.existsSync(m))try{await i.promises.rm(m,{recursive:!0,force:!0}),w.push(m),s(o,"INFO",`[delete-project] Directory deleted successfully: ${m}`,{projectId:e})}catch(d){f.push({path:m,error:d.message}),s(o,"ERROR",`[delete-project] Directory deleted failed: ${m}`,{projectId:e,error:d.message})}else s(o,"INFO",`[delete-project] Directory does not exist, skipping deletion: ${m}`,{projectId:e});const y={success:!0,message:`Project ${e} deleted successfully`,projectId:e,deletedDirectories:w,failedDirectories:f};return f.length>0&&(y.message+=`, but ${f.length} directories deleted failed`,s(o,"WARN","[delete-project] Some directories deleted failed",{projectId:e,failedDirs:f})),s(o,"INFO",`[delete-project] Project deleted successfully: ${e}`,{projectId:e,elapsedMs:Date.now()-r}),y}catch(c){throw s(o,"ERROR",`[delete-project] Failed to delete project: ${c.message}`,{projectId:e,originalError:c.message,elapsedMs:Date.now()-r}),c.isOperational?c:new R(`Failed to delete project: ${c.message}`,{projectId:e,originalError:c.message})}}async function H(e,t,l,a,r={}){const o=Date.now();if(!e)throw new h("Project ID cannot be empty",{field:"projectId"});if(t==null)throw new h("codeVersion cannot be empty",{field:"codeVersion"});const c=Number(t);if(!Number.isFinite(c))throw new h("codeVersion must be a number",{field:"codeVersion"});const w=v(e,r);if(!i.existsSync(w))throw new _("Project does not exist",{projectId:e});const f=u.join(P.UPLOAD_PROJECT_DIR,e),y=`${e}-v${c}.zip`,m=u.join(f,y);if(l!=="LATEST"){if(i.existsSync(m))return s(e,"INFO",`Using existing export file: ${m}`,{projectId:e,zipPath:m}),{success:!0,projectId:e,zipPath:m};throw new _(`Specified version zip file does not exist: ${m}`,{projectId:e,zipPath:m})}const d=u.join(w,"cpage_config.json");let D=!1;try{if(a)try{const p=JSON.stringify(a,null,2);await i.promises.writeFile(d,p,"utf8"),D=!0,s(e,"INFO",`Configuration file created successfully: ${d}`,{projectId:e,configFilePath:d})}catch(p){throw s(e,"ERROR",`Failed to create configuration file: ${p.message}`,{projectId:e,error:p.message}),new b("Failed to create configuration file",{projectId:e,configFilePath:d,originalError:p.message})}s(e,"DEBUG","Start executing export and packaging",{projectId:e,codeVersion:t});const n=await $(e,t,r);return s(e,"INFO",`Project exported successfully: ${n}`,{projectId:e,zipPath:n,elapsedMs:Date.now()-o}),{success:!0,projectId:e,zipPath:n}}catch(n){throw s(e,"ERROR",`Failed to export project: ${n?.message}`,{projectId:e,elapsedMs:Date.now()-o}),n.isOperational?n:new R("Failed to export project",{projectId:e,originalError:n&&n.message?C(n.message):n&&n.message})}finally{if(D&&i.existsSync(d))try{await i.promises.unlink(d),s(e,"INFO",`Temporary configuration file deleted successfully: ${d}`,{projectId:e,configFilePath:d})}catch(n){s(e,"WARN",`Failed to delete temporary configuration file: ${n.message}`,{projectId:e,error:n.message})}}}async function K(e,t,l={}){const a=Date.now();if(!e)throw new h("Project ID cannot be empty",{field:"projectId"});if(t==null)throw new h("codeVersion cannot be empty",{field:"codeVersion"});try{const r=await $(e,t,l);return s(e,"INFO",`Current version backed up successfully: ${r}`,{projectId:e,zipPath:r,elapsedMs:Date.now()-a}),{success:!0,projectId:e,zipPath:r}}catch(r){throw s(e,"ERROR",`Failed to backup current version: ${r?.message}`,{projectId:e,elapsedMs:Date.now()-a}),r.isOperational?r:new R("Failed to backup current version",{projectId:e,originalError:r&&r.message?C(r.message):r&&r.message})}}export{L as createProject,G as uploadProject,K as backupCurrentVersion,H as exportProject,$ as backupProjectOfVersion,A as cleanupProjectDirectory,J as handleFileUpload,X as pushSkillsToWorkspace,q as deleteProject};export default{createProject:L,uploadProject:G,backupCurrentVersion:K,exportProject:H,backupProjectOfVersion:$,cleanupProjectDirectory:A,handleFileUpload:J,pushSkillsToWorkspace:X,deleteProject:q};
1
+ import{log as i}from"../utils/log/logUtils.js";import P from"../appConfig/index.js";import u from"path";import s from"fs";import{extractZip as _}from"../utils/common/zipUtils.js";import"../utils/build/startDevUtils.js";import"../utils/build/restartDevUtils.js";import{stopDevServer as B}from"../utils/build/stopDevUtils.js";import{ValidationError as p,BusinessError as C,SystemError as E,FileError as b,ResourceError as z}from"../utils/error/errorHandler.js";import{sanitizeSensitivePaths as W}from"../utils/common/sensitiveUtils.js";import{removeNodeModules as j}from"../utils/buildDependency/dependencyManager.js";import{backupProjectToZip as I,copyDirectoryFiltered as ee}from"../utils/project/backupUtils.js";import{createPnpmNpmrc as G}from"../utils/common/npmrcUtils.js";import{copyNodeModulesFromCache as L}from"../utils/common/templateCacheUtils.js";import{resolveProjectPath as v}from"../utils/common/projectPathUtils.js";import{ensurePrimaryAgentDirs as te,syncAgents as ie}from"../utils/common/AgentWorkspaceUtils.js";async function J(e,t="react",l={}){const a=Date.now();if(!e)throw new p("Project ID cannot be empty",{field:"projectId"});const r=["react","vue3"];if(!r.includes(t))throw new p("Template type is invalid, only supports react or vue3",{field:"templateType",templateType:t,allowedValues:r});const c={react:P.INIT_PROJECT_NAME_REACT||"react-vite-template",vue3:P.INIT_PROJECT_NAME_VUE3||"vue3-vite-template"}[t],f=v(e,l);if(s.existsSync(f))throw new C(`Project directory ${e} already exists`,{projectId:e,projectPath:f});try{s.mkdirSync(f,{recursive:!0}),i(e,"INFO",`Project directory created successfully: ${f}`,{projectId:e});const w=P.INIT_PROJECT_DIR,y=u.join(w,`${c}.zip`),m=u.join(w,c);if(i(e,"DEBUG","Start checking template directory",{templateDir:m,templateZipPath:y}),!s.existsSync(m)){if(!s.existsSync(y))throw i(e,"ERROR",`Initialization template does not exist: ${y}`,{projectId:e,templateZipPath:y}),new z("Initialization template does not exist",{});if(i(e,"INFO",`Template directory does not exist, starting to unzip template: ${y}`,{projectId:e,templateZipPath:y}),await _(y,m),i(e,"INFO","Template unzip completed",{projectId:e}),!s.existsSync(m))throw new E("Template unzip directory still does not exist",{})}i(e,"DEBUG","Start copying template content to project directory",{templateDir:m,projectPath:f});const d=await s.promises.readdir(m,{withFileTypes:!0});for(const o of d){const h=u.join(m,o.name),N=u.join(f,o.name);o.isDirectory()?(await s.promises.mkdir(N,{recursive:!0}),await ee(h,N)):o.isFile()&&(await s.promises.mkdir(u.dirname(N),{recursive:!0}),await s.promises.copyFile(h,N))}i(e,"DEBUG","Try copying node_modules from template cache",{projectPath:f});const D=await L(f,e);return D.cached&&i(e,"INFO",`node_modules copied from cache: ${D.templateType}`,{elapsed:D.elapsed}),i(e,"DEBUG","Start creating .npmrc configuration file",{projectPath:f}),await G(f,e),i(e,"INFO",`Project ${e} initialized successfully`,{projectId:e,elapsedMs:Date.now()-a}),{success:!0,message:`Project ${e} created successfully`,projectPath:f}}catch(w){throw i(e,"ERROR",`Project ${e} initialization failed: ${w.message}`,{projectId:e,elapsedMs:Date.now()-a}),new E(`Project ${e} initialization failed: ${w.message}`,{projectId:e,projectPath:f,originalError:w.message})}}async function se(e){const t=await s.promises.readdir(e,{withFileTypes:!0}),l=P.TOP_LEVEL_NOISE_PATTERNS,a=t.filter(r=>{const n=r.name;return n.startsWith(".")?!1:!l.some(c=>c.endsWith("*")?n.startsWith(c.slice(0,-1)):n===c)});if(a.length===1&&a[0].isDirectory()){const r=u.join(e,a[0].name),n=u.join(e,"..",`temp_${Date.now()}`);await s.promises.rename(r,n);const c=await s.promises.readdir(n);for(const f of c){const w=u.join(n,f),y=u.join(e,f);await s.promises.rename(w,y)}await s.promises.rmdir(n)}}async function U(e,t={}){if(!e)throw new p("Project ID cannot be empty",{field:"projectId"});const l=v(e,t);if(s.existsSync(l))try{i(e,"INFO",`Start cleaning project directory: ${l}`,{projectId:e}),await s.promises.rm(l,{recursive:!0,force:!0}),i(e,"INFO",`Project directory cleaned up: ${l}`,{projectId:e})}catch(a){throw i(e,"ERROR",`Failed to clean project directory: ${a.message}`,{projectId:e,projectPath:l,originalError:a.message}),new E(`Failed to clean project directory: ${a.message}`,{projectId:e,projectPath:l,originalError:a.message})}else i(e,"INFO",`Project directory does not exist, no need to clean: ${l}`,{projectId:e})}function re(e){if(!s.existsSync(e))return!0;try{return s.readdirSync(e).filter(a=>!a.startsWith(".")&&a!=="node_modules").length===0}catch(t){const l=u.basename(e);return i(l,"ERROR",`Failed to check if directory is empty: ${t.message}`,{dirPath:e}),!0}}async function Z(e,t,l,a,r,n,c={}){const f=Date.now(),w=v(e,c);try{if(re(w))i(e,"INFO","Project directory is empty, directly deploying new project",{projectId:e});else{i(e,"INFO","Project directory is not empty, starting to backup current version",{projectId:e});const m=parseInt(a)-1,d=u.join(P.UPLOAD_PROJECT_DIR,e),D=u.join(d,`${e}-v${m}.zip`);if(s.existsSync(D))i(e,"INFO",`Backup file already exists, skipping backup: ${D}`,{projectId:e});else try{await O(e,m,c),i(e,"INFO",`Current version backed up: ${D}`,{projectId:e})}catch(o){throw i(e,"ERROR",`Failed to backup current version: ${o.message}`,{projectId:e}),new E(`Failed to backup current version: ${o.message}`,{projectId:e,originalError:o.message})}if(r&&!isNaN(Number(r))){const o=Number(r);i(e,"INFO",`Stopping old version dev server, PID: ${o}`,{projectId:e});try{await B(l,e,o,{strict:!0}),i(e,"INFO","Old version dev server stopped",{projectId:e})}catch(h){i(e,"WARN",`Failed to stop old version dev server: ${h.message}`,{projectId:e,pid:o})}}s.existsSync(w)&&(i(e,"INFO",`Cleaning project directory: ${w}`,{projectId:e}),await s.promises.rm(w,{recursive:!0,force:!0}))}return s.mkdirSync(w,{recursive:!0}),i(e,"INFO",`Project directory created successfully: ${w}`,{projectId:e}),i(e,"DEBUG","Start extracting zip file to project directory",{projectId:e,zipFilePath:t}),await _(t,w),i(e,"DEBUG","Zip file extracted successfully",{projectId:e}),i(e,"DEBUG","Check and remove top level folder",{projectId:e}),await se(w),i(e,"DEBUG","Check and remove node_modules folder",{projectId:e}),await j(w),i(e,"DEBUG","Try copying node_modules from template cache",{projectId:e}),await L(w,e),i(e,"DEBUG","Start creating .npmrc configuration file",{projectId:e}),await G(w,e),i(e,"INFO",`Project ${e} uploaded successfully`,{projectId:e,codeVersion:a,elapsedMs:Date.now()-f}),{success:!0,message:`Project ${e} uploaded successfully`,projectId:e,codeVersion:a}}catch(y){i(e,"ERROR",`Failed to upload project: ${y.message}`,{projectId:e,elapsedMs:Date.now()-f});try{await U(e,c),i(e,"INFO","Failed to upload project, project directory cleaned up",{projectId:e})}catch(m){i(e,"ERROR",`Failed to clean project directory: ${m.message}`,{projectId:e,originalError:m.message})}throw y.isOperational?y:new E(`Failed to upload project: ${y.message}`,{projectId:e,projectPath:w,zipFilePath:t,originalError:y.message})}}async function O(e,t,l={}){if(!e)throw new p("Project ID cannot be empty",{field:"projectId"});if(t==null)throw new p("codeVersion cannot be empty",{field:"codeVersion"});const a=Number(t);if(!Number.isFinite(a))throw new p("codeVersion must be a number",{field:"codeVersion"});const r=v(e,l);if(!s.existsSync(r))throw new z("Project does not exist",{projectId:e});const n=u.join(P.UPLOAD_PROJECT_DIR,e);s.existsSync(n)||s.mkdirSync(n,{recursive:!0});const c=`${e}-v${a}.zip`,f=u.join(n,c);return i(e,"DEBUG","Start backing up project to zip",{projectId:e,versionNum:a,outZipPath:f}),await I(e,r,f)}async function V(e,t,l){if(!e)throw new p("Project ID cannot be empty",{field:"projectId"});if(!t)throw new p("codeVersion cannot be empty",{field:"codeVersion"});if(!l)throw new p("Please upload a zip file",{field:"zipFile"});const a=u.join(P.UPLOAD_PROJECT_DIR,e);s.existsSync(a)||s.mkdirSync(a,{recursive:!0});const r=l.path,n=u.join(a,`${e}-v${t}.zip`);try{return s.renameSync(r,n),i(e,"INFO","File saved successfully",{projectId:e,codeVersion:t,filePath:n}),{success:!0,filePath:n}}catch(c){if(i(e,"ERROR","Failed to move file",{projectId:e,codeVersion:t,error:c.message}),s.existsSync(r))try{s.unlinkSync(r)}catch(f){i(e,"ERROR","Failed to clean temporary file",{projectId:e,error:f.message})}throw new E("Failed to save file",{projectId:e,codeVersion:t,originalError:c.message})}}function ae(e){if(!e)return[];if(Array.isArray(e))return e.map(t=>typeof t=="string"?t.trim():"").filter(Boolean);if(typeof e=="string"){const t=e.trim();if(!t)return[];try{const l=JSON.parse(t);if(Array.isArray(l))return l.map(a=>typeof a=="string"?a.trim():"").filter(Boolean)}catch{}return[t]}return[]}async function X(e){s.existsSync(e)&&await s.promises.rm(e,{recursive:!0,force:!0})}async function q(e,t){try{await s.promises.rename(e,t)}catch(l){if(l.code==="EXDEV"){async function a(r,n){if((await s.promises.lstat(r)).isDirectory()){await s.promises.mkdir(n,{recursive:!0});const f=await s.promises.readdir(r);for(const w of f)await a(u.join(r,w),u.join(n,w))}else await s.promises.copyFile(r,n)}await a(e,t),await s.promises.rm(e,{recursive:!0,force:!0})}else throw l}}async function oe(e,t){const l=u.join(e,t);if(s.existsSync(l)&&(await s.promises.lstat(l)).isDirectory())return l;const a=await s.promises.readdir(e,{withFileTypes:!0});for(const r of a){if(!r.isDirectory())continue;const n=u.join(e,r.name,t);if(s.existsSync(n)&&(await s.promises.lstat(n)).isDirectory())return n}return null}async function ne(e,t,l){let a;try{a=await fetch(e)}catch(f){throw new b(`Failed to download skill zip from url: ${e}`,{url:e,reason:f.message})}if(!a.ok)throw new b(`Failed to download skill zip from url: ${e}`,{url:e,status:a.status,statusText:a.statusText});const r=a.headers.get("content-type")||"";r&&!r.includes("zip")&&!r.includes("octet-stream")&&i(l,"WARN","Downloaded skill url content-type is not typical zip",{url:e,contentType:r});const n=await a.arrayBuffer(),c=Buffer.from(n);await s.promises.writeFile(t,c)}async function H(e,t,l,a={}){const r=String(e),n=Date.now(),c=ae(l),f=[],w=[];if(!e)throw new p("Project ID cannot be empty",{field:"projectId"});if(!t&&c.length===0)throw new p("file or skillUrls cannot both be empty",{field:"file|skillUrls"});if(t){if(!t.path)throw new p("Uploaded file has no valid path",{field:"file.path"});if(u.extname(t.originalname||t.filename||"").toLowerCase()!==".zip")throw new p("Only zip files are supported",{field:"file",originalName:t?.originalname})}const y=v(String(e),a);if(!s.existsSync(y))throw new p("Project does not exist",{field:"projectId"});const{skillsDir:m,agentTypes:d}=await te(y),D=u.join(P.UPLOAD_PROJECT_DIR,"temp");try{await s.promises.mkdir(m,{recursive:!0}),await s.promises.mkdir(D,{recursive:!0});const o=new Set;if(t){const k=u.join(D,`project_skill_push_${Date.now()}_${Math.round(Math.random()*1e6)}`);w.push(k),await s.promises.mkdir(k,{recursive:!0}),await _(t.path,k);const $=await oe(k,"skills");if(!$)i(r,"WARN","skills directory not found in uploaded zip",{projectId:e,extractRoot:k});else{const R=(await s.promises.readdir($,{withFileTypes:!0})).filter(F=>F.isDirectory()&&!F.name.startsWith("."));for(const F of R){const x=u.join($,F.name),T=u.join(m,F.name);s.existsSync(T)&&await X(T),await q(x,T),o.add(F.name)}}}for(let k=0;k<c.length;k+=1){const $=c[k],S=u.join(D,`project_skill_url_${Date.now()}_${k}_${Math.round(Math.random()*1e6)}.zip`);f.push(S);const R=u.join(D,`project_skill_url_extract_${Date.now()}_${k}_${Math.round(Math.random()*1e6)}`);w.push(R),await s.promises.mkdir(R,{recursive:!0}),await ne($,S,r),await _(S,R);const x=(await s.promises.readdir(R,{withFileTypes:!0})).filter(g=>g.isDirectory()&&!g.name.startsWith(".")),M=x.find(g=>g.name==="skills")?(await s.promises.readdir(u.join(R,"skills"),{withFileTypes:!0})).filter(g=>g.isDirectory()&&!g.name.startsWith(".")).map(g=>({name:g.name,sourcePath:u.join(R,"skills",g.name)})):x.map(g=>({name:g.name,sourcePath:u.join(R,g.name)}));if(M.length===0){i(r,"WARN","No skill directory found after extracting skill url zip",{projectId:e,skillUrl:$,extractRoot:R});continue}for(const g of M){const A=u.join(m,g.name);s.existsSync(A)&&await X(A),await q(g.sourcePath,A),o.add(g.name)}}const h=Array.from(o),N=h.length>0?`Pushed ${h.length} skills: ${h.join(", ")}`:"No valid skill directories found in file or skillUrls";return i(r,"INFO","Push skills to project workspace completed",{projectId:e,updatedSkills:h,skillUrlsCount:c.length,hasFile:!!t,agentTypes:d,elapsedMs:Date.now()-n}),await ie(y),{message:N,projectPath:y,updatedSkills:h}}catch(o){throw i(r,"ERROR","Push skills to project workspace failed",{projectId:e,error:o.message,elapsedMs:Date.now()-n}),o instanceof p||o instanceof b||o instanceof C||o instanceof E?o:new E(`Failed to push skills to project workspace: ${o.message}`,{projectId:e})}finally{for(const o of w)try{s.existsSync(o)&&await s.promises.rm(o,{recursive:!0,force:!0})}catch(h){i(r,"WARN","Failed to clean extracted skill temp dir",{extractRoot:o,error:h.message})}for(const o of f)try{s.existsSync(o)&&await s.promises.unlink(o)}catch(h){i(r,"WARN","Failed to clean downloaded skill zip",{zipPath:o,error:h.message})}try{t?.path&&s.existsSync(t.path)&&await s.promises.unlink(t.path)}catch(o){i(r,"WARN","Failed to clean uploaded skill zip",{tempZipPath:t?.path,error:o.message})}}}async function K(e,t,l,a={}){const r=Date.now();if(!e)throw new p("Project ID cannot be empty",{field:"projectId"});const n=null;try{if(t&&!isNaN(Number(t))){const m=Number(t);i(n,"INFO",`[delete-project] Stopping development server, PID: ${m}`,{projectId:e,pid:m});try{await B(l,e,m,{strict:!0}),i(n,"INFO","[delete-project] Development server stopped",{projectId:e})}catch(d){i(n,"WARN",`[delete-project] Failed to stop development server: ${d.message}`,{projectId:e,pid:m})}}const c=[u.join(P.UPLOAD_PROJECT_DIR,e),v(e,a),u.join(P.DIST_TARGET_DIR,e),u.join(P.LOG_BASE_DIR,e)],f=[],w=[];for(const m of c)if(s.existsSync(m))try{await s.promises.rm(m,{recursive:!0,force:!0}),f.push(m),i(n,"INFO",`[delete-project] Directory deleted successfully: ${m}`,{projectId:e})}catch(d){w.push({path:m,error:d.message}),i(n,"ERROR",`[delete-project] Directory deleted failed: ${m}`,{projectId:e,error:d.message})}else i(n,"INFO",`[delete-project] Directory does not exist, skipping deletion: ${m}`,{projectId:e});const y={success:!0,message:`Project ${e} deleted successfully`,projectId:e,deletedDirectories:f,failedDirectories:w};return w.length>0&&(y.message+=`, but ${w.length} directories deleted failed`,i(n,"WARN","[delete-project] Some directories deleted failed",{projectId:e,failedDirs:w})),i(n,"INFO",`[delete-project] Project deleted successfully: ${e}`,{projectId:e,elapsedMs:Date.now()-r}),y}catch(c){throw i(n,"ERROR",`[delete-project] Failed to delete project: ${c.message}`,{projectId:e,originalError:c.message,elapsedMs:Date.now()-r}),c.isOperational?c:new E(`Failed to delete project: ${c.message}`,{projectId:e,originalError:c.message})}}async function Q(e,t,l,a,r={}){const n=Date.now();if(!e)throw new p("Project ID cannot be empty",{field:"projectId"});if(t==null)throw new p("codeVersion cannot be empty",{field:"codeVersion"});const c=Number(t);if(!Number.isFinite(c))throw new p("codeVersion must be a number",{field:"codeVersion"});const f=v(e,r);if(!s.existsSync(f))throw new z("Project does not exist",{projectId:e});const w=u.join(P.UPLOAD_PROJECT_DIR,e),y=`${e}-v${c}.zip`,m=u.join(w,y);if(l!=="LATEST"){if(s.existsSync(m))return i(e,"INFO",`Using existing export file: ${m}`,{projectId:e,zipPath:m}),{success:!0,projectId:e,zipPath:m};throw new z(`Specified version zip file does not exist: ${m}`,{projectId:e,zipPath:m})}const d=u.join(f,"cpage_config.json");let D=!1;try{if(a)try{const h=JSON.stringify(a,null,2);await s.promises.writeFile(d,h,"utf8"),D=!0,i(e,"INFO",`Configuration file created successfully: ${d}`,{projectId:e,configFilePath:d})}catch(h){throw i(e,"ERROR",`Failed to create configuration file: ${h.message}`,{projectId:e,error:h.message}),new b("Failed to create configuration file",{projectId:e,configFilePath:d,originalError:h.message})}i(e,"DEBUG","Start executing export and packaging",{projectId:e,codeVersion:t});const o=await O(e,t,r);return i(e,"INFO",`Project exported successfully: ${o}`,{projectId:e,zipPath:o,elapsedMs:Date.now()-n}),{success:!0,projectId:e,zipPath:o}}catch(o){throw i(e,"ERROR",`Failed to export project: ${o?.message}`,{projectId:e,elapsedMs:Date.now()-n}),o.isOperational?o:new E("Failed to export project",{projectId:e,originalError:o&&o.message?W(o.message):o&&o.message})}finally{if(D&&s.existsSync(d))try{await s.promises.unlink(d),i(e,"INFO",`Temporary configuration file deleted successfully: ${d}`,{projectId:e,configFilePath:d})}catch(o){i(e,"WARN",`Failed to delete temporary configuration file: ${o.message}`,{projectId:e,error:o.message})}}}async function Y(e,t,l={}){const a=Date.now();if(!e)throw new p("Project ID cannot be empty",{field:"projectId"});if(t==null)throw new p("codeVersion cannot be empty",{field:"codeVersion"});try{const r=await O(e,t,l);return i(e,"INFO",`Current version backed up successfully: ${r}`,{projectId:e,zipPath:r,elapsedMs:Date.now()-a}),{success:!0,projectId:e,zipPath:r}}catch(r){throw i(e,"ERROR",`Failed to backup current version: ${r?.message}`,{projectId:e,elapsedMs:Date.now()-a}),r.isOperational?r:new E("Failed to backup current version",{projectId:e,originalError:r&&r.message?W(r.message):r&&r.message})}}export{J as createProject,Z as uploadProject,Y as backupCurrentVersion,Q as exportProject,O as backupProjectOfVersion,U as cleanupProjectDirectory,V as handleFileUpload,H as pushSkillsToWorkspace,K as deleteProject};export default{createProject:J,uploadProject:Z,backupCurrentVersion:Y,exportProject:Q,backupProjectOfVersion:O,cleanupProjectDirectory:U,handleFileUpload:V,pushSkillsToWorkspace:H,deleteProject:K};
@@ -1 +1 @@
1
- import c from"path";import l from"fs";import{log as r}from"../log/logUtils.js";import{BusinessError as v,FileError as g,ResourceError as S}from"../error/errorHandler.js";import{addStartingProject as p,removeStartingProject as P,startDev_NonBlocking as w}from"./processManager.js";import"../error/errorCodes.js";import{stopDevServer as N}from"./stopDevUtils.js";import{removeNodeModules as y}from"../buildDependency/dependencyManager.js";import{createPnpmNpmrc as F}from"../common/npmrcUtils.js";import{extractIsolationContext as d,resolveProjectPath as h}from"../common/projectPathUtils.js";async function O(e,t){r(t,"INFO","Start restarting development server",{projectId:t,requestId:e.requestId});const f=d(e?.query||{}),s=h(t,f),o=c.join(s,"package.json");if(!l.existsSync(o))throw r(t,"WARN","Project missing package.json file",{projectId:t,requestId:e.requestId}),new S("Project missing package.json file",{projectId:t,projectPath:s});let n;try{n=JSON.parse(l.readFileSync(o,"utf8"))}catch(i){throw new g("package.json file format error",{projectId:t,jsonFilePath:o,originalError:i.message})}const a=n.scripts.dev;if(!a)throw r(t,"WARN","Project missing dev script",{projectId:t,requestId:e.requestId}),new v("Project missing dev script",{projectId:t});p(t);try{const i=e.query.pid;await N(e,t,i,{strict:!1,waitForStop:!0}),r(t,"INFO","Start deleting node_modules and lock file",{projectId:t,requestId:e.requestId}),await y(s,t),r(t,"INFO","Create .npmrc file",{projectId:t,requestId:e.requestId}),await F(s,t),r(t,"INFO","Start starting dev server",{projectId:t,requestId:e.requestId});const{pid:m,port:u}=await w({req:e,projectId:t,projectPath:s,devScript:a});return r(t,"INFO","Dev server restart completed",{projectId:t,pid:m,port:u,requestId:e.requestId}),{success:!0,message:"Development server restart successfully",projectId:t,pid:m,port:u}}finally{P(t)}}export{O as restartDevServer};
1
+ import g from"path";import l from"fs";import{log as r}from"../log/logUtils.js";import{BusinessError as f,FileError as v,ResourceError as S}from"../error/errorHandler.js";import{isProjectStarting as w,addStartingProject as N,removeStartingProject as P,startDev_NonBlocking as p}from"./processManager.js";import y from"../error/errorCodes.js";import{stopDevServer as F}from"./stopDevUtils.js";import{removeNodeModules as d}from"../buildDependency/dependencyManager.js";import{createPnpmNpmrc as h}from"../common/npmrcUtils.js";import{copyNodeModulesFromCache as O}from"../common/templateCacheUtils.js";import{extractIsolationContext as R,resolveProjectPath as q}from"../common/projectPathUtils.js";async function x(t,e){r(e,"INFO","Start restarting development server",{projectId:e,requestId:t.requestId});const c=R(t?.query||{}),s=q(e,c),o=g.join(s,"package.json");if(!l.existsSync(o))throw r(e,"WARN","Project missing package.json file",{projectId:e,requestId:t.requestId}),new S("Project missing package.json file",{projectId:e,projectPath:s});let a;try{a=JSON.parse(l.readFileSync(o,"utf8"))}catch(i){throw new v("package.json file format error",{projectId:e,jsonFilePath:o,originalError:i.message})}const n=a.scripts.dev;if(!n)throw r(e,"WARN","Project missing dev script",{projectId:e,requestId:t.requestId}),new f("Project missing dev script",{projectId:e});if(w(e))throw new f("\u8BE5\u9879\u76EE\u6B63\u5728\u91CD\u542F\u4E2D\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5",{projectId:e,code:y.PROJECT_STARTING});N(e);try{const i=t.query.pid;await F(t,e,i,{strict:!1,waitForStop:!0}),r(e,"INFO","Start deleting node_modules and lock file",{projectId:e,requestId:t.requestId}),await d(s,e),r(e,"INFO","Try copying node_modules from template cache",{projectId:e,requestId:t.requestId}),await O(s,e),r(e,"INFO","Create .npmrc file",{projectId:e,requestId:t.requestId}),await h(s,e),r(e,"INFO","Start starting dev server",{projectId:e,requestId:t.requestId});const{pid:m,port:u}=await p({req:t,projectId:e,projectPath:s,devScript:n});return r(e,"INFO","Dev server restart completed",{projectId:e,pid:m,port:u,requestId:t.requestId}),{success:!0,message:"Development server restart successfully",projectId:e,pid:m,port:u}}finally{P(e)}}export{x as restartDevServer};
@@ -1 +1 @@
1
- import v from"path";import i from"fs";import{log as r}from"../log/logUtils.js";import{BusinessError as P,FileError as y,ResourceError as L}from"../error/errorHandler.js";import"../error/errorCodes.js";import{addStartingProject as N,removeStartingProject as w,startDev_NonBlocking as A}from"./processManager.js";import{removeNodeModules as D}from"../buildDependency/dependencyManager.js";import{extractIsolationContext as E,resolveProjectPath as _}from"../common/projectPathUtils.js";async function k(s,e){N(e);try{r(e,"INFO","Start starting development server",{projectId:e,requestId:s.requestId});const d=E(s?.query||{}),o=_(e,d),a=v.join(o,"package.json");if(!i.existsSync(a))throw r(e,"WARN","Project missing package.json file",{projectId:e,requestId:s.requestId}),new L("Project missing package.json file",{projectId:e,projectPath:o});let u;try{u=JSON.parse(i.readFileSync(a,"utf8"))}catch(t){throw new y("package.json file format error",{projectId:e,jsonFilePath:a,originalError:t.message})}const m=u.scripts.dev;if(!m)throw r(e,"WARN","Project missing dev script",{projectId:e,requestId:s.requestId}),new P("Project missing dev script, please add dev script in package.json",{projectId:e});try{if(process.platform==="linux"){const c=typeof process.report?.getReport=="function"?process.report.getReport():null,p=c&&c.header&&c.header.glibcVersionRuntime,l=!p,g=v.join(o,"node_modules",".pnpm");if(i.existsSync(g)){const f=await i.promises.readdir(g,{withFileTypes:!0}),S=f.some(n=>n.isDirectory()&&(n.name||"").includes("@rollup+rollup-linux-x64-gnu")),x=f.some(n=>n.isDirectory()&&(n.name||"").includes("@rollup+rollup-linux-x64-musl"));(l&&S||!l&&x)&&(r(e,"WARN","Detected Rollup native package does not match libc, clean dependencies and reinstall",{projectId:e,isMusl:l,glibcVersion:p||null}),await D(o,e))}}}catch(t){r(e,"WARN","Linux native package matching detection failed (ignore continue)",{error:t&&t.message})}try{process.env.ROLLUP_WASM=process.env.ROLLUP_WASM||"1",process.env.ROLLUP_DISABLE_NATIVE=process.env.ROLLUP_DISABLE_NATIVE||"1"}catch{}r(e,"INFO","Start executing dev script in non-blocking mode",{projectId:e,requestId:s.requestId});const{pid:h,port:R}=await A({req:s,projectId:e,projectPath:o,devScript:m});return{success:!0,message:"Development server started",projectId:e,pid:h,port:R}}finally{w(e)}}export{k as startDevServer};
1
+ import v from"path";import i from"fs";import{log as r}from"../log/logUtils.js";import{BusinessError as R,FileError as y,ResourceError as L}from"../error/errorHandler.js";import w from"../error/errorCodes.js";import{isProjectStarting as N,addStartingProject as A,removeStartingProject as E,startDev_NonBlocking as _}from"./processManager.js";import{removeNodeModules as D}from"../buildDependency/dependencyManager.js";import{extractIsolationContext as O,resolveProjectPath as k}from"../common/projectPathUtils.js";async function F(s,e){if(N(e))throw new R("\u8BE5\u9879\u76EE\u6B63\u5728\u542F\u52A8\u4E2D\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5",{projectId:e,code:w.PROJECT_STARTING});A(e);try{r(e,"INFO","Start starting development server",{projectId:e,requestId:s.requestId});const d=O(s?.query||{}),o=k(e,d),a=v.join(o,"package.json");if(!i.existsSync(a))throw r(e,"WARN","Project missing package.json file",{projectId:e,requestId:s.requestId}),new L("Project missing package.json file",{projectId:e,projectPath:o});let u;try{u=JSON.parse(i.readFileSync(a,"utf8"))}catch(t){throw new y("package.json file format error",{projectId:e,jsonFilePath:a,originalError:t.message})}const m=u.scripts.dev;if(!m)throw r(e,"WARN","Project missing dev script",{projectId:e,requestId:s.requestId}),new R("Project missing dev script, please add dev script in package.json",{projectId:e});try{if(process.platform==="linux"){const c=typeof process.report?.getReport=="function"?process.report.getReport():null,p=c&&c.header&&c.header.glibcVersionRuntime,l=!p,g=v.join(o,"node_modules",".pnpm");if(i.existsSync(g)){const f=await i.promises.readdir(g,{withFileTypes:!0}),P=f.some(n=>n.isDirectory()&&(n.name||"").includes("@rollup+rollup-linux-x64-gnu")),x=f.some(n=>n.isDirectory()&&(n.name||"").includes("@rollup+rollup-linux-x64-musl"));(l&&P||!l&&x)&&(r(e,"WARN","Detected Rollup native package does not match libc, clean dependencies and reinstall",{projectId:e,isMusl:l,glibcVersion:p||null}),await D(o,e))}}}catch(t){r(e,"WARN","Linux native package matching detection failed (ignore continue)",{error:t&&t.message})}try{process.env.ROLLUP_WASM=process.env.ROLLUP_WASM||"1",process.env.ROLLUP_DISABLE_NATIVE=process.env.ROLLUP_DISABLE_NATIVE||"1"}catch{}r(e,"INFO","Start executing dev script in non-blocking mode",{projectId:e,requestId:s.requestId});const{pid:h,port:S}=await _({req:s,projectId:e,projectPath:o,devScript:m});return{success:!0,message:"Development server started",projectId:e,pid:h,port:S}}finally{E(e)}}export{F as startDevServer};
@@ -1,17 +1,17 @@
1
- import{exec as N,spawn as T}from"child_process";import p from"path";import k from"fs";import{log as m}from"../log/logUtils.js";function x(o){try{return k.statSync(o).mtimeMs}catch{return 0}}function v(o){const u=p.join(o,"package.json"),l=p.join(o,"package-lock.json"),r=p.join(o,"yarn.lock"),s=p.join(o,"node_modules");if(!k.existsSync(s))return!0;const e=x(u),g=Math.max(x(l),x(r)),M=x(s);return Math.max(e,g)>M}async function I(o,u=null){const l=p.join(o,"node_modules"),r=u||p.basename(o);if(k.existsSync(l)){m(r,"INFO","Found node_modules folder, deleting",{projectPath:o,nodeModulesPath:l});try{await k.promises.rm(l,{recursive:!0,force:!0}),m(r,"INFO","node_modules folder deleted successfully",{projectPath:o,nodeModulesPath:l})}catch(n){m(r,"WARN",`Failed to delete node_modules folder: ${n.message}`,{projectPath:o,nodeModulesPath:l,error:n.message})}}const s=["package-lock.json","yarn.lock","pnpm-lock.yaml"];for(const n of s){const e=p.join(o,n);if(k.existsSync(e)){m(r,"INFO",`Found ${n} file, deleting`,{projectPath:o,lockFilePath:e});try{await k.promises.unlink(e),m(r,"INFO",`${n} file deleted successfully`,{projectPath:o,lockFilePath:e})}catch(g){m(r,"WARN",`Failed to delete ${n} file: ${g.message}`,{projectPath:o,lockFilePath:e,error:g.message})}}}}function O(o,u,l,r={}){const{outStream:s,tempOutStream:n,safeWrite:e}=r;return s&&n&&e?new Promise((g,M)=>{const F=`cd ${l} && pnpm install --prefer-offline`,d=Date.now(),y=`Start installing dependencies
1
+ import{exec as v,spawn as O}from"child_process";import f from"path";import p from"fs";import{log as m}from"../log/logUtils.js";function k(o){try{return p.statSync(o).mtimeMs}catch{return 0}}function _(o){const u=f.join(o,"package.json"),l=f.join(o,"package-lock.json"),r=f.join(o,"yarn.lock"),s=f.join(o,"node_modules");if(!p.existsSync(s))return!0;const e=k(u),M=Math.max(k(l),k(r)),x=k(s);return Math.max(e,M)>x}async function W(o,u=null){const l=f.join(o,"node_modules"),r=u||f.basename(o);if(p.existsSync(l)){m(r,"INFO","Found node_modules folder, deleting",{projectPath:o,nodeModulesPath:l});try{await p.promises.rm(l,{recursive:!0,force:!0}),m(r,"INFO","node_modules folder deleted successfully",{projectPath:o,nodeModulesPath:l})}catch(n){m(r,"WARN",`Failed to delete node_modules folder: ${n.message}`,{projectPath:o,nodeModulesPath:l,error:n.message})}}const s=["package-lock.json","yarn.lock","pnpm-lock.yaml"];for(const n of s){const e=f.join(o,n);if(p.existsSync(e)){m(r,"INFO",`Found ${n} file, deleting`,{projectPath:o,lockFilePath:e});try{await p.promises.unlink(e),m(r,"INFO",`${n} file deleted successfully`,{projectPath:o,lockFilePath:e})}catch(M){m(r,"WARN",`Failed to delete ${n} file: ${M.message}`,{projectPath:o,lockFilePath:e,error:M.message})}}}}function h(o,u,l,r={}){const{outStream:s,tempOutStream:n,safeWrite:e}=r,M=f.join(l,"node_modules"),x=p.existsSync(M);return s&&n&&e?new Promise((E,w)=>{const F=`cd ${l} && pnpm install --prefer-offline`,d=Date.now(),g=`Start installing dependencies (${x?"incremental (node_modules from cache)":"full install"})
2
2
  Command: pnpm install --prefer-offline
3
- `;e(s,y,"Main log"),e(n,y,"Temp log");let f=0;const D=setInterval(()=>{f++;const t=`Installing dependencies... (Elapsed time: ${Math.floor((Date.now()-d)/1e3)} seconds)
4
- `;e(s,t,"Main log"),e(n,t,"Temp log")},5e3),$=T("sh",["-c",F],{cwd:l,env:process.env,stdio:["ignore","pipe","pipe"]});let S="",c="";$.stdout&&$.stdout.on("data",i=>{const t=i.toString();S+=t,e(s,t,"Main log"),e(n,t,"Temp log")}),$.stderr&&$.stderr.on("data",i=>{const t=i.toString();c+=t,e(s,t,"Main log"),e(n,t,"Temp log")}),$.on("exit",(i,t)=>{clearInterval(D);const w=((Date.now()-d)/1e3).toFixed(2);if(i!==0){const a=`Dependency installation failed (Elapsed time: ${w} seconds)
3
+ `;e(s,g,"Main log"),e(n,g,"Temp log");let I=0;const N=setInterval(()=>{I++;const t=`Installing dependencies... (Elapsed time: ${Math.floor((Date.now()-d)/1e3)} seconds)
4
+ `;e(s,t,"Main log"),e(n,t,"Temp log")},5e3),y=O("sh",["-c",F],{cwd:l,env:process.env,stdio:["ignore","pipe","pipe"]});let D="",c="";y.stdout&&y.stdout.on("data",i=>{const t=i.toString();D+=t,e(s,t,"Main log"),e(n,t,"Temp log")}),y.stderr&&y.stderr.on("data",i=>{const t=i.toString();c+=t,e(s,t,"Main log"),e(n,t,"Temp log")}),y.on("exit",(i,t)=>{clearInterval(N);const $=((Date.now()-d)/1e3).toFixed(2);if(i!==0){const a=`Dependency installation failed (Elapsed time: ${$} seconds)
5
5
  Exit code: ${i}, Signal: ${t}
6
6
  ${c||"No error information"}
7
- `;return e(s,a,"Main log"),e(n,a,"Temp log"),M(new Error(`Dependency installation failed: Exit code ${i}, Signal ${t}
7
+ `;return e(s,a,"Main log"),e(n,a,"Temp log"),w(new Error(`Dependency installation failed: Exit code ${i}, Signal ${t}
8
8
  ${c}`))}if(c&&c.includes("Error")&&!c.includes("warning")){const a=`Warning occurred during dependency installation: ${c}
9
- `;e(s,a,"Main log"),e(n,a,"Temp log")}const E=`\u2713 Dependency installation successful (Elapsed time: ${w} seconds)
10
- `;if(e(s,E,"Main log"),e(n,E,"Temp log"),S||c){const a=`Installation details:
11
- ${S||"(No standard output)"}${c?`
9
+ `;e(s,a,"Main log"),e(n,a,"Temp log")}const T=`\u2713 Dependency installation successful (Elapsed time: ${$} seconds)
10
+ `;if(e(s,T,"Main log"),e(n,T,"Temp log"),D||c){const a=`Installation details:
11
+ ${D||"(No standard output)"}${c?`
12
12
  Warning information:
13
13
  `+c:""}
14
14
  `;e(s,a,"Main log"),e(n,a,"Temp log")}else{const a=`(Silent mode: No detailed output, dependency successfully linked from store to node_modules)
15
- `;e(s,a,"Main log"),e(n,a,"Temp log")}g(S)}),$.on("error",i=>{clearInterval(D);const w=`Dependency installation process error (Elapsed time: ${((Date.now()-d)/1e3).toFixed(2)} seconds): ${i.message}
16
- `;e(s,w,"Main log"),e(n,w,"Temp log"),M(new Error(`Dependency installation failed: ${i.message}`))})}):new Promise((g,M)=>{const F=`cd ${l} && pnpm install --prefer-offline --reporter=silent --loglevel=error`;m(u,"INFO","Start executing dependency installation command",{command:F,projectPath:l}),N(F,{maxBuffer:10*1024*1024,env:process.env},(d,y,f)=>{if(d)return m(u,"ERROR","Dependency installation failed",{error:d.message,code:d.code,stderr:f||d.message,stdout:y||""}),M(new Error(`Dependency installation failed: ${d.message}
17
- ${f||d.message}`));f&&f.includes("Error")&&!f.includes("warning")&&m(u,"WARN","Warning or error occurred during dependency installation",{stderr:f}),m(u,"INFO","Dependency installation completed",{stdout:y.substring(0,500)}),g(y)})})}export{x as getFileMtime,v as shouldInstallDeps,O as installDependencies,I as removeNodeModules};
15
+ `;e(s,a,"Main log"),e(n,a,"Temp log")}E(D)}),y.on("error",i=>{clearInterval(N);const $=`Dependency installation process error (Elapsed time: ${((Date.now()-d)/1e3).toFixed(2)} seconds): ${i.message}
16
+ `;e(s,$,"Main log"),e(n,$,"Temp log"),w(new Error(`Dependency installation failed: ${i.message}`))})}):new Promise((E,w)=>{const F=`cd ${l} && pnpm install --prefer-offline --reporter=silent --loglevel=error`;m(u,"INFO","Start executing dependency installation command",{command:F,projectPath:l,installMode:x?"incremental":"full"}),v(F,{maxBuffer:10*1024*1024,env:process.env},(d,S,g)=>{if(d)return m(u,"ERROR","Dependency installation failed",{error:d.message,code:d.code,stderr:g||d.message,stdout:S||""}),w(new Error(`Dependency installation failed: ${d.message}
17
+ ${g||d.message}`));g&&g.includes("Error")&&!g.includes("warning")&&m(u,"WARN","Warning or error occurred during dependency installation",{stderr:g}),m(u,"INFO","Dependency installation completed",{stdout:S.substring(0,500)}),E(S)})})}export{k as getFileMtime,_ as shouldInstallDeps,h as installDependencies,W as removeNodeModules};
@@ -1,7 +1,7 @@
1
- import c from"fs";import i from"path";import{log as a,getCSTDateTimeString as l}from"../log/logUtils.js";async function o(r,m=null){const t=m||i.basename(r),e=i.join(r,".npmrc"),n=`# pnpm \u78C1\u76D8\u7A7A\u95F4\u4F18\u5316\u914D\u7F6E
2
- # \u81EA\u52A8\u751F\u6210\u4E8E ${l()}
3
-
4
- package-import-method=hardlink
1
+ import m from"fs";import l from"path";import{log as i,getCSTDateTimeString as f}from"../log/logUtils.js";import{detectFilesystemType as g}from"./templateCacheUtils.js";async function y(s,d=null){const c=d||l.basename(s),e=l.join(s,".npmrc"),t=g(s),r=t==="fuse"?"copy":"hardlink",u=`# pnpm \u4F18\u5316\u914D\u7F6E
2
+ # \u81EA\u52A8\u751F\u6210\u4E8E ${f()}
3
+ # \u6587\u4EF6\u7CFB\u7EDF\u7C7B\u578B: ${t}
4
+ package-import-method=${r}
5
5
  auto-install-peers=true
6
6
  registry=https://registry.npmmirror.com
7
- `;try{return c.existsSync(e)?(a(t,"INFO",".npmrc file already exists, skip creation",{projectPath:r,npmrcPath:e}),{success:!0,created:!1,message:".npmrc file already exists",npmrcPath:e}):(await c.promises.writeFile(e,n,"utf8"),a(t,"INFO",".npmrc file created successfully",{projectPath:r,npmrcPath:e}),{success:!0,created:!0,message:".npmrc file created successfully",npmrcPath:e})}catch(s){return a(t,"WARN",`.npmrc file creation failed: ${s.message}`,{projectPath:r,npmrcPath:e,error:s.message}),{success:!1,created:!1,message:`.npmrc file creation failed: ${s.message}`,error:s.message}}}export{o as createPnpmNpmrc};
7
+ `;try{const n=m.existsSync(e);if(n){const a=m.readFileSync(e,"utf8").match(/package-import-method\s*=\s*(\S+)/),p=a?a[1]:null;if(p===r)return i(c,"INFO",".npmrc already optimal, skip creation",{projectPath:s,npmrcPath:e,importMethod:r,fsType:t}),{success:!0,created:!1,message:".npmrc already optimal",npmrcPath:e,importMethod:r,fsType:t};i(c,"INFO",".npmrc needs update",{projectPath:s,npmrcPath:e,existingMethod:p,newMethod:r,fsType:t})}await m.promises.writeFile(e,u,"utf8");const o=n?"updated":"created";return i(c,"INFO",`.npmrc ${o} successfully`,{projectPath:s,npmrcPath:e,importMethod:r,fsType:t}),{success:!0,created:!0,message:`.npmrc ${o} successfully`,npmrcPath:e,importMethod:r,fsType:t}}catch(n){return i(c,"WARN",`.npmrc file creation failed: ${n.message}`,{projectPath:s,npmrcPath:e,error:n.message}),{success:!1,created:!1,message:`.npmrc file creation failed: ${n.message}`,error:n.message}}}export{y as createPnpmNpmrc};
@@ -0,0 +1,6 @@
1
+ import n from"fs";import o from"path";import{execSync as d}from"child_process";import{log as i}from"../log/logUtils.js";function f(){if(process.env.TEMPLATE_CACHE_DIR)return process.env.TEMPLATE_CACHE_DIR;if(n.existsSync("/local-cache"))return"/local-cache/templates";const c=process.env.PROJECT_WORKSPACE_DIR||"/app/project_workspace";return o.join(o.dirname(c),".template-cache")}function h(c){try{const r=n.readFileSync("/proc/mounts","utf8").split(`
2
+ `);let e="",t="local";for(const p of r){const s=p.split(/\s+/);if(s.length<3)continue;const l=s[1],u=s[2];c.startsWith(l)&&l.length>e.length&&(e=l,t=u)}return t.startsWith("fuse")?"fuse":"local"}catch{return"local"}}function y(c){try{const a=o.join(c,"package.json");if(!n.existsSync(a))return null;const r=JSON.parse(n.readFileSync(a,"utf8")),e=(r.name||"").toLowerCase(),t=r.dependencies||{};if(e.includes("vue3")||e.includes("vue-vite"))return"vue3";if(e.includes("react-vite")||e.includes("react"))return"react";if(t.vue)return"vue3";if(t.react)return"react"}catch{}return null}async function C(c){const a=f();i("CACHE","INFO",`Template cache base dir: ${a}`);const r=[{name:"vue3",zip:"vue3-vite-template.zip"},{name:"react",zip:"react-vite-template.zip"}];n.mkdirSync(a,{recursive:!0});for(const e of r){const t=o.join(a,e.name),p=o.join(t,".cache-ready");if(n.existsSync(p)){i("CACHE","INFO",`Template cache already ready: ${e.name}`,{cachePath:t});continue}const s=o.join(c,e.zip);if(!n.existsSync(s)){i("CACHE","WARN",`Template zip not found, skip: ${s}`);continue}i("CACHE","INFO",`Warming up template cache: ${e.name}`,{zipPath:s,cachePath:t});const l=Date.now();try{n.mkdirSync(t,{recursive:!0}),d(`unzip -o "${s}" -d "${t}"`,{stdio:"pipe"}),g(t),n.writeFileSync(o.join(t,".npmrc"),`# Template cache npmrc - auto-generated
3
+ package-import-method=copy
4
+ auto-install-peers=true
5
+ registry=https://registry.npmmirror.com
6
+ `,"utf8"),i("CACHE","INFO",`Running pnpm install for ${e.name} cache...`),d(`cd "${t}" && pnpm install --prefer-offline`,{stdio:"pipe",timeout:18e4,env:process.env}),n.writeFileSync(p,new Date().toISOString(),"utf8");const m=((Date.now()-l)/1e3).toFixed(1);i("CACHE","INFO",`Template cache ready: ${e.name}`,{elapsed:`${m}s`,cachePath:t})}catch(u){i("CACHE","ERROR",`Template cache failed: ${e.name}`,{error:u.message});try{n.rmSync(t,{recursive:!0,force:!0})}catch{}}}}async function S(c,a){const r=a||o.basename(c),e=y(c);if(!e)return i(r,"WARN","Cannot detect template type, skip cache copy",{projectPath:c}),{cached:!1,reason:"unknown-template"};const t=f(),p=o.join(t,e,"node_modules"),s=o.join(t,e,".cache-ready");if(!n.existsSync(s)||!n.existsSync(p))return i(r,"INFO",`Template cache not ready: ${e}`,{cacheNodeModules:p}),{cached:!1,reason:"cache-not-ready"};const l=o.join(c,"node_modules");if(n.existsSync(l))return i(r,"INFO","node_modules already exists, skip cache copy"),{cached:!1,reason:"already-exists"};const u=Date.now();i(r,"INFO",`Copying node_modules from cache: ${e}`,{source:p,target:l});try{d(`cp -a "${p}" "${l}"`,{timeout:6e4,stdio:"pipe"});const m=((Date.now()-u)/1e3).toFixed(1);return i(r,"INFO","node_modules copied from cache successfully",{templateType:e,elapsed:`${m}s`}),{cached:!0,templateType:e,elapsed:m}}catch(m){i(r,"WARN",`Failed to copy node_modules from cache: ${m.message}`,{templateType:e});try{n.rmSync(l,{recursive:!0,force:!0})}catch{}return{cached:!1,reason:"copy-failed",error:m.message}}}function g(c){try{const r=n.readdirSync(c,{withFileTypes:!0}).filter(e=>!e.name.startsWith(".")&&e.name!=="node_modules");if(r.length===1&&r[0].isDirectory()){const e=o.join(c,r[0].name),t=o.join(c,`..tmp_${Date.now()}`);n.renameSync(e,t);const p=n.readdirSync(t);for(const s of p)n.renameSync(o.join(t,s),o.join(c,s));n.rmdirSync(t)}}catch{}}export{f as getCacheBaseDir,h as detectFilesystemType,y as detectTemplateType,C as warmupTemplateCache,S as copyNodeModulesFromCache};
@@ -1 +1 @@
1
- import s from"fs";import m from"path";import{log as t}from"../log/logUtils.js";import{copyDirectoryFiltered as $}from"./backupUtils.js";import{ValidationError as l,BusinessError as y,SystemError as x}from"../error/errorHandler.js";import{createPnpmNpmrc as E}from"../common/npmrcUtils.js";import{resolveProjectPath as w}from"../common/projectPathUtils.js";async function F(e,i,c={}){if(!e)throw new l("Source project ID cannot be empty",{field:"sourceProjectId"});if(!i)throw new l("Target project ID cannot be empty",{field:"targetProjectId"});const u=c.sourceIsolationContext||{},h=c.targetIsolationContext||{},a=w(e,u),r=w(i,h);if(!s.existsSync(a))throw new y(`Source project ${e} does not exist`,{sourceProjectId:e,sourceProjectPath:a});if(s.existsSync(r))throw new y(`Target project ${i} already exists`,{targetProjectId:i,targetProjectPath:r});try{t(i,"INFO",`Start copying project from ${e} to ${i}`,{sourceProjectId:e,targetProjectId:i}),s.mkdirSync(r,{recursive:!0}),t(i,"INFO",`Target project directory created successfully: ${r}`,{targetProjectId:i});const o=await s.promises.readdir(a,{withFileTypes:!0});for(const n of o){const f=m.join(a,n.name),p=m.join(r,n.name);n.isDirectory()?(await s.promises.mkdir(p,{recursive:!0}),await $(f,p)):n.isFile()&&(await s.promises.mkdir(m.dirname(p),{recursive:!0}),await s.promises.copyFile(f,p))}return t(i,"INFO",`Project copied successfully: ${i}`,{sourceProjectId:e,targetProjectId:i}),await E(r,i),{success:!0,message:`Project ${e} successfully copied to ${i}`,sourceProjectId:e,targetProjectId:i,targetProjectPath:r}}catch(o){if(t(i,"ERROR",`Copy project failed: ${o.message}`,{sourceProjectId:e,targetProjectId:i}),s.existsSync(r))try{await s.promises.rm(r,{recursive:!0,force:!0}),t(i,"INFO","Copy failed, target project directory cleaned",{targetProjectId:i})}catch(n){t(i,"ERROR",`Clean target project directory failed: ${n.message}`,{targetProjectId:i,originalError:n.message})}throw o.isOperational?o:new x(`Copy project failed: ${o.message}`,{sourceProjectId:e,targetProjectId:i,sourceProjectPath:a,targetProjectPath:r,originalError:o.message})}}export{F as copyProject};
1
+ import r from"fs";import m from"path";import{log as a}from"../log/logUtils.js";import{copyDirectoryFiltered as $}from"./backupUtils.js";import{ValidationError as l,BusinessError as y,SystemError as x}from"../error/errorHandler.js";import{createPnpmNpmrc as F}from"../common/npmrcUtils.js";import{copyNodeModulesFromCache as C}from"../common/templateCacheUtils.js";import{resolveProjectPath as w}from"../common/projectPathUtils.js";async function E(e,i,c={}){if(!e)throw new l("Source project ID cannot be empty",{field:"sourceProjectId"});if(!i)throw new l("Target project ID cannot be empty",{field:"targetProjectId"});const u=c.sourceIsolationContext||{},h=c.targetIsolationContext||{},t=w(e,u),o=w(i,h);if(!r.existsSync(t))throw new y(`Source project ${e} does not exist`,{sourceProjectId:e,sourceProjectPath:t});if(r.existsSync(o))throw new y(`Target project ${i} already exists`,{targetProjectId:i,targetProjectPath:o});try{a(i,"INFO",`Start copying project from ${e} to ${i}`,{sourceProjectId:e,targetProjectId:i}),r.mkdirSync(o,{recursive:!0}),a(i,"INFO",`Target project directory created successfully: ${o}`,{targetProjectId:i});const s=await r.promises.readdir(t,{withFileTypes:!0});for(const n of s){const f=m.join(t,n.name),p=m.join(o,n.name);n.isDirectory()?(await r.promises.mkdir(p,{recursive:!0}),await $(f,p)):n.isFile()&&(await r.promises.mkdir(m.dirname(p),{recursive:!0}),await r.promises.copyFile(f,p))}return a(i,"INFO",`Project copied successfully: ${i}`,{sourceProjectId:e,targetProjectId:i}),await C(o,i),await F(o,i),{success:!0,message:`Project ${e} successfully copied to ${i}`,sourceProjectId:e,targetProjectId:i,targetProjectPath:o}}catch(s){if(a(i,"ERROR",`Copy project failed: ${s.message}`,{sourceProjectId:e,targetProjectId:i}),r.existsSync(o))try{await r.promises.rm(o,{recursive:!0,force:!0}),a(i,"INFO","Copy failed, target project directory cleaned",{targetProjectId:i})}catch(n){a(i,"ERROR",`Clean target project directory failed: ${n.message}`,{targetProjectId:i,originalError:n.message})}throw s.isOperational?s:new x(`Copy project failed: ${s.message}`,{sourceProjectId:e,targetProjectId:i,sourceProjectPath:t,targetProjectPath:o,originalError:s.message})}}export{E as copyProject};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "nuwax-file-server",
3
3
  "displayName": "nuwax-file-server",
4
- "version": "1.2.9",
4
+ "version": "1.3.0-beta.4",
5
5
  "description": "Cross-platform file service deployment tool with start/stop/restart CLI commands",
6
6
  "type": "module",
7
7
  "main": "index.js",