@fnet/cli 0.4.26 → 0.5.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/dist/fbin/index.js +15 -0
- package/dist/fnet/index.0jcm9pn5.js +1 -0
- package/dist/fnet/index.2084z2ed.js +2 -0
- package/dist/fnet/index.33f1ggpr.js +1 -0
- package/dist/fnet/index.3exge2js.js +1 -0
- package/dist/fnet/index.3kfx538h.js +1 -0
- package/dist/fnet/index.490y87nc.js +1 -0
- package/dist/fnet/index.4g9yezkq.js +1 -0
- package/dist/fnet/index.4jkat7r4.js +1 -0
- package/dist/fnet/index.7crx8ky1.js +1 -0
- package/dist/fnet/index.7vw06nrn.js +1 -0
- package/dist/fnet/index.9567fa9x.js +1 -0
- package/dist/fnet/index.gh75wt1m.js +1 -0
- package/dist/fnet/index.hzsfswvp.js +1 -0
- package/dist/fnet/index.jgpc3grc.js +1 -0
- package/dist/fnet/index.js +20 -0
- package/dist/fnet/index.p0zb7e1b.js +1 -0
- package/dist/fnet/index.r19p3bpa.js +2 -0
- package/dist/fnet/index.r82rtnmz.js +1 -0
- package/dist/fnet/index.s662t98v.js +1 -0
- package/dist/fnet/index.w74dpnpn.js +1 -0
- package/dist/fnet/index.xeaw5xa9.js +1 -0
- package/dist/fnet/index.zm4kesg6.js +1 -0
- package/dist/fnode/index.05n3mvs9.js +2 -0
- package/dist/fnode/index.2hc9tbyx.js +1 -0
- package/dist/fnode/index.2pnjg6dc.js +1 -0
- package/dist/fnode/index.9qgtmxq3.js +2 -0
- package/dist/fnode/index.b1q7y05p.js +1 -0
- package/dist/fnode/index.bhapgrs7.js +1 -0
- package/dist/fnode/index.cvxrf34y.js +2 -0
- package/dist/fnode/index.dp9wyahp.js +1 -0
- package/dist/fnode/index.eqxmcpdc.js +1 -0
- package/dist/fnode/index.f2tb0x3t.js +1 -0
- package/dist/fnode/index.fbzv6wwf.js +1 -0
- package/dist/fnode/index.gazd9raq.js +1 -0
- package/dist/fnode/index.hv4s25f0.js +3 -0
- package/dist/fnode/index.j5z7dtsx.js +1 -0
- package/dist/fnode/index.js +10 -0
- package/dist/fnode/index.kb4e4bxf.js +1 -0
- package/dist/fnode/index.qn0schqp.js +1 -0
- package/dist/fnode/index.rht29phd.js +1 -0
- package/dist/fnode/index.rzsfmek6.js +3 -0
- package/dist/fnode/index.s0nk6cv8.js +4 -0
- package/dist/fnode/index.s66v6wt4.js +1 -0
- package/dist/fnode/index.sv7v0y60.js +1 -0
- package/dist/fnode/index.tgkhgnrp.js +1 -0
- package/dist/fnode/index.vq706f75.js +1 -0
- package/dist/fnode/index.y8pvdcny.js +1 -0
- package/dist/fnode/index.z4vz93ww.js +1 -0
- package/dist/frun/index.js +2 -0
- package/dist/fservice/index.js +19 -0
- package/dist/fservice/index.q01yvaz0.js +2 -0
- package/package.json +74 -57
- package/readme.md +298 -0
- package/template/fnet/core/assert.js +6 -0
- package/template/fnet/core/message.js +3 -0
- package/template/fnet/core/object.js +47 -0
- package/template/fnet/core/sleep.js +5 -0
- package/template/fnet/node/.gitignore.njk +9 -0
- package/template/fnet/node/build.js.njk +153 -0
- package/template/fnet/node/package.json.njk +121 -0
- package/template/fnet/node/readme.md.njk +21 -0
- package/template/fnet/node/rollup.config.mjs.njk +498 -0
- package/template/fnet/node/src/app/index.html.njk +67 -0
- package/template/fnet/node/src/app/index.js.njk +36 -0
- package/template/fnet/node/src/cli/index.js.njk +9 -0
- package/template/fnet/node/src/cli/index.js.v1.njk +318 -0
- package/template/fnet/node/src/cli/v2/core/args-parser.njk +10 -0
- package/template/fnet/node/src/cli/v2/core/imports.njk +31 -0
- package/template/fnet/node/src/cli/v2/core/run-wrapper.njk +25 -0
- package/template/fnet/node/src/cli/v2/index.js.njk +184 -0
- package/template/fnet/node/src/cli/v2/modes/default/extend.njk +11 -0
- package/template/fnet/node/src/cli/v2/modes/default/standard.njk +20 -0
- package/template/fnet/node/src/cli/v2/modes/http/builtin.njk +66 -0
- package/template/fnet/node/src/cli/v2/modes/http/imports.njk +9 -0
- package/template/fnet/node/src/cli/v2/modes/http/index.njk +12 -0
- package/template/fnet/node/src/cli/v2/modes/mcp/imports.njk +33 -0
- package/template/fnet/node/src/cli/v2/modes/mcp/index.njk +30 -0
- package/template/fnet/node/src/cli/v2/modes/mcp/server-setup.njk +15 -0
- package/template/fnet/node/src/cli/v2/modes/mcp/tool-handlers.njk +46 -0
- package/template/fnet/node/src/cli/v2/modes/mcp/transport-http.njk +222 -0
- package/template/fnet/node/src/cli/v2/modes/mcp/transport-stdio.njk +9 -0
- package/template/fnet/node/src/cli/v2/modes/pipeline/imports.njk +9 -0
- package/template/fnet/node/src/cli/v2/modes/pipeline/index.njk +113 -0
- package/template/fnet/node/src/cli/v2/modes/webhook/imports.njk +9 -0
- package/template/fnet/node/src/cli/v2/modes/webhook/index.njk +127 -0
- package/template/fnet/node/src/cli/v2/modes/websocket/imports.njk +15 -0
- package/template/fnet/node/src/cli/v2/modes/websocket/index.njk +104 -0
- package/template/fnet/node/src/default/blocks/assign.js.njk +15 -0
- package/template/fnet/node/src/default/blocks/call.js.njk +110 -0
- package/template/fnet/node/src/default/blocks/for.js.njk +71 -0
- package/template/fnet/node/src/default/blocks/form.js.njk +31 -0
- package/template/fnet/node/src/default/blocks/http.js.njk +135 -0
- package/template/fnet/node/src/default/blocks/modules.js.njk +15 -0
- package/template/fnet/node/src/default/blocks/new.js.njk +42 -0
- package/template/fnet/node/src/default/blocks/next.js.njk +15 -0
- package/template/fnet/node/src/default/blocks/output.js.njk +15 -0
- package/template/fnet/node/src/default/blocks/parallel.js.njk +64 -0
- package/template/fnet/node/src/default/blocks/pipeline.js.njk +109 -0
- package/template/fnet/node/src/default/blocks/raise.js.njk +15 -0
- package/template/fnet/node/src/default/blocks/retry.js.njk +70 -0
- package/template/fnet/node/src/default/blocks/return.js.njk +21 -0
- package/template/fnet/node/src/default/blocks/schedule.js.njk +66 -0
- package/template/fnet/node/src/default/blocks/signal.js.njk +15 -0
- package/template/fnet/node/src/default/blocks/steps.js.njk +47 -0
- package/template/fnet/node/src/default/blocks/switch.js.njk +49 -0
- package/template/fnet/node/src/default/blocks/tryexcept.js.njk +80 -0
- package/template/fnet/node/src/default/blocks/wait.js.njk +14 -0
- package/template/fnet/node/src/default/engine.js.njk +79 -0
- package/template/fnet/node/src/default/input.args.js.njk +125 -0
- package/template/fnet/node/src/default/macros/block-assign.js.njk +3 -0
- package/template/fnet/node/src/default/macros/block-body-header.js.njk +7 -0
- package/template/fnet/node/src/default/macros/block-entry-args.js.njk +2 -0
- package/template/fnet/node/src/default/macros/block-footer.js.njk +3 -0
- package/template/fnet/node/src/default/macros/block-header.js.njk +11 -0
- package/template/fnet/node/src/default/macros/block-library-header.js.njk +43 -0
- package/template/fnet/node/src/default/macros/block-modules-header.js.njk +8 -0
- package/template/fnet/node/src/default/macros/block-modules.js.njk +29 -0
- package/template/fnet/node/src/default/macros/block-next-header.js.njk +6 -0
- package/template/fnet/node/src/default/macros/block-next.js.njk +21 -0
- package/template/fnet/node/src/default/macros/block-run-footer.js.njk +7 -0
- package/template/fnet/node/src/default/macros/block-run-form.js.njk +32 -0
- package/template/fnet/node/src/default/macros/block-run-header.js.njk +43 -0
- package/template/fnet/node/src/default/macros/block-signal.js.njk +3 -0
- package/template/fnet/node/src/default/macros/page.js.njk +8 -0
- package/template/fnet/node/src/default/types/block.js.njk +133 -0
- package/template/fnet/node/src/default/workflow.js.njk +125 -0
- package/template/fnet/node/tsconfig.json.njk +16 -0
- package/template/fnet/project/.gitignore.njk +7 -0
- package/template/fnet/project/.vscode/launch.json.njk +41 -0
- package/template/fnet/project/.vscode/tasks.json.njk +46 -0
- package/template/fnet/project/fnet/flows.yaml.njk +4 -0
- package/template/fnet/project/fnet/targets.yaml.njk +7 -0
- package/template/fnet/project/fnet.yaml.njk +3 -0
- package/template/fnode/node/.gitignore.njk +9 -0
- package/template/fnode/node/build.js.njk +149 -0
- package/template/fnode/node/package.json.njk +121 -0
- package/template/fnode/node/readme.md.njk +21 -0
- package/template/fnode/node/rollup.config.mjs.njk +498 -0
- package/template/fnode/node/src/app/index-js-with-react-host.njk +50 -0
- package/template/fnode/node/src/app/index-js-without-react-host.njk +23 -0
- package/template/fnode/node/src/app/index.html.njk +67 -0
- package/template/fnode/node/src/app/index.js.njk +9 -0
- package/template/fnode/node/src/cli/index.js.njk +9 -0
- package/template/fnode/node/src/cli/index.js.v1.njk +464 -0
- package/template/fnode/node/src/cli/v2/core/args-parser.njk +10 -0
- package/template/fnode/node/src/cli/v2/core/imports.njk +29 -0
- package/template/fnode/node/src/cli/v2/core/run-wrapper.njk +25 -0
- package/template/fnode/node/src/cli/v2/index.js.njk +184 -0
- package/template/fnode/node/src/cli/v2/modes/default/extend.njk +11 -0
- package/template/fnode/node/src/cli/v2/modes/default/standard.njk +19 -0
- package/template/fnode/node/src/cli/v2/modes/http/builtin.njk +61 -0
- package/template/fnode/node/src/cli/v2/modes/http/imports.njk +9 -0
- package/template/fnode/node/src/cli/v2/modes/http/index.njk +12 -0
- package/template/fnode/node/src/cli/v2/modes/mcp/imports.njk +33 -0
- package/template/fnode/node/src/cli/v2/modes/mcp/index.njk +30 -0
- package/template/fnode/node/src/cli/v2/modes/mcp/server-setup.njk +15 -0
- package/template/fnode/node/src/cli/v2/modes/mcp/tool-handlers.njk +41 -0
- package/template/fnode/node/src/cli/v2/modes/mcp/transport-http.njk +212 -0
- package/template/fnode/node/src/cli/v2/modes/mcp/transport-stdio.njk +9 -0
- package/template/fnode/node/src/cli/v2/modes/pipeline/imports.njk +9 -0
- package/template/fnode/node/src/cli/v2/modes/pipeline/index.njk +103 -0
- package/template/fnode/node/src/cli/v2/modes/webhook/imports.njk +9 -0
- package/template/fnode/node/src/cli/v2/modes/webhook/index.njk +122 -0
- package/template/fnode/node/src/cli/v2/modes/websocket/imports.njk +15 -0
- package/template/fnode/node/src/cli/v2/modes/websocket/index.njk +99 -0
- package/template/fnode/node/src/default/engine.js.njk +17 -0
- package/template/fnode/node/src/default/input.args.js.njk +126 -0
- package/template/fnode/node/tsconfig.json.njk +16 -0
- package/template/fnode/project/.gitignore.njk +11 -0
- package/template/fnode/project/.vscode/launch.json.njk +78 -0
- package/template/fnode/project/.vscode/tasks.json.njk +46 -0
- package/template/fnode/project/fnet/targets.yaml.njk +25 -0
- package/template/fnode/project/fnode.yaml.njk +12 -0
- package/template/fnode/python/.gitignore.njk +7 -0
- package/template/fnode/python/package.json.njk +4 -0
- package/template/fnode/python/pyproject.toml.njk +3 -0
- package/template/fnode/python/readme.md.njk +12 -0
- package/template/fnode/python/setup.py.njk +19 -0
- package/template/fnode/python/src/cli/index.py.njk +25 -0
- package/template/schemas/to-npm.yaml +14 -0
- package/dist/builder/lib-cli.js +0 -2
- package/dist/builder/wf-cli.js +0 -2
- package/dist/index.js +0 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./index.cvxrf34y.js";import d from"@fnet/config";import l from"@flownet/lib-to-webos";import a from"lodash.clonedeep";import u from"semver";async function g({atom:r,target:e,onProgress:s,projectDir:o,dependencies:t,context:f,yamlTarget:c}){if(s)await s({message:"Deploying it as webos package."});let m=e?.config?await d({name:e.config,dir:o,optional:!0,transferEnv:!1,tags:f.tags}):void 0,n=u.inc(e.version||"0.1.0","patch");e.params.version=n,e.version=n,c.set("version",n);let i=a(e.params);i.dependencies=a(t);let p={atom:r,params:i,config:m?.config,src:o,dest:o};return{deployer:await l(p)}}export{g as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./index.cvxrf34y.js";import O from"semver";import P from"@fnet/config";import R from"node:fs";import S from"@fnet/shelljs";import D from"form-data";async function M({setProgress:h,context:t,deploymentProject:u,deploymentProjectTarget:o,yamlTarget:m}){await h({message:"Deploying it as fnet flow."});let{file:n,data:e}=await P({name:o.config||"fnet-flow",dir:t.project.projectDir,tags:t.tags});if(!e.env.ATOM_API_URL)throw Error(`ATOM_API_URL is required in ${n}`);if(!e.env.ATOM_API_USERNAME)throw Error(`ATOM_API_USERNAME is required in ${n}`);if(!e.env.ATOM_API_PASSWORD)throw Error(`ATOM_API_PASSWORD is required in ${n}`);let d=`${e.env.ATOM_API_URL}/v1/auth/token`,A=e.env.ATOM_API_USERNAME,_=e.env.ATOM_API_PASSWORD,s=await fetch(d,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({username:A,password:_})});if(!s.ok)throw Error(`Failed to fetch token: ${s.statusText}`);let i=(await s.json())?.access_token;if(!i)throw Error(`Invalid access_token from ${d}`);u.isDirty=!0;let a=O.inc(o.version,"patch");o.params.version=a,o.version=a,m.set("version",a);let T=`${e.env.ATOM_API_URL}/v1/service/fnet-flow/publish`,p=await fetch(T,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${i}`},body:JSON.stringify({name:o.params.name,version:o.params.version,docs:o.params.docs,configs:o.params.configs})});if(!p.ok)throw Error(`Error publishing fnet flow: ${p.statusText}`);let w=await p.json();if(w?.error)throw Error("Error publishing fnet flow.");let v=w?.upload.id,r="fnet-dir-zipper";r+=` --sourceDir='${t.project.projectDir}'`,r+=" --pattern=**/*",r+=" --stdout_format=json";let f=await S(r);if(f.code!==0)throw Error(f.stderr);let E=JSON.parse(f.stdout).path,c=new D;c.append("file",R.createReadStream(E));let l=await fetch(`${e.env.ATOM_API_URL}/v1/service/upload/single/${v}`,{method:"POST",headers:{...c.getHeaders(),Authorization:`Bearer ${i}`},body:c});if(!l.ok)throw Error(`Error uploading fnet flow: ${l.statusText}`);if((await l.json())?.error)throw Error("Error uploading fnet flow.")}export{M as default};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import k from"yargs";import{hideBin as G}from"yargs/helpers";import{promisify as V}from"node:util";import W from"tree-kill";var S=V(W),M=!1;function C(){process.on("uncaughtException",($)=>{if(!M)M=!0,setTimeout(()=>process.exit(1),500)}),process.on("unhandledRejection",($)=>{if(!M)M=!0,setTimeout(()=>process.exit(1),500)})}import U from"fs";import D from"path";import X from"@fnet/yaml";import Z from"@fnet/shell-flow";async function J({projectType:$,group:B,tags:H,args:I,argv:O}){try{let L=await _($),{parsed:Q}=await X({file:L.path,tags:H}),q=Q.commands;if(!q)throw Error(`Commands section not found in ${L.name}`);let z=q[B];if(!z)throw Error(`Command group '${B}' not found in ${L.name}`);await Z({commands:z,context:{args:I,argv:O,projectType:L.type}})}catch(L){console.error(`Error: ${L.message}`),process.exit(1)}}async function _($){let B=process.cwd(),H=D.resolve(B,"fnode.yaml"),I=D.resolve(B,"fnet.yaml");if($==="fnode"){if(U.existsSync(H))return{path:H,name:"fnode.yaml",type:"fnode"};throw Error("fnode.yaml file not found in current directory")}if($==="fnet"){if(U.existsSync(I))return{path:I,name:"fnet.yaml",type:"fnet"};throw Error("fnet.yaml file not found in current directory")}if(U.existsSync(H))return{path:H,name:"fnode.yaml",type:"fnode"};if(U.existsSync(I))return{path:I,name:"fnet.yaml",type:"fnet"};throw Error("No project file (fnode.yaml or fnet.yaml) found in current directory")}var R={command:"$0 <group> [options..]",describe:"Run a command group from project file",builder:($)=>{return $.positional("group",{type:"string",describe:"Command group to run"}).option("ftag",{type:"array",describe:"Tags for conditional configuration"}).example("$0 build","Run the build command group").example("$0 test --ftag dev","Run the test command group with dev tag")},handler:async($)=>{try{await J({projectType:"auto",group:$.group,tags:$.ftag,args:$,argv:process.argv})}catch(B){console.error(`Error: ${B.message}`),process.exit(1)}}},N=R;C();async function A(){try{let $=k(G(process.argv)).usage("Usage: $0 <command> [options]").command(N).help().version().argv}catch($){console.error(`Fatal error: ${$.message}`),process.exit(1)}}A().catch(($)=>{console.error(`Fatal error: ${$.message}`),process.exit(1)});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{a as V,b as A,c as o}from"./index.q01yvaz0.js";import rz from"yargs";import{hideBin as sz}from"yargs/helpers";import l from"chalk";import T from"chalk";import Zz from"@fnet/object-from-schema";import _z from"@fnet/prompt";import H from"node:fs";import P from"node:path";import zz from"node:os";import S from"chalk";import Jz from"yaml";import Bz from"@fnet/service";import q from"node:fs";import $ from"node:path";import F from"node:os";import B from"chalk";function n(){return $.join(F.homedir(),".fnet","bin")}function f(){return $.join(F.homedir(),".fnet","metadata")}function c(){return $.join(f(),"binaries.json")}function Lz(z){return(process.env.PATH||"").split($.delimiter).includes(z)}function m(){try{if(process.platform==="win32"){if(process.env.PSModulePath&&process.env.PSModulePath.includes("PowerShell"))return"powershell-core";if(process.env.PSModulePath)return"powershell";return"cmd"}let z=process.env.SHELL||"";if(z.includes("bash"))return"bash";if(z.includes("zsh"))return"zsh";if(z.includes("fish"))return"fish";if(z.includes("ksh"))return"ksh";if(z.includes("csh")||z.includes("tcsh"))return"csh";if(q.existsSync($.join(F.homedir(),".bashrc")))return"bash";if(q.existsSync($.join(F.homedir(),".zshrc")))return"zsh";if(q.existsSync($.join(F.homedir(),".config","fish","config.fish")))return"fish";return"unknown"}catch(z){return"unknown"}}function r(z){let J=F.homedir(),Q=[];switch(z){case"bash":Q.push({name:".bashrc",path:$.join(J,".bashrc")}),Q.push({name:".bash_profile",path:$.join(J,".bash_profile")}),Q.push({name:".profile",path:$.join(J,".profile")});break;case"zsh":Q.push({name:".zshrc",path:$.join(J,".zshrc")}),Q.push({name:".zprofile",path:$.join(J,".zprofile")});break;case"fish":Q.push({name:"config.fish",path:$.join(J,".config","fish","config.fish")});break;case"ksh":Q.push({name:".kshrc",path:$.join(J,".kshrc")}),Q.push({name:".profile",path:$.join(J,".profile")});break;case"csh":Q.push({name:".cshrc",path:$.join(J,".cshrc")}),Q.push({name:".tcshrc",path:$.join(J,".tcshrc")});break;case"powershell":Q.push({name:"Microsoft.PowerShell_profile.ps1",path:$.join(J,"Documents","WindowsPowerShell","Microsoft.PowerShell_profile.ps1")}),Q.push({name:"profile.ps1",path:$.join(J,"Documents","WindowsPowerShell","profile.ps1")});break;case"powershell-core":Q.push({name:"Microsoft.PowerShell_profile.ps1",path:$.join(J,"Documents","PowerShell","Microsoft.PowerShell_profile.ps1")}),Q.push({name:"profile.ps1",path:$.join(J,"Documents","PowerShell","profile.ps1")});break;case"cmd":Q.push({name:"fnet-path.bat",path:$.join(J,"fnet-path.bat")});break;default:break}return Q}function Oz(){let z=m(),J=r(z);for(let Q of J)if(q.existsSync(Q.path))return Q.path;return J.length>0?J[0].path:null}async function wz(){let z=n(),J=f(),Q=c();if(!q.existsSync(z))q.mkdirSync(z,{recursive:!0});if(!q.existsSync(J))q.mkdirSync(J,{recursive:!0});if(!q.existsSync(Q))q.writeFileSync(Q,JSON.stringify({binaries:{},lastUpdated:new Date().toISOString()},null,2))}function s(z,J){switch(z){case"bash":case"zsh":case"ksh":return`export PATH="${J}:$PATH"`;case"fish":return`set -gx PATH ${J} $PATH`;case"csh":return`setenv PATH ${J}:$PATH`;case"powershell":case"powershell-core":return`$env:PATH = "${J};" + $env:PATH`;case"cmd":return`@echo off
|
|
3
|
+
SETX PATH "%PATH%;${J}"
|
|
4
|
+
echo Path updated successfully`;default:return`export PATH="${J}:$PATH"`}}async function Vz(z,J,Q,K={}){try{let{autoBackup:X=!0}=K,W=s(z,Q);if(z==="cmd")return q.writeFileSync(J,W),console.log(B.yellow(`Created batch file at ${J}`)),console.log(B.yellow("Run this file to add the bin directory to your PATH")),!0;if((z==="powershell"||z==="powershell-core")&&!q.existsSync($.dirname(J)))q.mkdirSync($.dirname(J),{recursive:!0});if(!q.existsSync(J)){let R=z==="fish"?`# Fish shell configuration
|
|
5
|
+
|
|
6
|
+
`:z==="powershell"||z==="powershell-core"?`# PowerShell profile
|
|
7
|
+
|
|
8
|
+
`:`# Shell configuration
|
|
9
|
+
|
|
10
|
+
`;q.writeFileSync(J,R),console.log(B.green(`Created config file at ${J}`))}let Z=q.readFileSync(J,"utf8");if(Z.includes(Q))return!0;if(X&&q.existsSync(J)){let R=i(),w=$.basename(J);if(t(J,$.join(R,"configs"),w))a(R,{type:"auto",command:"addBinToPath",message:`Automatic backup before modifying ${w}`,files:[J]}),e(R),console.log(B.green(`✓ Backed up ${w} to ${R}`))}let _=`${Z.trim()}
|
|
11
|
+
|
|
12
|
+
# Added by @fnet/cli
|
|
13
|
+
${W}
|
|
14
|
+
`;if(q.writeFileSync(J,_),z==="powershell"||z==="powershell-core")console.log(B.yellow("You may need to set the PowerShell execution policy to run scripts:")),console.log(B.green("Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned"));return!0}catch(X){return console.error(B.red(`Failed to add bin directory to PATH: ${X.message}`)),!1}}function u(){return $.join(F.homedir(),".fnet","backups")}function i(){let z=new Date().toISOString().replace(/:/g,"-").replace(/\..+/,""),J=$.join(u(),z);if(!q.existsSync(J))q.mkdirSync(J,{recursive:!0});return J}function t(z,J,Q=null){try{if(!q.existsSync(z))return!1;let K=Q||$.basename(z),X=$.join(J,K),W=$.dirname(X);if(!q.existsSync(W))q.mkdirSync(W,{recursive:!0});return q.copyFileSync(z,X),!0}catch(K){return console.error(B.red(`Failed to backup file ${z}: ${K.message}`)),!1}}function a(z,J={}){let Q={timestamp:new Date().toISOString(),type:J.type||"manual",message:J.message||"",command:J.command||"",files:J.files||[],...J},K=$.join(z,"metadata.json");q.writeFileSync(K,JSON.stringify(Q,null,2))}function Az(){let z=u();if(!q.existsSync(z))return[];let J=[],Q=q.readdirSync(z);for(let K of Q){let X=$.join(z,K),W=q.statSync(X);if(W.isDirectory()&&K!=="latest"){let Z=$.join(X,"metadata.json"),_={};if(q.existsSync(Z))try{_=JSON.parse(q.readFileSync(Z,"utf8"))}catch(R){}J.push({timestamp:K,path:X,created:W.mtime,..._})}}return J.sort((K,X)=>new Date(X.created)-new Date(K.created)),J}function e(z){let J=$.join(u(),"latest");try{if(q.existsSync(J))q.unlinkSync(J);if(process.platform==="win32")q.writeFileSync(J+".txt",z);else q.symlinkSync(z,J)}catch(Q){console.warn(B.yellow(`Could not create latest symlink: ${Q.message}`))}}var D={getBinDirectory:n,getMetadataDirectory:f,getMetadataFilePath:c,checkIfInPath:Lz,detectUserShell:m,getShellConfigPath:Oz,getAllShellConfigPaths:r,createBinDirectoryStructure:wz,getExportPathCommand:s,addBinToPath:Vz,getBackupDirectory:u,createBackupDirectory:i,backupFile:t,createBackupMetadata:a,listBackups:Az,updateLatestSymlink:e};function k(){return P.join(zz.homedir(),".fnet","services")}function h(){return P.join(zz.homedir(),".fnet","metadata")}function p(){return P.join(h(),"services.json")}async function Uz(){let z=k(),J=h(),Q=p();if(!H.existsSync(z))H.mkdirSync(z,{recursive:!0});if(!H.existsSync(J))H.mkdirSync(J,{recursive:!0});if(!H.existsSync(Q))H.writeFileSync(Q,JSON.stringify({services:{},lastUpdated:new Date().toISOString()},null,2))}function Kz(){let z=p();if(!H.existsSync(z))return{services:{},lastUpdated:new Date().toISOString()};try{return JSON.parse(H.readFileSync(z,"utf8"))}catch(J){return console.warn(S.yellow(`Failed to parse service metadata file: ${J.message}`)),{services:{},lastUpdated:new Date().toISOString()}}}function Qz(z){let J=p();z.lastUpdated=new Date().toISOString(),H.writeFileSync(J,JSON.stringify(z,null,2))}function N(z){return P.join(k(),`${z}.yaml`)}function xz(z){let J=N(z);return H.existsSync(J)}function Wz(z){let J=N(z);if(!H.existsSync(J))return null;try{let Q=H.readFileSync(J,"utf8");return Jz.parse(Q)}catch(Q){return console.warn(S.yellow(`Failed to parse service manifest file: ${Q.message}`)),null}}function Fz(z,J){let Q=N(z);try{let K=Jz.stringify(J);return H.writeFileSync(Q,K),!0}catch(K){return console.error(S.red(`Failed to save service manifest: ${K.message}`)),!1}}function Mz(z){let J=N(z);if(!H.existsSync(J))return!1;try{return H.unlinkSync(J),!0}catch(Q){return console.error(S.red(`Failed to delete service manifest: ${Q.message}`)),!1}}function Cz(){let z=k();if(!H.existsSync(z))return[];try{return H.readdirSync(z).filter((J)=>J.endsWith(".yaml")).map((J)=>J.replace(".yaml",""))}catch(J){return console.error(S.red(`Failed to list service definitions: ${J.message}`)),[]}}function Xz(z){let J=[];if(!z.name)J.push("Service name is required");if(!z.binary)J.push("Binary name is required");if(z.binary){let Q=D.getBinDirectory(),K=P.join(Q,z.binary);if(!H.existsSync(K))J.push(`Binary '${z.binary}' not found in bin directory`)}return{valid:J.length===0,errors:J}}async function jz(z,J={}){let Q=Wz(z);if(!Q)throw Error(`Service manifest '${z}' not found`);let K=Xz(Q);if(!K.valid)throw Error(`Invalid service manifest: ${K.errors.join(", ")}`);let X=D.getBinDirectory(),W=P.join(X,Q.binary);try{await Bz({action:"register",name:Q.name,description:Q.description||`Service for ${Q.binary}`,command:[W,...Q.args||[]],env:Q.env||{},wdir:Q.workingDir,system:Q.system!==!1,autoStart:Q.autoStart===!0,restartOnFailure:Q.restartOnFailure!==!1,user:Q.user});let Z=Kz();return Z.services[Q.name]={manifest:z,binary:Q.binary,registered:new Date().toISOString(),status:"registered"},Qz(Z),{success:!0,name:Q.name,manifest:z}}catch(Z){throw Error(`Failed to register service: ${Z.message}`)}}var Y={getServicesDirectory:k,getServiceMetadataDirectory:h,getServiceMetadataFilePath:p,createServiceDirectoryStructure:Uz,loadServiceMetadata:Kz,saveServiceMetadata:Qz,getServiceManifestPath:N,servicManifestExists:xz,loadServiceManifest:Wz,saveServiceManifest:Fz,deleteServiceManifest:Mz,listServiceManifests:Cz,validateServiceManifest:Xz,registerService:jz};async function E(z){await Y.createServiceDirectoryStructure();let J=Y.getServicesDirectory(),Q=Y.getServiceMetadataDirectory(),K=Y.loadServiceMetadata();return{servicesDir:J,metadataDir:Q,metadata:K,args:z}}import d from"node:fs";function Dz(z=!0){let J=[];if(z)try{let Q=D.getBinDirectory(),K=D.getMetadataFilePath();if(d.existsSync(K)){let X=JSON.parse(d.readFileSync(K,"utf8"));J=Object.keys(X.binaries).map((W)=>({name:W,message:`${W} (${X.binaries[W].version||"unknown"})`}))}else if(d.existsSync(Q))J=d.readdirSync(Q).map((X)=>({name:X,message:X}))}catch(Q){console.warn(`Failed to get binary choices: ${Q.message}`)}return{type:"object",required:["name","binary"],properties:{name:{type:"string",description:"Service name","x-prompt":{type:"input",message:"Enter service name:"}},binary:{type:"string",description:"Binary name in the bin directory","x-prompt":{type:"select",message:"Select binary:",choices:J}},description:{type:"string",description:"Service description","x-prompt":{type:"input",message:"Enter service description:"}},args:{type:"array",items:{type:"string"},description:"Command line arguments","x-prompt":{type:"input",message:"Enter command line arguments (space-separated):",result:(Q)=>Q?Q.split(" "):[]}},env:{type:"object",additionalProperties:{type:"string"},description:"Environment variables","x-prompt":{type:"input",message:"Enter environment variables (KEY=VALUE format, one per line):",result:(Q)=>{if(!Q)return{};return Q.split(`
|
|
15
|
+
`).filter((K)=>K.includes("=")).reduce((K,X)=>{let[W,...Z]=X.split("=");return K[W.trim()]=Z.join("=").trim(),K},{})}}},workingDir:{type:"string",description:"Working directory","x-prompt":{type:"input",message:"Enter working directory (optional):"}},autoStart:{type:"boolean",description:"Start on boot",default:!1,"x-prompt":{type:"confirm",message:"Start service on boot?",initial:!1}},restartOnFailure:{type:"boolean",description:"Restart on failure",default:!0,"x-prompt":{type:"confirm",message:"Restart service on failure?",initial:!0}},system:{type:"boolean",description:"System service",default:!0,"x-prompt":{type:"confirm",message:"Register as system service?",initial:!0}},user:{type:"string",description:"User to run the service as","x-prompt":{type:"input",message:"Enter user to run the service as (optional):"}},instances:{type:"integer",description:"Number of instances to run",default:1,minimum:1,"x-prompt":{type:"number",message:"Enter number of instances to run:",initial:1}},metadata:{type:"object",additionalProperties:!0,description:"Custom metadata"}}}}var v={getServiceManifestSchema:Dz};import g from"chalk";import Yz from"@fnet/prompt";async function Pz(z){let{items:J,message:Q,nameField:K="name",valueField:X="name",initialValue:W=null,allowAbort:Z=!0}=z;if(!J||J.length===0)return console.log(g.yellow("No items available for selection.")),null;if(J.length===1&&!Z){let G=J[0],b=typeof G==="string"?G:G[X];return console.log(g.blue(`Only one option available: ${typeof G==="string"?G:G[K]}`)),b}let _=J.map((G)=>{if(typeof G==="string")return{name:G,value:G,message:G};else return{name:G[X],value:G[X],message:G[K]||G[X]}});if(Z)_.push({name:"cancel",value:null,message:g.yellow("Cancel")});let R=null;if(W){let G=_.findIndex((b)=>b.name===W);if(G!==-1)R=G}let w="selectedItem",{[w]:j}=await Yz({type:"select",name:w,message:Q,choices:_,initial:R});if(j==="cancel")return null;return j}async function bz(z){let{items:J,message:Q,nameField:K="name",valueField:X="name",initialValues:W=[],allowAbort:Z=!0}=z;if(!J||J.length===0)return console.log(g.yellow("No items available for selection.")),null;let _=J.map((G)=>{if(typeof G==="string")return{name:G,value:G,message:G};else return{name:G[X],value:G[X],message:G[K]||G[X]}}),R=[];if(W&&W.length>0)R=_.map((G,b)=>W.includes(G.name)?b:-1).filter((G)=>G!==-1);let w="selectedItems",j=await Yz({type:"multiselect",name:w,message:Q,choices:_,initial:R,hint:"(Use space to select, enter to confirm)",validate:(G)=>{if(G.length===0&&!Z)return"Please select at least one item";return!0}});if(Z&&j[w].length===0)return null;return j[w]}var L={promptForSelection:Pz,promptForMultipleSelection:bz};var Sz={command:"manifest <subcommand>",describe:"Manage service manifests",builder:(z)=>{return z.command({command:"create",describe:"Create a new service manifest",builder:(J)=>{return J.option("name",{describe:"Service manifest name",type:"string"}).option("output",{describe:"Output file path",type:"string",alias:"o"})},handler:Nz}).command({command:"list",describe:"List service manifests",builder:(J)=>{return J.option("format",{describe:"Output format",type:"string",choices:["json","text","table"],default:"table"})},handler:yz}).command({command:"show [n]",describe:"Show service manifest details",builder:(J)=>{return J.positional("name",{describe:"Service manifest name",type:"string",demandOption:!1}).option("format",{describe:"Output format",type:"string",choices:["json","yaml"],default:"yaml"})},handler:uz}).command({command:"edit [n]",describe:"Edit a service manifest",builder:(J)=>{return J.positional("name",{describe:"Service manifest name",type:"string",demandOption:!1})},handler:kz}).command({command:"delete [n]",describe:"Delete a service manifest",builder:(J)=>{return J.positional("name",{describe:"Service manifest name",type:"string",demandOption:!1}).option("force",{describe:"Force deletion without confirmation",type:"boolean",default:!1,alias:"f"})},handler:pz}).command({command:"validate [n]",describe:"Validate a service manifest",builder:(J)=>{return J.positional("name",{describe:"Service manifest name",type:"string",demandOption:!1})},handler:dz}).demandCommand(1,"You need to specify a subcommand")},handler:()=>{}};async function Nz(z){try{let J=await E(z),Q=v.getServiceManifestSchema(),K=await Zz({schema:Q,format:"yaml"});console.log("Result from fnetObjectFromSchema:",K);let X;if(typeof K==="string")try{X=(await import("yaml")).default.parse(K)}catch(_){console.warn(T.yellow(`Failed to parse YAML: ${_.message}`)),X={name:"service-"+Date.now()}}else if(K&&typeof K==="object")X=K.data||K;else X={name:"service-"+Date.now()};console.log("Generated manifest:",X);let W=z.name||(X&&X.name?X.name:"service-"+Date.now());if(Y.servicManifestExists(W)&&!z.force){let{confirmOverwrite:_}=await _z({type:"confirm",name:"confirmOverwrite",message:`Service manifest '${W}' already exists. Overwrite?`,initial:!1});if(!_){console.log(T.yellow("Operation cancelled."));return}}if(Y.saveServiceManifest(W,X))console.log(T.green(`Service manifest '${W}' created successfully.`)),console.log(T.blue(`Location: ${Y.getServiceManifestPath(W)}`));else console.error(T.red(`Failed to create service manifest '${W}'.`))}catch(J){console.error(T.red(`Error: ${J.message}`)),process.exit(1)}}async function yz(z){try{let J=await E(z),Q=Y.listServiceManifests();if(Q.length===0){console.log(T.yellow("No service manifests found."));return}if(z.format==="json")console.log(JSON.stringify(Q,null,2));else if(z.format==="text")Q.forEach((K)=>console.log(K));else{console.log(T.bold(`
|
|
16
|
+
Service Definitions:`));let K=(await import("./index.q01yvaz0.js")).default,X=["NAME","BINARY","DESCRIPTION"],W=K.createTable(X,{chars:{mid:"","mid-mid":"","left-mid":"","right-mid":""}});for(let Z of Q){let _=Y.loadServiceManifest(Z);if(_)W.push([T.white(Z),T.cyan(_.binary||"undefined"),_.description||""])}console.log(W.toString()),console.log(`Total: ${Q.length} manifest(s)`)}}catch(J){console.error(T.red(`Error: ${J.message}`)),process.exit(1)}}async function uz(z){try{let J=await E(z);if(!z.name){let K=Y.listServiceManifests();if(K.length===0){console.log(T.yellow("No service manifests found."));return}let X=await L.promptForSelection({items:K,message:"Select a service manifest to show:",allowAbort:!0});if(X===null){console.log(T.yellow("Operation cancelled."));return}z.name=X}let Q=Y.loadServiceManifest(z.name);if(!Q)console.error(T.red(`Service manifest '${z.name}' not found.`)),process.exit(1);if(z.format==="json")console.log(JSON.stringify(Q,null,2));else{let K=(await import("yaml")).default;console.log(K.stringify(Q))}}catch(J){console.error(T.red(`Error: ${J.message}`)),process.exit(1)}}async function kz(z){try{let J=await E(z);if(!z.name){let _=Y.listServiceManifests();if(_.length===0){console.log(T.yellow("No service manifests found."));return}let R=await L.promptForSelection({items:_,message:"Select a service manifest to edit:",allowAbort:!0});if(R===null){console.log(T.yellow("Operation cancelled."));return}z.name=R}let Q=Y.loadServiceManifest(z.name);if(!Q)console.error(T.red(`Service manifest '${z.name}' not found.`)),process.exit(1);let K=v.getServiceManifestSchema(),W=(await Zz({schema:K,ref:Q,format:"yaml"})).data;if(Y.saveServiceManifest(z.name,W))console.log(T.green(`Service manifest '${z.name}' updated successfully.`));else console.error(T.red(`Failed to update service manifest '${z.name}'.`))}catch(J){console.error(T.red(`Error: ${J.message}`)),process.exit(1)}}async function pz(z){try{let J=await E(z);if(!z.name){let K=Y.listServiceManifests();if(K.length===0){console.log(T.yellow("No service manifests found."));return}let X=await L.promptForSelection({items:K,message:"Select a service manifest to delete:",allowAbort:!0});if(X===null){console.log(T.yellow("Operation cancelled."));return}z.name=X}if(!Y.servicManifestExists(z.name))console.error(T.red(`Service manifest '${z.name}' not found.`)),process.exit(1);if(!z.force){let{confirmDelete:K}=await _z({type:"confirm",name:"confirmDelete",message:`Are you sure you want to delete service manifest '${z.name}'?`,initial:!1});if(!K){console.log(T.yellow("Operation cancelled."));return}}if(Y.deleteServiceManifest(z.name))console.log(T.green(`Service manifest '${z.name}' deleted successfully.`));else console.error(T.red(`Failed to delete service manifest '${z.name}'.`))}catch(J){console.error(T.red(`Error: ${J.message}`)),process.exit(1)}}async function dz(z){try{let J=await E(z);if(!z.name){let X=Y.listServiceManifests();if(X.length===0){console.log(T.yellow("No service manifests found."));return}let W=await L.promptForSelection({items:X,message:"Select a service manifest to validate:",allowAbort:!0});if(W===null){console.log(T.yellow("Operation cancelled."));return}z.name=W}let Q=Y.loadServiceManifest(z.name);if(!Q)console.error(T.red(`Service manifest '${z.name}' not found.`)),process.exit(1);let K=Y.validateServiceManifest(Q);if(K.valid)console.log(T.green(`Service manifest '${z.name}' is valid.`));else console.error(T.red(`Service manifest '${z.name}' is invalid:`)),K.errors.forEach((X)=>{console.error(T.red(`- ${X}`))}),process.exit(1)}catch(J){console.error(T.red(`Error: ${J.message}`)),process.exit(1)}}var $z=Sz;import U from"chalk";var gz={command:"register",describe:"Register a service manifest as a system service",builder:(z)=>{return z.option("manifest",{describe:"Service manifest name",type:"string",demandOption:!1,alias:"d"}).option("start",{describe:"Start the service after registration",type:"boolean",default:!1})},handler:async(z)=>{try{let J=await E(z),Q=z.manifest;if(!Q){let X=Y.listServiceManifests();if(X.length===0)console.log(U.yellow('No service manifests found. Create one first using "fservice manifest create".')),process.exit(1);if(Q=await L.promptForSelection({items:X,message:"Select a service manifest to register:",allowAbort:!0}),!Q){console.log(U.yellow("Operation cancelled."));return}}if(!Y.servicManifestExists(Q))console.error(U.red(`Service manifest '${Q}' not found.`)),process.exit(1);console.log(U.blue(`Registering service from manifest '${Q}'...`));let K=await Y.registerService(Q);if(console.log(U.green(`Service '${K.name}' registered successfully.`)),z.start){console.log(U.blue(`Starting service '${K.name}'...`));let X=(await import("@fnet/service")).default;try{await X({action:"start",name:K.name}),console.log(U.green(`Service '${K.name}' started successfully.`));let W=Y.loadServiceMetadata();if(W.services[K.name])W.services[K.name].status="running",W.services[K.name].lastStarted=new Date().toISOString(),Y.saveServiceMetadata(W)}catch(W){console.error(U.red(`Failed to start service: ${W.message}`))}}}catch(J){console.error(U.red(`Error: ${J.message}`)),process.exit(1)}}},Tz=gz;import O from"chalk";import fz from"@fnet/prompt";var hz={command:"unregister",describe:"Unregister a service from the system",builder:(z)=>{return z.option("name",{describe:"Service name",type:"string",demandOption:!1,alias:"n"}).option("keep-manifest",{describe:"Keep the service manifest",type:"boolean",default:!0}).option("force",{describe:"Force unregistration without confirmation",type:"boolean",default:!1,alias:"f"})},handler:async(z)=>{try{let J=await E(z),Q=Y.loadServiceMetadata(),K=z.name;if(!K){let W=Object.keys(Q.services);if(W.length===0)console.log(O.yellow("No registered services found.")),process.exit(1);if(K=await L.promptForSelection({items:W,message:"Select a service to unregister:",allowAbort:!0}),!K){console.log(O.yellow("Operation cancelled."));return}}if(!Q.services[K])console.error(O.red(`Service '${K}' not found in metadata.`)),process.exit(1);if(!z.force){let{confirmUnregister:W}=await fz({type:"confirm",name:"confirmUnregister",message:`Are you sure you want to unregister service '${K}'?`,initial:!1});if(!W){console.log(O.yellow("Operation cancelled."));return}}console.log(O.blue(`Unregistering service '${K}'...`));let X=(await import("@fnet/service")).default;try{let W=Q.services[K].manifest,Z=Y.loadServiceManifest(W);if(!Z)throw Error(`Service manifest '${W}' not found`);let _=Z.system!==!1;try{await X({action:"stop",name:K,system:_}),console.log(O.blue(`Service '${K}' stopped.`))}catch(R){console.warn(O.yellow(`Warning: Failed to stop service: ${R.message}`))}if(await X({action:"unregister",name:K,system:_}),console.log(O.green(`Service '${K}' unregistered successfully.`)),delete Q.services[K],Y.saveServiceMetadata(Q),!z.keepDefinition&&W){if(Y.servicManifestExists(W))if(Y.deleteServiceManifest(W))console.log(O.green(`Service manifest '${W}' deleted.`));else console.warn(O.yellow(`Warning: Failed to delete service manifest '${W}'.`))}}catch(W){console.error(O.red(`Failed to unregister service: ${W.message}`)),process.exit(1)}}catch(J){console.error(O.red(`Error: ${J.message}`)),process.exit(1)}}},qz=hz;import M from"chalk";var vz={command:"start",describe:"Start a registered service",builder:(z)=>{return z.option("name",{describe:"Service name",type:"string",demandOption:!1,alias:"n"})},handler:async(z)=>{try{let J=await E(z),Q=Y.loadServiceMetadata(),K=z.name;if(!K){let W=Object.keys(Q.services);if(W.length===0)console.log(M.yellow("No registered services found.")),process.exit(1);if(K=await L.promptForSelection({items:W,message:"Select a service to start:",allowAbort:!0}),!K){console.log(M.yellow("Operation cancelled."));return}}if(!Q.services[K])console.error(M.red(`Service '${K}' not found in metadata.`)),process.exit(1);console.log(M.blue(`Starting service '${K}'...`));let X=(await import("@fnet/service")).default;try{let W=Q.services[K].manifest,Z=Y.loadServiceManifest(W);if(!Z)throw Error(`Service manifest '${W}' not found`);await X({action:"start",name:K,system:Z.system!==!1}),console.log(M.green(`Service '${K}' started successfully.`)),Q.services[K].status="running",Q.services[K].lastStarted=new Date().toISOString(),Y.saveServiceMetadata(Q)}catch(W){console.error(M.red(`Failed to start service: ${W.message}`)),process.exit(1)}}catch(J){console.error(M.red(`Error: ${J.message}`)),process.exit(1)}}},Gz=vz;import C from"chalk";var oz={command:"stop",describe:"Stop a running service",builder:(z)=>{return z.option("name",{describe:"Service name",type:"string",demandOption:!1,alias:"n"})},handler:async(z)=>{try{let J=await E(z),Q=Y.loadServiceMetadata(),K=z.name;if(!K){let W=Object.keys(Q.services);if(W.length===0)console.log(C.yellow("No registered services found.")),process.exit(1);if(K=await L.promptForSelection({items:W,message:"Select a service to stop:",allowAbort:!0}),!K){console.log(C.yellow("Operation cancelled."));return}}if(!Q.services[K])console.error(C.red(`Service '${K}' not found in metadata.`)),process.exit(1);console.log(C.blue(`Stopping service '${K}'...`));let X=(await import("@fnet/service")).default;try{let W=Q.services[K].manifest,Z=Y.loadServiceManifest(W);if(!Z)throw Error(`Service manifest '${W}' not found`);await X({action:"stop",name:K,system:Z.system!==!1}),console.log(C.green(`Service '${K}' stopped successfully.`)),Q.services[K].status="stopped",Q.services[K].lastStopped=new Date().toISOString(),Y.saveServiceMetadata(Q)}catch(W){console.error(C.red(`Failed to stop service: ${W.message}`)),process.exit(1)}}catch(J){console.error(C.red(`Error: ${J.message}`)),process.exit(1)}}},Ez=oz;import x from"chalk";var lz={command:"restart",describe:"Restart a service",builder:(z)=>{return z.option("name",{describe:"Service name",type:"string",demandOption:!1,alias:"n"})},handler:async(z)=>{try{let J=await E(z),Q=Y.loadServiceMetadata(),K=z.name;if(!K){let W=Object.keys(Q.services);if(W.length===0)console.log(x.yellow("No registered services found.")),process.exit(1);if(K=await L.promptForSelection({items:W,message:"Select a service to restart:",allowAbort:!0}),!K){console.log(x.yellow("Operation cancelled."));return}}if(!Q.services[K])console.error(x.red(`Service '${K}' not found in metadata.`)),process.exit(1);console.log(x.blue(`Restarting service '${K}'...`));let X=(await import("@fnet/service")).default;try{await X({action:"stop",name:K}),console.log(x.blue(`Service '${K}' stopped.`)),await X({action:"start",name:K}),console.log(x.green(`Service '${K}' restarted successfully.`)),Q.services[K].status="running",Q.services[K].lastRestarted=new Date().toISOString(),Y.saveServiceMetadata(Q)}catch(W){console.error(x.red(`Failed to restart service: ${W.message}`)),process.exit(1)}}catch(J){console.error(x.red(`Error: ${J.message}`)),process.exit(1)}}},Rz=lz;import I from"chalk";var nz={command:"status",describe:"Check the status of a service",builder:(z)=>{return z.option("name",{describe:"Service name",type:"string",demandOption:!1,alias:"n"}).option("format",{describe:"Output format",type:"string",choices:["json","text","table"],default:"table"})},handler:async(z)=>{try{let J=await E(z),Q=Y.loadServiceMetadata(),K=z.name;if(!K){let W=Object.keys(Q.services);if(W.length===0)console.log(I.yellow("No registered services found.")),process.exit(1);if(K=await L.promptForSelection({items:W,message:"Select a service to check status:",allowAbort:!0}),!K){console.log(I.yellow("Operation cancelled."));return}}if(!Q.services[K])console.error(I.red(`Service '${K}' not found in metadata.`)),process.exit(1);console.log(I.blue(`Checking status of service '${K}'...`));let X=(await import("@fnet/service")).default;try{let W=Q.services[K].manifest,Z=Y.loadServiceManifest(W);if(!Z)throw Error(`Service manifest '${W}' not found`);let _=await X({action:"status",name:K,system:Z.system!==!1});if(console.log(I.green(`Service '${K}' status: ${_}`)),Q.services[K].status=_||"unknown",Q.services[K].lastChecked=new Date().toISOString(),Y.saveServiceMetadata(Q),z.format==="json")console.log(JSON.stringify({name:K,status:_||"unknown",manifest:W,binary:Q.services[K].binary},null,2));else if(z.format==="text")console.log(`Name: ${K}`),console.log(`Status: ${_||"unknown"}`),console.log(`Definition: ${W}`),console.log(`Binary: ${Q.services[K].binary}`);else console.log(I.bold(`
|
|
17
|
+
Service Status:`)),console.log(I.bold("─".repeat(50))),console.log(`${I.bold("Name:")} ${K}`),console.log(`${I.bold("Status:")} ${cz(_)(_||"unknown")}`),console.log(`${I.bold("Definition:")} ${W}`),console.log(`${I.bold("Binary:")} ${Q.services[K].binary}`),console.log(I.bold("─".repeat(50)))}catch(W){console.error(I.red(`Failed to check service status: ${W.message}`)),process.exit(1)}}catch(J){console.error(I.red(`Error: ${J.message}`)),process.exit(1)}}};function cz(z){switch(z){case"running":return I.green;case"stopped":return I.yellow;case"failed":return I.red;default:return I.gray}}var Hz=nz;import y from"chalk";var mz={command:"list",describe:"List all registered services",builder:(z)=>{return z.option("binary",{describe:"Filter by binary name",type:"string",alias:"b"}).option("status",{describe:"Filter by status",type:"string",choices:["running","stopped","failed","unknown"],alias:"s"}).option("format",{describe:"Output format",type:"string",choices:["json","text","table"],default:"table"})},handler:async(z)=>{try{let J=await E(z),Q=Y.loadServiceMetadata(),K=Object.entries(Q.services).map(([X,W])=>({name:X,...W}));if(z.binary)K=K.filter((X)=>X.binary===z.binary);if(z.status)K=K.filter((X)=>X.status===z.status);if(K.length===0){console.log(y.yellow("No services found."));return}if(z.format==="json")console.log(JSON.stringify(K,null,2));else if(z.format==="text")K.forEach((X)=>{console.log(`${X.name} (${X.status||"unknown"})`)});else{let X=["NAME","STATUS","BINARY","DEFINITION"],W=o.createTable(X,{chars:{mid:"","mid-mid":"","left-mid":"","right-mid":""}});K.forEach((Z)=>{let _=o.getStatusColor(Z.status);W.push([y.white(Z.name),_(Z.status||"unknown"),y.cyan(Z.binary),Z.manifest])}),console.log(y.bold(`
|
|
18
|
+
Registered Services:`)),console.log(W.toString()),console.log(`Total: ${K.length} service(s)`)}}catch(J){console.error(y.red(`Error: ${J.message}`)),process.exit(1)}}},Iz=mz;import{createRequire as iz}from"module";var tz=iz(import.meta.url),{version:az}=tz("../../package.json"),BK=rz(sz(process.argv)).scriptName("fservice").usage("Usage: $0 <command> [options]").version(az).command($z).command(Tz).command(qz).command(Gz).command(Ez).command(Rz).command(Hz).command(Iz).demandCommand(1,"You need to specify a command").strict().help().alias("h","help").alias("v","version").fail((z,J,Q)=>{if(J)console.error(l.red(`Error: ${J.message}`)),console.error(J);else console.error(l.red(`Error: ${z}`));console.error(l.yellow(`
|
|
19
|
+
Usage:`),Q.help()),process.exit(1)}).parse();
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{createRequire as L}from"node:module";var H=Object.create;var{getPrototypeOf:I,defineProperty:B,getOwnPropertyNames:J}=Object;var K=Object.prototype.hasOwnProperty;var Q=(j,y,z)=>{z=j!=null?H(I(j)):{};let q=y||!j||!j.__esModule?B(z,"default",{value:j,enumerable:!0}):z;for(let x of J(j))if(!K.call(q,x))B(q,x,{get:()=>j[x],enumerable:!0});return q};var U=L(import.meta.url);import M from"cli-table3";import A from"chalk";function E(j,y={}){let q={...{chars:{top:"─","top-mid":"─","top-left":" ","top-right":" ",bottom:"─","bottom-mid":"─","bottom-left":" ","bottom-right":" ",left:" ","left-mid":" ",mid:"─","mid-mid":"─",right:" ","right-mid":" ",middle:" "},style:{head:[],border:[],compact:!0},wordWrap:!0},...y,head:j.map((x)=>A.bold(x))};return new M(q)}function N(j,y,z={}){let q=E(j,z);if(Array.isArray(y))y.forEach((x)=>{q.push(x)});return q.toString()}function P(j){switch(j){case"running":return A.green;case"stopped":return A.yellow;case"failed":return A.red;case"registered":return A.blue;default:return A.gray}}var Z={createTable:E,createTableWithData:N,getStatusColor:P};export{P as getStatusColor,Z as default,N as createTableWithData,E as createTable};
|
|
2
|
+
export{Q as a,U as b,Z as c};
|
package/package.json
CHANGED
|
@@ -1,75 +1,92 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fnet/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"files": [
|
|
5
|
-
"dist"
|
|
5
|
+
"dist",
|
|
6
|
+
"template"
|
|
6
7
|
],
|
|
8
|
+
"type": "module",
|
|
9
|
+
"description": "CLI for Flownet",
|
|
7
10
|
"scripts": {
|
|
8
|
-
"
|
|
9
|
-
"build": "
|
|
10
|
-
"
|
|
11
|
-
"
|
|
11
|
+
"build": "bun run build.js",
|
|
12
|
+
"build:dev": "DEVELOPMENT=1 bun run build.js",
|
|
13
|
+
"watch": "DEVELOPMENT=1 bun run build.js --watch",
|
|
14
|
+
"deploy": "bun publish --access public",
|
|
15
|
+
"old:build": "rollup --config",
|
|
16
|
+
"old:build:dev": "rollup --config --sourcemap --environment DEVELOPMENT",
|
|
17
|
+
"old:watch": "rollup --config --watch --sourcemap --environment DEVELOPMENT --environment FLOWNET_WATCH"
|
|
12
18
|
},
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://gitlab.com/fnetai/cli.git"
|
|
22
|
+
},
|
|
23
|
+
"license": "MIT",
|
|
13
24
|
"dependencies": {
|
|
14
|
-
"@flownet/lib-atom-api-js": "^0.
|
|
15
|
-
"@flownet/lib-
|
|
16
|
-
"@flownet/lib-
|
|
17
|
-
"@flownet/lib-
|
|
18
|
-
"@flownet/lib-
|
|
19
|
-
"@flownet/lib-
|
|
20
|
-
"@flownet/lib-
|
|
21
|
-
"@flownet/lib-
|
|
22
|
-
"@flownet/lib-
|
|
23
|
-
"@flownet/lib-
|
|
24
|
-
"@flownet/lib-to-
|
|
25
|
-
"@
|
|
26
|
-
"@
|
|
27
|
-
"@
|
|
28
|
-
"@
|
|
29
|
-
"@
|
|
30
|
-
"@
|
|
31
|
-
"@
|
|
32
|
-
"@
|
|
33
|
-
"@
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
25
|
+
"@flownet/lib-atom-api-js": "^0.2.3",
|
|
26
|
+
"@flownet/lib-is-redis-online": "^0.1.15",
|
|
27
|
+
"@flownet/lib-parse-imports-js": "^0.4.4",
|
|
28
|
+
"@flownet/lib-parse-node-url": "^0.1.18",
|
|
29
|
+
"@flownet/lib-render-templates-dir": "^0.1.19",
|
|
30
|
+
"@flownet/lib-to-docker": "^0.3.26",
|
|
31
|
+
"@flownet/lib-to-electron": "^0.3.9",
|
|
32
|
+
"@flownet/lib-to-ios-app": "^0.3.16",
|
|
33
|
+
"@flownet/lib-to-macos-app": "^0.3.13",
|
|
34
|
+
"@flownet/lib-to-nextjs": "^0.3.10",
|
|
35
|
+
"@flownet/lib-to-webos": "^0.3.16",
|
|
36
|
+
"@fnet/auto-conda-env": "^0.2.1",
|
|
37
|
+
"@fnet/config": "^0.2.29",
|
|
38
|
+
"@fnet/dir-zipper": "^0.1.8",
|
|
39
|
+
"@fnet/files-to-gcs": "^0.3.12",
|
|
40
|
+
"@fnet/key-value-transformer": "^0.1.4",
|
|
41
|
+
"@fnet/npm-list-versions": "^0.1.35",
|
|
42
|
+
"@fnet/npm-pick-versions": "^0.1.14",
|
|
43
|
+
"@fnet/object-from-schema": "^0.1.25",
|
|
44
|
+
"@fnet/prompt": "^0.2.16",
|
|
45
|
+
"@fnet/rollup-plugin-delete": "^0.1.10",
|
|
46
|
+
"@fnet/service": "^0.1.8",
|
|
47
|
+
"@fnet/shell-flow": "^0.2.8",
|
|
48
|
+
"@fnet/shelljs": "^0.2.4",
|
|
49
|
+
"@fnet/to-pyip": "^0.2.1",
|
|
50
|
+
"@fnet/to-rust": "^0.1.14",
|
|
51
|
+
"@fnet/up-list-files": "^0.1.14",
|
|
52
|
+
"@fnet/yaml": "^0.1.45",
|
|
53
|
+
"@node-red/util": "^4.1.1",
|
|
54
|
+
"ajv": "^8.17.1",
|
|
55
|
+
"ajv-formats": "^3.0.1",
|
|
56
|
+
"bpmn-moddle": "^9.0.4",
|
|
57
|
+
"chalk": "^5.6.2",
|
|
58
|
+
"cli-table3": "^0.6.5",
|
|
59
|
+
"dagre": "^0.8.5",
|
|
60
|
+
"form-data": "^4.0.4",
|
|
61
|
+
"get-value": "^4.0.1",
|
|
41
62
|
"isobject": "^4.0.0",
|
|
42
|
-
"js-yaml": "^4.1.0",
|
|
43
|
-
"keycloak-connect": "^22.0.3",
|
|
44
63
|
"lodash.clonedeep": "^4.5.0",
|
|
45
64
|
"lodash.merge": "^4.6.2",
|
|
46
|
-
"minimist": "^1.2.8",
|
|
47
65
|
"nunjucks": "^3.2.4",
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"request-ip": "^3.3.0",
|
|
52
|
-
"semver": "^7.5.4",
|
|
53
|
-
"serve": "^14.2.1",
|
|
66
|
+
"object-hash": "^3.0.0",
|
|
67
|
+
"prettier": "^3.6.2",
|
|
68
|
+
"semver": "^7.7.3",
|
|
54
69
|
"set-value": "^4.1.0",
|
|
55
|
-
"
|
|
56
|
-
"typescript": "^5.
|
|
57
|
-
"
|
|
58
|
-
"yaml": "^2.
|
|
59
|
-
"yargs": "^
|
|
70
|
+
"tree-kill": "^1.2.2",
|
|
71
|
+
"typescript": "^5.9.3",
|
|
72
|
+
"winston": "^3.18.3",
|
|
73
|
+
"yaml": "^2.8.1",
|
|
74
|
+
"yargs": "^18.0.0",
|
|
75
|
+
"rollup": "^4.52.5"
|
|
60
76
|
},
|
|
61
77
|
"bin": {
|
|
62
|
-
"fnet": "dist/
|
|
63
|
-
"
|
|
64
|
-
"
|
|
65
|
-
"
|
|
78
|
+
"fnet": "dist/fnet/index.js",
|
|
79
|
+
"fnode": "dist/fnode/index.js",
|
|
80
|
+
"frun": "dist/frun/index.js",
|
|
81
|
+
"fbin": "dist/fbin/index.js",
|
|
82
|
+
"fservice": "dist/fservice/index.js"
|
|
66
83
|
},
|
|
67
84
|
"devDependencies": {
|
|
68
|
-
"@rollup/plugin-commonjs": "^
|
|
69
|
-
"@rollup/plugin-json": "^6.
|
|
70
|
-
"@rollup/plugin-node-resolve": "^
|
|
71
|
-
"@rollup/plugin-replace": "^
|
|
72
|
-
"@rollup/plugin-terser": "^0.4.
|
|
73
|
-
"
|
|
85
|
+
"@rollup/plugin-commonjs": "^28.0.8",
|
|
86
|
+
"@rollup/plugin-json": "^6.1.0",
|
|
87
|
+
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
88
|
+
"@rollup/plugin-replace": "^6.0.2",
|
|
89
|
+
"@rollup/plugin-terser": "^0.4.4",
|
|
90
|
+
"peggy": "^5.0.6"
|
|
74
91
|
}
|
|
75
92
|
}
|
package/readme.md
ADDED
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
# @fnet/cli: Flownet CLI
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<img src="https://raw.githubusercontent.com/fnetai/cli/main/assets/flownet-logo.png" alt="Flownet Logo" width="200"/>
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<b>Focus on functional code, let Flownet handle the rest</b>
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
<p align="center">
|
|
12
|
+
<a href="https://www.npmjs.com/package/@fnet/cli"><img src="https://img.shields.io/npm/v/@fnet/cli.svg" alt="npm version"></a>
|
|
13
|
+
<a href="https://www.npmjs.com/package/@fnet/cli"><img src="https://img.shields.io/npm/dm/@fnet/cli.svg" alt="npm downloads"></a>
|
|
14
|
+
<a href="https://github.com/fnetai/cli/blob/main/LICENSE"><img src="https://img.shields.io/github/license/fnetai/cli.svg" alt="license"></a>
|
|
15
|
+
</p>
|
|
16
|
+
|
|
17
|
+
## Overview
|
|
18
|
+
|
|
19
|
+
Flownet is a low-level flow framework that separates **Core** (your business logic) from **Layers** (infrastructure, dev, build, runtime, and delivery). This separation allows developers to focus on functional code while Flownet automates the surrounding infrastructure. The `@fnet/cli` package provides command-line tools to create, build, and manage Flownet projects.
|
|
20
|
+
|
|
21
|
+
Flownet provides primitives for composing workflows similar to how React provides components for UI development. It emphasizes a **schema-first approach** with **deterministic core**, enabling **multi-runtime portability**.
|
|
22
|
+
|
|
23
|
+
### Key Features
|
|
24
|
+
|
|
25
|
+
- **Core vs Layers Architecture**: Separate your business logic (Core) from infrastructure (Layers) for cleaner, more maintainable code
|
|
26
|
+
- **Nodes & Flows**: Reusable functional units (Nodes) with explicit I/O schemas, orchestrated by Flows
|
|
27
|
+
- **I/O Contracts**: Schema-first contracts define clear input/output expectations for deterministic behavior
|
|
28
|
+
- **Automatic Dependency Detection**: Simplified dependency management across your project
|
|
29
|
+
- **Multi-Runtime Support**: Deploy to Node.js, Bun, Deno, or Python - choose the best runtime for each task
|
|
30
|
+
- **Language Agnostic**: Support for multiple programming languages (JavaScript, Python) in the same project
|
|
31
|
+
- **Unified Interface**: Consistent commands across different project types
|
|
32
|
+
- **Tag-Based Configuration**: Powerful conditional configuration with `--ftag` parameter
|
|
33
|
+
- **Isolated Workspace**: All build artifacts and dependencies are kept in `.workspace` directory
|
|
34
|
+
- **Binary System**: Compile, install, and manage CLI tools with the integrated binary system
|
|
35
|
+
- **Project File Configuration**: Configure CLI features directly in your project files
|
|
36
|
+
- **Fast Startup**: Pre-compiled binaries start much faster than interpreted scripts
|
|
37
|
+
|
|
38
|
+
## Installation
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
# Using npm
|
|
42
|
+
npm install -g @fnet/cli
|
|
43
|
+
|
|
44
|
+
# Using yarn
|
|
45
|
+
yarn global add @fnet/cli
|
|
46
|
+
|
|
47
|
+
# Using bun
|
|
48
|
+
bun install -g @fnet/cli
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Quick Start
|
|
52
|
+
|
|
53
|
+
### Create a New Project
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
# Create a Node.js project
|
|
57
|
+
fnode create my-node-project
|
|
58
|
+
|
|
59
|
+
# Create a Python project
|
|
60
|
+
fnode create my-python-project --runtime python
|
|
61
|
+
|
|
62
|
+
# Create a Bun project
|
|
63
|
+
fnode create my-bun-project --runtime bun
|
|
64
|
+
|
|
65
|
+
# Create a workflow project
|
|
66
|
+
fnet create my-workflow-project
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Build and Run
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
# Build the project
|
|
73
|
+
frun build
|
|
74
|
+
|
|
75
|
+
# Run the project
|
|
76
|
+
fnode cli
|
|
77
|
+
|
|
78
|
+
# Execute a command group from project file
|
|
79
|
+
frun <command-group> [--ftag <tags>]
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Compile and Install
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
# Compile a JavaScript file to a binary
|
|
86
|
+
fbin compile script.js -o my-tool
|
|
87
|
+
|
|
88
|
+
# Install a compiled binary
|
|
89
|
+
fbin install ./my-tool --name awesome-tool
|
|
90
|
+
|
|
91
|
+
# Install a CLI-enabled project
|
|
92
|
+
cd my-project
|
|
93
|
+
fnode install --yes
|
|
94
|
+
|
|
95
|
+
# Or use npm scripts in your project
|
|
96
|
+
npm run compile
|
|
97
|
+
npm run install-bin
|
|
98
|
+
|
|
99
|
+
# List installed binaries
|
|
100
|
+
fbin list
|
|
101
|
+
|
|
102
|
+
# Uninstall a binary
|
|
103
|
+
fbin uninstall awesome-tool --yes
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Core Concepts
|
|
107
|
+
|
|
108
|
+
### Nodes & Flows
|
|
109
|
+
|
|
110
|
+
**Nodes** are reusable functional units that encapsulate business logic with explicit input/output schemas. Each Node:
|
|
111
|
+
|
|
112
|
+
- Has a deterministic, pure function at its core
|
|
113
|
+
- Defines clear I/O contracts (input and output schemas)
|
|
114
|
+
- Can be composed and reused across different Flows
|
|
115
|
+
- Supports automatic dependency detection
|
|
116
|
+
|
|
117
|
+
**Flows** orchestrate Nodes and sub-Flows to create complex workflows. Flows:
|
|
118
|
+
|
|
119
|
+
- Connect multiple Nodes with explicit data contracts
|
|
120
|
+
- Enable multi-runtime portability through schema-first design
|
|
121
|
+
- Support complex data transformations and conditional logic
|
|
122
|
+
- Maintain deterministic behavior across different runtimes
|
|
123
|
+
|
|
124
|
+
### I/O Contracts
|
|
125
|
+
|
|
126
|
+
Flownet uses schema-first contracts to define clear input and output expectations. This approach:
|
|
127
|
+
|
|
128
|
+
- Ensures deterministic behavior across different runtimes
|
|
129
|
+
- Enables automatic validation and type checking
|
|
130
|
+
- Facilitates multi-runtime portability
|
|
131
|
+
- Improves code clarity and maintainability
|
|
132
|
+
|
|
133
|
+
## Project Types
|
|
134
|
+
|
|
135
|
+
Flownet supports two main project types:
|
|
136
|
+
|
|
137
|
+
### fnode Project
|
|
138
|
+
|
|
139
|
+
An **fnode project** (Flow Node Project) is a classic/node-style project that focuses on creating reusable Nodes or standalone applications. These projects:
|
|
140
|
+
|
|
141
|
+
- Use `fnode.yaml` as their configuration file
|
|
142
|
+
- Typically contain a single code file in the `src` directory
|
|
143
|
+
- Can be built with different runtimes (Node.js, Python, Bun)
|
|
144
|
+
- Support multiple programming languages simultaneously
|
|
145
|
+
- Ideal for creating reusable components with explicit I/O contracts
|
|
146
|
+
|
|
147
|
+
### fnet Project
|
|
148
|
+
|
|
149
|
+
An **fnet project** (Flow Project) is a workflow-oriented project that focuses on orchestrating multiple Nodes and Flows. These projects:
|
|
150
|
+
|
|
151
|
+
- Use `fnet.yaml` as their configuration file
|
|
152
|
+
- Define workflows that connect multiple Nodes and sub-Flows
|
|
153
|
+
- Support complex data flows and transformations
|
|
154
|
+
- Enable multi-runtime deployment strategies
|
|
155
|
+
- Ideal for building complex business logic orchestrations
|
|
156
|
+
|
|
157
|
+
## CLI Tools
|
|
158
|
+
|
|
159
|
+
Flownet provides five main CLI tools:
|
|
160
|
+
|
|
161
|
+
- **`fnode`**: For Node/classic projects (uses `fnode.yaml`) - create and manage reusable Nodes
|
|
162
|
+
- **`fnet`**: For Workflow projects (uses `fnet.yaml`) - create and manage Flows that orchestrate Nodes
|
|
163
|
+
- **`frun`**: Unified interface that works with both project types (auto-detects project file)
|
|
164
|
+
- **`fbin`**: Binary management system for installing, compiling, and managing CLI tools
|
|
165
|
+
- **`fservice`**: Service management for deploying and running Flownet applications
|
|
166
|
+
|
|
167
|
+
## Multi-Language & Multi-Runtime Support
|
|
168
|
+
|
|
169
|
+
Flownet supports multiple programming languages and runtimes simultaneously within the same project:
|
|
170
|
+
|
|
171
|
+
```text
|
|
172
|
+
my-project/
|
|
173
|
+
├── src/
|
|
174
|
+
│ ├── index.js # JavaScript implementation (used by Node.js and Bun)
|
|
175
|
+
│ └── index.py # Python implementation
|
|
176
|
+
├── fnode.yaml # Project configuration file
|
|
177
|
+
└── .workspace/ # Build infrastructure (managed by CLI)
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Supported Runtimes
|
|
181
|
+
|
|
182
|
+
- **Node.js**: JavaScript/TypeScript execution with full npm ecosystem support
|
|
183
|
+
- **Bun**: Fast JavaScript runtime with improved performance
|
|
184
|
+
- **Deno**: Secure JavaScript/TypeScript runtime with built-in tooling
|
|
185
|
+
- **Python**: Python 3.x for data processing and scientific computing
|
|
186
|
+
|
|
187
|
+
### Multi-Runtime Portability
|
|
188
|
+
|
|
189
|
+
Thanks to Flownet's schema-first approach and deterministic core:
|
|
190
|
+
|
|
191
|
+
- Write your core logic once and deploy to multiple runtimes
|
|
192
|
+
- Choose the best runtime for each specific use case
|
|
193
|
+
- Migrate between runtimes without changing your business logic
|
|
194
|
+
- Use JavaScript with Node.js for quick development, Python for data processing, and Bun for improved performance
|
|
195
|
+
|
|
196
|
+
## Tag-Based Configuration
|
|
197
|
+
|
|
198
|
+
Both CLI tools support the `--ftag` parameter for powerful conditional configuration:
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
frun build --ftag dev --ftag local
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
This activates sections in your project file marked with `t::dev::` or `t::local::` tags:
|
|
205
|
+
|
|
206
|
+
```yaml
|
|
207
|
+
# Base configuration
|
|
208
|
+
name: my-project
|
|
209
|
+
|
|
210
|
+
# Development environment configuration
|
|
211
|
+
t::dev::database:
|
|
212
|
+
url: "mongodb://localhost:27017"
|
|
213
|
+
|
|
214
|
+
# Production environment configuration
|
|
215
|
+
t::prod::database:
|
|
216
|
+
url: "mongodb://production-server:27017"
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## Binary System
|
|
220
|
+
|
|
221
|
+
Flownet includes a powerful binary system that makes it easy to create, distribute, and manage CLI tools:
|
|
222
|
+
|
|
223
|
+
### Binary System Features
|
|
224
|
+
|
|
225
|
+
- **Fast Startup**: Pre-compiled binaries start much faster than interpreted scripts
|
|
226
|
+
- **Cross-Platform Support**: Works on macOS, Linux, and Windows
|
|
227
|
+
- **Multiple Shell Support**: Compatible with Bash, Zsh, Fish, PowerShell, and more
|
|
228
|
+
- **Version Management**: Keeps track of binary versions and metadata
|
|
229
|
+
- **Project Integration**: Easily compile and install CLI-enabled projects
|
|
230
|
+
- **Automation Support**: All commands support the `--yes` flag for scripting
|
|
231
|
+
|
|
232
|
+
### Setup and Usage
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
# Initialize the bin system
|
|
236
|
+
fbin setup
|
|
237
|
+
|
|
238
|
+
# Add bin directory to PATH
|
|
239
|
+
fbin path
|
|
240
|
+
|
|
241
|
+
# Compile a JavaScript file to a binary
|
|
242
|
+
fbin compile script.js -o my-tool
|
|
243
|
+
|
|
244
|
+
# Install a binary to the bin directory
|
|
245
|
+
fbin install ./my-tool --name awesome-tool
|
|
246
|
+
|
|
247
|
+
# List installed binaries
|
|
248
|
+
fbin list
|
|
249
|
+
|
|
250
|
+
# Uninstall a binary
|
|
251
|
+
fbin uninstall awesome-tool
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### Project Integration
|
|
255
|
+
|
|
256
|
+
The binary system integrates seamlessly with Flownet projects:
|
|
257
|
+
|
|
258
|
+
```bash
|
|
259
|
+
# Compile and install a CLI-enabled fnode project
|
|
260
|
+
fnode compile
|
|
261
|
+
fnode install
|
|
262
|
+
|
|
263
|
+
# Compile and install a CLI-enabled fnet project
|
|
264
|
+
fnet compile
|
|
265
|
+
fnet install
|
|
266
|
+
|
|
267
|
+
# Using npm scripts in your project
|
|
268
|
+
npm run compile
|
|
269
|
+
npm run install-bin
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
This makes it easy to distribute your Flownet projects as standalone CLI tools.
|
|
273
|
+
|
|
274
|
+
### CLI Configuration in Project Files
|
|
275
|
+
|
|
276
|
+
You can configure CLI features directly in your project files:
|
|
277
|
+
|
|
278
|
+
```yaml
|
|
279
|
+
# In fnode.yaml or fnet.yaml
|
|
280
|
+
name: my-project
|
|
281
|
+
|
|
282
|
+
features:
|
|
283
|
+
# For fnode projects
|
|
284
|
+
s::runtime.type: node # or python, bun
|
|
285
|
+
|
|
286
|
+
# CLI configuration
|
|
287
|
+
cli:
|
|
288
|
+
enabled: true
|
|
289
|
+
bin: custom-bin-name # Name of the binary (defaults to project name)
|
|
290
|
+
installable: true # Enable 'fnode install' or 'fnet install' command
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
This configuration will:
|
|
294
|
+
|
|
295
|
+
1. Enable CLI functionality for your project
|
|
296
|
+
2. Set the binary name to `custom-bin-name`
|
|
297
|
+
3. Add `compile` and `install-bin` scripts to your package.json
|
|
298
|
+
4. Allow you to install the binary with `fnode install` or `npm run install-bin`
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// import getValue from "get-value";
|
|
2
|
+
// import setValue from "set-value";
|
|
3
|
+
import { setProperty, getProperty } from "dot-prop";
|
|
4
|
+
|
|
5
|
+
export default class Object {
|
|
6
|
+
#property;
|
|
7
|
+
#context;
|
|
8
|
+
#module;
|
|
9
|
+
|
|
10
|
+
constructor(context) {
|
|
11
|
+
|
|
12
|
+
this.#property = {}
|
|
13
|
+
this.#module = {};
|
|
14
|
+
this.#context = context;
|
|
15
|
+
|
|
16
|
+
this.get = (path, options) => {
|
|
17
|
+
return getProperty(this.#property, path, options);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
this.set = (path, value, options) => {
|
|
21
|
+
return setProperty(this.#property, path, value, options);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
this.print = console.log;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
get module() {
|
|
28
|
+
return this.#module;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
getModule(path) {
|
|
32
|
+
return getProperty(this.#module, path);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
setModule(path, module) {
|
|
36
|
+
if (typeof module !== "function") throw new Error("Module must be a function");
|
|
37
|
+
return setProperty(this.#module, path, module);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
get getValue() {
|
|
41
|
+
return getProperty;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
get setValue() {
|
|
45
|
+
return setProperty;
|
|
46
|
+
}
|
|
47
|
+
}
|