@runium/cli 0.0.3 → 0.0.5

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 (106) hide show
  1. package/{lib/commands → commands}/index.js +0 -0
  2. package/{lib/constants → constants}/index.js +0 -0
  3. package/{lib/macros → macros}/index.js +0 -0
  4. package/package.json +7 -32
  5. package/{lib/services → services}/index.js +0 -0
  6. package/{lib/utils → utils}/index.js +0 -0
  7. package/{lib/validation → validation}/index.js +0 -0
  8. package/.eslintrc.json +0 -31
  9. package/.prettierrc.json +0 -10
  10. package/README.md +0 -3
  11. package/build.js +0 -125
  12. package/lib/package.json +0 -22
  13. package/src/app.ts +0 -190
  14. package/src/commands/index.ts +0 -2
  15. package/src/commands/plugin/plugin-add.ts +0 -48
  16. package/src/commands/plugin/plugin-command.ts +0 -36
  17. package/src/commands/plugin/plugin-disable.ts +0 -46
  18. package/src/commands/plugin/plugin-enable.ts +0 -50
  19. package/src/commands/plugin/plugin-list.ts +0 -61
  20. package/src/commands/plugin/plugin-remove.ts +0 -42
  21. package/src/commands/plugin/plugin.ts +0 -36
  22. package/src/commands/project/project-add.ts +0 -64
  23. package/src/commands/project/project-command.ts +0 -43
  24. package/src/commands/project/project-list.ts +0 -32
  25. package/src/commands/project/project-remove.ts +0 -41
  26. package/src/commands/project/project-start.ts +0 -158
  27. package/src/commands/project/project-state-command.ts +0 -53
  28. package/src/commands/project/project-status.ts +0 -116
  29. package/src/commands/project/project-stop.ts +0 -59
  30. package/src/commands/project/project-validate.ts +0 -56
  31. package/src/commands/project/project.ts +0 -40
  32. package/src/commands/runium-command.ts +0 -52
  33. package/src/constants/error-code.ts +0 -28
  34. package/src/constants/index.ts +0 -1
  35. package/src/global.d.ts +0 -6
  36. package/src/index.ts +0 -24
  37. package/src/macros/conditional.ts +0 -31
  38. package/src/macros/date.ts +0 -15
  39. package/src/macros/empty.ts +0 -6
  40. package/src/macros/env.ts +0 -8
  41. package/src/macros/index.ts +0 -17
  42. package/src/macros/path.ts +0 -24
  43. package/src/services/command.ts +0 -171
  44. package/src/services/config.ts +0 -119
  45. package/src/services/file.ts +0 -272
  46. package/src/services/index.ts +0 -9
  47. package/src/services/output.ts +0 -205
  48. package/src/services/plugin-context.ts +0 -140
  49. package/src/services/plugin.ts +0 -248
  50. package/src/services/profile.ts +0 -199
  51. package/src/services/project.ts +0 -142
  52. package/src/services/shutdown.ts +0 -147
  53. package/src/utils/convert-path-to-valid-file-name.ts +0 -39
  54. package/src/utils/debounce.ts +0 -23
  55. package/src/utils/format-timestamp.ts +0 -17
  56. package/src/utils/get-version.ts +0 -13
  57. package/src/utils/index.ts +0 -4
  58. package/src/validation/create-validator.ts +0 -27
  59. package/src/validation/get-config-schema.ts +0 -59
  60. package/src/validation/get-error-messages.ts +0 -35
  61. package/src/validation/get-plugin-schema.ts +0 -137
  62. package/src/validation/index.ts +0 -4
  63. package/tsconfig.json +0 -38
  64. /package/{lib/app.js → app.js} +0 -0
  65. /package/{lib/commands → commands}/plugin/plugin-add.js +0 -0
  66. /package/{lib/commands → commands}/plugin/plugin-command.js +0 -0
  67. /package/{lib/commands → commands}/plugin/plugin-disable.js +0 -0
  68. /package/{lib/commands → commands}/plugin/plugin-enable.js +0 -0
  69. /package/{lib/commands → commands}/plugin/plugin-list.js +0 -0
  70. /package/{lib/commands → commands}/plugin/plugin-remove.js +0 -0
  71. /package/{lib/commands → commands}/plugin/plugin.js +0 -0
  72. /package/{lib/commands → commands}/project/project-add.js +0 -0
  73. /package/{lib/commands → commands}/project/project-command.js +0 -0
  74. /package/{lib/commands → commands}/project/project-list.js +0 -0
  75. /package/{lib/commands → commands}/project/project-remove.js +0 -0
  76. /package/{lib/commands → commands}/project/project-start.js +0 -0
  77. /package/{lib/commands → commands}/project/project-state-command.js +0 -0
  78. /package/{lib/commands → commands}/project/project-status.js +0 -0
  79. /package/{lib/commands → commands}/project/project-stop.js +0 -0
  80. /package/{lib/commands → commands}/project/project-validate.js +0 -0
  81. /package/{lib/commands → commands}/project/project.js +0 -0
  82. /package/{lib/commands → commands}/runium-command.js +0 -0
  83. /package/{lib/constants → constants}/error-code.js +0 -0
  84. /package/{lib/index.js → index.js} +0 -0
  85. /package/{lib/macros → macros}/conditional.js +0 -0
  86. /package/{lib/macros → macros}/date.js +0 -0
  87. /package/{lib/macros → macros}/empty.js +0 -0
  88. /package/{lib/macros → macros}/env.js +0 -0
  89. /package/{lib/macros → macros}/path.js +0 -0
  90. /package/{lib/services → services}/command.js +0 -0
  91. /package/{lib/services → services}/config.js +0 -0
  92. /package/{lib/services → services}/file.js +0 -0
  93. /package/{lib/services → services}/output.js +0 -0
  94. /package/{lib/services → services}/plugin-context.js +0 -0
  95. /package/{lib/services → services}/plugin.js +0 -0
  96. /package/{lib/services → services}/profile.js +0 -0
  97. /package/{lib/services → services}/project.js +0 -0
  98. /package/{lib/services → services}/shutdown.js +0 -0
  99. /package/{lib/utils → utils}/convert-path-to-valid-file-name.js +0 -0
  100. /package/{lib/utils → utils}/debounce.js +0 -0
  101. /package/{lib/utils → utils}/format-timestamp.js +0 -0
  102. /package/{lib/utils → utils}/get-version.js +0 -0
  103. /package/{lib/validation → validation}/create-validator.js +0 -0
  104. /package/{lib/validation → validation}/get-config-schema.js +0 -0
  105. /package/{lib/validation → validation}/get-error-messages.js +0 -0
  106. /package/{lib/validation → validation}/get-plugin-schema.js +0 -0
