@runium/cli 0.0.1 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build.js +21 -0
- package/lib/app.js +2 -2
- package/lib/commands/plugin/plugin-add.js +1 -1
- package/lib/commands/plugin/plugin-disable.js +1 -1
- package/lib/commands/plugin/plugin-enable.js +1 -1
- package/lib/commands/plugin/plugin-remove.js +1 -1
- package/lib/commands/plugin/plugin.js +1 -1
- package/lib/commands/project/project-add.js +1 -1
- package/lib/commands/project/project-command.js +1 -1
- package/lib/commands/project/project-start.js +1 -1
- package/lib/commands/project/project-state-command.js +1 -1
- package/lib/commands/project/project-status.js +1 -1
- package/lib/commands/project/project-stop.js +1 -1
- package/lib/commands/project/project-validate.js +4 -1
- package/lib/commands/project/project.js +1 -1
- package/lib/commands/runium-command.js +1 -1
- package/lib/constants/error-code.js +1 -1
- package/lib/index.js +1 -1
- package/lib/macros/date.js +1 -0
- package/lib/macros/index.js +1 -1
- package/lib/macros/path.js +1 -1
- package/lib/package.json +3 -2
- package/lib/services/command.js +1 -0
- package/lib/services/config.js +1 -1
- package/lib/services/file.js +1 -0
- package/lib/services/index.js +1 -1
- package/lib/services/output.js +3 -3
- package/lib/services/plugin-context.js +1 -1
- package/lib/services/plugin.js +1 -1
- package/lib/services/profile.js +1 -1
- package/lib/services/project.js +1 -1
- package/lib/services/shutdown.js +1 -1
- package/lib/utils/get-version.js +1 -0
- package/lib/utils/index.js +1 -1
- package/lib/validation/create-validator.js +1 -0
- package/lib/validation/get-config-schema.js +1 -0
- package/lib/validation/get-error-messages.js +1 -0
- package/lib/validation/get-plugin-schema.js +1 -0
- package/lib/validation/index.js +1 -0
- package/package.json +5 -2
- package/src/app.ts +35 -20
- package/src/commands/plugin/plugin-add.ts +2 -2
- package/src/commands/plugin/plugin-disable.ts +1 -1
- package/src/commands/plugin/plugin-enable.ts +2 -2
- package/src/commands/plugin/plugin-remove.ts +1 -1
- package/src/commands/plugin/plugin.ts +11 -16
- package/src/commands/project/project-add.ts +23 -5
- package/src/commands/project/project-command.ts +8 -1
- package/src/commands/project/project-start.ts +18 -12
- package/src/commands/project/project-state-command.ts +4 -19
- package/src/commands/project/project-status.ts +16 -3
- package/src/commands/project/project-stop.ts +5 -1
- package/src/commands/project/project-validate.ts +23 -10
- package/src/commands/project/project.ts +13 -18
- package/src/commands/runium-command.ts +18 -16
- package/src/constants/error-code.ts +15 -2
- package/src/global.d.ts +6 -0
- package/src/index.ts +5 -2
- package/src/macros/date.ts +15 -0
- package/src/macros/index.ts +6 -1
- package/src/macros/path.ts +17 -2
- package/src/services/command.ts +171 -0
- package/src/services/config.ts +47 -4
- package/src/services/file.ts +272 -0
- package/src/services/index.ts +2 -0
- package/src/services/output.ts +5 -1
- package/src/services/plugin-context.ts +68 -9
- package/src/services/plugin.ts +122 -18
- package/src/services/profile.ts +37 -49
- package/src/services/project.ts +31 -3
- package/src/services/shutdown.ts +25 -8
- package/src/utils/get-version.ts +13 -0
- package/src/utils/index.ts +1 -0
- package/src/validation/create-validator.ts +27 -0
- package/src/validation/get-config-schema.ts +59 -0
- package/src/validation/get-error-messages.ts +35 -0
- package/src/validation/get-plugin-schema.ts +137 -0
- package/src/validation/index.ts +4 -0
- package/tsconfig.json +8 -10
package/build.js
CHANGED
|
@@ -6,6 +6,8 @@ import {
|
|
|
6
6
|
mkdirSync,
|
|
7
7
|
readFileSync,
|
|
8
8
|
writeFileSync,
|
|
9
|
+
readdirSync,
|
|
10
|
+
unlinkSync,
|
|
9
11
|
} from 'node:fs';
|
|
10
12
|
import { resolve } from 'node:path';
|
|
11
13
|
import { build } from 'esbuild';
|
|
@@ -30,8 +32,10 @@ const buildConfig = {
|
|
|
30
32
|
],
|
|
31
33
|
metafile: true,
|
|
32
34
|
logLevel: 'info',
|
|
35
|
+
|
|
33
36
|
};
|
|
34
37
|
|
|
38
|
+
|
|
35
39
|
async function buildProject() {
|
|
36
40
|
try {
|
|
37
41
|
console.log('🔨 Building project with esbuild...');
|
|
@@ -43,6 +47,10 @@ async function buildProject() {
|
|
|
43
47
|
|
|
44
48
|
const result = await build(buildConfig);
|
|
45
49
|
|
|
50
|
+
// Delete .d.js files from the output directory
|
|
51
|
+
console.log('🧹 Cleaning up .d.js files...');
|
|
52
|
+
deleteDtsFiles(libDir);
|
|
53
|
+
|
|
46
54
|
if (process.platform !== 'win32') {
|
|
47
55
|
try {
|
|
48
56
|
chmodSync('lib/index.js', '755');
|
|
@@ -101,4 +109,17 @@ async function processPackageJson() {
|
|
|
101
109
|
);
|
|
102
110
|
}
|
|
103
111
|
|
|
112
|
+
function deleteDtsFiles(dir) {
|
|
113
|
+
const files = readdirSync(dir, { withFileTypes: true });
|
|
114
|
+
for (const file of files) {
|
|
115
|
+
const fullPath = resolve(dir, file.name);
|
|
116
|
+
if (file.isDirectory()) {
|
|
117
|
+
deleteDtsFiles(fullPath);
|
|
118
|
+
} else if (file.name.endsWith('.d.js')) {
|
|
119
|
+
unlinkSync(fullPath);
|
|
120
|
+
console.log(`🗑️ Deleted ${fullPath}`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
104
125
|
buildProject();
|
package/lib/app.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import{existsSync as
|
|
1
|
+
import{existsSync as c}from"node:fs";import{resolve as u}from"node:path";import{Command as v}from"commander";import{Container as o}from"typedi";import*as p from"./commands/index.js";import{CommandService as g,ConfigService as l,ProfileService as m,PluginService as h,ShutdownService as d,OutputService as S,OutputLevel as n,PluginContextService as f}from"./services/index.js";import{getVersion as P}from"./utils/index.js";const C=`\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
2
|
\u2551 \u2554\u2550\u2557 \u2566 \u2566 \u2554\u2557\u2554 \u2566 \u2566 \u2566 \u2554\u2566\u2557 \u2551
|
|
3
3
|
\u2551 \u2560\u2566\u255D \u2551 \u2551 \u2551\u2551\u2551 \u2551 \u2551 \u2551 \u2551\u2551\u2551 \u2551
|
|
4
4
|
\u2551 \u2569\u255A\u2550 \u255A\u2550\u255D \u255D\u255A\u255D \u2569 \u255A\u2550\u255D \u2569 \u2569 \u2551
|
|
5
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
|
|
6
|
+
One Tool to Run Them All!`;class A{program;commandService;configService;profileService;pluginService;shutdownService;outputService;pluginContextService;constructor(){this.program=new v("runium"),this.configService=o.get(l),this.profileService=o.get(m),this.pluginService=o.get(h),this.shutdownService=o.get(d),this.outputService=o.get(S),this.pluginContextService=o.get(f),this.commandService=o.get(g)}async start(){return await this.configService.init().catch(i=>{throw this.initOutput(),i}),await this.shutdownService.init(),await this.profileService.init(),await this.pluginContextService.init(),this.initOutput(),this.initEnv(),await this.loadPlugins(),await this.initProgram(),await this.initPlugins(),this.program.parseAsync()}async loadPlugins(){const i=this.profileService.getPlugins();for(const e of i)if(e.disabled!==!0)try{const t=this.pluginService.resolvePath(e.path,e.file);await this.pluginService.loadPlugin(t,e.options)}catch(t){this.outputService.error(`Failed to load plugin "${e.name}"`);const{code:r,message:s,payload:a}=t;this.outputService.debug("Error details:",{message:s,code:r,payload:a})}}async initProgram(){const i=this.program;i.option("-D, --debug","enable debug mode"),i.option("-E, --env [paths...]","load env files"),i.version(P()),i.description(C),i.on("option:debug",()=>{this.setDebugMode()}),i.on("option:env",e=>{this.loadEnvFiles([e])}),Object.values(p).forEach(e=>{this.commandService.registerCommand(e,i)})}initOutput(){(this.configService.get("output").debug||process.argv.includes("-D")||process.argv.includes("--debug"))&&this.setDebugMode()}setDebugMode(){this.outputService.getLevel()!==n.DEBUG&&(this.outputService.setLevel(n.DEBUG),this.outputService.debug("Debug mode enabled"))}initEnv(){const i=this.configService.get("env");i.path.length>0&&this.loadEnvFiles(i.path)}loadEnvFiles(i){for(const e of i){const t=u(e&&e.trim()||".env");c(t)?process.loadEnvFile(t):this.outputService.error(`Env file "${t}" not found`)}}async initPlugins(){const i=this.pluginService.getAllPlugins();for(const e of i){const t=e.app?.commands;if(t)for(const r of t)this.commandService.registerCommand(r,this.program,e.name)}await this.pluginService.runHook("app.afterInit",{profilePath:this.configService.get("profile").path})}}export{A as RuniumCliApp};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{RuniumError as a}from"@runium/core";import{ErrorCode as l}from"../../constants/index.js";import{PluginCommand as
|
|
1
|
+
import{RuniumError as a}from"@runium/core";import{ErrorCode as l}from"../../constants/index.js";import{PluginCommand as s}from"./plugin-command.js";class g extends s{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,{}),t=this.pluginService.getPluginByName(i);if(t)await this.profileService.addPlugin({name:i,path:n?o:e,file:n,disabled:!1,options:t.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};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{PluginCommand as
|
|
1
|
+
import{PluginCommand as l}from"./plugin-command.js";class o extends l{config(){this.command.name("disable").description("disable plugin").option("-a, --all","disable all plugins").argument("[name...]","plugin names")}async handle(e,{all:n}){if(e.length===0&&!n){this.outputService.warn("No plugins specified to disable");return}n&&(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};
|
|
@@ -1 +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("[
|
|
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("[name...]","plugin names")}async handle(n,{all:l}){if(n.length===0&&!l){this.outputService.warn("No plugins specified to enable");return}l&&(n=this.profileService.getPlugins().map(e=>e.name));for(const e of n){const i=this.ensureProfilePlugin(e);if(!i.disabled){this.outputService.info('Plugin "%s" is already enabled',e);continue}await this.profileService.updatePlugin(e,{disabled:!1});const t=this.pluginService.resolvePath(i.path,i.file);await this.pluginService.loadPlugin(t,i.options),this.outputService.success('Plugin "%s" successfully enabled',e)}}}export{u as PluginEnableCommand};
|
|
@@ -1 +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("[
|
|
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("[name...]","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};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{RuniumCommand as
|
|
1
|
+
import{RuniumCommand as m}from"../runium-command.js";import{PluginAddCommand as o}from"./plugin-add.js";import{PluginDisableCommand as n}from"./plugin-disable.js";import{PluginEnableCommand as i}from"./plugin-enable.js";import{PluginListCommand as d}from"./plugin-list.js";import{PluginRemoveCommand as a}from"./plugin-remove.js";class g extends m{subcommands=[d,o,a,n,i];config(){this.command.name("plugin").description("manage plugins")}async handle(){this.command.help()}}export{g as PluginCommand};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{RuniumError as i}from"@runium/core";import{ErrorCode as
|
|
1
|
+
import{ID_REGEX as d,RuniumError as i}from"@runium/core";import{ErrorCode as a}from"../../constants/index.js";import{ProjectCommand as s}from"./project-command.js";class h extends s{config(){this.command.name("add").description("add project").argument("<path>","project file path").argument("[name]","project name (default: project config id)")}async handle(e,c){const t=this.projectService.resolvePath(e),o=await this.projectService.initProject(t);if(o){const r=c??o.getConfig().id;this.validateProjectName(r),await this.profileService.addProject({name:r,path:t}),this.outputService.success('Project "%s" successfully added',r)}else throw new i(`Failed to add project "${t}"`,a.PROJECT_NOT_FOUND,{path:t})}validateProjectName(e){if(!d.test(e))throw new i(`Invalid project name "${e}". Only letters, digits, underscores (_), and hyphens (-) are allowed.`,a.INVALID_ARGUMENT,{name:e})}}export{h as ProjectAddCommand};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{Container as
|
|
1
|
+
import{Container as r}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 f,FileService as p}from"../../services/index.js";class a extends i{projectService;profileService;fileService;constructor(e){super(e),this.projectService=r.get(f),this.profileService=r.get(m),this.fileService=r.get(p)}ensureProfileProject(e){const o=this.profileService.getProjectByName(e);if(!o)throw new t(`Project "${e}" not found`,c.PROJECT_NOT_FOUND,{name:e});return o}}export{a as ProjectCommand};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{dirname as d}from"node:path";import{Option as j}from"commander";import{Container as
|
|
1
|
+
import{dirname as d}from"node:path";import{Option as j}from"commander";import{Container as u}from"typedi";import{ProjectEvent as h,RuniumError as m}from"@runium/core";import{ErrorCode as f}from"../../constants/index.js";import{ShutdownService as P}from"../../services/index.js";import{ProjectStateCommand as S}from"./project-state-command.js";class y extends S{shutdownService;fileWriter=null;constructor(t){super(t),this.shutdownService=u.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:p,output:c}){const r=s?this.projectService.resolvePath(t):this.ensureProfileProject(t).path,i=this.getProjectDataFileName(s?r:t),e=this.profileService.getPath("projects",i),o=await this.readProjectData(e);if(o&&this.isProjectProcessStarted(o.pid))throw new m(`Project "${t}" is already started`,f.PROJECT_ALREADY_STARTED,{name:t});if(p==="project"){const n=d(r);n!==process.cwd()&&process.chdir(n)}const a=await this.projectService.initProject(r);this.shutdownService.addBlocker(n=>a.stop(n)),await this.fileService.ensureDirExists(d(e)),this.fileWriter=this.fileService.createAtomicWriter(e),this.addProjectListeners(a,{projectPath:r,output:c}),await this.projectService.runHook("project.beforeStart",a),await a.start()}addProjectListeners(t,s){const{projectPath:p,output:c}=s,r={id:t.getConfig().id,pid:process.pid,cwd:process.cwd(),path:p,state:{project:[],tasks:{}}},i=()=>{this.fileWriter.writeJson(r).then()};i(),t.on(h.STATE_CHANGE,async e=>{r.state.project.push(e),i(),c&&this.outputService.info("Project %s",e.status)}),t.on(h.TASK_STATE_CHANGE,(e,o)=>{r.state.tasks[e]||(r.state.tasks[e]=[]),r.state.tasks[e].push(o),i(),c&&this.outputService.info("Task %s %s %s",e,o.status,o.exitCode||o.error||"")})}}export{y as ProjectStartCommand};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{convertPathToValidFileName as r}from"../../utils/index.js";import{ProjectCommand as
|
|
1
|
+
import{convertPathToValidFileName as r}from"../../utils/index.js";import{ProjectCommand as a}from"./project-command.js";class d extends a{getProjectDataFileName(e){let t=r(e);return t.endsWith(".json")||(t=t+".json"),t}async readProjectData(e){return this.fileService.readJson(e).catch(()=>null)}isProjectProcessStarted(e){try{return process.kill(Number(e),0)}catch(t){return t.code==="EPERM"}}}export{d as ProjectStateCommand};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{formatTimestamp as
|
|
1
|
+
import{formatTimestamp as m}from"../../utils/index.js";import{ProjectStateCommand as S}from"./project-state-command.js";class D extends S{config(){this.command.name("status").description("get project status").option("-f, --file","use file path instead of project name").option("-t, --tasks","show tasks status").option("-a, --all","show status change history").argument("<name>","project name")}async handle(o,{file:p,tasks:l,all:c}){const d=p?this.projectService.resolvePath(o):this.ensureProfileProject(o).path,f=this.getProjectDataFileName(p?d:o),j=this.profileService.getPath("projects",f),r=await this.readProjectData(j);if(r){let{project:s=[]}=r.state;if(c||(s=s.length>0?[s[s.length-1]]:[]),l){const n=s.map(t=>({name:"Project",status:t.status,time:m(t.timestamp),timestamp:t.timestamp,reason:t.reason||""})),{tasks:i=[]}=r.state,h=[];Object.entries(i).forEach(([t,e])=>{c||(e=e.length>0?[e[e.length-1]]:[]),e.forEach(a=>{h.push({name:t,status:a.status,time:m(a.timestamp),timestamp:a.timestamp,reason:[a.reason,a.exitCode].filter(Boolean).join(" ")})})});const u=[...n,...h];u.sort((t,e)=>t.timestamp-e.timestamp),this.outputService.table(u,["time","name","status","reason"])}else{const n=s.map(i=>({status:i.status,time:m(i.timestamp)}));this.outputService.table(n,["time","status"])}}else this.outputService.info(`No project status for "${o}"`)}}export{D as ProjectStatusCommand};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{RuniumError as r}from"@runium/core";import{ErrorCode as i}from"../../constants/index.js";import{ProjectStateCommand as
|
|
1
|
+
import{RuniumError as r}from"@runium/core";import{ErrorCode as i}from"../../constants/index.js";import{ProjectStateCommand as n}from"./project-state-command.js";class l extends n{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,c=this.getProjectDataFileName(e?a:t),s=this.profileService.getPath("projects",c),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(p){throw new r(`Failed to stop project "${t}"`,i.PROJECT_STOP_ERROR,{name:t,original:p})}}}export{l as ProjectStopCommand};
|
|
@@ -1 +1,4 @@
|
|
|
1
|
-
import{ProjectCommand as
|
|
1
|
+
import{ProjectCommand as c}from"./project-command.js";import{getErrorMessages as n}from"../../validation/index.js";class m extends c{config(){this.command.name("validate").description("validate project").option("-f, --file","use file path instead of project name").argument("<name>","project name")}async handle(e,{file:r}){const t=r?this.projectService.resolvePath(e):this.ensureProfileProject(e).path,o=await this.projectService.initProject(t);try{await o.validate(),this.outputService.success('Project "%s" is valid',e)}catch(i){const a=n(i.payload.errors,{filter:s=>s.original?.keyword!=="oneOf"});this.outputService.error(`Project "%s" validation failed:
|
|
2
|
+
|
|
3
|
+
%s`,e,a.join(`
|
|
4
|
+
`))}}}export{m as ProjectValidateCommand};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{RuniumCommand as
|
|
1
|
+
import{RuniumCommand as o}from"../runium-command.js";import{ProjectAddCommand as m}from"./project-add.js";import{ProjectListCommand as t}from"./project-list.js";import{ProjectRemoveCommand as r}from"./project-remove.js";import{ProjectStartCommand as e}from"./project-start.js";import{ProjectStopCommand as a}from"./project-stop.js";import{ProjectStatusCommand as d}from"./project-status.js";import{ProjectValidateCommand as n}from"./project-validate.js";class l extends o{subcommands=[t,m,r,e,a,d,n];config(){this.command.name("project").description("manage projects")}async handle(){this.command.help()}}export{l as ProjectCommand};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{Command as
|
|
1
|
+
import{Command as n}from"commander";import{Container as o}from"typedi";import{CommandService as r,OutputService as a}from"../services/index.js";class u{outputService;command;subcommands=[];run;constructor(t){const m=o.get(r);this.run=m.createRunCommand(this.handle,this),this.outputService=o.get(a),this.command=new n,this.config(),this.command.action(this.run.bind(this)),t.addCommand(this.command)}}export{u as RuniumCommand};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var _=(R=>(R.FILE_READ_JSON_ERROR="file-read-json-error",R.FILE_WRITE_JSON_ERROR="file-write-json-error",R.FILE_READ_ERROR="file-read-error",R.FILE_WRITE_ERROR="file-write-error",R.FILE_CREATE_DIR_ERROR="file-create-dir-error",R.PLUGIN_NOT_FOUND="plugin-not-found",R.PLUGIN_FILE_NOT_FOUND="plugin-file-not-found",R.PLUGIN_INCORRECT_MODULE="plugin-incorrect-module",R.PLUGIN_INVALID="plugin-invalid",R.PLUGIN_PATH_RESOLVE_ERROR="plugin-path-resolve-error",R.PLUGIN_LOAD_ERROR="plugin-load-error",R.PLUGIN_HOOK_ERROR="plugin-hook-error",R.PROJECT_ALREADY_STARTED="project-already-started",R.PROJECT_NOT_STARTED="project-not-started",R.PROJECT_STOP_ERROR="project-stop-error",R.PROJECT_NOT_FOUND="project-not-found",R.PROJECT_FILE_NOT_FOUND="project-file-not-found",R.PROJECT_FILE_CAN_NOT_READ="project-file-can-not-read",R.PROJECT_JSON_PARSE_ERROR="project-json-parse-error",R.INVALID_ARGUMENT="invalid-argument",R.INVALID_PATH="invalid-path",R.CONFIG_INVALID_DATA="config-invalid-data",R.COMMAND_REGISTRATION_ERROR="command-registration-error",R.COMMAND_INCORRECT="command-incorrect",R.COMMAND_NOT_FOUND="command-not-found",R.COMMAND_RUN_ERROR="command-run-error",R))(_||{});export{_ as ErrorCode};
|
package/lib/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
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
|
|
2
|
+
import"reflect-metadata";import{Container as e}from"typedi";import{RuniumCliApp as r}from"./app.js";import{OutputService as i,ShutdownService as n}from"./services/index.js";async function c(){await new r().start()}c().catch(t=>{const o=e.get(i);o.error("Error: %s",t.message),o.debug("Error details:",{code:t.code,payload:t.payload}),e.get(n).shutdown("error").catch(()=>{process.exit(1)})});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{formatTimestamp as t}from"../utils/format-timestamp.js";function o(){return t(Date.now())}function n(){return Date.now().toString()}export{o as dateMacro,n as timestampMacro};
|
package/lib/macros/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{eqMacro as
|
|
1
|
+
import{eqMacro as r,neMacro as o}from"./conditional.js";import{dateMacro as m,timestampMacro as t}from"./date.js";import{emptyMacro as a}from"./empty.js";import{envMacro as e}from"./env.js";import{homeDirMacro as p,pathMacro as c,tmpDirMacro as i}from"./path.js";const s={date:m,homedir:p,env:e,empty:a,eq:r,ne:o,path:c,tmpdir:i,timestamp:t};export{s as macros};
|
package/lib/macros/path.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{resolve as t}from"node:path";function
|
|
1
|
+
import{resolve as t}from"node:path";import{homedir as o,tmpdir as i}from"node:os";function m(...r){return t(...r)}function p(){return i()}function c(){return o()}export{c as homeDirMacro,m as pathMacro,p as tmpDirMacro};
|
package/lib/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@runium/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "Runium CLI",
|
|
5
5
|
"author": "TheBeastApp",
|
|
6
6
|
"license": "MIT",
|
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
},
|
|
14
14
|
"module": "./index.js",
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@runium/core": "^0.0.
|
|
16
|
+
"@runium/core": "^0.0.8",
|
|
17
|
+
"@segment/ajv-human-errors": "^2.15.0",
|
|
17
18
|
"commander": "^14.0.2",
|
|
18
19
|
"reflect-metadata": "^0.2.2",
|
|
19
20
|
"typedi": "^0.10.0"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var p=function(i,n,t,a){var m=arguments.length,o=m<3?n:a===null?a=Object.getOwnPropertyDescriptor(n,t):a,r;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")o=Reflect.decorate(i,n,t,a);else for(var e=i.length-1;e>=0;e--)(r=i[e])&&(o=(m<3?r(o):m>3?r(n,t,o):r(n,t))||o);return m>3&&o&&Object.defineProperty(n,t,o),o};import{Command as C}from"commander";import{Container as R,Service as w}from"typedi";import{isRuniumError as u,RuniumError as c}from"@runium/core";import{ErrorCode as f}from"../constants/index.js";import{PluginService as P}from"./index.js";import{RuniumCommand as O}from"../commands/runium-command.js";const d="runium";let l=class{fullPathCommands=new Map;getCommandFullPath(n){const t=n.command?.name();if(t===d||!n.command.parent)return"";const a=Array.from(this.fullPathCommands.values()).find(o=>o.command===n.command.parent);if(a)return[this.getCommandFullPath(a),t].filter(Boolean).join(" ").trim();const m=n.command.parent.name();return m===d?t:[m,t].join(" ").trim()}registerCommand(n,t,a="app"){const m=(o,r)=>{try{if(!(o.prototype instanceof O))throw new c(`Command "${o.name}" for "${a}" must be a subclass of "RuniumCommand"`,f.COMMAND_INCORRECT,{context:a,CommandConstructor:o});const e=new o(r),h=this.getCommandFullPath(e);this.fullPathCommands.set(h,e),e.subcommands.length>0&&e.subcommands.forEach(s=>{m(s,e.command)})}catch(e){throw u(e)?e:new c(`Failed to register command "${o.name}" for "${a}"`,f.COMMAND_REGISTRATION_ERROR,{original:e})}};m(n,t)}createRunCommand(n,t){const a=R.get(P);return async(...m)=>{m?.length>0&&m[m.length-1]instanceof C&&m.pop();const o=this.getCommandFullPath(t);await a.runHook("app.beforeCommandRun",{command:o,args:m}),await n.call(t,...m),await a.runHook("app.afterCommandRun",{command:o,args:m})}}hasCommand(n){return this.fullPathCommands.has(n)}async runCommand(n,...t){const a=this.fullPathCommands.get(n);if(!a)throw new c(`Command "${n}" not found`,f.COMMAND_NOT_FOUND,{path:n});try{await a.run(...t)}catch(m){throw u(m)?m:new c(`Failed to run command "${n}"`,f.COMMAND_RUN_ERROR,{original:m})}}};l=p([w()],l);export{l as CommandService};
|
package/lib/services/config.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var d=function(
|
|
1
|
+
var d=function(i,t,e,r){var n=arguments.length,o=n<3?t:r===null?r=Object.getOwnPropertyDescriptor(t,e):r,a;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")o=Reflect.decorate(i,t,e,r);else for(var s=i.length-1;s>=0;s--)(a=i[s])&&(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 l}from"node:fs";import{homedir as g}from"node:os";import{join as c,resolve as u}from"node:path";import{Service as v}from"typedi";import{readJsonFile as _,RuniumError as O}from"@runium/core";import{ErrorCode as I}from"../constants/index.js";import{createValidator as E,getConfigSchema as j,getErrorMessages as C}from"../validation/index.js";const F=".runiumrc.json",p=c(process.cwd(),F),h=".runium",b=c(g(),h),m=c(process.cwd(),h);function A(i){const t=j(),e=E(t);if(!e(i)&&e.errors){const n=C(e.errors);throw new O(`Invalid "${p}" configuration data`,I.CONFIG_INVALID_DATA,n)}}let f=class{data={profile:{path:b},plugins:{},output:{debug:!1},env:{path:[]}};async init(){if(l(m)&&(this.data.profile.path=m),l(p)){const t=await _(p);if(t){A(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??{}),plugins:Object.assign({},this.data.plugins,t.plugins??{})};e.env.path=e.env.path.map(r=>u(r)),e.profile.path=u(e.profile.path),this.data=e}}}get(t){return this.data[t]}};f=d([v()],f);export{f as ConfigService};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var m=function(r,e,t,i){var a=arguments.length,n=a<3?e:i===null?i=Object.getOwnPropertyDescriptor(e,t):i,c;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")n=Reflect.decorate(r,e,t,i);else for(var l=r.length-1;l>=0;l--)(c=r[l])&&(n=(a<3?c(n):a>3?c(e,t,n):c(e,t))||n);return a>3&&n&&Object.defineProperty(e,t,n),n};import{basename as u,dirname as R,join as y}from"node:path";import{access as E,constants as x,mkdir as d,readFile as h,rename as _,writeFile as f}from"node:fs/promises";import{fileURLToPath as O}from"node:url";import{Service as F}from"typedi";import{RuniumError as o}from"@runium/core";import{ErrorCode as s}from"../constants/index.js";let w=class{async read(e,t={}){try{return await h(e,{encoding:t.encoding||"utf-8"})}catch(i){throw new o(`Can not read file ${e}`,s.FILE_READ_ERROR,{path:e,options:t,original:i})}}async write(e,t,i={}){try{await f(e,t,{encoding:i.encoding||"utf-8"})}catch(a){throw new o(`Can not write file ${e}`,s.FILE_WRITE_ERROR,{path:e,data:t,options:i,original:a})}}async readJson(e){try{const t=await h(e,{encoding:"utf-8"});return JSON.parse(t)}catch(t){throw new o(`Can not read JSON file ${e}`,s.FILE_READ_JSON_ERROR,{path:e,original:t})}}async writeJson(e,t){try{await f(e,JSON.stringify(t,null,2),{encoding:"utf-8"})}catch(i){throw new o(`Can not write JSON file ${e}`,s.FILE_WRITE_JSON_ERROR,{path:e,data:t,original:i})}}async isExists(e){try{return await E(e,x.F_OK),!0}catch{return!1}}async ensureDirExists(e){try{await d(e,{recursive:!0})}catch(t){throw new o(`Can not create directory ${e}`,s.FILE_CREATE_DIR_ERROR,{path:e,original:t})}}createAtomicWriter(e){return new p(e)}};w=m([F()],w);function g(r){const e=r instanceof URL?O(r):r.toString();return y(R(e),`.${u(e)}.tmp`)}async function D(r,e,t){for(let i=0;i<e;i++)try{return await r()}catch(a){if(i<e-1)await new Promise(n=>setTimeout(n,t));else throw a}}class p{filename;tempFilename;locked=!1;prev=null;next=null;nextPromise=null;nextData=null;constructor(e){this.filename=e,this.tempFilename=g(e)}addData(e){return this.nextData=e,this.nextPromise||=new Promise((t,i)=>{this.next=[t,i]}),new Promise((t,i)=>{this.nextPromise?.then(t).catch(i)})}async writeData(e){this.locked=!0;try{await f(this.tempFilename,e,"utf-8"),await D(async()=>{await _(this.tempFilename,this.filename)},10,100),this.prev?.[0]()}catch(t){throw t instanceof Error&&this.prev?.[1](t),t}finally{if(this.locked=!1,this.prev=this.next,this.next=this.nextPromise=null,this.nextData!==null){const t=this.nextData;this.nextData=null,await this.write(t)}}}async write(e){try{await(this.locked?this.addData(e):this.writeData(e))}catch(t){throw new o(`Can not write file ${this.filename}`,s.FILE_WRITE_ERROR,{path:this.filename,data:e,original:t})}}async writeJson(e){return this.write(JSON.stringify(e,null,2))}}export{p as AtomicWriter,w as FileService};
|
package/lib/services/index.js
CHANGED
|
@@ -1 +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";
|
|
1
|
+
export*from"./command.js";export*from"./config.js";export*from"./file.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";
|
package/lib/services/output.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var a=
|
|
2
|
-
`).map(r=>r.charAt(0)+r.slice(
|
|
3
|
-
`)}}const
|
|
1
|
+
var a=Object.defineProperty;var c=Object.getOwnPropertyDescriptor;var u=(i,o,t,s)=>{for(var n=s>1?void 0:s?c(o,t):o,r=i.length-1,e;r>=0;r--)(e=i[r])&&(n=(s?e(o,t,n):e(n))||n);return s&&n&&a(o,t,n),n};import{Console as p}from"node:console";import{Transform as d}from"node:stream";import{inspect as v}from"node:util";import{Service as f}from"typedi";var g=(e=>(e[e.TRACE=0]="TRACE",e[e.DEBUG=1]="DEBUG",e[e.INFO=2]="INFO",e[e.WARN=3]="WARN",e[e.ERROR=4]="ERROR",e[e.SILENT=5]="SILENT",e))(g||{});class L extends p{transform;constructor(){v.defaultOptions.depth=5;const o=new d({transform:(t,s,n)=>n(null,t)});super({stdout:o,stderr:o,colorMode:!1}),this.transform=o}getPatchedTable(o,t){this.table(o,t);const s=(this.transform.read()||"").toString(),n=s.indexOf("\u252C")+1;return s.split(`
|
|
2
|
+
`).map(r=>r.charAt(0)+r.slice(n)).join(`
|
|
3
|
+
`).replace(/'([^']*)'/g,"$1 ")}}const h=new L;let l=class{outputLevel=2;setLevel(o){this.outputLevel=o}getLevel(){return this.outputLevel}trace(o,...t){this.outputLevel<=0&&console.log(o,...t)}debug(o,...t){this.outputLevel<=1&&console.log(o,...t)}info(o,...t){this.outputLevel<=2&&console.log(o,...t)}success(o,...t){this.outputLevel<=2&&console.log(o,...t)}warn(o,...t){this.outputLevel<=3&&console.warn(o,...t)}error(o,...t){this.outputLevel<=4&&console.error(o,...t)}log(o,...t){this.outputLevel<5&&console.log(o,...t)}table(o,t){if(this.outputLevel<5){const s=o.map((r,e)=>({...r,"#":e+1})),n=h.getPatchedTable(s,t?["#",...t]:void 0);console.log(n)}}newLine(){this.outputLevel<5&&console.log("")}clear(){this.outputLevel<5&&console.clear()}};l=u([f()],l);export{g as OutputLevel,l as OutputService};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var P=function(a,t,e,r){var o=arguments.length,i=o<3?t:r===null?r=Object.getOwnPropertyDescriptor(t,e):r,n;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")i=Reflect.decorate(a,t,e,r);else for(var u=a.length-1;u>=0;u--)(n=a[u])&&(i=(o<3?n(i):o>3?n(t,e,i):n(t,e))||i);return o>3&&i&&Object.defineProperty(t,e,i),i},j=function(a,t){if(typeof Reflect=="object"&&typeof Reflect.metadata=="function")return Reflect.metadata(a,t)},c=function(a,t){return function(e,r){t(e,r,a)}},d,p,l,m,v;import{delimiter as O}from"node:path";import{Inject as s,Service as R}from"typedi";import{RuniumError as h,isRuniumError as W,RuniumTask as E,RuniumTrigger as x,applyMacros as C,TaskEvent as L,TaskStatus as A,ProjectEvent as T,ProjectStatus as B}from"@runium/core";import{RuniumCommand as D}from"../commands/runium-command.js";import{CommandService as S,FileService as b,OutputLevel as g,OutputService as y,ProfileService as _,ShutdownService as w}from"./index.js";import{getVersion as I}from"../utils/index.js";import{ErrorCode as J}from"../constants/index.js";global.runium=null;let f=class{commandService;outputService;shutdownService;fileService;profileService;constructor(t,e,r,o,i){this.commandService=t,this.outputService=e,this.shutdownService=r,this.fileService=o,this.profileService=i}createStorageWrapper(t){return((...e)=>{const[r,...o]=e,i=this.resolveProfilePath(r),n=this.fileService[t];return n(i,...o)})}resolveProfilePath(t){const e=Array.isArray(t)?t:t.split(O);if(e.length===0||e.every(r=>r.trim()===""))throw new h("Invalid path",J.INVALID_PATH,{path:t});return this.profileService.getPath(...e)}async init(){const t=this.commandService,e=this.outputService,r=this.shutdownService,o={class:{RuniumCommand:D,RuniumError:h,RuniumTask:E,RuniumTrigger:x},enum:{OutputLevel:Object.keys(g).filter(i=>isNaN(Number(i))).reduce((i,n)=>(i[n]=g[n],i),{}),ProjectEvent:T,ProjectStatus:B,TaskEvent:L,TaskStatus:A},utils:{applyMacros:C,isRuniumError:W},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:r.addBlocker.bind(r),removeBlocker:r.removeBlocker.bind(r)},command:{has:t.hasCommand.bind(t),run:t.runCommand.bind(t)},storage:{read:this.createStorageWrapper("read"),write:this.createStorageWrapper("write"),readJson:this.createStorageWrapper("readJson"),writeJson:this.createStorageWrapper("writeJson"),isExists:this.createStorageWrapper("isExists"),ensureDirExists:this.createStorageWrapper("ensureDirExists"),createAtomicWriter:this.createStorageWrapper("createAtomicWriter"),getPath:this.resolveProfilePath.bind(this)},version:I()};global.runium=Object.freeze(o)}};f=P([R(),c(0,s()),c(1,s()),c(2,s()),c(3,s()),c(4,s()),j("design:paramtypes",[typeof(d=typeof S<"u"&&S)=="function"?d:Object,typeof(p=typeof y<"u"&&y)=="function"?p:Object,typeof(l=typeof w<"u"&&w)=="function"?l:Object,typeof(m=typeof b<"u"&&b)=="function"?m:Object,typeof(v=typeof _<"u"&&_)=="function"?v:Object])],f);export{f as PluginContextService};
|
package/lib/services/plugin.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var v=function(n,e,r,t){var i=arguments.length,o=i<3?e:t===null?t=Object.getOwnPropertyDescriptor(e,r):t,l;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")o=Reflect.decorate(n,e,r,t);else for(var a=n.length-1;a>=0;a--)(l=n[a])&&(o=(i<3?l(o):i>3?l(e,r,o):l(e,r))||o);return i>3&&o&&Object.defineProperty(e,r,o),o},_=function(n,e){if(typeof Reflect=="object"&&typeof Reflect.metadata=="function")return Reflect.metadata(n,e)},P=function(n,e){return function(r,t){e(r,t,n)}},p;import{existsSync as R}from"node:fs";import{resolve as w}from"node:path";import{Inject as y,Service as O}from"typedi";import{isRuniumError as E,RuniumError as s}from"@runium/core";import{ErrorCode as u}from"../constants/index.js";import{OutputService as m}from"./index.js";import{createValidator as I,getErrorMessages as N,getPluginSchema as L}from"../validation/index.js";let d=class{outputService;plugins=new Map;validator=I(L());constructor(e){this.outputService=e}getAllPlugins(){return Array.from(this.plugins.values())}getPluginByName(e){return this.plugins.get(e)}async loadPlugin(e,r){if(!e||!R(e))throw new s(`Plugin file "${e}" does not exist`,u.PLUGIN_FILE_NOT_FOUND,{path:e});try{const t=await import(e),{default:i}=t;if(!i||typeof i!="function")throw new s("Plugin module must have a default function",u.PLUGIN_INCORRECT_MODULE,{path:e});const o=i(r);return this.validate(o),this.plugins.set(o.name,o),o.name}catch(t){throw E(t)?t:new s(`Failed to load plugin "${e}"`,u.PLUGIN_LOAD_ERROR,{path:e,original:t})}}async unloadPlugin(e){return this.plugins.delete(e)}resolvePath(e,r=!1){try{return(r?w(e):import.meta.resolve(e)).replace("file://","")}catch(t){throw new s(`Failed to resolve plugin path "${e}"`,u.PLUGIN_PATH_RESOLVE_ERROR,{path:e,original:t})}}async runHook(e,r,{mutable:t,onError:i}={}){const o=this.getAllPlugins(),[l,a]=e.split(".");for(const f of o){const g=f.hooks?.[l]?.[a];if(g)try{t?r=await g(r)??r:await g(r)}catch(h){const c=new s(`Failed to run "${f.name}.${e}" hook`,u.PLUGIN_HOOK_ERROR,{plugin:f.name,hook:e,original:h});i?i(c):(this.outputService.error("Error: %s",c.message),this.outputService.debug("Error details:",{code:c.code,payload:c.payload}))}}return t?r:void 0}validate(e){if(!this.validator(e||{})&&this.validator.errors){const t=N(this.validator.errors);throw new s("Incorrect plugin format",u.PLUGIN_INVALID,{errors:t})}}};d=v([O(),P(0,y()),_("design:paramtypes",[typeof(p=typeof m<"u"&&m)=="function"?p:Object])],d);export{d as PluginService};
|
package/lib/services/profile.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var j=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 c=s.length-1;c>=0;c--)(a=s[c])&&(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},d=function(s,t){if(typeof Reflect=="object"&&typeof Reflect.metadata=="function")return Reflect.metadata(s,t)},h=function(s,t){return function(i,e){t(i,e,s)}},f,p;import{existsSync as v}from"node:fs";import{mkdir as w}from"node:fs/promises";import{join as y}from"node:path";import{Inject as l,Service as S}from"typedi";import{ConfigService as u,FileService as g}from"./index.js";const m="plugins.json",P="projects.json";let o=class{configService;fileService;path=process.cwd();plugins=[];projects=[];constructor(t,i){this.configService=t,this.fileService=i}async init(){this.path=this.configService.get("profile").path,v(this.path)||await w(this.path,{recursive:!0}),await this.readPlugins(),await this.patchPlugins(),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()}getPath(...t){return y(this.path,...t)}async readPlugins(){this.plugins=await this.fileService.readJson(this.getPath(m)).catch(()=>[])||this.plugins}async writePlugins(){await this.fileService.writeJson(this.getPath(m),this.plugins)}async patchPlugins(){const t=this.configService.get("plugins");for(const i of this.plugins){const e=t[i.name];e&&Object.assign(i,e)}}async readProjects(){this.projects=await this.fileService.readJson(this.getPath(P)).catch(()=>[])||this.projects}async writeProjects(){await this.fileService.writeJson(this.getPath(P),this.projects)}};o=j([S(),h(0,l()),h(1,l()),d("design:paramtypes",[typeof(f=typeof u<"u"&&u)=="function"?f:Object,typeof(p=typeof g<"u"&&g)=="function"?p:Object])],o);export{o as ProfileService};
|
package/lib/services/project.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var h=function(n,e,r,t){var o=arguments.length,i=o<3?e:t===null?t=Object.getOwnPropertyDescriptor(e,r):t,c;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")i=Reflect.decorate(n,e,r,t);else for(var a=n.length-1;a>=0;a--)(c=n[a])&&(i=(o<3?c(i):o>3?c(e,r,i):c(e,r))||i);return o>3&&i&&Object.defineProperty(e,r,i),i},d=function(n,e){if(typeof Reflect=="object"&&typeof Reflect.metadata=="function")return Reflect.metadata(n,e)},j=function(n,e){return function(r,t){e(r,t,n)}},g;import{existsSync as _}from"node:fs";import{readFile as v}from"node:fs/promises";import{resolve as P}from"node:path";import{Inject as S,Service as R}from"typedi";import{applyMacros as O,Project as y,RuniumError as u}from"@runium/core";import{ErrorCode as f}from"../constants/index.js";import{macros as E}from"../macros/index.js";import{PluginService as m}from"./index.js";let p=class{pluginService;constructor(e){this.pluginService=e}async initProject(e){await this.pluginService.runHook("project.beforeConfigRead",e);let r=await this.readFile(e);r=await this.pluginService.runHook("project.afterConfigRead",r,{mutable:!0}),r=this.applyMacros(r),r=await this.pluginService.runHook("project.afterConfigMacrosApply",r,{mutable:!0});let t=this.parseProjectContent(r,e);t=await this.pluginService.runHook("project.afterConfigParse",t,{mutable:!0});const o=new y(t);return this.extendProjectWithPlugins(o)}resolvePath(e){return P(e)}runHook(...e){return this.pluginService.runHook(...e)}async readFile(e){if(!_(e))throw new u(`Project file "${e}" does not exist`,f.PROJECT_FILE_NOT_FOUND,{path:e});try{return v(e,{encoding:"utf-8"})}catch(r){throw new u(`Failed to read project file "${e}"`,f.PROJECT_FILE_CAN_NOT_READ,{path:e,original:r})}}applyMacros(e){const t=this.pluginService.getAllPlugins().reduce((o,i)=>({...o,...i.project?.macros||{}}),{});return O(e,{...t,...E})}parseProjectContent(e,r){try{return JSON.parse(e)}catch(t){throw new u(`Failed to parse project "${r}"`,f.PROJECT_JSON_PARSE_ERROR,{path:r,original:t})}}extendProjectWithPlugins(e){return this.pluginService.getAllPlugins().forEach(t=>{const{tasks:o,actions:i,triggers:c,validationSchema:a}=t.project||{};Object.entries(i||{}).forEach(([s,l])=>{e.registerAction(s,l)}),Object.entries(o||{}).forEach(([s,l])=>{e.registerTask(s,l)}),Object.entries(c||{}).forEach(([s,l])=>{e.registerTrigger(s,l)}),a&&e.extendValidationSchema(a)}),e}};p=h([R(),j(0,S()),d("design:paramtypes",[typeof(g=typeof m<"u"&&m)=="function"?g:Object])],p);export{p as ProjectService};
|
package/lib/services/shutdown.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var m=function(c,e,t,r){var o=arguments.length,i=o<3?e:r===null?r=Object.getOwnPropertyDescriptor(e,t):r,s;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")i=Reflect.decorate(c,e,t,r);else for(var n=c.length-1;n>=0;n--)(s=c[n])&&(i=(o<3?s(i):o>3?s(e,t,i):s(e,t))||i);return o>3&&i&&Object.defineProperty(e,t,i),i},v=function(c,e){if(typeof Reflect=="object"&&typeof Reflect.metadata=="function")return Reflect.metadata(c,e)},h=function(c,e){return function(t,r){e(t,r,c)}},a,p;import{Inject as l,Service as g}from"typedi";import{OutputService as f,PluginService as d}from"./index.js";const S=3e4,w=250,b=["SIGHUP","SIGINT","SIGTERM","SIGQUIT"];let u=class{outputService;pluginService;blockers=new Set;isShuttingDown=!1;constructor(e,t){this.outputService=e,this.pluginService=t}async init(){b.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;const t=this.pluginService.getAllPlugins();for(const o of t){const i=o.hooks?.app?.beforeExit;i&&this.addBlocker(i.bind(o,e))}this.isShuttingDown=!0;const r=o=>{setTimeout(()=>{process.exit(o)},w)};if(this.blockers.size===0){r(0);return}try{await this.executeBlockersWithTimeout(e),r(0)}catch{r(1)}}async executeBlockersWithTimeout(e){const t=Array.from(this.blockers).map(o=>this.executeBlocker(o,e)),r=new Promise((o,i)=>{setTimeout(()=>{i(new Error(`Shutdown timeout after ${S}ms`))},S)});await Promise.race([Promise.allSettled(t),r])}async executeBlocker(e,t){await e(t)}};u=m([g(),h(0,l()),h(1,l()),v("design:paramtypes",[typeof(a=typeof f<"u"&&f)=="function"?a:Object,typeof(p=typeof d<"u"&&d)=="function"?p:Object])],u);export{u as ShutdownService};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{readFileSync as r}from"node:fs";import{resolve as o}from"node:path";let n=null;function i(){if(!n){const e=o(import.meta.dirname,"..","package.json");n=JSON.parse(r(e,"utf-8")).version}return n}export{i as getVersion};
|
package/lib/utils/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{convertPathToValidFileName as r}from"./convert-path-to-valid-file-name.js";import{debounce as
|
|
1
|
+
import{convertPathToValidFileName as r}from"./convert-path-to-valid-file-name.js";import{debounce as m}from"./debounce.js";import{formatTimestamp as f}from"./format-timestamp.js";import{getVersion as i}from"./get-version.js";export{r as convertPathToValidFileName,m as debounce,f as formatTimestamp,i as getVersion};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import a from"ajv/dist/2020.js";const r={Function};function c(e){const n=new a({allowUnionTypes:!0,allErrors:!0,verbose:!0});return n.addKeyword({keyword:"instanceof",schemaType:"string",validate:(o,t)=>t instanceof r[o]}),n.compile(e)}export{c as createValidator};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function e(){return{$schema:"https://json-schema.org/draft/2020-12/schema",$id:"https://runium.dev/schemas/config.json",title:"Runium Config",type:"object",properties:{env:{type:"object",properties:{path:{type:"array",items:{type:"string"}}},additionalProperties:!1},output:{type:"object",properties:{debug:{type:"boolean"}},additionalProperties:!1},profile:{type:"object",properties:{path:{type:"string"}},additionalProperties:!1},plugins:{type:"object",additionalProperties:{type:"object",properties:{disabled:{type:"boolean"},options:{type:"object"}},additionalProperties:!1}}},additionalProperties:!1}}export{e as getConfigSchema};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{AggregateAjvError as i}from"@segment/ajv-human-errors";function E(o,t={filter:()=>!0}){const s=new i(o,{fieldLabels:"jsonPath",includeData:!0,includeOriginalError:!0}),n=t?.filter??(r=>!0),e=[];for(const r of s)n(r)&&e.push(r.message);return e}export{E as getErrorMessages};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{ID_REGEX as e}from"@runium/core";function o(){return{$schema:"https://json-schema.org/draft/2020-12/schema",$id:"https://runium.dev/schemas/plugin.json",title:"Runium Plugin",type:"object",properties:{name:{type:"string",minLength:2,pattern:e.source},options:{type:"object",description:"Plugin configuration options"},project:{type:"object",description:"Project-level plugin definitions",properties:{macros:{type:"object",description:"Macro definitions for config file expansion",additionalProperties:{instanceof:"Function"}},tasks:{type:"object",description:"Task constructor definitions",additionalProperties:{instanceof:"Function"}},actions:{type:"object",description:"Action function definitions",additionalProperties:{instanceof:"Function"}},triggers:{type:"object",description:"Trigger constructor definitions",additionalProperties:{instanceof:"Function"}},validationSchema:{type:"object",description:"JSON Schema extension for project config validation",additionalProperties:!0}},additionalProperties:!1},app:{type:"object",description:"Application-level plugin definitions",properties:{commands:{type:"array",description:"Command constructor definitions",items:{instanceof:"Function"}}},additionalProperties:!1},hooks:{type:"object",description:"Lifecycle hooks for app and project events",properties:{app:{type:"object",description:"Application lifecycle hooks",properties:{afterInit:{instanceof:"Function",description:"Hook called after app initialization"},beforeExit:{instanceof:"Function",description:"Hook called before app exit"},beforeCommandRun:{instanceof:"Function",description:"Hook called before command execution"},afterCommandRun:{instanceof:"Function",description:"Hook called after command execution"}},additionalProperties:!1},project:{type:"object",description:"Project lifecycle hooks",properties:{beforeConfigRead:{instanceof:"Function",description:"Hook called before reading config file"},afterConfigRead:{instanceof:"Function",description:"Hook called after reading config file content"},afterConfigMacrosApply:{instanceof:"Function",description:"Hook called after applying macros to config"},afterConfigParse:{instanceof:"Function",description:"Hook called after parsing config"},beforeStart:{instanceof:"Function",description:"Hook called before project starts"}},additionalProperties:!1}},additionalProperties:!1}},required:["name"],additionalProperties:!1}}export{o as getPluginSchema};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createValidator as o}from"./create-validator.js";import{getConfigSchema as a}from"./get-config-schema.js";import{getErrorMessages as m}from"./get-error-messages.js";import{getPluginSchema as p}from"./get-plugin-schema.js";export{o as createValidator,a as getConfigSchema,m as getErrorMessages,p as getPluginSchema};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@runium/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "Runium CLI",
|
|
5
5
|
"author": "TheBeastApp",
|
|
6
6
|
"license": "MIT",
|
|
@@ -18,15 +18,18 @@
|
|
|
18
18
|
"format": "prettier --write \"src/**/*.ts\""
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@runium/core": "^0.0.
|
|
21
|
+
"@runium/core": "^0.0.8",
|
|
22
|
+
"@segment/ajv-human-errors": "^2.15.0",
|
|
22
23
|
"commander": "^14.0.2",
|
|
23
24
|
"reflect-metadata": "^0.2.2",
|
|
24
25
|
"typedi": "^0.10.0"
|
|
25
26
|
},
|
|
26
27
|
"devDependencies": {
|
|
28
|
+
"@runium/types-core": "^0.0.8",
|
|
27
29
|
"@types/node": "^22.18.0",
|
|
28
30
|
"@typescript-eslint/eslint-plugin": "^7.0.0",
|
|
29
31
|
"@typescript-eslint/parser": "^7.0.0",
|
|
32
|
+
"ajv": "^8.17.1",
|
|
30
33
|
"esbuild": "^0.25.11",
|
|
31
34
|
"esbuild-decorators": "^1.0.0",
|
|
32
35
|
"eslint": "^8.56.0",
|
package/src/app.ts
CHANGED
|
@@ -3,9 +3,9 @@ import { resolve } from 'node:path';
|
|
|
3
3
|
import { Command } from 'commander';
|
|
4
4
|
import { Container } from 'typedi';
|
|
5
5
|
import { RuniumError } from '@runium/core';
|
|
6
|
-
import { RuniumCommand } from '@commands/runium-command';
|
|
7
6
|
import * as commands from '@commands';
|
|
8
7
|
import {
|
|
8
|
+
CommandService,
|
|
9
9
|
ConfigService,
|
|
10
10
|
ProfileService,
|
|
11
11
|
PluginService,
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
OutputLevel,
|
|
15
15
|
PluginContextService,
|
|
16
16
|
} from '@services';
|
|
17
|
+
import { getVersion } from '@utils';
|
|
17
18
|
|
|
18
19
|
const RUNIUM_DESCRIPTION = `╔═══════════════════════╗
|
|
19
20
|
║ ╔═╗ ╦ ╦ ╔╗╔ ╦ ╦ ╦ ╔╦╗ ║
|
|
@@ -25,6 +26,7 @@ One Tool to Run Them All!`;
|
|
|
25
26
|
export class RuniumCliApp {
|
|
26
27
|
private readonly program: Command;
|
|
27
28
|
|
|
29
|
+
private commandService: CommandService;
|
|
28
30
|
private configService: ConfigService;
|
|
29
31
|
private profileService: ProfileService;
|
|
30
32
|
private pluginService: PluginService;
|
|
@@ -40,14 +42,18 @@ export class RuniumCliApp {
|
|
|
40
42
|
this.shutdownService = Container.get(ShutdownService);
|
|
41
43
|
this.outputService = Container.get(OutputService);
|
|
42
44
|
this.pluginContextService = Container.get(PluginContextService);
|
|
45
|
+
this.commandService = Container.get(CommandService);
|
|
43
46
|
}
|
|
44
47
|
|
|
45
48
|
/**
|
|
46
49
|
* Start the application
|
|
47
50
|
*/
|
|
48
51
|
async start(): Promise<Command> {
|
|
52
|
+
await this.configService.init().catch(error => {
|
|
53
|
+
this.initOutput();
|
|
54
|
+
throw error;
|
|
55
|
+
});
|
|
49
56
|
await this.shutdownService.init();
|
|
50
|
-
await this.configService.init();
|
|
51
57
|
await this.profileService.init();
|
|
52
58
|
await this.pluginContextService.init();
|
|
53
59
|
|
|
@@ -56,6 +62,7 @@ export class RuniumCliApp {
|
|
|
56
62
|
|
|
57
63
|
await this.loadPlugins();
|
|
58
64
|
await this.initProgram();
|
|
65
|
+
await this.initPlugins();
|
|
59
66
|
|
|
60
67
|
return this.program.parseAsync();
|
|
61
68
|
}
|
|
@@ -72,7 +79,7 @@ export class RuniumCliApp {
|
|
|
72
79
|
plugin.path,
|
|
73
80
|
plugin.file
|
|
74
81
|
);
|
|
75
|
-
await this.pluginService.loadPlugin(pluginPath);
|
|
82
|
+
await this.pluginService.loadPlugin(pluginPath, plugin.options);
|
|
76
83
|
} catch (error) {
|
|
77
84
|
this.outputService.error(`Failed to load plugin "${plugin.name}"`);
|
|
78
85
|
const { code, message, payload } = error as RuniumError;
|
|
@@ -93,8 +100,8 @@ export class RuniumCliApp {
|
|
|
93
100
|
const program = this.program;
|
|
94
101
|
|
|
95
102
|
program.option('-D, --debug', 'enable debug mode');
|
|
96
|
-
program.option('-
|
|
97
|
-
program.version(
|
|
103
|
+
program.option('-E, --env [paths...]', 'load env files');
|
|
104
|
+
program.version(getVersion());
|
|
98
105
|
program.description(RUNIUM_DESCRIPTION);
|
|
99
106
|
|
|
100
107
|
program.on('option:debug', () => {
|
|
@@ -105,9 +112,8 @@ export class RuniumCliApp {
|
|
|
105
112
|
this.loadEnvFiles([path]);
|
|
106
113
|
});
|
|
107
114
|
|
|
108
|
-
const programCommands: RuniumCommand[] = [];
|
|
109
115
|
Object.values(commands).forEach(CommandConstructor => {
|
|
110
|
-
|
|
116
|
+
this.commandService.registerCommand(CommandConstructor, program);
|
|
111
117
|
});
|
|
112
118
|
}
|
|
113
119
|
|
|
@@ -151,25 +157,34 @@ export class RuniumCliApp {
|
|
|
151
157
|
*/
|
|
152
158
|
private loadEnvFiles(path: string[]): void {
|
|
153
159
|
for (const p of path) {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
} else {
|
|
158
|
-
this.outputService.debug(`Env file "${p}" not found`);
|
|
159
|
-
}
|
|
160
|
+
const envPath = resolve((p && p.trim()) || '.env');
|
|
161
|
+
if (existsSync(envPath)) {
|
|
162
|
+
process.loadEnvFile(envPath);
|
|
160
163
|
} else {
|
|
161
|
-
|
|
164
|
+
this.outputService.error(`Env file "${envPath}" not found`);
|
|
162
165
|
}
|
|
163
166
|
}
|
|
164
167
|
}
|
|
168
|
+
|
|
165
169
|
/**
|
|
166
|
-
*
|
|
170
|
+
* Initialize plugins
|
|
167
171
|
*/
|
|
168
|
-
private async
|
|
169
|
-
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
+
private async initPlugins(): Promise<void> {
|
|
173
|
+
const plugins = this.pluginService.getAllPlugins();
|
|
174
|
+
for (const plugin of plugins) {
|
|
175
|
+
const commands = plugin.app?.commands;
|
|
176
|
+
if (commands) {
|
|
177
|
+
for (const command of commands) {
|
|
178
|
+
this.commandService.registerCommand(
|
|
179
|
+
command,
|
|
180
|
+
this.program,
|
|
181
|
+
plugin.name
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
await this.pluginService.runHook('app.afterInit', {
|
|
187
|
+
profilePath: this.configService.get('profile').path,
|
|
172
188
|
});
|
|
173
|
-
return packageJson.default.version;
|
|
174
189
|
}
|
|
175
190
|
}
|