commandmate 0.1.10 → 0.1.11
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/.env.example +8 -3
- package/.next/BUILD_ID +1 -1
- package/.next/app-build-manifest.json +11 -11
- 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/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_not-found.html +1 -1
- package/.next/server/app/_not-found.rsc +1 -1
- package/.next/server/app/api/external-apps/[id]/health/route.js +11 -12
- package/.next/server/app/api/external-apps/[id]/route.js +14 -15
- package/.next/server/app/api/external-apps/route.js +12 -13
- package/.next/server/app/api/hooks/claude-done/route.js +1 -1
- package/.next/server/app/api/repositories/clone/[jobId]/route.js +1 -1
- package/.next/server/app/api/repositories/clone/route.js +1 -1
- package/.next/server/app/api/repositories/route.js +1 -1
- package/.next/server/app/api/repositories/scan/route.js +1 -1
- package/.next/server/app/api/repositories/sync/route.js +1 -1
- package/.next/server/app/api/slash-commands.body +1 -1
- package/.next/server/app/api/worktrees/[id]/auto-yes/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/auto-yes/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/cli-tool/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/current-output/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/files/[...path]/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/interrupt/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/kill-session/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/logs/[filename]/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/logs/route.js +7 -7
- package/.next/server/app/api/worktrees/[id]/memos/[memoId]/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/memos/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/messages/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/prompt-response/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/respond/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/search/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/send/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/slash-commands/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/start-polling/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tree/[...path]/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tree/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/upload/[...path]/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/viewed/route.js +1 -1
- package/.next/server/app/api/worktrees/route.js +1 -1
- package/.next/server/app/index.html +2 -2
- package/.next/server/app/index.rsc +2 -2
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app/proxy/[...path]/route.js +12 -13
- package/.next/server/app/worktrees/[id]/files/[...path]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/page.js +3 -3
- package/.next/server/app/worktrees/[id]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/simple-terminal/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/terminal/page_client-reference-manifest.js +1 -1
- package/.next/server/app-paths-manifest.json +10 -10
- package/.next/server/chunks/1318.js +4 -4
- package/.next/server/chunks/1528.js +1 -1
- package/.next/server/chunks/7425.js +97 -48
- package/.next/server/chunks/9723.js +1 -1
- 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/server/src/middleware.js +2 -2
- package/.next/server/src/middleware.js.map +1 -1
- package/.next/static/chunks/app/worktrees/[id]/page-720605c2fb074444.js +1 -0
- package/.next/trace +5 -5
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +6 -4
- 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 +64 -17
- package/dist/cli/commands/status.d.ts +4 -1
- package/dist/cli/commands/status.d.ts.map +1 -1
- package/dist/cli/commands/status.js +95 -6
- 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 +27 -10
- package/dist/cli/index.js +16 -2
- package/dist/cli/types/index.d.ts +20 -0
- package/dist/cli/types/index.d.ts.map +1 -1
- package/dist/cli/utils/daemon-factory.d.ts +105 -0
- package/dist/cli/utils/daemon-factory.d.ts.map +1 -0
- package/dist/cli/utils/daemon-factory.js +117 -0
- package/dist/cli/utils/daemon.d.ts.map +1 -1
- package/dist/cli/utils/daemon.js +4 -0
- package/dist/cli/utils/env-setup.d.ts +24 -12
- package/dist/cli/utils/env-setup.d.ts.map +1 -1
- package/dist/cli/utils/env-setup.js +64 -43
- package/dist/cli/utils/input-validators.d.ts +103 -0
- package/dist/cli/utils/input-validators.d.ts.map +1 -0
- package/dist/cli/utils/input-validators.js +163 -0
- package/dist/cli/utils/install-context.d.ts +53 -0
- package/dist/cli/utils/install-context.d.ts.map +1 -0
- package/dist/cli/utils/install-context.js +96 -0
- package/dist/cli/utils/pid-manager.d.ts +34 -0
- package/dist/cli/utils/pid-manager.d.ts.map +1 -1
- package/dist/cli/utils/pid-manager.js +43 -0
- package/dist/cli/utils/port-allocator.d.ts +108 -0
- package/dist/cli/utils/port-allocator.d.ts.map +1 -0
- package/dist/cli/utils/port-allocator.js +166 -0
- package/dist/cli/utils/resource-resolvers.d.ts +92 -0
- package/dist/cli/utils/resource-resolvers.d.ts.map +1 -0
- package/dist/cli/utils/resource-resolvers.js +175 -0
- package/dist/cli/utils/worktree-detector.d.ts +82 -0
- package/dist/cli/utils/worktree-detector.d.ts.map +1 -0
- package/dist/cli/utils/worktree-detector.js +221 -0
- package/dist/lib/errors.d.ts +111 -0
- package/dist/lib/errors.d.ts.map +1 -0
- package/dist/lib/errors.js +153 -0
- package/dist/server/server.js +3 -0
- package/dist/server/src/cli/utils/install-context.js +96 -0
- package/dist/server/src/config/system-directories.js +40 -0
- package/dist/server/src/lib/auto-yes-manager.js +325 -0
- package/dist/server/src/lib/auto-yes-resolver.js +34 -0
- package/dist/server/src/lib/cli-patterns.js +6 -1
- package/dist/server/src/lib/db-instance.js +12 -2
- package/dist/server/src/lib/db-migrations.js +68 -1
- package/dist/server/src/lib/db-path-resolver.js +99 -0
- package/dist/server/src/lib/db.js +21 -0
- package/dist/server/src/lib/env.js +52 -3
- package/dist/server/src/lib/worktrees.js +36 -1
- package/dist/server/src/types/external-apps.js +20 -0
- package/package.json +1 -1
- package/.next/static/chunks/app/worktrees/[id]/page-aea2d5e7e28955be.js +0 -1
- /package/.next/static/chunks/app/{page-96a8aa2ec30a44e9.js → page-fe35d61f14b90a51.js} +0 -0
- /package/.next/static/{8o5rUyZun0GklIHWDgkmv → gRNW5YXY43KqCKbCdaJoJ}/_buildManifest.js +0 -0
- /package/.next/static/{8o5rUyZun0GklIHWDgkmv → gRNW5YXY43KqCKbCdaJoJ}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Input Validators
|
|
4
|
+
* Issue #136: Phase 1 - Foundation
|
|
5
|
+
*
|
|
6
|
+
* Provides input validation functions for worktree-related operations.
|
|
7
|
+
* These validators are critical for security (command injection prevention).
|
|
8
|
+
*
|
|
9
|
+
* @module input-validators
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.MAX_BRANCH_NAME_LENGTH = exports.BRANCH_NAME_PATTERN = exports.MAX_ISSUE_NO = void 0;
|
|
13
|
+
exports.validateIssueNo = validateIssueNo;
|
|
14
|
+
exports.validateBranchName = validateBranchName;
|
|
15
|
+
exports.validatePortNumber = validatePortNumber;
|
|
16
|
+
exports.isValidIssueNo = isValidIssueNo;
|
|
17
|
+
exports.validateIssueNoResult = validateIssueNoResult;
|
|
18
|
+
exports.isValidBranchName = isValidBranchName;
|
|
19
|
+
/**
|
|
20
|
+
* Maximum allowed issue number (2^31 - 1)
|
|
21
|
+
* NTH-SEC-002: Prevent integer overflow
|
|
22
|
+
*/
|
|
23
|
+
exports.MAX_ISSUE_NO = 2147483647;
|
|
24
|
+
/**
|
|
25
|
+
* Branch name whitelist pattern
|
|
26
|
+
* MF-SEC-001: Only allow safe characters to prevent command injection
|
|
27
|
+
* Allowed: a-z, A-Z, 0-9, underscore, hyphen, forward slash
|
|
28
|
+
*/
|
|
29
|
+
exports.BRANCH_NAME_PATTERN = /^[a-zA-Z0-9_/-]+$/;
|
|
30
|
+
/**
|
|
31
|
+
* Maximum branch name length
|
|
32
|
+
* Prevents DoS via overly long input
|
|
33
|
+
*/
|
|
34
|
+
exports.MAX_BRANCH_NAME_LENGTH = 255;
|
|
35
|
+
/**
|
|
36
|
+
* Validate issue number
|
|
37
|
+
* SEC-001: Strict positive integer validation
|
|
38
|
+
*
|
|
39
|
+
* @param issueNo - The issue number to validate
|
|
40
|
+
* @throws Error with code 'INVALID_ISSUE_NO' if not a valid integer
|
|
41
|
+
* @throws Error with code 'ISSUE_NO_OUT_OF_RANGE' if out of range
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```typescript
|
|
45
|
+
* validateIssueNo(135); // OK
|
|
46
|
+
* validateIssueNo('135'); // throws INVALID_ISSUE_NO
|
|
47
|
+
* validateIssueNo(-1); // throws ISSUE_NO_OUT_OF_RANGE
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
function validateIssueNo(issueNo) {
|
|
51
|
+
if (typeof issueNo !== 'number' ||
|
|
52
|
+
!Number.isInteger(issueNo) ||
|
|
53
|
+
!Number.isFinite(issueNo)) {
|
|
54
|
+
throw new Error('INVALID_ISSUE_NO');
|
|
55
|
+
}
|
|
56
|
+
if (issueNo <= 0 || issueNo > exports.MAX_ISSUE_NO) {
|
|
57
|
+
throw new Error('ISSUE_NO_OUT_OF_RANGE');
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Validate branch name
|
|
62
|
+
* MF-SEC-001: Whitelist validation to prevent command injection
|
|
63
|
+
*
|
|
64
|
+
* @param branchName - The branch name to validate
|
|
65
|
+
* @throws Error with code 'INVALID_BRANCH_NAME' if contains invalid characters
|
|
66
|
+
* @throws Error with code 'BRANCH_NAME_TOO_LONG' if exceeds max length
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ```typescript
|
|
70
|
+
* validateBranchName('feature/136-worktree'); // OK
|
|
71
|
+
* validateBranchName('feature`rm -rf /`'); // throws INVALID_BRANCH_NAME
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
function validateBranchName(branchName) {
|
|
75
|
+
if (!branchName || !exports.BRANCH_NAME_PATTERN.test(branchName)) {
|
|
76
|
+
throw new Error('INVALID_BRANCH_NAME');
|
|
77
|
+
}
|
|
78
|
+
if (branchName.length > exports.MAX_BRANCH_NAME_LENGTH) {
|
|
79
|
+
throw new Error('BRANCH_NAME_TOO_LONG');
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Validate port number
|
|
84
|
+
* SEC-003: Port validation for worktree servers
|
|
85
|
+
*
|
|
86
|
+
* @param port - The port number to validate
|
|
87
|
+
* @param minPort - Minimum allowed port (default: 1024, non-privileged)
|
|
88
|
+
* @param maxPort - Maximum allowed port (default: 65535)
|
|
89
|
+
* @throws Error with code 'INVALID_PORT' if not a valid port number
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* ```typescript
|
|
93
|
+
* validatePortNumber(3001); // OK
|
|
94
|
+
* validatePortNumber(80); // throws INVALID_PORT (privileged)
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
97
|
+
function validatePortNumber(port, minPort = 1024, maxPort = 65535) {
|
|
98
|
+
if (typeof port !== 'number' ||
|
|
99
|
+
!Number.isInteger(port) ||
|
|
100
|
+
!Number.isFinite(port)) {
|
|
101
|
+
throw new Error('INVALID_PORT');
|
|
102
|
+
}
|
|
103
|
+
if (port < minPort || port > maxPort) {
|
|
104
|
+
throw new Error('INVALID_PORT');
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Type guard for checking if a value is a valid issue number
|
|
109
|
+
*
|
|
110
|
+
* @param value - Value to check
|
|
111
|
+
* @returns true if value is a valid issue number
|
|
112
|
+
*/
|
|
113
|
+
function isValidIssueNo(value) {
|
|
114
|
+
try {
|
|
115
|
+
validateIssueNo(value);
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Validate issue number and return result (for CLI commands)
|
|
124
|
+
* Issue #136: CLI-friendly validation that returns result instead of throwing
|
|
125
|
+
*
|
|
126
|
+
* @param issueNo - The issue number to validate
|
|
127
|
+
* @returns Validation result with error message if invalid
|
|
128
|
+
*/
|
|
129
|
+
function validateIssueNoResult(issueNo) {
|
|
130
|
+
try {
|
|
131
|
+
validateIssueNo(issueNo);
|
|
132
|
+
return { valid: true };
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
136
|
+
switch (message) {
|
|
137
|
+
case 'INVALID_ISSUE_NO':
|
|
138
|
+
return { valid: false, error: 'Issue number must be a positive integer' };
|
|
139
|
+
case 'ISSUE_NO_OUT_OF_RANGE':
|
|
140
|
+
return { valid: false, error: `Issue number must be between 1 and ${exports.MAX_ISSUE_NO}` };
|
|
141
|
+
default:
|
|
142
|
+
return { valid: false, error: 'Invalid issue number' };
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Type guard for checking if a value is a valid branch name
|
|
148
|
+
*
|
|
149
|
+
* @param value - Value to check
|
|
150
|
+
* @returns true if value is a valid branch name
|
|
151
|
+
*/
|
|
152
|
+
function isValidBranchName(value) {
|
|
153
|
+
if (typeof value !== 'string') {
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
try {
|
|
157
|
+
validateBranchName(value);
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Install Context Utility
|
|
3
|
+
* Issue #136: DRY refactoring - extract common install detection functions
|
|
4
|
+
*
|
|
5
|
+
* This module provides centralized functions for detecting the install type
|
|
6
|
+
* (global vs local npm install) and resolving configuration directories.
|
|
7
|
+
*
|
|
8
|
+
* Extracted from env-setup.ts to solve circular import issues and follow DRY principle.
|
|
9
|
+
*
|
|
10
|
+
* @module install-context
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Check if running as global npm package
|
|
14
|
+
* Issue #119: Determine .env location based on install type
|
|
15
|
+
* Issue #136: Extracted to avoid circular imports
|
|
16
|
+
*
|
|
17
|
+
* @returns true if running as global npm package
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* if (isGlobalInstall()) {
|
|
22
|
+
* // Use ~/.commandmate for config
|
|
23
|
+
* } else {
|
|
24
|
+
* // Use current working directory
|
|
25
|
+
* }
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export declare function isGlobalInstall(): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Get the config directory path
|
|
31
|
+
* Issue #119: Returns ~/.commandmate for global, cwd for local
|
|
32
|
+
* Issue #125: Added symlink resolution for security (path traversal protection)
|
|
33
|
+
* Issue #136: Extracted to avoid circular imports
|
|
34
|
+
*
|
|
35
|
+
* @returns Path to config directory (absolute, with symlinks resolved)
|
|
36
|
+
* @throws Error if config directory resolves outside home directory (for global install)
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```typescript
|
|
40
|
+
* const configDir = getConfigDir();
|
|
41
|
+
* // Global: /Users/username/.commandmate
|
|
42
|
+
* // Local: /path/to/project (resolved from symlinks)
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export declare function getConfigDir(): string;
|
|
46
|
+
/**
|
|
47
|
+
* Ensure the config directory exists with proper permissions
|
|
48
|
+
* Issue #136: Utility for creating config directory if needed
|
|
49
|
+
*
|
|
50
|
+
* @returns Path to config directory (created if needed)
|
|
51
|
+
*/
|
|
52
|
+
export declare function ensureConfigDir(): string;
|
|
53
|
+
//# sourceMappingURL=install-context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install-context.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/install-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAMH;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,eAAe,IAAI,OAAO,CAYzC;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAqBrC;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAUxC"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Install Context Utility
|
|
4
|
+
* Issue #136: DRY refactoring - extract common install detection functions
|
|
5
|
+
*
|
|
6
|
+
* This module provides centralized functions for detecting the install type
|
|
7
|
+
* (global vs local npm install) and resolving configuration directories.
|
|
8
|
+
*
|
|
9
|
+
* Extracted from env-setup.ts to solve circular import issues and follow DRY principle.
|
|
10
|
+
*
|
|
11
|
+
* @module install-context
|
|
12
|
+
*/
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.isGlobalInstall = isGlobalInstall;
|
|
15
|
+
exports.getConfigDir = getConfigDir;
|
|
16
|
+
exports.ensureConfigDir = ensureConfigDir;
|
|
17
|
+
const fs_1 = require("fs");
|
|
18
|
+
const path_1 = require("path");
|
|
19
|
+
const os_1 = require("os");
|
|
20
|
+
/**
|
|
21
|
+
* Check if running as global npm package
|
|
22
|
+
* Issue #119: Determine .env location based on install type
|
|
23
|
+
* Issue #136: Extracted to avoid circular imports
|
|
24
|
+
*
|
|
25
|
+
* @returns true if running as global npm package
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```typescript
|
|
29
|
+
* if (isGlobalInstall()) {
|
|
30
|
+
* // Use ~/.commandmate for config
|
|
31
|
+
* } else {
|
|
32
|
+
* // Use current working directory
|
|
33
|
+
* }
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
function isGlobalInstall() {
|
|
37
|
+
// Check if running from global node_modules
|
|
38
|
+
// Global installs typically have paths like:
|
|
39
|
+
// - /usr/local/lib/node_modules/
|
|
40
|
+
// - /Users/xxx/.npm-global/lib/node_modules/
|
|
41
|
+
// - C:\Users\xxx\AppData\Roaming\npm\node_modules\
|
|
42
|
+
const currentPath = (0, path_1.dirname)(__dirname);
|
|
43
|
+
return (currentPath.includes('/lib/node_modules/') ||
|
|
44
|
+
currentPath.includes('\\node_modules\\') ||
|
|
45
|
+
currentPath.includes('/node_modules/commandmate'));
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Get the config directory path
|
|
49
|
+
* Issue #119: Returns ~/.commandmate for global, cwd for local
|
|
50
|
+
* Issue #125: Added symlink resolution for security (path traversal protection)
|
|
51
|
+
* Issue #136: Extracted to avoid circular imports
|
|
52
|
+
*
|
|
53
|
+
* @returns Path to config directory (absolute, with symlinks resolved)
|
|
54
|
+
* @throws Error if config directory resolves outside home directory (for global install)
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```typescript
|
|
58
|
+
* const configDir = getConfigDir();
|
|
59
|
+
* // Global: /Users/username/.commandmate
|
|
60
|
+
* // Local: /path/to/project (resolved from symlinks)
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
function getConfigDir() {
|
|
64
|
+
if (isGlobalInstall()) {
|
|
65
|
+
const configDir = (0, path_1.join)((0, os_1.homedir)(), '.commandmate');
|
|
66
|
+
// Verify config directory is within home directory (security check)
|
|
67
|
+
// Only validate if the directory exists (it may not exist yet during init)
|
|
68
|
+
if ((0, fs_1.existsSync)(configDir)) {
|
|
69
|
+
const realPath = (0, fs_1.realpathSync)(configDir);
|
|
70
|
+
const realHome = (0, fs_1.realpathSync)((0, os_1.homedir)());
|
|
71
|
+
if (!realPath.startsWith(realHome)) {
|
|
72
|
+
throw new Error(`Security error: Config directory ${configDir} is outside home directory`);
|
|
73
|
+
}
|
|
74
|
+
return realPath;
|
|
75
|
+
}
|
|
76
|
+
return configDir;
|
|
77
|
+
}
|
|
78
|
+
// Local install - resolve symlinks in cwd
|
|
79
|
+
const cwd = process.cwd();
|
|
80
|
+
return (0, fs_1.realpathSync)(cwd);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Ensure the config directory exists with proper permissions
|
|
84
|
+
* Issue #136: Utility for creating config directory if needed
|
|
85
|
+
*
|
|
86
|
+
* @returns Path to config directory (created if needed)
|
|
87
|
+
*/
|
|
88
|
+
function ensureConfigDir() {
|
|
89
|
+
const configDir = isGlobalInstall()
|
|
90
|
+
? (0, path_1.join)((0, os_1.homedir)(), '.commandmate')
|
|
91
|
+
: process.cwd();
|
|
92
|
+
if (!(0, fs_1.existsSync)(configDir)) {
|
|
93
|
+
(0, fs_1.mkdirSync)(configDir, { recursive: true, mode: 0o700 });
|
|
94
|
+
}
|
|
95
|
+
return getConfigDir();
|
|
96
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* PID File Manager
|
|
3
3
|
* Issue #96: npm install CLI support
|
|
4
|
+
* Issue #136: Phase 2 - Task 2.4 - Added factory functions for Issue number support
|
|
4
5
|
* SF-1: SRP - Separated from daemon.ts for single responsibility
|
|
5
6
|
* MF-SEC-2: TOCTOU protection with O_EXCL atomic writes
|
|
6
7
|
*/
|
|
@@ -39,4 +40,37 @@ export declare class PidManager {
|
|
|
39
40
|
*/
|
|
40
41
|
isProcessRunning(): boolean;
|
|
41
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* Factory function to create PidManager instance
|
|
45
|
+
* Issue #136: Uses PidPathResolver for path resolution
|
|
46
|
+
*
|
|
47
|
+
* @param issueNo - Optional issue number for worktree-specific PID
|
|
48
|
+
* @returns PidManager instance
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```typescript
|
|
52
|
+
* // Main server PID manager
|
|
53
|
+
* const mainManager = createPidManager();
|
|
54
|
+
*
|
|
55
|
+
* // Worktree-specific PID manager
|
|
56
|
+
* const issueManager = createPidManager(135);
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
export declare function createPidManager(issueNo?: number): PidManager;
|
|
60
|
+
/**
|
|
61
|
+
* Factory function to create PidManager for a specific issue
|
|
62
|
+
* Issue #136: Convenience function for worktree PID management
|
|
63
|
+
*
|
|
64
|
+
* @param issueNo - Issue number
|
|
65
|
+
* @returns PidManager instance for the specified issue
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* ```typescript
|
|
69
|
+
* const manager = createIssuePidManager(135);
|
|
70
|
+
* if (manager.isProcessRunning()) {
|
|
71
|
+
* console.log('Worktree server for issue #135 is running');
|
|
72
|
+
* }
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
export declare function createIssuePidManager(issueNo: number): PidManager;
|
|
42
76
|
//# sourceMappingURL=pid-manager.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pid-manager.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/pid-manager.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"pid-manager.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/pid-manager.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAaH;;GAEG;AACH,qBAAa,UAAU;IACT,OAAO,CAAC,QAAQ,CAAC,WAAW;gBAAX,WAAW,EAAE,MAAM;IAEhD;;OAEG;IACH,MAAM,IAAI,OAAO;IAIjB;;;OAGG;IACH,OAAO,IAAI,MAAM,GAAG,IAAI;IAmBxB;;;;;;OAMG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAwB9B;;OAEG;IACH,SAAS,IAAI,IAAI;IAUjB;;;;;OAKG;IACH,gBAAgB,IAAI,OAAO;CAmB5B;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,UAAU,CAI7D;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,CAEjE"}
|
|
@@ -2,12 +2,16 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* PID File Manager
|
|
4
4
|
* Issue #96: npm install CLI support
|
|
5
|
+
* Issue #136: Phase 2 - Task 2.4 - Added factory functions for Issue number support
|
|
5
6
|
* SF-1: SRP - Separated from daemon.ts for single responsibility
|
|
6
7
|
* MF-SEC-2: TOCTOU protection with O_EXCL atomic writes
|
|
7
8
|
*/
|
|
8
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
10
|
exports.PidManager = void 0;
|
|
11
|
+
exports.createPidManager = createPidManager;
|
|
12
|
+
exports.createIssuePidManager = createIssuePidManager;
|
|
10
13
|
const fs_1 = require("fs");
|
|
14
|
+
const resource_resolvers_1 = require("./resource-resolvers");
|
|
11
15
|
/**
|
|
12
16
|
* PID file manager for daemon process tracking
|
|
13
17
|
*/
|
|
@@ -109,3 +113,42 @@ class PidManager {
|
|
|
109
113
|
}
|
|
110
114
|
}
|
|
111
115
|
exports.PidManager = PidManager;
|
|
116
|
+
/**
|
|
117
|
+
* Factory function to create PidManager instance
|
|
118
|
+
* Issue #136: Uses PidPathResolver for path resolution
|
|
119
|
+
*
|
|
120
|
+
* @param issueNo - Optional issue number for worktree-specific PID
|
|
121
|
+
* @returns PidManager instance
|
|
122
|
+
*
|
|
123
|
+
* @example
|
|
124
|
+
* ```typescript
|
|
125
|
+
* // Main server PID manager
|
|
126
|
+
* const mainManager = createPidManager();
|
|
127
|
+
*
|
|
128
|
+
* // Worktree-specific PID manager
|
|
129
|
+
* const issueManager = createPidManager(135);
|
|
130
|
+
* ```
|
|
131
|
+
*/
|
|
132
|
+
function createPidManager(issueNo) {
|
|
133
|
+
const resolver = new resource_resolvers_1.PidPathResolver();
|
|
134
|
+
const pidPath = resolver.resolve(issueNo);
|
|
135
|
+
return new PidManager(pidPath);
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Factory function to create PidManager for a specific issue
|
|
139
|
+
* Issue #136: Convenience function for worktree PID management
|
|
140
|
+
*
|
|
141
|
+
* @param issueNo - Issue number
|
|
142
|
+
* @returns PidManager instance for the specified issue
|
|
143
|
+
*
|
|
144
|
+
* @example
|
|
145
|
+
* ```typescript
|
|
146
|
+
* const manager = createIssuePidManager(135);
|
|
147
|
+
* if (manager.isProcessRunning()) {
|
|
148
|
+
* console.log('Worktree server for issue #135 is running');
|
|
149
|
+
* }
|
|
150
|
+
* ```
|
|
151
|
+
*/
|
|
152
|
+
function createIssuePidManager(issueNo) {
|
|
153
|
+
return createPidManager(issueNo);
|
|
154
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Port Allocator
|
|
3
|
+
* Issue #136: Phase 1 - Foundation
|
|
4
|
+
*
|
|
5
|
+
* Provides automatic port allocation for worktree servers.
|
|
6
|
+
* SF-SEC-002: Implements MAX_WORKTREES limit to prevent port exhaustion attacks.
|
|
7
|
+
*
|
|
8
|
+
* @module port-allocator
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Default port range for worktree servers
|
|
12
|
+
* Main server uses 3000, worktrees use 3001-3100
|
|
13
|
+
*/
|
|
14
|
+
export declare const DEFAULT_PORT_RANGE: {
|
|
15
|
+
readonly min: 3001;
|
|
16
|
+
readonly max: 3100;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Maximum number of concurrent worktree servers
|
|
20
|
+
* SF-SEC-002: Prevents port exhaustion attacks
|
|
21
|
+
*/
|
|
22
|
+
export declare const MAX_WORKTREES = 10;
|
|
23
|
+
/**
|
|
24
|
+
* Port range configuration
|
|
25
|
+
*/
|
|
26
|
+
export interface PortRange {
|
|
27
|
+
min: number;
|
|
28
|
+
max: number;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Port allocator for managing worktree server ports
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```typescript
|
|
35
|
+
* const allocator = new PortAllocator();
|
|
36
|
+
* const port = await allocator.findAvailablePort();
|
|
37
|
+
* allocator.markAllocated(port);
|
|
38
|
+
* // ... start server on port ...
|
|
39
|
+
* allocator.release(port);
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export declare class PortAllocator {
|
|
43
|
+
private static instance;
|
|
44
|
+
private readonly range;
|
|
45
|
+
private readonly allocatedPorts;
|
|
46
|
+
private readonly issuePortMap;
|
|
47
|
+
constructor(range?: PortRange);
|
|
48
|
+
/**
|
|
49
|
+
* Get singleton instance
|
|
50
|
+
* Issue #136: Singleton pattern for CLI commands
|
|
51
|
+
*/
|
|
52
|
+
static getInstance(): PortAllocator;
|
|
53
|
+
/**
|
|
54
|
+
* Allocate a port for a specific issue (synchronous, deterministic)
|
|
55
|
+
* Issue #136: Simple port allocation based on issue number
|
|
56
|
+
*
|
|
57
|
+
* @param issueNo - Issue number
|
|
58
|
+
* @returns Allocated port number
|
|
59
|
+
*/
|
|
60
|
+
allocate(issueNo: number): number;
|
|
61
|
+
/**
|
|
62
|
+
* Find an available port in the configured range
|
|
63
|
+
*
|
|
64
|
+
* @returns Available port number
|
|
65
|
+
* @throws AppError with code PORT_EXHAUSTED if no ports available
|
|
66
|
+
* @throws AppError with code MAX_WORKTREES_EXCEEDED if limit reached
|
|
67
|
+
*/
|
|
68
|
+
findAvailablePort(): Promise<number>;
|
|
69
|
+
/**
|
|
70
|
+
* Check if a specific port is available
|
|
71
|
+
*
|
|
72
|
+
* @param port - Port number to check
|
|
73
|
+
* @returns true if port is available
|
|
74
|
+
*/
|
|
75
|
+
isPortAvailable(port: number): Promise<boolean>;
|
|
76
|
+
/**
|
|
77
|
+
* Mark a port as allocated
|
|
78
|
+
*
|
|
79
|
+
* @param port - Port number to mark as allocated
|
|
80
|
+
*/
|
|
81
|
+
markAllocated(port: number): void;
|
|
82
|
+
/**
|
|
83
|
+
* Release an allocated port
|
|
84
|
+
*
|
|
85
|
+
* @param port - Port number to release
|
|
86
|
+
*/
|
|
87
|
+
release(port: number): void;
|
|
88
|
+
/**
|
|
89
|
+
* Get the count of allocated ports
|
|
90
|
+
*/
|
|
91
|
+
getAllocatedCount(): number;
|
|
92
|
+
/**
|
|
93
|
+
* Check if a port is currently allocated (in-memory tracking)
|
|
94
|
+
*
|
|
95
|
+
* @param port - Port number to check
|
|
96
|
+
* @returns true if port is allocated
|
|
97
|
+
*/
|
|
98
|
+
isAllocated(port: number): boolean;
|
|
99
|
+
/**
|
|
100
|
+
* Get all allocated ports
|
|
101
|
+
*/
|
|
102
|
+
getAllocatedPorts(): number[];
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Create a port allocator with default settings
|
|
106
|
+
*/
|
|
107
|
+
export declare function createPortAllocator(range?: PortRange): PortAllocator;
|
|
108
|
+
//# sourceMappingURL=port-allocator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"port-allocator.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/port-allocator.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH;;;GAGG;AACH,eAAO,MAAM,kBAAkB;;;CAGrB,CAAC;AAEX;;;GAGG;AACH,eAAO,MAAM,aAAa,KAAK,CAAC;AAEhC;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAA8B;IACrD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAY;IAClC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAc;IAC7C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAsB;gBAEvC,KAAK,GAAE,SAA8B;IAMjD;;;OAGG;IACH,MAAM,CAAC,WAAW,IAAI,aAAa;IAOnC;;;;;;OAMG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAcjC;;;;;;OAMG;IACG,iBAAiB,IAAI,OAAO,CAAC,MAAM,CAAC;IA4B1C;;;;;OAKG;IACG,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAgBrD;;;;OAIG;IACH,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAIjC;;;;OAIG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAI3B;;OAEG;IACH,iBAAiB,IAAI,MAAM;IAI3B;;;;;OAKG;IACH,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAIlC;;OAEG;IACH,iBAAiB,IAAI,MAAM,EAAE;CAG9B;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,CAAC,EAAE,SAAS,GAAG,aAAa,CAEpE"}
|