commandmate 0.1.6 → 0.1.8
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/.next/BUILD_ID +1 -1
- package/.next/app-path-routes-manifest.json +1 -1
- package/.next/build-manifest.json +2 -2
- package/.next/cache/.tsbuildinfo +1 -1
- package/.next/cache/config.json +3 -3
- package/.next/cache/webpack/client-production/0.pack +0 -0
- package/.next/cache/webpack/client-production/1.pack +0 -0
- package/.next/cache/webpack/client-production/2.pack +0 -0
- package/.next/cache/webpack/client-production/index.pack +0 -0
- package/.next/cache/webpack/client-production/index.pack.old +0 -0
- package/.next/cache/webpack/edge-server-production/0.pack +0 -0
- package/.next/cache/webpack/edge-server-production/index.pack +0 -0
- package/.next/cache/webpack/server-production/0.pack +0 -0
- package/.next/cache/webpack/server-production/index.pack +0 -0
- package/.next/next-server.js.nft.json +1 -1
- package/.next/prerender-manifest.json +1 -1
- package/.next/server/app/_not-found.html +1 -1
- package/.next/server/app/_not-found.rsc +1 -1
- package/.next/server/app/index.html +1 -1
- package/.next/server/app/index.rsc +1 -1
- package/.next/server/app-paths-manifest.json +7 -7
- package/.next/server/functions-config-manifest.json +1 -1
- package/.next/server/middleware-manifest.json +5 -5
- package/.next/server/pages/404.html +1 -1
- package/.next/server/pages/500.html +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/trace +5 -5
- package/dist/cli/commands/init.d.ts +1 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +143 -27
- package/dist/cli/commands/start.d.ts +2 -0
- package/dist/cli/commands/start.d.ts.map +1 -1
- package/dist/cli/commands/start.js +10 -8
- package/dist/cli/commands/status.d.ts +2 -0
- package/dist/cli/commands/status.d.ts.map +1 -1
- package/dist/cli/commands/status.js +11 -4
- package/dist/cli/commands/stop.d.ts +2 -0
- package/dist/cli/commands/stop.d.ts.map +1 -1
- package/dist/cli/commands/stop.js +7 -4
- package/dist/cli/types/index.d.ts +26 -0
- package/dist/cli/types/index.d.ts.map +1 -1
- package/dist/cli/types/index.js +14 -0
- package/dist/cli/utils/daemon.d.ts +3 -0
- package/dist/cli/utils/daemon.d.ts.map +1 -1
- package/dist/cli/utils/daemon.js +34 -2
- package/dist/cli/utils/env-setup.d.ts +43 -0
- package/dist/cli/utils/env-setup.d.ts.map +1 -1
- package/dist/cli/utils/env-setup.js +98 -1
- package/dist/cli/utils/prompt.d.ts +68 -0
- package/dist/cli/utils/prompt.d.ts.map +1 -0
- package/dist/cli/utils/prompt.js +208 -0
- package/package.json +2 -1
- /package/.next/static/{pQTquVjewvoJa7BML07ip → KtDmF-FzoCLvoKXEcXyU-}/_buildManifest.js +0 -0
- /package/.next/static/{pQTquVjewvoJa7BML07ip → KtDmF-FzoCLvoKXEcXyU-}/_ssgManifest.js +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"env-setup.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/env-setup.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"env-setup.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/env-setup.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAaH,OAAO,EACL,SAAS,EACT,eAAe,EACf,gBAAgB,EACjB,MAAM,UAAU,CAAC;AAElB;;;GAGG;AACH,eAAO,MAAM,YAAY;;;;;;CAMf,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,gBAAgB,QAA2B,CAAC;AAEzD;;;;;GAKG;AACH,wBAAgB,eAAe,IAAI,OAAO,CAYzC;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,IAAI,MAAM,CAcnC;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,MAAM,CASpF;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAqBrC;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED;;;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"}
|
|
@@ -5,7 +5,12 @@
|
|
|
5
5
|
* Migrated from scripts/setup-env.sh
|
|
6
6
|
*/
|
|
7
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
-
exports.EnvSetup = exports.ENV_DEFAULTS = void 0;
|
|
8
|
+
exports.EnvSetup = exports.DEFAULT_ROOT_DIR = exports.ENV_DEFAULTS = void 0;
|
|
9
|
+
exports.isGlobalInstall = isGlobalInstall;
|
|
10
|
+
exports.getEnvPath = getEnvPath;
|
|
11
|
+
exports.resolveSecurePath = resolveSecurePath;
|
|
12
|
+
exports.getConfigDir = getConfigDir;
|
|
13
|
+
exports.getPidFilePath = getPidFilePath;
|
|
9
14
|
exports.sanitizeInput = sanitizeInput;
|
|
10
15
|
exports.sanitizePath = sanitizePath;
|
|
11
16
|
exports.validatePort = validatePort;
|
|
@@ -13,6 +18,7 @@ exports.escapeEnvValue = escapeEnvValue;
|
|
|
13
18
|
const fs_1 = require("fs");
|
|
14
19
|
const path_1 = require("path");
|
|
15
20
|
const crypto_1 = require("crypto");
|
|
21
|
+
const os_1 = require("os");
|
|
16
22
|
/**
|
|
17
23
|
* Default environment configuration values
|
|
18
24
|
* SF-4: DRY - Centralized defaults
|
|
@@ -24,6 +30,97 @@ exports.ENV_DEFAULTS = {
|
|
|
24
30
|
CM_LOG_LEVEL: 'info',
|
|
25
31
|
CM_LOG_FORMAT: 'text',
|
|
26
32
|
};
|
|
33
|
+
/**
|
|
34
|
+
* Default root directory for worktrees
|
|
35
|
+
*/
|
|
36
|
+
exports.DEFAULT_ROOT_DIR = (0, path_1.join)((0, os_1.homedir)(), 'repos');
|
|
37
|
+
/**
|
|
38
|
+
* Check if running as global npm package
|
|
39
|
+
* Issue #119: Determine .env location based on install type
|
|
40
|
+
*
|
|
41
|
+
* @returns true if running as global npm package
|
|
42
|
+
*/
|
|
43
|
+
function isGlobalInstall() {
|
|
44
|
+
// Check if running from global node_modules
|
|
45
|
+
// Global installs typically have paths like:
|
|
46
|
+
// - /usr/local/lib/node_modules/
|
|
47
|
+
// - /Users/xxx/.npm-global/lib/node_modules/
|
|
48
|
+
// - C:\Users\xxx\AppData\Roaming\npm\node_modules\
|
|
49
|
+
const currentPath = (0, path_1.dirname)(__dirname);
|
|
50
|
+
return (currentPath.includes('/lib/node_modules/') ||
|
|
51
|
+
currentPath.includes('\\node_modules\\') ||
|
|
52
|
+
currentPath.includes('/node_modules/commandmate'));
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Get the path to .env file based on install type
|
|
56
|
+
* Issue #119: Global install uses ~/.commandmate/, local uses cwd
|
|
57
|
+
*
|
|
58
|
+
* @returns Path to .env file
|
|
59
|
+
*/
|
|
60
|
+
function getEnvPath() {
|
|
61
|
+
if (isGlobalInstall()) {
|
|
62
|
+
const configDir = (0, path_1.join)((0, os_1.homedir)(), '.commandmate');
|
|
63
|
+
// Create config directory if it doesn't exist
|
|
64
|
+
if (!(0, fs_1.existsSync)(configDir)) {
|
|
65
|
+
(0, fs_1.mkdirSync)(configDir, { recursive: true, mode: 0o700 });
|
|
66
|
+
}
|
|
67
|
+
return (0, path_1.join)(configDir, '.env');
|
|
68
|
+
}
|
|
69
|
+
// Local install - use current working directory
|
|
70
|
+
return (0, path_1.join)(process.cwd(), '.env');
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Resolve path securely by resolving symlinks and verifying within allowed directory
|
|
74
|
+
* Issue #125: Path traversal protection (OWASP A01:2021 - Broken Access Control)
|
|
75
|
+
*
|
|
76
|
+
* @param targetPath - The path to resolve and verify
|
|
77
|
+
* @param allowedBaseDir - The base directory that targetPath must be within
|
|
78
|
+
* @returns The resolved real path
|
|
79
|
+
* @throws Error if path resolves outside allowed directory
|
|
80
|
+
*/
|
|
81
|
+
function resolveSecurePath(targetPath, allowedBaseDir) {
|
|
82
|
+
const realPath = (0, fs_1.realpathSync)(targetPath);
|
|
83
|
+
const realBaseDir = (0, fs_1.realpathSync)(allowedBaseDir);
|
|
84
|
+
if (!realPath.startsWith(realBaseDir)) {
|
|
85
|
+
throw new Error(`Path traversal detected: ${targetPath} resolves outside of ${allowedBaseDir}`);
|
|
86
|
+
}
|
|
87
|
+
return realPath;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Get the config directory path
|
|
91
|
+
* Issue #119: Returns ~/.commandmate for global, cwd for local
|
|
92
|
+
* Issue #125: Added symlink resolution for security (path traversal protection)
|
|
93
|
+
*
|
|
94
|
+
* @returns Path to config directory (absolute, with symlinks resolved)
|
|
95
|
+
*/
|
|
96
|
+
function getConfigDir() {
|
|
97
|
+
if (isGlobalInstall()) {
|
|
98
|
+
const configDir = (0, path_1.join)((0, os_1.homedir)(), '.commandmate');
|
|
99
|
+
// Verify config directory is within home directory (security check)
|
|
100
|
+
// Only validate if the directory exists (it may not exist yet during init)
|
|
101
|
+
if ((0, fs_1.existsSync)(configDir)) {
|
|
102
|
+
const realPath = (0, fs_1.realpathSync)(configDir);
|
|
103
|
+
const realHome = (0, fs_1.realpathSync)((0, os_1.homedir)());
|
|
104
|
+
if (!realPath.startsWith(realHome)) {
|
|
105
|
+
throw new Error(`Security error: Config directory ${configDir} is outside home directory`);
|
|
106
|
+
}
|
|
107
|
+
return realPath;
|
|
108
|
+
}
|
|
109
|
+
return configDir;
|
|
110
|
+
}
|
|
111
|
+
// Local install - resolve symlinks in cwd
|
|
112
|
+
const cwd = process.cwd();
|
|
113
|
+
return (0, fs_1.realpathSync)(cwd);
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Get the PID file path based on install type
|
|
117
|
+
* Issue #125: DRY principle - centralized PID file path resolution
|
|
118
|
+
*
|
|
119
|
+
* @returns Path to PID file (uses getConfigDir for consistency)
|
|
120
|
+
*/
|
|
121
|
+
function getPidFilePath() {
|
|
122
|
+
return (0, path_1.join)(getConfigDir(), '.commandmate.pid');
|
|
123
|
+
}
|
|
27
124
|
/**
|
|
28
125
|
* Sanitize input by removing control characters
|
|
29
126
|
* SF-SEC-3: Input sanitization
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interactive Prompt Utilities
|
|
3
|
+
* Issue #119: Interactive init support
|
|
4
|
+
*
|
|
5
|
+
* Provides readline-based prompts for CLI commands.
|
|
6
|
+
* Reference implementation: scripts/setup-env.sh
|
|
7
|
+
*/
|
|
8
|
+
import { PromptOptions, ConfirmOptions } from '../types';
|
|
9
|
+
/**
|
|
10
|
+
* Close readline interface
|
|
11
|
+
* Should be called when all prompts are done
|
|
12
|
+
*/
|
|
13
|
+
export declare function closeReadline(): void;
|
|
14
|
+
/**
|
|
15
|
+
* Expand tilde (~) to home directory
|
|
16
|
+
*
|
|
17
|
+
* @param path - Path that may contain tilde
|
|
18
|
+
* @returns Path with tilde expanded to home directory
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* expandTilde('~/repos') // '/Users/xxx/repos'
|
|
22
|
+
* expandTilde('/absolute/path') // '/absolute/path'
|
|
23
|
+
*/
|
|
24
|
+
export declare function expandTilde(path: string): string;
|
|
25
|
+
/**
|
|
26
|
+
* Resolve path to absolute path
|
|
27
|
+
* Handles tilde expansion and relative paths
|
|
28
|
+
*
|
|
29
|
+
* @param path - Path to resolve
|
|
30
|
+
* @returns Absolute path
|
|
31
|
+
*/
|
|
32
|
+
export declare function resolvePath(path: string): string;
|
|
33
|
+
/**
|
|
34
|
+
* Validate port number
|
|
35
|
+
*
|
|
36
|
+
* @param input - Port number as string
|
|
37
|
+
* @returns Error message or true if valid
|
|
38
|
+
*/
|
|
39
|
+
export declare function validatePort(input: string): string | true;
|
|
40
|
+
/**
|
|
41
|
+
* Interactive prompt with default value and validation
|
|
42
|
+
*
|
|
43
|
+
* @param question - Question to display
|
|
44
|
+
* @param options - Prompt options (default, validate)
|
|
45
|
+
* @returns User input or default value
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* const port = await prompt('Server port', { default: '3000', validate: validatePort });
|
|
49
|
+
*/
|
|
50
|
+
export declare function prompt(question: string, options?: PromptOptions): Promise<string>;
|
|
51
|
+
/**
|
|
52
|
+
* Interactive Yes/No confirmation
|
|
53
|
+
*
|
|
54
|
+
* @param question - Question to display
|
|
55
|
+
* @param options - Confirm options (default)
|
|
56
|
+
* @returns true for Yes, false for No
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* const enableExternal = await confirm('Enable external access?', { default: false });
|
|
60
|
+
*/
|
|
61
|
+
export declare function confirm(question: string, options?: ConfirmOptions): Promise<boolean>;
|
|
62
|
+
/**
|
|
63
|
+
* Check if running in interactive mode (TTY)
|
|
64
|
+
*
|
|
65
|
+
* @returns true if stdin is a TTY (interactive terminal)
|
|
66
|
+
*/
|
|
67
|
+
export declare function isInteractive(): boolean;
|
|
68
|
+
//# sourceMappingURL=prompt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/prompt.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAqBzD;;;GAGG;AACH,wBAAgB,aAAa,IAAI,IAAI,CAKpC;AAED;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQhD;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGhD;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CASzD;AAED;;;;;;;;;GASG;AACH,wBAAsB,MAAM,CAC1B,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,MAAM,CAAC,CA8BjB;AAED;;;;;;;;;GASG;AACH,wBAAsB,OAAO,CAC3B,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC,OAAO,CAAC,CAmClB;AAED;;;;GAIG;AACH,wBAAgB,aAAa,IAAI,OAAO,CAEvC"}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Interactive Prompt Utilities
|
|
4
|
+
* Issue #119: Interactive init support
|
|
5
|
+
*
|
|
6
|
+
* Provides readline-based prompts for CLI commands.
|
|
7
|
+
* Reference implementation: scripts/setup-env.sh
|
|
8
|
+
*/
|
|
9
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
12
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
13
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
14
|
+
}
|
|
15
|
+
Object.defineProperty(o, k2, desc);
|
|
16
|
+
}) : (function(o, m, k, k2) {
|
|
17
|
+
if (k2 === undefined) k2 = k;
|
|
18
|
+
o[k2] = m[k];
|
|
19
|
+
}));
|
|
20
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
21
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
22
|
+
}) : function(o, v) {
|
|
23
|
+
o["default"] = v;
|
|
24
|
+
});
|
|
25
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
26
|
+
var ownKeys = function(o) {
|
|
27
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
28
|
+
var ar = [];
|
|
29
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
30
|
+
return ar;
|
|
31
|
+
};
|
|
32
|
+
return ownKeys(o);
|
|
33
|
+
};
|
|
34
|
+
return function (mod) {
|
|
35
|
+
if (mod && mod.__esModule) return mod;
|
|
36
|
+
var result = {};
|
|
37
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
38
|
+
__setModuleDefault(result, mod);
|
|
39
|
+
return result;
|
|
40
|
+
};
|
|
41
|
+
})();
|
|
42
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
43
|
+
exports.closeReadline = closeReadline;
|
|
44
|
+
exports.expandTilde = expandTilde;
|
|
45
|
+
exports.resolvePath = resolvePath;
|
|
46
|
+
exports.validatePort = validatePort;
|
|
47
|
+
exports.prompt = prompt;
|
|
48
|
+
exports.confirm = confirm;
|
|
49
|
+
exports.isInteractive = isInteractive;
|
|
50
|
+
const readline = __importStar(require("readline"));
|
|
51
|
+
const os_1 = require("os");
|
|
52
|
+
const path_1 = require("path");
|
|
53
|
+
/**
|
|
54
|
+
* Readline interface singleton
|
|
55
|
+
* Reused across prompts to avoid creating multiple interfaces
|
|
56
|
+
*/
|
|
57
|
+
let rlInstance = null;
|
|
58
|
+
/**
|
|
59
|
+
* Get or create readline interface
|
|
60
|
+
*/
|
|
61
|
+
function getReadlineInterface() {
|
|
62
|
+
if (!rlInstance) {
|
|
63
|
+
rlInstance = readline.createInterface({
|
|
64
|
+
input: process.stdin,
|
|
65
|
+
output: process.stdout,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
return rlInstance;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Close readline interface
|
|
72
|
+
* Should be called when all prompts are done
|
|
73
|
+
*/
|
|
74
|
+
function closeReadline() {
|
|
75
|
+
if (rlInstance) {
|
|
76
|
+
rlInstance.close();
|
|
77
|
+
rlInstance = null;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Expand tilde (~) to home directory
|
|
82
|
+
*
|
|
83
|
+
* @param path - Path that may contain tilde
|
|
84
|
+
* @returns Path with tilde expanded to home directory
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* expandTilde('~/repos') // '/Users/xxx/repos'
|
|
88
|
+
* expandTilde('/absolute/path') // '/absolute/path'
|
|
89
|
+
*/
|
|
90
|
+
function expandTilde(path) {
|
|
91
|
+
if (path.startsWith('~/')) {
|
|
92
|
+
return path.replace(/^~/, (0, os_1.homedir)());
|
|
93
|
+
}
|
|
94
|
+
if (path === '~') {
|
|
95
|
+
return (0, os_1.homedir)();
|
|
96
|
+
}
|
|
97
|
+
return path;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Resolve path to absolute path
|
|
101
|
+
* Handles tilde expansion and relative paths
|
|
102
|
+
*
|
|
103
|
+
* @param path - Path to resolve
|
|
104
|
+
* @returns Absolute path
|
|
105
|
+
*/
|
|
106
|
+
function resolvePath(path) {
|
|
107
|
+
const expanded = expandTilde(path);
|
|
108
|
+
return (0, path_1.resolve)(expanded);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Validate port number
|
|
112
|
+
*
|
|
113
|
+
* @param input - Port number as string
|
|
114
|
+
* @returns Error message or true if valid
|
|
115
|
+
*/
|
|
116
|
+
function validatePort(input) {
|
|
117
|
+
const port = parseInt(input, 10);
|
|
118
|
+
if (isNaN(port)) {
|
|
119
|
+
return 'Port must be a number';
|
|
120
|
+
}
|
|
121
|
+
if (port < 1 || port > 65535) {
|
|
122
|
+
return 'Port must be between 1 and 65535';
|
|
123
|
+
}
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Interactive prompt with default value and validation
|
|
128
|
+
*
|
|
129
|
+
* @param question - Question to display
|
|
130
|
+
* @param options - Prompt options (default, validate)
|
|
131
|
+
* @returns User input or default value
|
|
132
|
+
*
|
|
133
|
+
* @example
|
|
134
|
+
* const port = await prompt('Server port', { default: '3000', validate: validatePort });
|
|
135
|
+
*/
|
|
136
|
+
async function prompt(question, options = {}) {
|
|
137
|
+
const rl = getReadlineInterface();
|
|
138
|
+
const { default: defaultValue, validate } = options;
|
|
139
|
+
const displayQuestion = defaultValue
|
|
140
|
+
? `? ${question} [${defaultValue}]: `
|
|
141
|
+
: `? ${question}: `;
|
|
142
|
+
return new Promise((resolvePromise) => {
|
|
143
|
+
const askQuestion = () => {
|
|
144
|
+
rl.question(displayQuestion, (answer) => {
|
|
145
|
+
const trimmedAnswer = answer.trim();
|
|
146
|
+
const result = trimmedAnswer || defaultValue || '';
|
|
147
|
+
// Validate if validator provided
|
|
148
|
+
if (validate && result) {
|
|
149
|
+
const validationResult = validate(result);
|
|
150
|
+
if (validationResult !== true) {
|
|
151
|
+
console.log(` Error: ${validationResult}`);
|
|
152
|
+
askQuestion(); // Re-ask
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
resolvePromise(result);
|
|
157
|
+
});
|
|
158
|
+
};
|
|
159
|
+
askQuestion();
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Interactive Yes/No confirmation
|
|
164
|
+
*
|
|
165
|
+
* @param question - Question to display
|
|
166
|
+
* @param options - Confirm options (default)
|
|
167
|
+
* @returns true for Yes, false for No
|
|
168
|
+
*
|
|
169
|
+
* @example
|
|
170
|
+
* const enableExternal = await confirm('Enable external access?', { default: false });
|
|
171
|
+
*/
|
|
172
|
+
async function confirm(question, options = {}) {
|
|
173
|
+
const rl = getReadlineInterface();
|
|
174
|
+
const defaultValue = options.default ?? false;
|
|
175
|
+
const hint = defaultValue ? '(Y/n)' : '(y/N)';
|
|
176
|
+
const displayQuestion = `? ${question} ${hint}: `;
|
|
177
|
+
return new Promise((resolvePromise) => {
|
|
178
|
+
const askQuestion = () => {
|
|
179
|
+
rl.question(displayQuestion, (answer) => {
|
|
180
|
+
const trimmedAnswer = answer.trim().toLowerCase();
|
|
181
|
+
if (trimmedAnswer === '') {
|
|
182
|
+
resolvePromise(defaultValue);
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
if (trimmedAnswer === 'y' || trimmedAnswer === 'yes') {
|
|
186
|
+
resolvePromise(true);
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
if (trimmedAnswer === 'n' || trimmedAnswer === 'no') {
|
|
190
|
+
resolvePromise(false);
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
// Invalid input - re-ask
|
|
194
|
+
console.log(' Please answer with y/n or yes/no');
|
|
195
|
+
askQuestion();
|
|
196
|
+
});
|
|
197
|
+
};
|
|
198
|
+
askQuestion();
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Check if running in interactive mode (TTY)
|
|
203
|
+
*
|
|
204
|
+
* @returns true if stdin is a TTY (interactive terminal)
|
|
205
|
+
*/
|
|
206
|
+
function isInteractive() {
|
|
207
|
+
return process.stdin.isTTY === true;
|
|
208
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "commandmate",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"description": "Git worktree management with Claude CLI and tmux sessions",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
"better-sqlite3": "^12.4.1",
|
|
42
42
|
"commander": "^14.0.2",
|
|
43
43
|
"date-fns": "^4.1.0",
|
|
44
|
+
"dotenv": "^17.2.3",
|
|
44
45
|
"gray-matter": "^4.0.3",
|
|
45
46
|
"http-proxy": "^1.18.1",
|
|
46
47
|
"isomorphic-dompurify": "^2.35.0",
|
|
File without changes
|
|
File without changes
|