@runium/cli 0.0.1

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 (87) hide show
  1. package/.eslintrc.json +31 -0
  2. package/.prettierrc.json +10 -0
  3. package/README.md +3 -0
  4. package/build.js +104 -0
  5. package/lib/app.js +6 -0
  6. package/lib/commands/index.js +1 -0
  7. package/lib/commands/plugin/plugin-add.js +1 -0
  8. package/lib/commands/plugin/plugin-command.js +1 -0
  9. package/lib/commands/plugin/plugin-disable.js +1 -0
  10. package/lib/commands/plugin/plugin-enable.js +1 -0
  11. package/lib/commands/plugin/plugin-list.js +1 -0
  12. package/lib/commands/plugin/plugin-remove.js +1 -0
  13. package/lib/commands/plugin/plugin.js +1 -0
  14. package/lib/commands/project/project-add.js +1 -0
  15. package/lib/commands/project/project-command.js +1 -0
  16. package/lib/commands/project/project-list.js +1 -0
  17. package/lib/commands/project/project-remove.js +1 -0
  18. package/lib/commands/project/project-start.js +1 -0
  19. package/lib/commands/project/project-state-command.js +1 -0
  20. package/lib/commands/project/project-status.js +1 -0
  21. package/lib/commands/project/project-stop.js +1 -0
  22. package/lib/commands/project/project-validate.js +1 -0
  23. package/lib/commands/project/project.js +1 -0
  24. package/lib/commands/runium-command.js +1 -0
  25. package/lib/constants/error-code.js +1 -0
  26. package/lib/constants/index.js +1 -0
  27. package/lib/index.js +2 -0
  28. package/lib/macros/conditional.js +1 -0
  29. package/lib/macros/empty.js +1 -0
  30. package/lib/macros/env.js +1 -0
  31. package/lib/macros/index.js +1 -0
  32. package/lib/macros/path.js +1 -0
  33. package/lib/package.json +21 -0
  34. package/lib/services/config.js +1 -0
  35. package/lib/services/index.js +1 -0
  36. package/lib/services/output.js +3 -0
  37. package/lib/services/plugin-context.js +1 -0
  38. package/lib/services/plugin.js +1 -0
  39. package/lib/services/profile.js +1 -0
  40. package/lib/services/project.js +1 -0
  41. package/lib/services/shutdown.js +1 -0
  42. package/lib/utils/convert-path-to-valid-file-name.js +1 -0
  43. package/lib/utils/debounce.js +1 -0
  44. package/lib/utils/format-timestamp.js +1 -0
  45. package/lib/utils/index.js +1 -0
  46. package/package.json +44 -0
  47. package/src/app.ts +175 -0
  48. package/src/commands/index.ts +2 -0
  49. package/src/commands/plugin/plugin-add.ts +48 -0
  50. package/src/commands/plugin/plugin-command.ts +36 -0
  51. package/src/commands/plugin/plugin-disable.ts +46 -0
  52. package/src/commands/plugin/plugin-enable.ts +50 -0
  53. package/src/commands/plugin/plugin-list.ts +61 -0
  54. package/src/commands/plugin/plugin-remove.ts +42 -0
  55. package/src/commands/plugin/plugin.ts +41 -0
  56. package/src/commands/project/project-add.ts +46 -0
  57. package/src/commands/project/project-command.ts +36 -0
  58. package/src/commands/project/project-list.ts +32 -0
  59. package/src/commands/project/project-remove.ts +41 -0
  60. package/src/commands/project/project-start.ts +152 -0
  61. package/src/commands/project/project-state-command.ts +68 -0
  62. package/src/commands/project/project-status.ts +103 -0
  63. package/src/commands/project/project-stop.ts +55 -0
  64. package/src/commands/project/project-validate.ts +43 -0
  65. package/src/commands/project/project.ts +45 -0
  66. package/src/commands/runium-command.ts +50 -0
  67. package/src/constants/error-code.ts +15 -0
  68. package/src/constants/index.ts +1 -0
  69. package/src/index.ts +21 -0
  70. package/src/macros/conditional.ts +31 -0
  71. package/src/macros/empty.ts +6 -0
  72. package/src/macros/env.ts +8 -0
  73. package/src/macros/index.ts +12 -0
  74. package/src/macros/path.ts +9 -0
  75. package/src/services/config.ts +76 -0
  76. package/src/services/index.ts +7 -0
  77. package/src/services/output.ts +201 -0
  78. package/src/services/plugin-context.ts +81 -0
  79. package/src/services/plugin.ts +144 -0
  80. package/src/services/profile.ts +211 -0
  81. package/src/services/project.ts +114 -0
  82. package/src/services/shutdown.ts +130 -0
  83. package/src/utils/convert-path-to-valid-file-name.ts +39 -0
  84. package/src/utils/debounce.ts +23 -0
  85. package/src/utils/format-timestamp.ts +17 -0
  86. package/src/utils/index.ts +3 -0
  87. package/tsconfig.json +40 -0
