commandmate 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/.env.example +64 -0
  2. package/LICENSE +21 -0
  3. package/README.md +148 -0
  4. package/bin/commandmate.js +7 -0
  5. package/dist/cli/commands/init.d.ts +11 -0
  6. package/dist/cli/commands/init.d.ts.map +1 -0
  7. package/dist/cli/commands/init.js +126 -0
  8. package/dist/cli/commands/start.d.ts +11 -0
  9. package/dist/cli/commands/start.d.ts.map +1 -0
  10. package/dist/cli/commands/start.js +117 -0
  11. package/dist/cli/commands/status.d.ts +10 -0
  12. package/dist/cli/commands/status.d.ts.map +1 -0
  13. package/dist/cli/commands/status.js +55 -0
  14. package/dist/cli/commands/stop.d.ts +11 -0
  15. package/dist/cli/commands/stop.d.ts.map +1 -0
  16. package/dist/cli/commands/stop.js +82 -0
  17. package/dist/cli/config/cli-dependencies.d.ts +23 -0
  18. package/dist/cli/config/cli-dependencies.d.ts.map +1 -0
  19. package/dist/cli/config/cli-dependencies.js +65 -0
  20. package/dist/cli/index.d.ts +6 -0
  21. package/dist/cli/index.d.ts.map +1 -0
  22. package/dist/cli/index.js +64 -0
  23. package/dist/cli/types/index.d.ts +124 -0
  24. package/dist/cli/types/index.d.ts.map +1 -0
  25. package/dist/cli/types/index.js +20 -0
  26. package/dist/cli/utils/daemon.d.ts +39 -0
  27. package/dist/cli/utils/daemon.d.ts.map +1 -0
  28. package/dist/cli/utils/daemon.js +141 -0
  29. package/dist/cli/utils/env-setup.d.ts +62 -0
  30. package/dist/cli/utils/env-setup.d.ts.map +1 -0
  31. package/dist/cli/utils/env-setup.js +157 -0
  32. package/dist/cli/utils/logger.d.ts +48 -0
  33. package/dist/cli/utils/logger.d.ts.map +1 -0
  34. package/dist/cli/utils/logger.js +99 -0
  35. package/dist/cli/utils/pid-manager.d.ts +42 -0
  36. package/dist/cli/utils/pid-manager.d.ts.map +1 -0
  37. package/dist/cli/utils/pid-manager.js +111 -0
  38. package/dist/cli/utils/preflight.d.ts +34 -0
  39. package/dist/cli/utils/preflight.d.ts.map +1 -0
  40. package/dist/cli/utils/preflight.js +129 -0
  41. package/dist/cli/utils/security-logger.d.ts +29 -0
  42. package/dist/cli/utils/security-logger.d.ts.map +1 -0
  43. package/dist/cli/utils/security-logger.js +53 -0
  44. package/package.json +78 -0
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ /**
3
+ * Stop Command
4
+ * Issue #96: npm install CLI support
5
+ * Stop CommandMate server
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.stopCommand = stopCommand;
9
+ const path_1 = require("path");
10
+ const types_1 = require("../types");
11
+ const logger_1 = require("../utils/logger");
12
+ const daemon_1 = require("../utils/daemon");
13
+ const security_logger_1 = require("../utils/security-logger");
14
+ const logger = new logger_1.CLILogger();
15
+ const PID_FILE = (0, path_1.join)(process.cwd(), '.commandmate.pid');
16
+ /**
17
+ * Execute stop command
18
+ */
19
+ async function stopCommand(options) {
20
+ try {
21
+ const daemonManager = new daemon_1.DaemonManager(PID_FILE);
22
+ // Check if running
23
+ if (!(await daemonManager.isRunning())) {
24
+ const status = await daemonManager.getStatus();
25
+ if (status === null) {
26
+ logger.info('Server is not running (no PID file found)');
27
+ logger.info('Status: Stopped');
28
+ }
29
+ else {
30
+ logger.info('Server is not running (stale PID file)');
31
+ }
32
+ process.exit(types_1.ExitCode.SUCCESS);
33
+ return;
34
+ }
35
+ const status = await daemonManager.getStatus();
36
+ const pid = status?.pid;
37
+ if (options.force) {
38
+ logger.warn(`Force stopping server (PID: ${pid})...`);
39
+ (0, security_logger_1.logSecurityEvent)({
40
+ timestamp: new Date().toISOString(),
41
+ command: 'stop',
42
+ action: 'warning',
43
+ details: `--force flag used (SIGKILL) on PID ${pid}`,
44
+ });
45
+ }
46
+ else {
47
+ logger.info(`Stopping server (PID: ${pid})...`);
48
+ }
49
+ const result = await daemonManager.stop(options.force);
50
+ if (result) {
51
+ logger.success('Server stopped');
52
+ (0, security_logger_1.logSecurityEvent)({
53
+ timestamp: new Date().toISOString(),
54
+ command: 'stop',
55
+ action: 'success',
56
+ details: `Server stopped (PID: ${pid})`,
57
+ });
58
+ process.exit(types_1.ExitCode.SUCCESS);
59
+ }
60
+ else {
61
+ logger.error('Failed to stop server');
62
+ (0, security_logger_1.logSecurityEvent)({
63
+ timestamp: new Date().toISOString(),
64
+ command: 'stop',
65
+ action: 'failure',
66
+ details: `Failed to stop PID ${pid}`,
67
+ });
68
+ process.exit(types_1.ExitCode.STOP_FAILED);
69
+ }
70
+ }
71
+ catch (error) {
72
+ const message = error instanceof Error ? error.message : String(error);
73
+ logger.error(`Stop failed: ${message}`);
74
+ (0, security_logger_1.logSecurityEvent)({
75
+ timestamp: new Date().toISOString(),
76
+ command: 'stop',
77
+ action: 'failure',
78
+ details: message,
79
+ });
80
+ process.exit(types_1.ExitCode.UNEXPECTED_ERROR);
81
+ }
82
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * CLI Dependencies Configuration
3
+ * Issue #96: npm install CLI support
4
+ * SF-2: OCP - External configuration for extensibility
5
+ */
6
+ import { DependencyCheck } from '../types';
7
+ /**
8
+ * System dependencies required by CommandMate
9
+ */
10
+ export declare const DEPENDENCIES: DependencyCheck[];
11
+ /**
12
+ * Get all dependencies
13
+ */
14
+ export declare function getDependencies(): DependencyCheck[];
15
+ /**
16
+ * Get only required dependencies
17
+ */
18
+ export declare function getRequiredDependencies(): DependencyCheck[];
19
+ /**
20
+ * Get only optional dependencies
21
+ */
22
+ export declare function getOptionalDependencies(): DependencyCheck[];
23
+ //# sourceMappingURL=cli-dependencies.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-dependencies.d.ts","sourceRoot":"","sources":["../../../src/cli/config/cli-dependencies.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAE3C;;GAEG;AACH,eAAO,MAAM,YAAY,EAAE,eAAe,EAgCzC,CAAC;AAEF;;GAEG;AACH,wBAAgB,eAAe,IAAI,eAAe,EAAE,CAEnD;AAED;;GAEG;AACH,wBAAgB,uBAAuB,IAAI,eAAe,EAAE,CAE3D;AAED;;GAEG;AACH,wBAAgB,uBAAuB,IAAI,eAAe,EAAE,CAE3D"}
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ /**
3
+ * CLI Dependencies Configuration
4
+ * Issue #96: npm install CLI support
5
+ * SF-2: OCP - External configuration for extensibility
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.DEPENDENCIES = void 0;
9
+ exports.getDependencies = getDependencies;
10
+ exports.getRequiredDependencies = getRequiredDependencies;
11
+ exports.getOptionalDependencies = getOptionalDependencies;
12
+ /**
13
+ * System dependencies required by CommandMate
14
+ */
15
+ exports.DEPENDENCIES = [
16
+ {
17
+ name: 'Node.js',
18
+ command: 'node',
19
+ versionArg: '-v',
20
+ required: true,
21
+ minVersion: '20.0.0',
22
+ },
23
+ {
24
+ name: 'npm',
25
+ command: 'npm',
26
+ versionArg: '-v',
27
+ required: true,
28
+ },
29
+ {
30
+ name: 'tmux',
31
+ command: 'tmux',
32
+ versionArg: '-V',
33
+ required: true,
34
+ },
35
+ {
36
+ name: 'git',
37
+ command: 'git',
38
+ versionArg: '--version',
39
+ required: true,
40
+ },
41
+ {
42
+ name: 'Claude CLI',
43
+ command: 'claude',
44
+ versionArg: '--version',
45
+ required: false,
46
+ },
47
+ ];
48
+ /**
49
+ * Get all dependencies
50
+ */
51
+ function getDependencies() {
52
+ return [...exports.DEPENDENCIES];
53
+ }
54
+ /**
55
+ * Get only required dependencies
56
+ */
57
+ function getRequiredDependencies() {
58
+ return exports.DEPENDENCIES.filter(d => d.required);
59
+ }
60
+ /**
61
+ * Get only optional dependencies
62
+ */
63
+ function getOptionalDependencies() {
64
+ return exports.DEPENDENCIES.filter(d => !d.required);
65
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * CommandMate CLI Entry Point
3
+ * Issue #96: npm install CLI support
4
+ */
5
+ export {};
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ /**
3
+ * CommandMate CLI Entry Point
4
+ * Issue #96: npm install CLI support
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ const commander_1 = require("commander");
8
+ const init_1 = require("./commands/init");
9
+ const start_1 = require("./commands/start");
10
+ const stop_1 = require("./commands/stop");
11
+ const status_1 = require("./commands/status");
12
+ // Read version from package.json
13
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
14
+ const pkg = require('../../package.json');
15
+ const program = new commander_1.Command();
16
+ program
17
+ .name('commandmate')
18
+ .description('Git worktree management with Claude CLI and tmux sessions')
19
+ .version(pkg.version);
20
+ // Init command
21
+ program
22
+ .command('init')
23
+ .description('Initialize CommandMate configuration')
24
+ .option('-d, --defaults', 'Use default values (non-interactive)')
25
+ .option('-f, --force', 'Overwrite existing configuration')
26
+ .action(async (options) => {
27
+ await (0, init_1.initCommand)({
28
+ defaults: options.defaults,
29
+ force: options.force,
30
+ });
31
+ });
32
+ // Start command
33
+ program
34
+ .command('start')
35
+ .description('Start the CommandMate server')
36
+ .option('--dev', 'Start in development mode')
37
+ .option('--daemon', 'Run in background')
38
+ .option('-p, --port <number>', 'Override port number', parseInt)
39
+ .action(async (options) => {
40
+ await (0, start_1.startCommand)({
41
+ dev: options.dev,
42
+ daemon: options.daemon,
43
+ port: options.port,
44
+ });
45
+ });
46
+ // Stop command
47
+ program
48
+ .command('stop')
49
+ .description('Stop the CommandMate server')
50
+ .option('-f, --force', 'Force stop (SIGKILL)')
51
+ .action(async (options) => {
52
+ await (0, stop_1.stopCommand)({
53
+ force: options.force,
54
+ });
55
+ });
56
+ // Status command
57
+ program
58
+ .command('status')
59
+ .description('Show server status')
60
+ .action(async () => {
61
+ await (0, status_1.statusCommand)();
62
+ });
63
+ // Parse and execute
64
+ program.parse();
@@ -0,0 +1,124 @@
1
+ /**
2
+ * CLI Common Type Definitions
3
+ * Issue #96: npm install CLI support
4
+ */
5
+ /**
6
+ * Exit codes for CLI commands
7
+ * NTH-4: DRY - centralized exit code definitions
8
+ */
9
+ export declare enum ExitCode {
10
+ SUCCESS = 0,
11
+ DEPENDENCY_ERROR = 1,
12
+ CONFIG_ERROR = 2,
13
+ START_FAILED = 3,
14
+ STOP_FAILED = 4,
15
+ UNEXPECTED_ERROR = 99
16
+ }
17
+ /**
18
+ * Options for init command
19
+ */
20
+ export interface InitOptions {
21
+ /** Use default values (non-interactive) */
22
+ defaults?: boolean;
23
+ /** Overwrite existing configuration */
24
+ force?: boolean;
25
+ }
26
+ /**
27
+ * Options for start command
28
+ */
29
+ export interface StartOptions {
30
+ /** Start in development mode */
31
+ dev?: boolean;
32
+ /** Run in background */
33
+ daemon?: boolean;
34
+ /** Override port number */
35
+ port?: number;
36
+ }
37
+ /**
38
+ * Options for stop command
39
+ */
40
+ export interface StopOptions {
41
+ /** Force stop (SIGKILL) */
42
+ force?: boolean;
43
+ }
44
+ /**
45
+ * Daemon process status
46
+ */
47
+ export interface DaemonStatus {
48
+ /** Whether the daemon is running */
49
+ running: boolean;
50
+ /** Process ID (if running) */
51
+ pid?: number;
52
+ /** Port number (if running) */
53
+ port?: number;
54
+ /** Uptime in seconds (if running) */
55
+ uptime?: number;
56
+ /** URL to access the server (if running) */
57
+ url?: string;
58
+ }
59
+ /**
60
+ * System dependency definition
61
+ * SF-2: OCP - external configuration for extensibility
62
+ */
63
+ export interface DependencyCheck {
64
+ /** Display name */
65
+ name: string;
66
+ /** Command to check */
67
+ command: string;
68
+ /** Argument to get version */
69
+ versionArg: string;
70
+ /** Whether this dependency is required */
71
+ required: boolean;
72
+ /** Minimum version (optional) */
73
+ minVersion?: string;
74
+ }
75
+ /**
76
+ * Result of a single dependency check
77
+ */
78
+ export interface DependencyStatus {
79
+ /** Dependency name */
80
+ name: string;
81
+ /** Check status */
82
+ status: 'ok' | 'missing' | 'version_mismatch';
83
+ /** Detected version (if available) */
84
+ version?: string;
85
+ }
86
+ /**
87
+ * Result of preflight checks
88
+ */
89
+ export interface PreflightResult {
90
+ /** Whether all required dependencies are satisfied */
91
+ success: boolean;
92
+ /** Individual dependency results */
93
+ results: DependencyStatus[];
94
+ }
95
+ /**
96
+ * Environment configuration for CLI
97
+ * Used by env-setup.ts for .env file generation
98
+ */
99
+ export interface EnvConfig {
100
+ CM_ROOT_DIR: string;
101
+ CM_PORT: number;
102
+ CM_BIND: string;
103
+ CM_AUTH_TOKEN?: string;
104
+ CM_DB_PATH: string;
105
+ CM_LOG_LEVEL: string;
106
+ CM_LOG_FORMAT: string;
107
+ }
108
+ /**
109
+ * Options for env file creation
110
+ */
111
+ export interface EnvSetupOptions {
112
+ /** Force overwrite existing file */
113
+ force?: boolean;
114
+ /** Path to .env file (defaults to .env in cwd) */
115
+ envPath?: string;
116
+ }
117
+ /**
118
+ * Validation result
119
+ */
120
+ export interface ValidationResult {
121
+ valid: boolean;
122
+ errors: string[];
123
+ }
124
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/cli/types/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;GAGG;AACH,oBAAY,QAAQ;IAClB,OAAO,IAAI;IACX,gBAAgB,IAAI;IACpB,YAAY,IAAI;IAChB,YAAY,IAAI;IAChB,WAAW,IAAI;IACf,gBAAgB,KAAK;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,uCAAuC;IACvC,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,gCAAgC;IAChC,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,wBAAwB;IACxB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,2BAA2B;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,2BAA2B;IAC3B,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,oCAAoC;IACpC,OAAO,EAAE,OAAO,CAAC;IACjB,8BAA8B;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,+BAA+B;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,qCAAqC;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4CAA4C;IAC5C,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,mBAAmB;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,uBAAuB;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,8BAA8B;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,0CAA0C;IAC1C,QAAQ,EAAE,OAAO,CAAC;IAClB,iCAAiC;IACjC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,sBAAsB;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,mBAAmB;IACnB,MAAM,EAAE,IAAI,GAAG,SAAS,GAAG,kBAAkB,CAAC;IAC9C,sCAAsC;IACtC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,sDAAsD;IACtD,OAAO,EAAE,OAAO,CAAC;IACjB,oCAAoC;IACpC,OAAO,EAAE,gBAAgB,EAAE,CAAC;CAC7B;AAED;;;GAGG;AACH,MAAM,WAAW,SAAS;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,oCAAoC;IACpC,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,kDAAkD;IAClD,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB"}
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ /**
3
+ * CLI Common Type Definitions
4
+ * Issue #96: npm install CLI support
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.ExitCode = void 0;
8
+ /**
9
+ * Exit codes for CLI commands
10
+ * NTH-4: DRY - centralized exit code definitions
11
+ */
12
+ var ExitCode;
13
+ (function (ExitCode) {
14
+ ExitCode[ExitCode["SUCCESS"] = 0] = "SUCCESS";
15
+ ExitCode[ExitCode["DEPENDENCY_ERROR"] = 1] = "DEPENDENCY_ERROR";
16
+ ExitCode[ExitCode["CONFIG_ERROR"] = 2] = "CONFIG_ERROR";
17
+ ExitCode[ExitCode["START_FAILED"] = 3] = "START_FAILED";
18
+ ExitCode[ExitCode["STOP_FAILED"] = 4] = "STOP_FAILED";
19
+ ExitCode[ExitCode["UNEXPECTED_ERROR"] = 99] = "UNEXPECTED_ERROR";
20
+ })(ExitCode || (exports.ExitCode = ExitCode = {}));
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Daemon Process Manager
3
+ * Issue #96: npm install CLI support
4
+ * SF-1: SRP - Process management only (PID handling delegated to PidManager)
5
+ */
6
+ import { DaemonStatus, StartOptions } from '../types';
7
+ /**
8
+ * Daemon manager for background server process
9
+ */
10
+ export declare class DaemonManager {
11
+ private pidManager;
12
+ constructor(pidFilePath: string);
13
+ /**
14
+ * Start daemon process
15
+ * @returns Process ID of the started daemon
16
+ * @throws Error if already running
17
+ */
18
+ start(options: StartOptions): Promise<number>;
19
+ /**
20
+ * Stop daemon process
21
+ * @param force Use SIGKILL instead of SIGTERM
22
+ * @returns true if stopped successfully, false if not running
23
+ */
24
+ stop(force?: boolean): Promise<boolean>;
25
+ /**
26
+ * Get daemon status
27
+ * @returns Status object or null if no PID file
28
+ */
29
+ getStatus(): Promise<DaemonStatus | null>;
30
+ /**
31
+ * Check if daemon is running
32
+ */
33
+ isRunning(): Promise<boolean>;
34
+ /**
35
+ * Wait for process to exit
36
+ */
37
+ private waitForExit;
38
+ }
39
+ //# sourceMappingURL=daemon.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/daemon.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAGtD;;GAEG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,UAAU,CAAa;gBAEnB,WAAW,EAAE,MAAM;IAI/B;;;;OAIG;IACG,KAAK,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IA6CnD;;;;OAIG;IACG,IAAI,CAAC,KAAK,GAAE,OAAe,GAAG,OAAO,CAAC,OAAO,CAAC;IA8BpD;;;OAGG;IACG,SAAS,IAAI,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IA6B/C;;OAEG;IACG,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;IAInC;;OAEG;YACW,WAAW;CAiB1B"}
@@ -0,0 +1,141 @@
1
+ "use strict";
2
+ /**
3
+ * Daemon Process Manager
4
+ * Issue #96: npm install CLI support
5
+ * SF-1: SRP - Process management only (PID handling delegated to PidManager)
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.DaemonManager = void 0;
9
+ const child_process_1 = require("child_process");
10
+ const pid_manager_1 = require("./pid-manager");
11
+ /**
12
+ * Daemon manager for background server process
13
+ */
14
+ class DaemonManager {
15
+ pidManager;
16
+ constructor(pidFilePath) {
17
+ this.pidManager = new pid_manager_1.PidManager(pidFilePath);
18
+ }
19
+ /**
20
+ * Start daemon process
21
+ * @returns Process ID of the started daemon
22
+ * @throws Error if already running
23
+ */
24
+ async start(options) {
25
+ if (this.pidManager.isProcessRunning()) {
26
+ const pid = this.pidManager.readPid();
27
+ throw new Error(`Server is already running (PID: ${pid})`);
28
+ }
29
+ // Clean up stale PID file
30
+ this.pidManager.removePid();
31
+ const npmScript = options.dev ? 'dev' : 'start';
32
+ const cwd = process.cwd();
33
+ // Build environment
34
+ const env = { ...process.env };
35
+ if (options.port) {
36
+ env.CM_PORT = String(options.port);
37
+ }
38
+ // Spawn detached process
39
+ const child = (0, child_process_1.spawn)('npm', ['run', npmScript], {
40
+ cwd,
41
+ env,
42
+ detached: true,
43
+ stdio: 'ignore',
44
+ });
45
+ // Unref to allow parent to exit
46
+ child.unref();
47
+ const pid = child.pid;
48
+ // Write PID file atomically
49
+ if (!this.pidManager.writePid(pid)) {
50
+ // Failed to write PID - another process may have started
51
+ try {
52
+ process.kill(pid, 'SIGTERM');
53
+ }
54
+ catch {
55
+ // Ignore kill errors
56
+ }
57
+ throw new Error('Failed to write PID file - server may already be running');
58
+ }
59
+ return pid;
60
+ }
61
+ /**
62
+ * Stop daemon process
63
+ * @param force Use SIGKILL instead of SIGTERM
64
+ * @returns true if stopped successfully, false if not running
65
+ */
66
+ async stop(force = false) {
67
+ const pid = this.pidManager.readPid();
68
+ if (pid === null) {
69
+ return false;
70
+ }
71
+ if (!this.pidManager.isProcessRunning()) {
72
+ // Process not running - clean up stale PID file
73
+ this.pidManager.removePid();
74
+ return true;
75
+ }
76
+ const signal = force ? 'SIGKILL' : 'SIGTERM';
77
+ try {
78
+ process.kill(pid, signal);
79
+ // Wait for process to exit
80
+ await this.waitForExit(pid, 10000);
81
+ // Clean up PID file
82
+ this.pidManager.removePid();
83
+ return true;
84
+ }
85
+ catch {
86
+ return false;
87
+ }
88
+ }
89
+ /**
90
+ * Get daemon status
91
+ * @returns Status object or null if no PID file
92
+ */
93
+ async getStatus() {
94
+ const pid = this.pidManager.readPid();
95
+ if (pid === null) {
96
+ return null;
97
+ }
98
+ const running = this.pidManager.isProcessRunning();
99
+ if (!running) {
100
+ return { running: false };
101
+ }
102
+ // Get port from environment or default
103
+ const port = parseInt(process.env.CM_PORT || '3000', 10);
104
+ const bind = process.env.CM_BIND || '127.0.0.1';
105
+ const url = `http://${bind === '0.0.0.0' ? '127.0.0.1' : bind}:${port}`;
106
+ // Note: Getting accurate uptime would require storing start time
107
+ // For now, we don't track uptime
108
+ return {
109
+ running: true,
110
+ pid,
111
+ port,
112
+ url,
113
+ };
114
+ }
115
+ /**
116
+ * Check if daemon is running
117
+ */
118
+ async isRunning() {
119
+ return this.pidManager.isProcessRunning();
120
+ }
121
+ /**
122
+ * Wait for process to exit
123
+ */
124
+ async waitForExit(pid, timeout) {
125
+ const startTime = Date.now();
126
+ const checkInterval = 100;
127
+ while (Date.now() - startTime < timeout) {
128
+ try {
129
+ process.kill(pid, 0);
130
+ // Process still exists, wait
131
+ await new Promise(resolve => setTimeout(resolve, checkInterval));
132
+ }
133
+ catch {
134
+ // Process exited
135
+ return;
136
+ }
137
+ }
138
+ // Timeout - process may still be running
139
+ }
140
+ }
141
+ exports.DaemonManager = DaemonManager;
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Environment Setup Utility
3
+ * Issue #96: npm install CLI support
4
+ * Migrated from scripts/setup-env.sh
5
+ */
6
+ import { EnvConfig, EnvSetupOptions, ValidationResult } from '../types';
7
+ /**
8
+ * Default environment configuration values
9
+ * SF-4: DRY - Centralized defaults
10
+ */
11
+ export declare const ENV_DEFAULTS: {
12
+ readonly CM_PORT: 3000;
13
+ readonly CM_BIND: "127.0.0.1";
14
+ readonly CM_DB_PATH: "./data/cm.db";
15
+ readonly CM_LOG_LEVEL: "info";
16
+ readonly CM_LOG_FORMAT: "text";
17
+ };
18
+ /**
19
+ * Sanitize input by removing control characters
20
+ * SF-SEC-3: Input sanitization
21
+ */
22
+ export declare function sanitizeInput(input: string): string;
23
+ /**
24
+ * Sanitize path input
25
+ * SF-SEC-3: Path sanitization
26
+ */
27
+ export declare function sanitizePath(input: string): string;
28
+ /**
29
+ * Validate port number
30
+ * SF-SEC-3: Port validation
31
+ */
32
+ export declare function validatePort(input: string): number;
33
+ /**
34
+ * Escape value for .env file
35
+ * SF-SEC-3: Safe .env value escaping
36
+ */
37
+ export declare function escapeEnvValue(value: string): string;
38
+ /**
39
+ * Environment setup utility
40
+ */
41
+ export declare class EnvSetup {
42
+ private envPath;
43
+ constructor(envPath?: string);
44
+ /**
45
+ * Create .env file
46
+ * SF-SEC-1: Sets file permissions to 0600
47
+ */
48
+ createEnvFile(config: EnvConfig, options?: EnvSetupOptions): Promise<void>;
49
+ /**
50
+ * Backup existing .env file
51
+ */
52
+ backupExisting(): Promise<string | null>;
53
+ /**
54
+ * Generate secure authentication token
55
+ */
56
+ generateAuthToken(): string;
57
+ /**
58
+ * Validate configuration
59
+ */
60
+ validateConfig(config: EnvConfig): ValidationResult;
61
+ }
62
+ //# sourceMappingURL=env-setup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env-setup.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/env-setup.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAUH,OAAO,EACL,SAAS,EACT,eAAe,EACf,gBAAgB,EACjB,MAAM,UAAU,CAAC;AAElB;;;GAGG;AACH,eAAO,MAAM,YAAY;;;;;;CAMf,CAAC;AAEX;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGnD;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGlD;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMlD;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAUpD;AAED;;GAEG;AACH,qBAAa,QAAQ;IACnB,OAAO,CAAC,OAAO,CAAS;gBAEZ,OAAO,CAAC,EAAE,MAAM;IAI5B;;;OAGG;IACG,aAAa,CACjB,MAAM,EAAE,SAAS,EACjB,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,IAAI,CAAC;IAiChB;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAY9C;;OAEG;IACH,iBAAiB,IAAI,MAAM;IAI3B;;OAEG;IACH,cAAc,CAAC,MAAM,EAAE,SAAS,GAAG,gBAAgB;CAoCpD"}