@@ -1,61 +0,0 @@
1
- import { Option } from 'commander';
2
- import { PluginCommand } from './plugin-command.js';
3
-
4
- /**
5
- * Plugin list command
6
- */
7
- export class PluginListCommand extends PluginCommand {
8
- /**
9
- * Config command
10
- */
11
- protected config(): void {
12
- this.command
13
- .name('list')
14
- .addOption(
15
- new Option('-d, --disabled', 'show only disabled plugins').conflicts(
16
- 'enabled'
17
- )
18
- )
19
- .addOption(
20
- new Option('-e, --enabled', 'show only enabled plugins').conflicts(
21
- 'disabled'
22
- )
23
- )
24
- .option('-s, --sort', 'sort by name')
25
- .description('list plugins');
26
- }
27
-
28
- /**
29
- * Handle command
30
- */
31
- protected async handle({
32
- disabled,
33
- enabled,
34
- sort,
35
- }: {
36
- disabled: boolean;
37
- enabled: boolean;
38
- sort: boolean;
39
- }): Promise<void> {
40
- let plugins = this.profileService.getPlugins();
41
- if (disabled) {
42
- plugins = plugins.filter(p => p.disabled === true);
43
- }
44
- if (enabled) {
45
- plugins = plugins.filter(p => p.disabled === false);
46
- }
47
- if (sort) {
48
- plugins.sort((a, b) => a.name.localeCompare(b.name));
49
- }
50
-
51
- if (plugins.length !== 0) {
52
- this.outputService.table(plugins, [
53
- 'name',
54
- 'path',
55
- ...(disabled || enabled ? [] : ['disabled']),
56
- ]);
57
- } else {
58
- this.outputService.warn('No plugins found');
59
- }
60
- }
61
- }
@@ -1,42 +0,0 @@
1
- import { PluginCommand } from './plugin-command.js';
2
-
3
- /**
4
- * Plugin remove command
5
- */
6
- export class PluginRemoveCommand extends PluginCommand {
7
- /**
8
- * Config command
9
- */
10
- protected config(): void {
11
- this.command
12
- .name('remove')
13
- .description('remove plugin')
14
- .option('-a, --all', 'remove all plugins')
15
- .argument('[name...]', 'plugin names');
16
- }
17
-
18
- /**
19
- * Handle command
20
- * @param names
21
- * @param all
22
- */
23
- protected async handle(
24
- names: string[],
25
- { all }: { all: boolean }
26
- ): Promise<void> {
27
- if (names.length === 0 && !all) {
28
- this.outputService.warn('No plugins specified to remove');
29
- return;
30
- }
31
- if (all) {
32
- names = this.profileService.getPlugins().map(p => p.name);
33
- }
34
-
35
- for (const name of names) {
36
- this.ensureProfilePlugin(name);
37
- await this.profileService.removePlugin(name);
38
- await this.pluginService.unloadPlugin(name);
39
- this.outputService.success(`Plugin "%s" successfully removed`, name);
40
- }
41
- }
42
- }
@@ -1,36 +0,0 @@
1
- import { RuniumCommand } from '@commands/runium-command.js';
2
- import { PluginAddCommand } from './plugin-add.js';
3
- import { PluginDisableCommand } from './plugin-disable.js';
4
- import { PluginEnableCommand } from './plugin-enable.js';
5
- import { PluginListCommand } from './plugin-list.js';
6
- import { PluginRemoveCommand } from './plugin-remove.js';
7
-
8
- /**
9
- * Plugin group command
10
- */
11
- export class PluginCommand extends RuniumCommand {
12
- /**
13
- * Subcommands
14
- */
15
- subcommands = [
16
- PluginListCommand,
17
- PluginAddCommand,
18
- PluginRemoveCommand,
19
- PluginDisableCommand,
20
- PluginEnableCommand,
21
- ];
22
-
23
- /**
24
- * Config command
25
- */
26
- protected config(): void {
27
- this.command.name('plugin').description('manage plugins');
28
- }
29
-
30
- /**
31
- * Handle command
32
- */
33
- protected async handle(): Promise<void> {
34
- this.command.help();
35
- }
36
- }
@@ -1,64 +0,0 @@
1
- import { ID_REGEX, RuniumError } from '@runium/core';
2
- import { ErrorCode } from '@constants';
3
- import { ProjectCommand } from './project-command.js';
4
-
5
- /**
6
- * Project add command
7
- */
8
- export class ProjectAddCommand extends ProjectCommand {
9
- /**
10
- * Config command
11
- */
12
- protected config(): void {
13
- this.command
14
- .name('add')
15
- .description('add project')
16
- .argument('<path>', 'project file path')
17
- .argument('[name]', 'project name (default: project config id)');
18
- }
19
-
20
- /**
21
- * Handle command
22
- * @param path
23
- * @param name
24
- */
25
- protected async handle(path: string, name?: string): Promise<void> {
26
- const projectPath = this.projectService.resolvePath(path);
27
- const project = await this.projectService.initProject(projectPath);
28
- if (project) {
29
- const projectName = name ?? project.getConfig().id;
30
-
31
- this.validateProjectName(projectName);
32
-
33
- await this.profileService.addProject({
34
- name: projectName,
35
- path: projectPath,
36
- });
37
- this.outputService.success(
38
- `Project "%s" successfully added`,
39
- projectName
40
- );
41
- } else {
42
- throw new RuniumError(
43
- `Failed to add project "${projectPath}"`,
44
- ErrorCode.PROJECT_NOT_FOUND,
45
- { path: projectPath }
46
- );
47
- }
48
- }
49
-
50
- /**
51
- * Validate project name
52
- * @param name - project name to validate
53
- * @throws RuniumError if name contains invalid characters
54
- */
55
- private validateProjectName(name: string): void {
56
- if (!ID_REGEX.test(name)) {
57
- throw new RuniumError(
58
- `Invalid project name "${name}". Only letters, digits, underscores (_), and hyphens (-) are allowed.`,
59
- ErrorCode.INVALID_ARGUMENT,
60
- { name }
61
- );
62
- }
63
- }
64
- }
@@ -1,43 +0,0 @@
1
- import { Command } from 'commander';
2
- import { Container } from 'typedi';
3
- import { RuniumError } from '@runium/core';
4
- import { RuniumCommand } from '@commands/runium-command.js';
5
- import { ErrorCode } from '@constants';
6
- import {
7
- ProfileService,
8
- ProjectService,
9
- ProfileProject,
10
- FileService,
11
- } from '@services';
12
-
13
- /**
14
- * Base project command
15
- */
16
- export abstract class ProjectCommand extends RuniumCommand {
17
- protected projectService: ProjectService;
18
- protected profileService: ProfileService;
19
- protected fileService: FileService;
20
-
21
- constructor(parent: Command) {
22
- super(parent);
23
- this.projectService = Container.get(ProjectService);
24
- this.profileService = Container.get(ProfileService);
25
- this.fileService = Container.get(FileService);
26
- }
27
-
28
- /**
29
- * Ensure project exists
30
- * @param name
31
- */
32
- protected ensureProfileProject(name: string): ProfileProject {
33
- const project = this.profileService.getProjectByName(name);
34
- if (!project) {
35
- throw new RuniumError(
36
- `Project "${name}" not found`,
37
- ErrorCode.PROJECT_NOT_FOUND,
38
- { name }
39
- );
40
- }
41
- return project;
42
- }
43
- }
@@ -1,32 +0,0 @@
1
- import { ProjectCommand } from './project-command.js';
2
-
3
- /**
4
- * Project list command
5
- */
6
- export class ProjectListCommand extends ProjectCommand {
7
- /**
8
- * Config command
9
- */
10
- protected config(): void {
11
- this.command
12
- .name('list')
13
- .description('list projects')
14
- .option('-s, --sort', 'sort by name');
15
- }
16
-
17
- /**
18
- * Handle command
19
- */
20
- protected async handle({ sort }: { sort: boolean }): Promise<void> {
21
- const projects = this.profileService.getProjects();
22
- if (sort) {
23
- projects.sort((a, b) => a.name.localeCompare(b.name));
24
- }
25
- // handle empty list
26
- if (projects.length !== 0) {
27
- this.outputService.table(projects, ['name', 'path']);
28
- } else {
29
- this.outputService.warn('No projects found');
30
- }
31
- }
32
- }
@@ -1,41 +0,0 @@
1
- import { ProjectCommand } from './project-command.js';
2
-
3
- /**
4
- * Project remove command
5
- */
6
- export class ProjectRemoveCommand extends ProjectCommand {
7
- /**
8
- * Config command
9
- */
10
- protected config(): void {
11
- this.command
12
- .name('remove')
13
- .description('remove project')
14
- .option('-a, --all', 'remove all projects')
15
- .argument('[name...]', 'project names');
16
- }
17
-
18
- /**
19
- * Handle command
20
- * @param names
21
- * @param all
22
- */
23
- protected async handle(
24
- names: string[],
25
- { all }: { all: boolean }
26
- ): Promise<void> {
27
- if (names.length === 0 && !all) {
28
- this.outputService.warn('No projects specified to remove');
29
- return;
30
- }
31
- if (all) {
32
- names = this.profileService.getProjects().map(p => p.name);
33
- }
34
-
35
- for (const name of names) {
36
- this.ensureProfileProject(name);
37
- await this.profileService.removeProject(name);
38
- this.outputService.success(`Project "%s" successfully removed`, name);
39
- }
40
- }
41
- }
@@ -1,158 +0,0 @@
1
- import { dirname } from 'node:path';
2
- import { Command, Option } from 'commander';
3
- import { Container } from 'typedi';
4
- import {
5
- Project,
6
- ProjectEvent,
7
- ProjectState,
8
- RuniumError,
9
- TaskState,
10
- } from '@runium/core';
11
- import { ErrorCode } from '@constants';
12
- import { AtomicWriter, ShutdownService } from '@services';
13
- import { ProjectData, ProjectStateCommand } from './project-state-command.js';
14
-
15
- /**
16
- * Project start command
17
- */
18
- export class ProjectStartCommand extends ProjectStateCommand {
19
- protected shutdownService: ShutdownService;
20
- protected fileWriter: AtomicWriter | null = null;
21
-
22
- constructor(parent: Command) {
23
- super(parent);
24
- this.shutdownService = Container.get(ShutdownService);
25
- }
26
-
27
- /**
28
- * Config command
29
- */
30
- protected config(): void {
31
- this.command
32
- .name('start')
33
- .description('start project')
34
- .option('-f, --file', 'use file path instead of project name')
35
- .option('-o, --output', 'output project state changes')
36
- .addOption(
37
- new Option('-w, --working-dir <choice>', 'set working directory')
38
- .choices(['cwd', 'project'])
39
- .default('cwd')
40
- )
41
- .argument('<name>', 'project name');
42
- }
43
-
44
- /**
45
- * Handle command
46
- * @param name
47
- * @param file
48
- * @param workingDir
49
- * @param output
50
- */
51
- protected async handle(
52
- name: string,
53
- {
54
- file,
55
- workingDir,
56
- output,
57
- }: { file: boolean; workingDir?: string; output?: boolean }
58
- ): Promise<void> {
59
- const path = file
60
- ? this.projectService.resolvePath(name)
61
- : this.ensureProfileProject(name).path;
62
-
63
- const projectDataFileName = this.getProjectDataFileName(file ? path : name);
64
- const projectDataFilePath = this.profileService.getPath(
65
- 'projects',
66
- projectDataFileName
67
- );
68
-
69
- // check if project is started
70
- const projectData = await this.readProjectData(projectDataFilePath);
71
- if (projectData && this.isProjectProcessStarted(projectData.pid)) {
72
- throw new RuniumError(
73
- `Project "${name}" is already started`,
74
- ErrorCode.PROJECT_ALREADY_STARTED,
75
- { name }
76
- );
77
- }
78
-
79
- if (workingDir === 'project') {
80
- const projectDir = dirname(path);
81
- if (projectDir !== process.cwd()) {
82
- process.chdir(projectDir);
83
- }
84
- }
85
-
86
- const project = await this.projectService.initProject(path);
87
- this.shutdownService.addBlocker((reason?: string) => project.stop(reason));
88
-
89
- await this.fileService.ensureDirExists(dirname(projectDataFilePath));
90
- this.fileWriter = this.fileService.createAtomicWriter(projectDataFilePath);
91
-
92
- this.addProjectListeners(project, {
93
- projectPath: path,
94
- output,
95
- });
96
-
97
- await this.projectService.runHook('project.beforeStart', project);
98
-
99
- await project.start();
100
- }
101
-
102
- /**
103
- * Add project listeners
104
- * @param project
105
- * @param options
106
- */
107
- protected addProjectListeners(
108
- project: Project,
109
- options: { projectPath: string; output?: boolean }
110
- ): void {
111
- const { projectPath, output } = options;
112
-
113
- const projectData: ProjectData = {
114
- id: project.getConfig().id,
115
- pid: process.pid,
116
- cwd: process.cwd(),
117
- path: projectPath,
118
- state: {
119
- project: [],
120
- tasks: {},
121
- },
122
- };
123
-
124
- const writeProjectData = () => {
125
- this.fileWriter!.writeJson(projectData).then();
126
- };
127
-
128
- // write initial data
129
- writeProjectData();
130
-
131
- project.on(ProjectEvent.STATE_CHANGE, async (state: ProjectState) => {
132
- projectData.state.project.push(state);
133
- writeProjectData();
134
- if (output) {
135
- this.outputService.info('Project %s', state.status);
136
- }
137
- });
138
-
139
- project.on(
140
- ProjectEvent.TASK_STATE_CHANGE,
141
- (taskId: string, state: TaskState) => {
142
- if (!projectData.state.tasks[taskId]) {
143
- projectData.state.tasks[taskId] = [];
144
- }
145
- projectData.state.tasks[taskId].push(state);
146
- writeProjectData();
147
- if (output) {
148
- this.outputService.info(
149
- 'Task %s %s %s',
150
- taskId,
151
- state.status,
152
- state.exitCode || state.error || ''
153
- );
154
- }
155
- }
156
- );
157
- }
158
- }
@@ -1,53 +0,0 @@
1
- import { convertPathToValidFileName } from '@utils';
2
- import { ProjectCommand } from './project-command.js';
3
- import { ProjectState, TaskState } from '@runium/core';
4
-
5
- export interface ProjectData {
6
- id: string;
7
- pid: number;
8
- cwd: string;
9
- path: string;
10
- state: {
11
- project: ProjectState[];
12
- tasks: Record<string, TaskState[]>;
13
- };
14
- }
15
-
16
- /**
17
- * Base project state command
18
- */
19
- export abstract class ProjectStateCommand extends ProjectCommand {
20
- /**
21
- * Get project data file name
22
- * @param name
23
- */
24
- protected getProjectDataFileName(name: string): string {
25
- let fileName = convertPathToValidFileName(name);
26
- if (!fileName.endsWith('.json')) {
27
- fileName = fileName + '.json';
28
- }
29
- return fileName;
30
- }
31
-
32
- /**
33
- * Read project data
34
- * @param path
35
- */
36
- protected async readProjectData(path: string): Promise<ProjectData | null> {
37
- return this.fileService
38
- .readJson(path)
39
- .catch(() => null) as Promise<ProjectData | null>;
40
- }
41
-
42
- /**
43
- * Checks if a project process is started
44
- * @param pid
45
- */
46
- protected isProjectProcessStarted(pid: number): boolean {
47
- try {
48
- return process.kill(Number(pid), 0);
49
- } catch (ex) {
50
- return (ex as { code?: string }).code === 'EPERM';
51
- }
52
- }
53
- }
@@ -1,116 +0,0 @@
1
- import { formatTimestamp } from '@utils';
2
- import { ProjectStateCommand } from './project-state-command.js';
3
-
4
- interface StateRecord {
5
- name: string;
6
- status: string;
7
- time: string;
8
- timestamp: number;
9
- reason?: string;
10
- exitCode?: number;
11
- }
12
-
13
- /**
14
- * Project status command
15
- */
16
- export class ProjectStatusCommand extends ProjectStateCommand {
17
- /**
18
- * Config command
19
- */
20
- protected config(): void {
21
- this.command
22
- .name('status')
23
- .description('get project status')
24
- .option('-f, --file', 'use file path instead of project name')
25
- .option('-t, --tasks', 'show tasks status')
26
- .option('-a, --all', 'show status change history')
27
- .argument('<name>', 'project name');
28
- }
29
-
30
- /**
31
- * Handle command
32
- * @param name
33
- * @param file
34
- * @param tasks
35
- * @param all
36
- */
37
- protected async handle(
38
- name: string,
39
- { file, tasks, all }: { file: boolean; tasks: boolean; all: boolean }
40
- ): Promise<void> {
41
- const path = file
42
- ? this.projectService.resolvePath(name)
43
- : this.ensureProfileProject(name).path;
44
-
45
- const projectDataFileName = this.getProjectDataFileName(file ? path : name);
46
- const projectDataFilePath = this.profileService.getPath(
47
- 'projects',
48
- projectDataFileName
49
- );
50
-
51
- const projectData = await this.readProjectData(projectDataFilePath);
52
- if (projectData) {
53
- let { project: projectState = [] } = projectData.state;
54
- if (!all) {
55
- projectState =
56
- projectState.length > 0
57
- ? [projectState[projectState.length - 1]]
58
- : [];
59
- }
60
-
61
- if (tasks) {
62
- const projectMappedState = projectState.map(state => {
63
- return {
64
- name: 'Project',
65
- status: state.status,
66
- time: formatTimestamp(state.timestamp),
67
- timestamp: state.timestamp,
68
- reason: state.reason || '',
69
- };
70
- });
71
-
72
- const { tasks: tasksState = [] } = projectData.state;
73
-
74
- const tasksMappedState: StateRecord[] = [];
75
- Object.entries(tasksState).forEach(([key, value]) => {
76
- if (!all) {
77
- value = value.length > 0 ? [value[value.length - 1]] : [];
78
- }
79
- value.forEach(task => {
80
- tasksMappedState.push({
81
- name: key,
82
- status: task.status,
83
- time: formatTimestamp(task.timestamp),
84
- timestamp: task.timestamp,
85
- reason: [task.reason, task.exitCode].filter(Boolean).join(' '),
86
- });
87
- });
88
- });
89
-
90
- const mappedState: StateRecord[] = [
91
- ...projectMappedState,
92
- ...tasksMappedState,
93
- ];
94
- mappedState.sort((a, b) => a.timestamp - b.timestamp);
95
-
96
- this.outputService.table(mappedState, [
97
- 'time',
98
- 'name',
99
- 'status',
100
- 'reason',
101
- ]);
102
- } else {
103
- const mappedState = projectState.map(state => {
104
- return {
105
- status: state.status,
106
- time: formatTimestamp(state.timestamp),
107
- };
108
- });
109
-
110
- this.outputService.table(mappedState, ['time', 'status']);
111
- }
112
- } else {
113
- this.outputService.info(`No project status for "${name}"`);
114
- }
115
- }
116
- }