package/.eslintrc.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "env": {
3
+ "node": true,
4
+ "es2022": true
5
+ },
6
+ "extends": [
7
+ "eslint:recommended",
8
+ "plugin:@typescript-eslint/recommended",
9
+ "plugin:prettier/recommended"
10
+ ],
11
+ "parser": "@typescript-eslint/parser",
12
+ "parserOptions": {
13
+ "ecmaVersion": 2022,
14
+ "sourceType": "module"
15
+ },
16
+ "plugins": ["@typescript-eslint", "prettier"],
17
+ "rules": {
18
+ "prettier/prettier": "error",
19
+ "@typescript-eslint/no-unused-vars": [
20
+ "error",
21
+ {
22
+ "argsIgnorePattern": "^_",
23
+ "varsIgnorePattern": "^_"
24
+ }
25
+ ],
26
+ "@typescript-eslint/no-explicit-any": "warn",
27
+ "no-console": "warn",
28
+ "@typescript-eslint/ban-ts-ignore": "off"
29
+ },
30
+ "ignorePatterns": ["dist/", "node_modules/"]
31
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "semi": true,
3
+ "trailingComma": "es5",
4
+ "singleQuote": true,
5
+ "printWidth": 80,
6
+ "tabWidth": 2,
7
+ "useTabs": false,
8
+ "endOfLine": "lf",
9
+ "arrowParens": "avoid"
10
+ }
package/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # @runium/cli
2
+
3
+ Runium CLI
package/build.js ADDED
@@ -0,0 +1,104 @@
1
+ #!/usr/bin/env node
2
+
3
+ import {
4
+ chmodSync,
5
+ existsSync,
6
+ mkdirSync,
7
+ readFileSync,
8
+ writeFileSync,
9
+ } from 'node:fs';
10
+ import { resolve } from 'node:path';
11
+ import { build } from 'esbuild';
12
+ import { esbuildDecorators } from 'esbuild-decorators';
13
+
14
+ const buildConfig = {
15
+ entryPoints: ['src/**/*.ts'],
16
+ bundle: false,
17
+ platform: 'node',
18
+ target: 'esnext',
19
+ format: 'esm',
20
+ outdir: 'lib',
21
+ sourcemap: false,
22
+ minify: true,
23
+ treeShaking: false,
24
+ plugins: [
25
+ esbuildDecorators({
26
+ experimentalDecorators: true,
27
+ emitDecoratorMetadata: true,
28
+ customDecoratorHandler: undefined,
29
+ }),
30
+ ],
31
+ metafile: true,
32
+ logLevel: 'info',
33
+ };
34
+
35
+ async function buildProject() {
36
+ try {
37
+ console.log('🔨 Building project with esbuild...');
38
+
39
+ const libDir = 'lib';
40
+ if (!existsSync(libDir)) {
41
+ mkdirSync(libDir, { recursive: true });
42
+ }
43
+
44
+ const result = await build(buildConfig);
45
+
46
+ if (process.platform !== 'win32') {
47
+ try {
48
+ chmodSync('lib/index.js', '755');
49
+ console.log('🔐 Made lib/index.js executable');
50
+ } catch (error) {
51
+ console.warn('⚠️ Could not make file executable:', error.message);
52
+ }
53
+ }
54
+
55
+ console.log('🔨 Processing package.json...');
56
+ await processPackageJson();
57
+
58
+ console.log('✅ Build completed successfully!');
59
+
60
+ // Print build stats if in development mode
61
+ if (result.metafile) {
62
+ console.log('\n📊 Build statistics:');
63
+ const outputs = result.metafile.outputs;
64
+ for (const [file, info] of Object.entries(outputs)) {
65
+ console.log(` ${file}: ${(info.bytes / 1024).toFixed(2)} KB`);
66
+ }
67
+ }
68
+ } catch (error) {
69
+ console.error('❌ Build failed:', error);
70
+ process.exit(1);
71
+ }
72
+ }
73
+
74
+ async function processPackageJson() {
75
+ let originalPackageJson = readFileSync('./package.json', {
76
+ encoding: 'utf-8',
77
+ });
78
+
79
+ let packageJson = JSON.parse(originalPackageJson);
80
+ const { dependencies } = packageJson;
81
+
82
+ delete packageJson.dependencies;
83
+ delete packageJson.devDependencies;
84
+ delete packageJson.scripts;
85
+ delete packageJson.module;
86
+ delete packageJson.main;
87
+
88
+ console.log('✅ Processed package.json', packageJson);
89
+
90
+ packageJson = Object.assign(packageJson, {
91
+ module: './index.js',
92
+ bin: {
93
+ runium: './index.js',
94
+ },
95
+ dependencies,
96
+ });
97
+
98
+ writeFileSync(
99
+ resolve(import.meta.dirname, 'lib', 'package.json'),
100
+ JSON.stringify(packageJson, null, 2)
101
+ );
102
+ }
103
+
104
+ buildProject();
package/lib/app.js ADDED
@@ -0,0 +1,6 @@
1
+ import{existsSync as u}from"node:fs";import{resolve as p}from"node:path";import{Command as v}from"commander";import{Container as o}from"typedi";import*as c from"./commands/index.js";import{ConfigService as l,ProfileService as g,PluginService as d,ShutdownService as m,OutputService as h,OutputLevel as r,PluginContextService as S}from"./services/index.js";const f=`\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
2
+ \u2551 \u2554\u2550\u2557 \u2566 \u2566 \u2554\u2557\u2554 \u2566 \u2566 \u2566 \u2554\u2566\u2557 \u2551
3
+ \u2551 \u2560\u2566\u255D \u2551 \u2551 \u2551\u2551\u2551 \u2551 \u2551 \u2551 \u2551\u2551\u2551 \u2551
4
+ \u2551 \u2569\u255A\u2550 \u255A\u2550\u255D \u255D\u255A\u255D \u2569 \u255A\u2550\u255D \u2569 \u2569 \u2551
5
+ \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
6
+ One Tool to Run Them All!`;class F{program;configService;profileService;pluginService;shutdownService;outputService;pluginContextService;constructor(){this.program=new v("runium"),this.configService=o.get(l),this.profileService=o.get(g),this.pluginService=o.get(d),this.shutdownService=o.get(m),this.outputService=o.get(h),this.pluginContextService=o.get(S)}async start(){return await this.shutdownService.init(),await this.configService.init(),await this.profileService.init(),await this.pluginContextService.init(),this.initOutput(),this.initEnv(),await this.loadPlugins(),await this.initProgram(),this.program.parseAsync()}async loadPlugins(){const e=this.profileService.getPlugins();for(const i of e)if(i.disabled!==!0)try{const t=this.pluginService.resolvePath(i.path,i.file);await this.pluginService.loadPlugin(t)}catch(t){this.outputService.error(`Failed to load plugin "${i.name}"`);const{code:n,message:s,payload:a}=t;this.outputService.debug("Error details:",{message:s,code:n,payload:a})}}async initProgram(){const e=this.program;e.option("-D, --debug","enable debug mode"),e.option("-e, --env [paths...]","load env files"),e.version(await this.getVersion()),e.description(f),e.on("option:debug",()=>{this.setDebugMode()}),e.on("option:env",t=>{this.loadEnvFiles([t])});const i=[];Object.values(c).forEach(t=>{i.push(new t(e))})}initOutput(){(this.configService.get("output").debug||process.argv.includes("-D")||process.argv.includes("--debug"))&&this.setDebugMode()}setDebugMode(){this.outputService.getLevel()!==r.DEBUG&&(this.outputService.setLevel(r.DEBUG),this.outputService.debug("Debug mode enabled"))}initEnv(){const e=this.configService.get("env");e.path.length>0&&this.loadEnvFiles(e.path)}loadEnvFiles(e){for(const i of e)i?u(i)?process.loadEnvFile(p(i)):this.outputService.debug(`Env file "${i}" not found`):process.loadEnvFile()}async getVersion(){return(await import("./package.json",{with:{type:"json"}})).default.version}}export{F as RuniumCliApp};
@@ -0,0 +1 @@
1
+ import{PluginCommand as r}from"./plugin/plugin.js";import{ProjectCommand as n}from"./project/project.js";export{r as PluginCommand,n as ProjectCommand};
@@ -0,0 +1 @@
1
+ import{RuniumError as a}from"@runium/core";import{ErrorCode as l}from"../../constants/index.js";import{PluginCommand as t}from"./plugin-command.js";class g extends t{config(){this.command.name("add").description("add plugin").option("-f, --file","use file path instead of plugin package name").argument("<plugin>","plugin package name or absolute file path")}async handle(e,{file:n}){const o=this.pluginService.resolvePath(e,n),i=await this.pluginService.loadPlugin(o);if(this.pluginService.getPluginByName(i))await this.profileService.addPlugin({name:i,path:n?o:e,file:n,disabled:!1,options:{}}),this.outputService.success('Plugin "%s" successfully added',i);else throw new a(`Failed to add plugin "${e}"`,l.PLUGIN_NOT_FOUND,{name:i,path:e})}}export{g as PluginAddCommand};
@@ -0,0 +1 @@
1
+ import{Container as i}from"typedi";import{RuniumError as o}from"@runium/core";import{RuniumCommand as t}from"../runium-command.js";import{ErrorCode as n}from"../../constants/index.js";import{PluginService as m,ProfileService as l}from"../../services/index.js";class S extends t{pluginService;profileService;constructor(r){super(r),this.pluginService=i.get(m),this.profileService=i.get(l)}ensureProfilePlugin(r){const e=this.profileService.getPluginByName(r);if(!e)throw new o(`Plugin "${r}" not found`,n.PLUGIN_NOT_FOUND,{name:r});return e}}export{S as PluginCommand};
@@ -0,0 +1 @@
1
+ import{PluginCommand as n}from"./plugin-command.js";class o extends n{config(){this.command.name("disable").description("disable plugin").option("-a, --all","disable all plugins").argument("[plugin...]","plugin names")}async handle(e,{all:l}){if(e.length===0&&!l){this.outputService.warn("No plugins specified to disable");return}l&&(e=this.profileService.getPlugins().map(i=>i.name));for(const i of e){if(this.ensureProfilePlugin(i).disabled){this.outputService.info('Plugin "%s" is already disabled',i);continue}await this.profileService.updatePlugin(i,{disabled:!0}),await this.pluginService.unloadPlugin(i),this.outputService.success('Plugin "%s" successfully disabled',i)}}}export{o as PluginDisableCommand};
@@ -0,0 +1 @@
1
+ import{PluginCommand as a}from"./plugin-command.js";class u extends a{config(){this.command.name("enable").description("enable plugin").option("-a, --all","enable all plugins").argument("[plugin...]","plugin names")}async handle(i,{all:l}){if(i.length===0&&!l){this.outputService.warn("No plugins specified to enable");return}l&&(i=this.profileService.getPlugins().map(e=>e.name));for(const e of i){const n=this.ensureProfilePlugin(e);if(!n.disabled){this.outputService.info('Plugin "%s" is already enabled',e);continue}await this.profileService.updatePlugin(e,{disabled:!1});const t=this.pluginService.resolvePath(n.path,n.file);await this.pluginService.loadPlugin(t),this.outputService.success('Plugin "%s" successfully enabled',e)}}}export{u as PluginEnableCommand};
@@ -0,0 +1 @@
1
+ import{Option as l}from"commander";import{PluginCommand as a}from"./plugin-command.js";class m extends a{config(){this.command.name("list").addOption(new l("-d, --disabled","show only disabled plugins").conflicts("enabled")).addOption(new l("-e, --enabled","show only enabled plugins").conflicts("disabled")).option("-s, --sort","sort by name").description("list plugins")}async handle({disabled:o,enabled:n,sort:t}){let e=this.profileService.getPlugins();o&&(e=e.filter(i=>i.disabled===!0)),n&&(e=e.filter(i=>i.disabled===!1)),t&&e.sort((i,s)=>i.name.localeCompare(s.name)),e.length!==0?this.outputService.table(e,["name","path",...o||n?[]:["disabled"]]):this.outputService.warn("No plugins found")}}export{m as PluginListCommand};
@@ -0,0 +1 @@
1
+ import{PluginCommand as n}from"./plugin-command.js";class l extends n{config(){this.command.name("remove").description("remove plugin").option("-a, --all","remove all plugins").argument("[plugin...]","plugin names")}async handle(i,{all:o}){if(i.length===0&&!o){this.outputService.warn("No plugins specified to remove");return}o&&(i=this.profileService.getPlugins().map(e=>e.name));for(const e of i)this.ensureProfilePlugin(e),await this.profileService.removePlugin(e),await this.pluginService.unloadPlugin(e),this.outputService.success('Plugin "%s" successfully removed',e)}}export{l as PluginRemoveCommand};
@@ -0,0 +1 @@
1
+ import{RuniumCommand as n}from"../runium-command.js";import{PluginAddCommand as i}from"./plugin-add.js";import{PluginDisableCommand as d}from"./plugin-disable.js";import{PluginEnableCommand as t}from"./plugin-enable.js";import{PluginListCommand as r}from"./plugin-list.js";import{PluginRemoveCommand as a}from"./plugin-remove.js";class f extends n{config(){this.command.name("plugin").description("manage plugins")}async handle(){this.command.help()}addSubcommands(){const o=[r,i,a,d,t];for(const m of o)this.subcommands.push(new m(this.command))}}export{f as PluginCommand};
@@ -0,0 +1 @@
1
+ import{RuniumError as i}from"@runium/core";import{ErrorCode as c}from"../../constants/index.js";import{ProjectCommand as d}from"./project-command.js";class m extends d{config(){this.command.name("add").description("add project").argument("<path>","project file path").argument("[name]","project name (default: project config id)")}async handle(r,o){const e=this.projectService.resolvePath(r),t=await this.projectService.initProject(e);if(t)await this.profileService.addProject({name:o??t.getConfig().id,path:e}),this.outputService.success('Project "%s" successfully added',o??t.getConfig().id);else throw new i(`Failed to add project "${e}"`,c.PROJECT_NOT_FOUND,{path:e})}}export{m as ProjectAddCommand};
@@ -0,0 +1 @@
1
+ import{Container as o}from"typedi";import{RuniumError as t}from"@runium/core";import{RuniumCommand as i}from"../runium-command.js";import{ErrorCode as c}from"../../constants/index.js";import{ProfileService as m,ProjectService as p}from"../../services/index.js";class S extends i{projectService;profileService;constructor(r){super(r),this.projectService=o.get(p),this.profileService=o.get(m)}ensureProfileProject(r){const e=this.profileService.getProjectByName(r);if(!e)throw new t(`Project "${r}" not found`,c.PROJECT_NOT_FOUND,{name:r});return e}}export{S as ProjectCommand};
@@ -0,0 +1 @@
1
+ import{ProjectCommand as r}from"./project-command.js";class c extends r{config(){this.command.name("list").description("list projects").option("-s, --sort","sort by name")}async handle({sort:o}){const e=this.profileService.getProjects();o&&e.sort((t,s)=>t.name.localeCompare(s.name)),e.length!==0?this.outputService.table(e,["name","path"]):this.outputService.warn("No projects found")}}export{c as ProjectListCommand};
@@ -0,0 +1 @@
1
+ import{ProjectCommand as t}from"./project-command.js";class s extends t{config(){this.command.name("remove").description("remove project").option("-a, --all","remove all projects").argument("[name...]","project names")}async handle(o,{all:r}){if(o.length===0&&!r){this.outputService.warn("No projects specified to remove");return}r&&(o=this.profileService.getProjects().map(e=>e.name));for(const e of o)this.ensureProfileProject(e),await this.profileService.removeProject(e),this.outputService.success('Project "%s" successfully removed',e)}}export{s as ProjectRemoveCommand};
@@ -0,0 +1 @@
1
+ import{dirname as d}from"node:path";import{Option as j}from"commander";import{Container as m}from"typedi";import{ProjectEvent as p,RuniumError as h}from"@runium/core";import{ErrorCode as u}from"../../constants/index.js";import{ShutdownService as P}from"../../services/index.js";import{debounce as f}from"../../utils/index.js";import{ProjectStateCommand as w}from"./project-state-command.js";const S=100;class b extends w{shutdownService;constructor(t){super(t),this.shutdownService=m.get(P)}config(){this.command.name("start").description("start project").option("-f, --file","use file path instead of project name").option("-o, --output","output project state changes").addOption(new j("-w, --working-dir <choice>","set working directory").choices(["cwd","project"]).default("cwd")).argument("<name>","project name")}async handle(t,{file:s,workingDir:c,output:n}){const r=s?this.projectService.resolvePath(t):this.ensureProfileProject(t).path,o=this.getProjectDataFileName(s?r:t),a=await this.readProjectData(o);if(a&&this.isProjectProcessStarted(a.pid))throw new h(`Project "${t}" is already started`,u.PROJECT_ALREADY_STARTED,{name:t});if(c==="project"){const i=d(r);i!==process.cwd()&&process.chdir(i)}const e=await this.projectService.initProject(r);this.shutdownService.addBlocker(()=>e.stop()),this.addProjectListeners(e,{dataFileName:o,projectPath:r,output:n}),await e.start()}addProjectListeners(t,s){const{dataFileName:c,projectPath:n,output:r}=s,o={id:t.getConfig().id,pid:process.pid,cwd:process.cwd(),path:n,state:{project:[],tasks:{}}},a=f(()=>{this.writeProjectData(o,c)},S);a(),t.on(p.STATE_CHANGE,async e=>{o.state.project.push(e),a(),r&&this.outputService.info("Project %s",e.status)}),t.on(p.TASK_STATE_CHANGE,(e,i)=>{o.state.tasks[e]||(o.state.tasks[e]=[]),o.state.tasks[e].push(i),a(),r&&this.outputService.info("Task %s %s %s",e,i.status,i.exitCode||i.error||"")})}}export{b as ProjectStartCommand};
@@ -0,0 +1 @@
1
+ import{convertPathToValidFileName as r}from"../../utils/index.js";import{ProjectCommand as o}from"./project-command.js";class l extends o{getProjectDataFileName(e){let t=r(e);return t.endsWith(".json")||(t=t+".json"),t}async readProjectData(e){return this.profileService.readJsonFile("projects",e).catch(()=>null)}async writeProjectData(e,t){return this.profileService.writeJsonFile(e,"projects",t)}isProjectProcessStarted(e){try{return process.kill(Number(e),0)}catch(t){return t.code==="EPERM"}}}export{l as ProjectStateCommand};
@@ -0,0 +1 @@
1
+ import{formatTimestamp as p}from"../../utils/index.js";import{ProjectStateCommand as j}from"./project-state-command.js";class b extends j{config(){this.command.name("status").description("get project status").option("-f, --file","use file path instead of project name").option("-t, --tasks","show task status").option("-a, --all","show status change history").argument("<name>","project name")}async handle(a,{file:n,tasks:d,all:c}){const f=n?this.projectService.resolvePath(a):this.ensureProfileProject(a).path,l=this.getProjectDataFileName(n?f:a),i=await this.readProjectData(l);if(i){let{project:s=[]}=i.state;if(c||(s=s.length>0?[s[s.length-1]]:[]),d){const r=s.map(t=>({name:"Project",status:t.status,time:p(t.timestamp),timestamp:t.timestamp})),{tasks:o=[]}=i.state,h=[];Object.entries(o).forEach(([t,e])=>{c||(e=e.length>0?[e[e.length-1]]:[]),e.forEach(m=>{h.push({name:t,status:m.status,time:p(m.timestamp),timestamp:m.timestamp})})});const u=[...r,...h];u.sort((t,e)=>t.timestamp-e.timestamp),this.outputService.table(u,["time","name","status"])}else{const r=s.map(o=>({status:o.status,time:p(o.timestamp)}));this.outputService.table(r,["time","status"])}}else this.outputService.info(`No project status for "${a}"`)}}export{b as ProjectStatusCommand};
@@ -0,0 +1 @@
1
+ import{RuniumError as r}from"@runium/core";import{ErrorCode as i}from"../../constants/index.js";import{ProjectStateCommand as p}from"./project-state-command.js";class j extends p{config(){this.command.name("stop").description("stop project").option("-f, --file","use file path instead of project name").argument("<name>","project name")}async handle(t,{file:e}){const a=e?this.projectService.resolvePath(t):this.ensureProfileProject(t).path,s=this.getProjectDataFileName(e?a:t),o=await this.readProjectData(s);if(!o||!this.isProjectProcessStarted(o.pid))throw new r(`Project "${t}" is not started`,i.PROJECT_NOT_STARTED,{name:t});try{process.kill(o.pid,"SIGTERM")}catch(c){throw new r(`Failed to stop project "${t}"`,i.PROJECT_STOP_ERROR,{name:t,original:c})}}}export{j as ProjectStopCommand};
@@ -0,0 +1 @@
1
+ import{ProjectCommand as r}from"./project-command.js";class n extends r{config(){this.command.name("validate").description("validate project").option("-f, --file","use file path instead of project name").argument("<project>","project name or file path")}async handle(e,{file:t}){const i=t?this.projectService.resolvePath(e):this.ensureProfileProject(e).path,o=await this.projectService.initProject(i);try{await o.validate(),this.outputService.success('Project "%s" is valid',e)}catch(a){this.outputService.error('Project "%s" validation failed',e,a)}}}export{n as ProjectValidateCommand};
@@ -0,0 +1 @@
1
+ import{RuniumCommand as t}from"../runium-command.js";import{ProjectAddCommand as r}from"./project-add.js";import{ProjectListCommand as d}from"./project-list.js";import{ProjectRemoveCommand as e}from"./project-remove.js";import{ProjectStartCommand as n}from"./project-start.js";import{ProjectStopCommand as c}from"./project-stop.js";import{ProjectStatusCommand as a}from"./project-status.js";import{ProjectValidateCommand as i}from"./project-validate.js";class v extends t{config(){this.command.name("project").description("manage projects")}async handle(){this.command.help()}addSubcommands(){const o=[d,r,e,n,c,a,i];for(const m of o)this.subcommands.push(new m(this.command))}}export{v as ProjectCommand};
@@ -0,0 +1 @@
1
+ import{Command as o}from"commander";import{Container as m}from"typedi";import{OutputService as d}from"../services/index.js";class i{outputService;parent;command;subcommands=[];constructor(t){this.outputService=m.get(d),this.parent=t,this.command=new o,this.config(),this.command.action(this.handle.bind(this)),this.addSubcommands(),this.parent.addCommand(this.command)}addSubcommands(){}}export{i as RuniumCommand};
@@ -0,0 +1 @@
1
+ var R=(_=>(_.PLUGIN_NOT_FOUND="plugin-not-found",_.PLUGIN_FILE_NOT_FOUND="plugin-file-not-found",_.PLUGIN_INCORRECT_MODULE="plugin-incorrect-module",_.PLUGIN_INCORRECT_PLUGIN="plugin-incorrect-plugin",_.PLUGIN_PATH_RESOLVE_ERROR="plugin-path-resolve-error",_.PROJECT_ALREADY_STARTED="project-already-started",_.PROJECT_NOT_STARTED="project-not-started",_.PROJECT_STOP_ERROR="project-stop-error",_.PROJECT_NOT_FOUND="project-not-found",_.PROJECT_FILE_NOT_FOUND="project-file-not-found",_.PROJECT_FILE_CAN_NOT_READ="project-file-can-not-read",_.PROJECT_JSON_PARSE_ERROR="project-json-parse-error",_.PROFILE_JSON_WRITE_ERROR="profile-json-write-error",_))(R||{});export{R as ErrorCode};
@@ -0,0 +1 @@
1
+ export*from"./error-code.js";
package/lib/index.js ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import"reflect-metadata";import{Container as e}from"typedi";import{RuniumCliApp as r}from"./app.js";import{OutputService as a}from"./services/index.js";async function i(){await new r().start()}i().catch(o=>{const t=e.get(a);t.error("Error: %s",o.message),t.debug("Error details:",{code:o.code,payload:o.payload}),process.exit(1)});
@@ -0,0 +1 @@
1
+ function g(r,n,t,i){return r===n?t??"":i??""}function s(r,n,t,i){return r!==n?t??"":i??""}export{g as eqMacro,s as neMacro};
@@ -0,0 +1 @@
1
+ function r(){return""}export{r as emptyMacro};
@@ -0,0 +1 @@
1
+ function t(n,r){return process.env[n]??r??""}export{t as envMacro};
@@ -0,0 +1 @@
1
+ import{eqMacro as o,neMacro as r}from"./conditional.js";import{emptyMacro as m}from"./empty.js";import{envMacro as t}from"./env.js";import{pathMacro as e}from"./path.js";const M={env:t,empty:m,eq:o,ne:r,path:e};export{M as macros};
@@ -0,0 +1 @@
1
+ import{resolve as t}from"node:path";function n(r){return t(r)}export{n as pathMacro};
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "@runium/cli",
3
+ "version": "0.0.1",
4
+ "description": "Runium CLI",
5
+ "author": "TheBeastApp",
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "bin": {
9
+ "runium": "./index.js"
10
+ },
11
+ "engines": {
12
+ "node": ">=20.12.0"
13
+ },
14
+ "module": "./index.js",
15
+ "dependencies": {
16
+ "@runium/core": "^0.0.3",
17
+ "commander": "^14.0.2",
18
+ "reflect-metadata": "^0.2.2",
19
+ "typedi": "^0.10.0"
20
+ }
21
+ }
@@ -0,0 +1 @@
1
+ var d=function(r,t,e,i){var n=arguments.length,o=n<3?t:i===null?i=Object.getOwnPropertyDescriptor(t,e):i,a;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")o=Reflect.decorate(r,t,e,i);else for(var p=r.length-1;p>=0;p--)(a=r[p])&&(o=(n<3?a(o):n>3?a(t,e,o):a(t,e))||o);return n>3&&o&&Object.defineProperty(t,e,o),o};import{existsSync as f}from"node:fs";import{homedir as _}from"node:os";import{join as s,resolve as l}from"node:path";import{Service as v}from"typedi";import{readJsonFile as O}from"@runium/core";const j=".runiumrc.json",u=s(process.cwd(),j),h=".runium",E=s(_(),h),m=s(process.cwd(),h);let c=class{data={profile:{path:E},output:{debug:!1},env:{path:[]}};async init(){if(f(m)&&(this.data.profile.path=m),f(u)){const t=await O(u);if(t){const e={env:Object.assign({},this.data.env,t.env??{}),output:Object.assign({},this.data.output,t.output??{}),profile:Object.assign({},this.data.profile,t.profile??{})};e.env.path=e.env.path.map(i=>l(i)),e.profile.path=l(e.profile.path),this.data=e}}}get(t){return this.data[t]}};c=d([v()],c);export{c as ConfigService};
@@ -0,0 +1 @@
1
+ export*from"./config.js";export*from"./output.js";export*from"./profile.js";export*from"./plugin.js";export*from"./project.js";export*from"./shutdown.js";export*from"./plugin-context.js";
@@ -0,0 +1,3 @@
1
+ var a=function(t,o,e,s){var i=arguments.length,r=i<3?o:s===null?s=Object.getOwnPropertyDescriptor(o,e):s,l;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")r=Reflect.decorate(t,o,e,s);else for(var c=t.length-1;c>=0;c--)(l=t[c])&&(r=(i<3?l(r):i>3?l(o,e,r):l(o,e))||r);return i>3&&r&&Object.defineProperty(o,e,r),r};import{Console as u}from"node:console";import{Transform as h}from"node:stream";import{Service as p}from"typedi";var n;(function(t){t[t.TRACE=0]="TRACE",t[t.DEBUG=1]="DEBUG",t[t.INFO=2]="INFO",t[t.WARN=3]="WARN",t[t.ERROR=4]="ERROR",t[t.SILENT=5]="SILENT"})(n||(n={}));class R extends u{transform;constructor(){const o=new h({transform:(e,s,i)=>i(null,e)});super({stdout:o,stderr:o,colorMode:!1}),this.transform=o}getPatchedTable(o,e){this.table(o,e);const s=(this.transform.read()||"").toString(),i=s.indexOf("\u252C")+1;return s.split(`
2
+ `).map(r=>r.charAt(0)+r.slice(i)).join(`
3
+ `)}}const m=new R;let f=class{outputLevel=n.INFO;setLevel(o){this.outputLevel=o}getLevel(){return this.outputLevel}trace(o,...e){this.outputLevel<=n.TRACE&&console.log(o,...e)}debug(o,...e){this.outputLevel<=n.DEBUG&&console.log(o,...e)}info(o,...e){this.outputLevel<=n.INFO&&console.log(o,...e)}success(o,...e){this.outputLevel<=n.INFO&&console.log(o,...e)}warn(o,...e){this.outputLevel<=n.WARN&&console.warn(o,...e)}error(o,...e){this.outputLevel<=n.ERROR&&console.error(o,...e)}log(o,...e){this.outputLevel<n.SILENT&&console.log(o,...e)}table(o,e){if(this.outputLevel<n.SILENT){const s=o.map((r,l)=>({...r,"#":l+1})),i=m.getPatchedTable(s,e?["#",...e]:void 0);console.log(i)}}newLine(){this.outputLevel<n.SILENT&&console.log("")}clear(){this.outputLevel<n.SILENT&&console.clear()}};f=a([p()],f);export{n as OutputLevel,f as OutputService};
@@ -0,0 +1 @@
1
+ var v=function(i,e,t,o){var n=arguments.length,r=n<3?e:o===null?o=Object.getOwnPropertyDescriptor(e,t):o,u;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")r=Reflect.decorate(i,e,t,o);else for(var c=i.length-1;c>=0;c--)(u=i[c])&&(r=(n<3?u(r):n>3?u(e,t,r):u(e,t))||r);return n>3&&r&&Object.defineProperty(e,t,r),r},h=function(i,e){if(typeof Reflect=="object"&&typeof Reflect.metadata=="function")return Reflect.metadata(i,e)},l=function(i,e){return function(t,o){e(t,o,i)}},f,s;import{Inject as d,Service as S}from"typedi";import{RuniumError as _,isRuniumError as g,RuniumTask as j,RuniumTrigger as R,readJsonFile as w,writeJsonFile as O,applyMacros as y,TaskStatus as L,ProjectEvent as P,ProjectStatus as B}from"@runium/core";import{RuniumCommand as x}from"../commands/runium-command.js";import{OutputLevel as m,OutputService as p,ShutdownService as b}from"./index.js";global.runium=null;let a=class{outputService;shutdownService;constructor(e,t){this.outputService=e,this.shutdownService=t}async init(){const e=this.outputService,t=this.shutdownService,o={class:{RuniumCommand:x,RuniumError:_,RuniumTask:j,RuniumTrigger:R},enum:{OutputLevel:Object.keys(m).filter(n=>isNaN(Number(n))).reduce((n,r)=>(n[r]=m[r],n),{}),ProjectEvent:P,ProjectStatus:B,TaskStatus:L},utils:{applyMacros:y,isRuniumError:g,readJsonFile:w,writeJsonFile:O},output:{getLevel:e.getLevel.bind(e),setLevel:e.setLevel.bind(e),trace:e.trace.bind(e),debug:e.debug.bind(e),info:e.info.bind(e),warn:e.warn.bind(e),error:e.error.bind(e),table:e.table.bind(e),log:e.log.bind(e)},shutdown:{addBlocker:t.addBlocker.bind(t),removeBlocker:t.removeBlocker.bind(t)}};global.runium=Object.freeze(o)}};a=v([S(),l(0,d()),l(1,d()),h("design:paramtypes",[typeof(f=typeof p<"u"&&p)=="function"?f:Object,typeof(s=typeof b<"u"&&b)=="function"?s:Object])],a);export{a as PluginContextService};
@@ -0,0 +1 @@
1
+ var c=function(u,e,r,n){var t=arguments.length,o=t<3?e:n===null?n=Object.getOwnPropertyDescriptor(e,r):n,s;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")o=Reflect.decorate(u,e,r,n);else for(var a=u.length-1;a>=0;a--)(s=u[a])&&(o=(t<3?s(o):t>3?s(e,r,o):s(e,r))||o);return t>3&&o&&Object.defineProperty(e,r,o),o};import{existsSync as m}from"node:fs";import{resolve as g}from"node:path";import{Service as P}from"typedi";import{isRuniumError as d,RuniumError as i}from"@runium/core";import{ErrorCode as l}from"../constants/index.js";let f=class{plugins=new Map;getAllPlugins(){return Array.from(this.plugins.values())}getPluginByName(e){return this.plugins.get(e)}async loadPlugin(e){if(!e||!m(e))throw new i(`Plugin file "${e}" does not exist`,l.PLUGIN_FILE_NOT_FOUND,{path:e});try{const r=await import(e),{default:n}=r;if(!n||typeof n!="function")throw new i("Plugin module must have a default function",l.PLUGIN_INCORRECT_MODULE,{path:e});const t=n();return this.validate(t),this.plugins.set(t.name,t),t.name}catch(r){throw d(r)?r:new i(`Failed to load plugin "${e}"`,l.PLUGIN_INCORRECT_PLUGIN,{path:e,original:r})}}async unloadPlugin(e){return this.plugins.delete(e)}resolvePath(e,r=!1){try{return(r?g(e):import.meta.resolve(e)).replace("file://","")}catch(n){throw new i(`Failed to resolve plugin path "${e}"`,l.PLUGIN_PATH_RESOLVE_ERROR,{path:e,original:n})}}validate(e){if(!e||!e?.name)throw new i("Incorrect plugin format",l.PLUGIN_INCORRECT_PLUGIN,{name:e.name})}};f=c([P()],f);export{f as PluginService};
@@ -0,0 +1 @@
1
+ var d=function(s,t,i,e){var r=arguments.length,n=r<3?t:e===null?e=Object.getOwnPropertyDescriptor(t,i):e,a;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")n=Reflect.decorate(s,t,i,e);else for(var o=s.length-1;o>=0;o--)(a=s[o])&&(n=(r<3?a(n):r>3?a(t,i,n):a(t,i))||n);return r>3&&n&&Object.defineProperty(t,i,n),n},j=function(s,t){if(typeof Reflect=="object"&&typeof Reflect.metadata=="function")return Reflect.metadata(s,t)},w=function(s,t){return function(i,e){t(i,e,s)}},l;import{existsSync as u}from"node:fs";import{mkdir as f}from"node:fs/promises";import{dirname as y,join as _}from"node:path";import{Inject as v,Service as R}from"typedi";import{readJsonFile as c,RuniumError as S,writeJsonFile as h}from"@runium/core";import{ConfigService as g}from"./index.js";import{ErrorCode as E}from"../constants/index.js";const m="plugins.json",P="projects.json";let p=class{configService;path=process.cwd();plugins=[];projects=[];constructor(t){this.configService=t}async init(){this.path=this.configService.get("profile").path,u(this.path)||await f(this.path,{recursive:!0}),await this.readPlugins(),await this.readProjects()}getPlugins(){return this.plugins}getPluginByName(t){return this.plugins.find(i=>i.name===t)}async addPlugin(t){this.plugins=this.plugins.filter(i=>i.name!==t.name).concat(t),await this.writePlugins()}async removePlugin(t){this.plugins=this.plugins.filter(i=>i.name!==t),await this.writePlugins()}async updatePlugin(t,i){const e=this.plugins.findIndex(r=>r.name===t);e!==-1&&(this.plugins[e]={...this.plugins[e],...i},await this.writePlugins())}getProjects(){return this.projects}getProjectByName(t){return this.projects.find(i=>i.name===t)}async addProject(t){this.projects=this.projects.filter(i=>i.name!==t.name).concat(t),await this.writeProjects()}async removeProject(t){this.projects=this.projects.filter(i=>i.name!==t),await this.writeProjects()}async readJsonFile(...t){return c(this.getPath(...t))}async writeJsonFile(t,...i){const e=this.getPath(...i);try{const r=y(e);u(r)||await f(r,{recursive:!0}),await h(e,t)}catch(r){throw new S("Failed to write JSON file",E.PROFILE_JSON_WRITE_ERROR,{path:e,data:t,original:r})}}getPath(...t){return _(this.path,...t)}async readPlugins(){this.plugins=await c(this.getPath(m)).catch(()=>[])||this.plugins}async writePlugins(){await h(this.getPath(m),this.plugins)}async readProjects(){this.projects=await c(this.getPath(P)).catch(()=>[])||this.projects}async writeProjects(){await h(this.getPath(P),this.projects)}};p=d([R(),w(0,v()),j("design:paramtypes",[typeof(l=typeof g<"u"&&g)=="function"?l:Object])],p);export{p as ProfileService};
@@ -0,0 +1 @@
1
+ var d=function(n,e,t,r){var i=arguments.length,o=i<3?e:r===null?r=Object.getOwnPropertyDescriptor(e,t):r,c;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")o=Reflect.decorate(n,e,t,r);else for(var s=n.length-1;s>=0;s--)(c=n[s])&&(o=(i<3?c(o):i>3?c(e,t,o):c(e,t))||o);return i>3&&o&&Object.defineProperty(e,t,o),o},_=function(n,e){if(typeof Reflect=="object"&&typeof Reflect.metadata=="function")return Reflect.metadata(n,e)},h=function(n,e){return function(t,r){e(t,r,n)}},m;import{existsSync as j}from"node:fs";import{readFile as P}from"node:fs/promises";import{resolve as v}from"node:path";import{Inject as O,Service as R}from"typedi";import{applyMacros as E,Project as S,RuniumError as f}from"@runium/core";import{ErrorCode as u}from"../constants/index.js";import{macros as y}from"../macros/index.js";import{PluginService as g}from"./index.js";let p=class{pluginService;constructor(e){this.pluginService=e}async initProject(e){const t=await this.readFile(e),r=this.applyMacros(t),i=this.parseProjectContent(r,e),o=new S(i);return this.extendProjectWithPlugins(o)}resolvePath(e){return v(e)}async readFile(e){if(!j(e))throw new f(`Project file "${e}" does not exist`,u.PROJECT_FILE_NOT_FOUND,{path:e});try{return P(e,{encoding:"utf-8"})}catch(t){throw new f(`Failed to read project file "${e}"`,u.PROJECT_FILE_CAN_NOT_READ,{path:e,original:t})}}applyMacros(e){const r=this.pluginService.getAllPlugins().reduce((i,o)=>({...i,...o.project?.macros||{}}),{});return E(e,{...r,...y})}parseProjectContent(e,t){try{return JSON.parse(e)}catch(r){throw new f(`Failed to parse project "${t}"`,u.PROJECT_JSON_PARSE_ERROR,{path:t,original:r})}}extendProjectWithPlugins(e){return this.pluginService.getAllPlugins().forEach(r=>{const{tasks:i,actions:o,triggers:c,validationSchema:s}=r.project||{};Object.entries(o||{}).forEach(([a,l])=>{e.registerAction(a,l)}),Object.entries(i||{}).forEach(([a,l])=>{e.registerTask(a,l)}),Object.entries(c||{}).forEach(([a,l])=>{e.registerTrigger(a,l)}),s&&e.extendValidationSchema(s)}),e}};p=d([R(),h(0,O()),_("design:paramtypes",[typeof(m=typeof g<"u"&&g)=="function"?m:Object])],p);export{p as ProjectService};
@@ -0,0 +1 @@
1
+ var d=function(r,e,t,o){var s=arguments.length,i=s<3?e:o===null?o=Object.getOwnPropertyDescriptor(e,t):o,c;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")i=Reflect.decorate(r,e,t,o);else for(var n=r.length-1;n>=0;n--)(c=r[n])&&(i=(s<3?c(i):s>3?c(e,t,i):c(e,t))||i);return s>3&&i&&Object.defineProperty(e,t,i),i},f=function(r,e){if(typeof Reflect=="object"&&typeof Reflect.metadata=="function")return Reflect.metadata(r,e)},l=function(r,e){return function(t,o){e(t,o,r)}},a;import{Inject as m,Service as S}from"typedi";import{OutputService as h}from"./index.js";const p=3e4,w=250,v=["SIGHUP","SIGINT","SIGTERM","SIGQUIT"];let u=class{outputService;blockers=new Set;isShuttingDown=!1;constructor(e){this.outputService=e}async init(){v.forEach(e=>{process.on(e,()=>{this.shutdown(e).catch(t=>{this.outputService.error("Error during shutdown: %s",t.message),this.outputService.debug("Error details:",t),process.exit(1)})})}),process.on("uncaughtException",e=>{this.outputService.error("Uncaught exception: %s",e.message),this.outputService.debug("Error details:",e),this.shutdown("uncaughtException").catch(()=>{process.exit(1)})}),process.on("unhandledRejection",(e,t)=>{this.outputService.error("Unhandled rejection at:",t,"reason:",e),this.outputService.debug("Error details:",{reason:e,promise:t}),this.shutdown("unhandledRejection").catch(()=>{process.exit(1)})}),process.on("beforeExit",()=>{this.isShuttingDown||this.shutdown("exit").catch(()=>{process.exit(1)})})}addBlocker(e){this.blockers.add(e)}removeBlocker(e){return this.blockers.delete(e)}async shutdown(e){if(this.isShuttingDown||!e)return;this.isShuttingDown=!0;const t=o=>{setTimeout(()=>{process.exit(o)},w)};if(this.blockers.size===0){t(0);return}try{await this.executeBlockersWithTimeout(),t(0)}catch{t(1)}}async executeBlockersWithTimeout(){const e=Array.from(this.blockers).map(o=>this.executeBlocker(o)),t=new Promise((o,s)=>{setTimeout(()=>{s(new Error(`Shutdown timeout after ${p}ms`))},p)});await Promise.race([Promise.allSettled(e),t])}async executeBlocker(e){await e()}};u=d([S(),l(0,m()),f("design:paramtypes",[typeof(a=typeof h<"u"&&h)=="function"?a:Object])],u);export{u as ShutdownService};
@@ -0,0 +1 @@
1
+ const n=200;function i(g,r="_"){let e=g.trim();e=e.replace(/^\/+/,""),e=e.replace(/[/\\]/g,r),e=e.replace(/[<>:"|?*\x00-\x1F]/g,r),e=e.replace(/^[.\s]+|[.\s]+$/g,"");const t=new RegExp(`${r.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}+`,"g");return e=e.replace(t,r),e.length>200&&(e=e.substring(0,200)),e}export{i as convertPathToValidFileName};
@@ -0,0 +1 @@
1
+ function r(n,u){let e=null;return function(...o){e!==null&&clearTimeout(e),e=setTimeout(()=>{n(...o),e=null},u)}}export{r as debounce};
@@ -0,0 +1 @@
1
+ function i(n){const t=typeof n=="number"?new Date(n):n,e=t.getFullYear(),r=String(t.getMonth()+1).padStart(2,"0"),o=String(t.getDate()).padStart(2,"0"),a=String(t.getHours()).padStart(2,"0"),s=String(t.getMinutes()).padStart(2,"0"),g=String(t.getSeconds()).padStart(2,"0"),c=String(t.getMilliseconds()).padStart(3,"0");return`${e}-${r}-${o} ${a}:${s}:${g}.${c}`}export{i as formatTimestamp};
@@ -0,0 +1 @@
1
+ import{convertPathToValidFileName as r}from"./convert-path-to-valid-file-name.js";import{debounce as t}from"./debounce.js";import{formatTimestamp as f}from"./format-timestamp.js";export{r as convertPathToValidFileName,t as debounce,f as formatTimestamp};
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@runium/cli",
3
+ "version": "0.0.1",
4
+ "description": "Runium CLI",
5
+ "author": "TheBeastApp",
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "module": "./lib/index.js",
9
+ "bin": {
10
+ "runium": "./lib/index.js"
11
+ },
12
+ "scripts": {
13
+ "build": "rimraf ./lib && node build.js && tsc-alias",
14
+ "build:tsc": "rimraf ./lib && tsc",
15
+ "start": "node lib/index.js",
16
+ "lint": "eslint ./src --ext .ts",
17
+ "lint:fix": "eslint ./src --ext .ts --fix",
18
+ "format": "prettier --write \"src/**/*.ts\""
19
+ },
20
+ "dependencies": {
21
+ "@runium/core": "^0.0.3",
22
+ "commander": "^14.0.2",
23
+ "reflect-metadata": "^0.2.2",
24
+ "typedi": "^0.10.0"
25
+ },
26
+ "devDependencies": {
27
+ "@types/node": "^22.18.0",
28
+ "@typescript-eslint/eslint-plugin": "^7.0.0",
29
+ "@typescript-eslint/parser": "^7.0.0",
30
+ "esbuild": "^0.25.11",
31
+ "esbuild-decorators": "^1.0.0",
32
+ "eslint": "^8.56.0",
33
+ "eslint-config-prettier": "^9.1.0",
34
+ "eslint-plugin-prettier": "^5.1.3",
35
+ "prettier": "^3.2.5",
36
+ "rimraf": "^6.1.0",
37
+ "ts-node": "^10.9.2",
38
+ "tsc-alias": "^1.8.16",
39
+ "typescript": "^5.3.3"
40
+ },
41
+ "engines": {
42
+ "node": ">=20.12.0"
43
+ }
44
+ }
package/src/app.ts ADDED
@@ -0,0 +1,175 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
3
+ import { Command } from 'commander';
4
+ import { Container } from 'typedi';
5
+ import { RuniumError } from '@runium/core';
6
+ import { RuniumCommand } from '@commands/runium-command';
7
+ import * as commands from '@commands';
8
+ import {
9
+ ConfigService,
10
+ ProfileService,
11
+ PluginService,
12
+ ShutdownService,
13
+ OutputService,
14
+ OutputLevel,
15
+ PluginContextService,
16
+ } from '@services';
17
+
18
+ const RUNIUM_DESCRIPTION = `╔═══════════════════════╗
19
+ ║ ╔═╗ ╦ ╦ ╔╗╔ ╦ ╦ ╦ ╔╦╗ ║
20
+ ║ ╠╦╝ ║ ║ ║║║ ║ ║ ║ ║║║ ║
21
+ ║ ╩╚═ ╚═╝ ╝╚╝ ╩ ╚═╝ ╩ ╩ ║
22
+ ╚═══════════════════════╝
23
+ One Tool to Run Them All!`;
24
+
25
+ export class RuniumCliApp {
26
+ private readonly program: Command;
27
+
28
+ private configService: ConfigService;
29
+ private profileService: ProfileService;
30
+ private pluginService: PluginService;
31
+ private shutdownService: ShutdownService;
32
+ private outputService: OutputService;
33
+ private pluginContextService: PluginContextService;
34
+
35
+ constructor() {
36
+ this.program = new Command('runium');
37
+ this.configService = Container.get(ConfigService);
38
+ this.profileService = Container.get(ProfileService);
39
+ this.pluginService = Container.get(PluginService);
40
+ this.shutdownService = Container.get(ShutdownService);
41
+ this.outputService = Container.get(OutputService);
42
+ this.pluginContextService = Container.get(PluginContextService);
43
+ }
44
+
45
+ /**
46
+ * Start the application
47
+ */
48
+ async start(): Promise<Command> {
49
+ await this.shutdownService.init();
50
+ await this.configService.init();
51
+ await this.profileService.init();
52
+ await this.pluginContextService.init();
53
+
54
+ this.initOutput();
55
+ this.initEnv();
56
+
57
+ await this.loadPlugins();
58
+ await this.initProgram();
59
+
60
+ return this.program.parseAsync();
61
+ }
62
+
63
+ /**
64
+ * Load plugins
65
+ */
66
+ private async loadPlugins(): Promise<void> {
67
+ const plugins = this.profileService.getPlugins();
68
+ for (const plugin of plugins) {
69
+ if (plugin.disabled !== true) {
70
+ try {
71
+ const pluginPath = this.pluginService.resolvePath(
72
+ plugin.path,
73
+ plugin.file
74
+ );
75
+ await this.pluginService.loadPlugin(pluginPath);
76
+ } catch (error) {
77
+ this.outputService.error(`Failed to load plugin "${plugin.name}"`);
78
+ const { code, message, payload } = error as RuniumError;
79
+ this.outputService.debug('Error details:', {
80
+ message,
81
+ code,
82
+ payload,
83
+ });
84
+ }
85
+ }
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Initialize the program
91
+ */
92
+ private async initProgram(): Promise<void> {
93
+ const program = this.program;
94
+
95
+ program.option('-D, --debug', 'enable debug mode');
96
+ program.option('-e, --env [paths...]', 'load env files');
97
+ program.version(await this.getVersion());
98
+ program.description(RUNIUM_DESCRIPTION);
99
+
100
+ program.on('option:debug', () => {
101
+ this.setDebugMode();
102
+ });
103
+
104
+ program.on('option:env', path => {
105
+ this.loadEnvFiles([path]);
106
+ });
107
+
108
+ const programCommands: RuniumCommand[] = [];
109
+ Object.values(commands).forEach(CommandConstructor => {
110
+ programCommands.push(new CommandConstructor(program));
111
+ });
112
+ }
113
+
114
+ /**
115
+ * Initialize the output
116
+ */
117
+ private initOutput(): void {
118
+ const output = this.configService.get('output');
119
+ if (
120
+ output.debug ||
121
+ process.argv.includes('-D') ||
122
+ process.argv.includes('--debug')
123
+ ) {
124
+ this.setDebugMode();
125
+ }
126
+ }
127
+
128
+ /**
129
+ * Set debug mode
130
+ */
131
+ private setDebugMode(): void {
132
+ if (this.outputService.getLevel() !== OutputLevel.DEBUG) {
133
+ this.outputService.setLevel(OutputLevel.DEBUG);
134
+ this.outputService.debug('Debug mode enabled');
135
+ }
136
+ }
137
+
138
+ /**
139
+ * Initialize the environment
140
+ */
141
+ private initEnv(): void {
142
+ const env = this.configService.get('env');
143
+ if (env.path.length > 0) {
144
+ this.loadEnvFiles(env.path);
145
+ }
146
+ }
147
+
148
+ /**
149
+ * Set environment variables
150
+ * @param path
151
+ */
152
+ private loadEnvFiles(path: string[]): void {
153
+ for (const p of path) {
154
+ if (p) {
155
+ if (existsSync(p)) {
156
+ process.loadEnvFile(resolve(p));
157
+ } else {
158
+ this.outputService.debug(`Env file "${p}" not found`);
159
+ }
160
+ } else {
161
+ process.loadEnvFile();
162
+ }
163
+ }
164
+ }
165
+ /**
166
+ * Get version from package.json
167
+ */
168
+ private async getVersion(): Promise<string> {
169
+ // @ts-expect-error incorrect path for src but not build
170
+ const packageJson = await import(`./package.json`, {
171
+ with: { type: 'json' },
172
+ });
173
+ return packageJson.default.version;
174
+ }
175
+ }
@@ -0,0 +1,2 @@
1
+ export { PluginCommand } from '@commands/plugin/plugin.js';
2
+ export { ProjectCommand } from '@commands/project/project.js';