@fnet/cli 0.4.25 → 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.
Files changed (184) hide show
  1. package/dist/fbin/index.js +15 -0
  2. package/dist/fnet/index.0jcm9pn5.js +1 -0
  3. package/dist/fnet/index.2084z2ed.js +2 -0
  4. package/dist/fnet/index.33f1ggpr.js +1 -0
  5. package/dist/fnet/index.3exge2js.js +1 -0
  6. package/dist/fnet/index.3kfx538h.js +1 -0
  7. package/dist/fnet/index.490y87nc.js +1 -0
  8. package/dist/fnet/index.4g9yezkq.js +1 -0
  9. package/dist/fnet/index.4jkat7r4.js +1 -0
  10. package/dist/fnet/index.7crx8ky1.js +1 -0
  11. package/dist/fnet/index.7vw06nrn.js +1 -0
  12. package/dist/fnet/index.9567fa9x.js +1 -0
  13. package/dist/fnet/index.gh75wt1m.js +1 -0
  14. package/dist/fnet/index.hzsfswvp.js +1 -0
  15. package/dist/fnet/index.jgpc3grc.js +1 -0
  16. package/dist/fnet/index.js +20 -0
  17. package/dist/fnet/index.p0zb7e1b.js +1 -0
  18. package/dist/fnet/index.r19p3bpa.js +2 -0
  19. package/dist/fnet/index.r82rtnmz.js +1 -0
  20. package/dist/fnet/index.s662t98v.js +1 -0
  21. package/dist/fnet/index.w74dpnpn.js +1 -0
  22. package/dist/fnet/index.xeaw5xa9.js +1 -0
  23. package/dist/fnet/index.zm4kesg6.js +1 -0
  24. package/dist/fnode/index.05n3mvs9.js +2 -0
  25. package/dist/fnode/index.2hc9tbyx.js +1 -0
  26. package/dist/fnode/index.2pnjg6dc.js +1 -0
  27. package/dist/fnode/index.9qgtmxq3.js +2 -0
  28. package/dist/fnode/index.b1q7y05p.js +1 -0
  29. package/dist/fnode/index.bhapgrs7.js +1 -0
  30. package/dist/fnode/index.cvxrf34y.js +2 -0
  31. package/dist/fnode/index.dp9wyahp.js +1 -0
  32. package/dist/fnode/index.eqxmcpdc.js +1 -0
  33. package/dist/fnode/index.f2tb0x3t.js +1 -0
  34. package/dist/fnode/index.fbzv6wwf.js +1 -0
  35. package/dist/fnode/index.gazd9raq.js +1 -0
  36. package/dist/fnode/index.hv4s25f0.js +3 -0
  37. package/dist/fnode/index.j5z7dtsx.js +1 -0
  38. package/dist/fnode/index.js +10 -0
  39. package/dist/fnode/index.kb4e4bxf.js +1 -0
  40. package/dist/fnode/index.qn0schqp.js +1 -0
  41. package/dist/fnode/index.rht29phd.js +1 -0
  42. package/dist/fnode/index.rzsfmek6.js +3 -0
  43. package/dist/fnode/index.s0nk6cv8.js +4 -0
  44. package/dist/fnode/index.s66v6wt4.js +1 -0
  45. package/dist/fnode/index.sv7v0y60.js +1 -0
  46. package/dist/fnode/index.tgkhgnrp.js +1 -0
  47. package/dist/fnode/index.vq706f75.js +1 -0
  48. package/dist/fnode/index.y8pvdcny.js +1 -0
  49. package/dist/fnode/index.z4vz93ww.js +1 -0
  50. package/dist/frun/index.js +2 -0
  51. package/dist/fservice/index.js +19 -0
  52. package/dist/fservice/index.q01yvaz0.js +2 -0
  53. package/package.json +74 -57
  54. package/readme.md +298 -0
  55. package/template/fnet/core/assert.js +6 -0
  56. package/template/fnet/core/message.js +3 -0
  57. package/template/fnet/core/object.js +47 -0
  58. package/template/fnet/core/sleep.js +5 -0
  59. package/template/fnet/node/.gitignore.njk +9 -0
  60. package/template/fnet/node/build.js.njk +153 -0
  61. package/template/fnet/node/package.json.njk +121 -0
  62. package/template/fnet/node/readme.md.njk +21 -0
  63. package/template/fnet/node/rollup.config.mjs.njk +498 -0
  64. package/template/fnet/node/src/app/index.html.njk +67 -0
  65. package/template/fnet/node/src/app/index.js.njk +36 -0
  66. package/template/fnet/node/src/cli/index.js.njk +9 -0
  67. package/template/fnet/node/src/cli/index.js.v1.njk +318 -0
  68. package/template/fnet/node/src/cli/v2/core/args-parser.njk +10 -0
  69. package/template/fnet/node/src/cli/v2/core/imports.njk +31 -0
  70. package/template/fnet/node/src/cli/v2/core/run-wrapper.njk +25 -0
  71. package/template/fnet/node/src/cli/v2/index.js.njk +184 -0
  72. package/template/fnet/node/src/cli/v2/modes/default/extend.njk +11 -0
  73. package/template/fnet/node/src/cli/v2/modes/default/standard.njk +20 -0
  74. package/template/fnet/node/src/cli/v2/modes/http/builtin.njk +66 -0
  75. package/template/fnet/node/src/cli/v2/modes/http/imports.njk +9 -0
  76. package/template/fnet/node/src/cli/v2/modes/http/index.njk +12 -0
  77. package/template/fnet/node/src/cli/v2/modes/mcp/imports.njk +33 -0
  78. package/template/fnet/node/src/cli/v2/modes/mcp/index.njk +30 -0
  79. package/template/fnet/node/src/cli/v2/modes/mcp/server-setup.njk +15 -0
  80. package/template/fnet/node/src/cli/v2/modes/mcp/tool-handlers.njk +46 -0
  81. package/template/fnet/node/src/cli/v2/modes/mcp/transport-http.njk +222 -0
  82. package/template/fnet/node/src/cli/v2/modes/mcp/transport-stdio.njk +9 -0
  83. package/template/fnet/node/src/cli/v2/modes/pipeline/imports.njk +9 -0
  84. package/template/fnet/node/src/cli/v2/modes/pipeline/index.njk +113 -0
  85. package/template/fnet/node/src/cli/v2/modes/webhook/imports.njk +9 -0
  86. package/template/fnet/node/src/cli/v2/modes/webhook/index.njk +127 -0
  87. package/template/fnet/node/src/cli/v2/modes/websocket/imports.njk +15 -0
  88. package/template/fnet/node/src/cli/v2/modes/websocket/index.njk +104 -0
  89. package/template/fnet/node/src/default/blocks/assign.js.njk +15 -0
  90. package/template/fnet/node/src/default/blocks/call.js.njk +110 -0
  91. package/template/fnet/node/src/default/blocks/for.js.njk +71 -0
  92. package/template/fnet/node/src/default/blocks/form.js.njk +31 -0
  93. package/template/fnet/node/src/default/blocks/http.js.njk +135 -0
  94. package/template/fnet/node/src/default/blocks/modules.js.njk +15 -0
  95. package/template/fnet/node/src/default/blocks/new.js.njk +42 -0
  96. package/template/fnet/node/src/default/blocks/next.js.njk +15 -0
  97. package/template/fnet/node/src/default/blocks/output.js.njk +15 -0
  98. package/template/fnet/node/src/default/blocks/parallel.js.njk +64 -0
  99. package/template/fnet/node/src/default/blocks/pipeline.js.njk +109 -0
  100. package/template/fnet/node/src/default/blocks/raise.js.njk +15 -0
  101. package/template/fnet/node/src/default/blocks/retry.js.njk +70 -0
  102. package/template/fnet/node/src/default/blocks/return.js.njk +21 -0
  103. package/template/fnet/node/src/default/blocks/schedule.js.njk +66 -0
  104. package/template/fnet/node/src/default/blocks/signal.js.njk +15 -0
  105. package/template/fnet/node/src/default/blocks/steps.js.njk +47 -0
  106. package/template/fnet/node/src/default/blocks/switch.js.njk +49 -0
  107. package/template/fnet/node/src/default/blocks/tryexcept.js.njk +80 -0
  108. package/template/fnet/node/src/default/blocks/wait.js.njk +14 -0
  109. package/template/fnet/node/src/default/engine.js.njk +79 -0
  110. package/template/fnet/node/src/default/input.args.js.njk +125 -0
  111. package/template/fnet/node/src/default/macros/block-assign.js.njk +3 -0
  112. package/template/fnet/node/src/default/macros/block-body-header.js.njk +7 -0
  113. package/template/fnet/node/src/default/macros/block-entry-args.js.njk +2 -0
  114. package/template/fnet/node/src/default/macros/block-footer.js.njk +3 -0
  115. package/template/fnet/node/src/default/macros/block-header.js.njk +11 -0
  116. package/template/fnet/node/src/default/macros/block-library-header.js.njk +43 -0
  117. package/template/fnet/node/src/default/macros/block-modules-header.js.njk +8 -0
  118. package/template/fnet/node/src/default/macros/block-modules.js.njk +29 -0
  119. package/template/fnet/node/src/default/macros/block-next-header.js.njk +6 -0
  120. package/template/fnet/node/src/default/macros/block-next.js.njk +21 -0
  121. package/template/fnet/node/src/default/macros/block-run-footer.js.njk +7 -0
  122. package/template/fnet/node/src/default/macros/block-run-form.js.njk +32 -0
  123. package/template/fnet/node/src/default/macros/block-run-header.js.njk +43 -0
  124. package/template/fnet/node/src/default/macros/block-signal.js.njk +3 -0
  125. package/template/fnet/node/src/default/macros/page.js.njk +8 -0
  126. package/template/fnet/node/src/default/types/block.js.njk +133 -0
  127. package/template/fnet/node/src/default/workflow.js.njk +125 -0
  128. package/template/fnet/node/tsconfig.json.njk +16 -0
  129. package/template/fnet/project/.gitignore.njk +7 -0
  130. package/template/fnet/project/.vscode/launch.json.njk +41 -0
  131. package/template/fnet/project/.vscode/tasks.json.njk +46 -0
  132. package/template/fnet/project/fnet/flows.yaml.njk +4 -0
  133. package/template/fnet/project/fnet/targets.yaml.njk +7 -0
  134. package/template/fnet/project/fnet.yaml.njk +3 -0
  135. package/template/fnode/node/.gitignore.njk +9 -0
  136. package/template/fnode/node/build.js.njk +149 -0
  137. package/template/fnode/node/package.json.njk +121 -0
  138. package/template/fnode/node/readme.md.njk +21 -0
  139. package/template/fnode/node/rollup.config.mjs.njk +498 -0
  140. package/template/fnode/node/src/app/index-js-with-react-host.njk +50 -0
  141. package/template/fnode/node/src/app/index-js-without-react-host.njk +23 -0
  142. package/template/fnode/node/src/app/index.html.njk +67 -0
  143. package/template/fnode/node/src/app/index.js.njk +9 -0
  144. package/template/fnode/node/src/cli/index.js.njk +9 -0
  145. package/template/fnode/node/src/cli/index.js.v1.njk +464 -0
  146. package/template/fnode/node/src/cli/v2/core/args-parser.njk +10 -0
  147. package/template/fnode/node/src/cli/v2/core/imports.njk +29 -0
  148. package/template/fnode/node/src/cli/v2/core/run-wrapper.njk +25 -0
  149. package/template/fnode/node/src/cli/v2/index.js.njk +184 -0
  150. package/template/fnode/node/src/cli/v2/modes/default/extend.njk +11 -0
  151. package/template/fnode/node/src/cli/v2/modes/default/standard.njk +19 -0
  152. package/template/fnode/node/src/cli/v2/modes/http/builtin.njk +61 -0
  153. package/template/fnode/node/src/cli/v2/modes/http/imports.njk +9 -0
  154. package/template/fnode/node/src/cli/v2/modes/http/index.njk +12 -0
  155. package/template/fnode/node/src/cli/v2/modes/mcp/imports.njk +33 -0
  156. package/template/fnode/node/src/cli/v2/modes/mcp/index.njk +30 -0
  157. package/template/fnode/node/src/cli/v2/modes/mcp/server-setup.njk +15 -0
  158. package/template/fnode/node/src/cli/v2/modes/mcp/tool-handlers.njk +41 -0
  159. package/template/fnode/node/src/cli/v2/modes/mcp/transport-http.njk +212 -0
  160. package/template/fnode/node/src/cli/v2/modes/mcp/transport-stdio.njk +9 -0
  161. package/template/fnode/node/src/cli/v2/modes/pipeline/imports.njk +9 -0
  162. package/template/fnode/node/src/cli/v2/modes/pipeline/index.njk +103 -0
  163. package/template/fnode/node/src/cli/v2/modes/webhook/imports.njk +9 -0
  164. package/template/fnode/node/src/cli/v2/modes/webhook/index.njk +122 -0
  165. package/template/fnode/node/src/cli/v2/modes/websocket/imports.njk +15 -0
  166. package/template/fnode/node/src/cli/v2/modes/websocket/index.njk +99 -0
  167. package/template/fnode/node/src/default/engine.js.njk +17 -0
  168. package/template/fnode/node/src/default/input.args.js.njk +126 -0
  169. package/template/fnode/node/tsconfig.json.njk +16 -0
  170. package/template/fnode/project/.gitignore.njk +11 -0
  171. package/template/fnode/project/.vscode/launch.json.njk +78 -0
  172. package/template/fnode/project/.vscode/tasks.json.njk +46 -0
  173. package/template/fnode/project/fnet/targets.yaml.njk +25 -0
  174. package/template/fnode/project/fnode.yaml.njk +12 -0
  175. package/template/fnode/python/.gitignore.njk +7 -0
  176. package/template/fnode/python/package.json.njk +4 -0
  177. package/template/fnode/python/pyproject.toml.njk +3 -0
  178. package/template/fnode/python/readme.md.njk +12 -0
  179. package/template/fnode/python/setup.py.njk +19 -0
  180. package/template/fnode/python/src/cli/index.py.njk +25 -0
  181. package/template/schemas/to-npm.yaml +14 -0
  182. package/dist/builder/lib-cli.js +0 -2
  183. package/dist/builder/wf-cli.js +0 -2
  184. 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.4.25",
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
- "start": "node src/index.js",
9
- "build": "rollup --config --bundleConfigAsCjs",
10
- "deploy": "npm install && npm run build && npm publish --access public",
11
- "watch": "rollup --config --watch --sourcemap --bundleConfigAsCjs --environment DEVELOPMENT"
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.1.7",
15
- "@flownet/lib-bpmn-from-flow": "^0.1.6",
16
- "@flownet/lib-execute-command-with-ipc-file": "^0.1.10",
17
- "@flownet/lib-is-redis-online": "^0.1.12",
18
- "@flownet/lib-list-npm-versions": "^0.1.25",
19
- "@flownet/lib-load-fnet-config": "^0.1.20",
20
- "@flownet/lib-parse-imports-js": "^0.1.34",
21
- "@flownet/lib-parse-node-url": "^0.1.15",
22
- "@flownet/lib-pick-npm-versions": "^0.1.5",
23
- "@flownet/lib-render-templates-dir": "^0.1.11",
24
- "@flownet/lib-to-electron": "^0.1.19",
25
- "@flownet/lib-to-gitlab": "^0.1.20",
26
- "@flownet/lib-to-ios-app": "^0.1.20",
27
- "@flownet/lib-to-macos-app": "^0.1.11",
28
- "@flownet/lib-to-nextjs": "^0.1.10",
29
- "@flownet/lib-to-webos": "^0.1.9",
30
- "@flownet/lib-upload-files-to-gcs": "^0.1.11",
31
- "@flownet/template-node-library": "^0.4.20",
32
- "@flownet/template-node-workflow": "^0.4.24",
33
- "@node-red/util": "^3.1.0",
34
- "axios": "^1.5.0",
35
- "chalk": "^4.1.2",
36
- "cors": "^2.8.5",
37
- "express": "^4.18.2",
38
- "express-session": "^1.17.3",
39
- "get-value": "^3.0.1",
40
- "helmet": "^5.1.1",
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
- "prettier": "^3.0.3",
49
- "qs": "^6.11.2",
50
- "redis": "^4.6.10",
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
- "shelljs": "^0.8.5",
56
- "typescript": "^5.2.2",
57
- "uuid": "^9.0.1",
58
- "yaml": "^2.3.2",
59
- "yargs": "^17.7.2"
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/builder/wf-cli.js",
63
- "flib": "dist/builder/lib-cli.js",
64
- "fsrv": "dist/index.js",
65
- "fnode": "dist/builder/lib-cli.js"
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": "^25.0.4",
69
- "@rollup/plugin-json": "^6.0.0",
70
- "@rollup/plugin-node-resolve": "^15.2.1",
71
- "@rollup/plugin-replace": "^5.0.2",
72
- "@rollup/plugin-terser": "^0.4.3",
73
- "rollup": "^3.29.1"
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,6 @@
1
+ export default (args) => {
2
+ const [condition, message] = Array.isArray(args) ? args : [args];
3
+ if (!condition) {
4
+ throw new Error(message || "Assertion failed");
5
+ }
6
+ }
@@ -0,0 +1,3 @@
1
+ export default {
2
+ USE_CONSTRUCTOR: "Please call it as constructor!"
3
+ }
@@ -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
+ }
@@ -0,0 +1,5 @@
1
+ export default (delay) => {
2
+ return new Promise(function (resolve) {
3
+ setTimeout(resolve, delay);
4
+ });
5
+ }
@@ -0,0 +1,9 @@
1
+ node_modules
2
+ .DS_Store
3
+ .fnet
4
+ .out
5
+ .package
6
+ .vscode
7
+ .output
8
+ .npmrc
9
+ .bin