commandmate 0.1.12 → 0.2.1
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 +4 -9
- package/.next/BUILD_ID +1 -1
- package/.next/app-build-manifest.json +24 -24
- package/.next/app-path-routes-manifest.json +1 -1
- package/.next/build-manifest.json +7 -7
- 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/react-loadable-manifest.json +7 -7
- package/.next/required-server-files.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 +2 -2
- package/.next/server/app/api/hooks/claude-done/route.js +1 -19
- package/.next/server/app/api/hooks/claude-done/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/clone/[jobId]/route.js +1 -1
- package/.next/server/app/api/repositories/clone/[jobId]/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/clone/route.js +1 -1
- package/.next/server/app/api/repositories/clone/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/excluded/route.js +36 -0
- package/.next/server/app/api/repositories/excluded/route.js.nft.json +1 -0
- package/.next/server/app/api/repositories/excluded.body +1 -0
- package/.next/server/app/api/repositories/excluded.meta +1 -0
- package/.next/server/app/api/repositories/restore/route.js +36 -0
- package/.next/server/app/api/repositories/restore/route.js.nft.json +1 -0
- package/.next/server/app/api/repositories/route.js +36 -1
- package/.next/server/app/api/repositories/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/scan/route.js +1 -1
- package/.next/server/app/api/repositories/sync/route.js +36 -1
- package/.next/server/app/api/slash-commands/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]/capture/route.js +2 -2
- package/.next/server/app/api/worktrees/[id]/current-output/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/current-output/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/interrupt/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/interrupt/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/kill-session/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/kill-session/route.js.nft.json +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 +1 -1
- package/.next/server/app/api/worktrees/[id]/prompt-response/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/prompt-response/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/respond/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/respond/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/route.js.nft.json +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]/send/route.js.nft.json +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]/start-polling/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/terminal/route.js +1 -1
- package/.next/server/app/api/worktrees/route.js +1 -1
- package/.next/server/app/api/worktrees/route.js.nft.json +1 -1
- package/.next/server/app/index.html +2 -2
- package/.next/server/app/index.rsc +3 -3
- package/.next/server/app/page.js +7 -7
- package/.next/server/app/page.js.nft.json +1 -1
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app/proxy/[...path]/route.js +2 -2
- package/.next/server/app/worktrees/[id]/files/[...path]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/page.js +4 -4
- package/.next/server/app/worktrees/[id]/page.js.nft.json +1 -1
- 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 -8
- package/.next/server/chunks/5488.js +36 -0
- package/.next/server/chunks/6550.js +1 -1
- package/.next/server/chunks/7425.js +53 -50
- package/.next/server/chunks/7536.js +1 -0
- package/.next/server/chunks/8174.js +23 -0
- package/.next/server/chunks/9367.js +19 -0
- package/.next/server/middleware-build-manifest.js +1 -1
- package/.next/server/middleware-manifest.json +2 -28
- package/.next/server/middleware-react-loadable-manifest.js +1 -1
- 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/static/chunks/4327.740cc7fe2d0b5049.js +60 -0
- package/.next/static/chunks/4343-ebe884a2a80eb033.js +1 -0
- package/.next/static/chunks/6568-38a33aa67d82e12b.js +1 -0
- package/.next/static/chunks/816-c254f4e2406e696a.js +1 -0
- package/.next/static/chunks/app/layout-4804cfba519283cf.js +1 -0
- package/.next/static/chunks/app/page-3926224c4cdf315b.js +1 -0
- package/.next/static/chunks/app/worktrees/[id]/page-8bd88bdc29607413.js +1 -0
- package/.next/static/chunks/main-b6d727aa9248d4f2.js +1 -0
- package/.next/static/chunks/{webpack-3fc79fab9bb738d7.js → webpack-4f85dcef6279c6ee.js} +1 -1
- package/.next/static/css/28be35e4727ae7ef.css +3 -0
- package/.next/trace +5 -5
- package/.next/types/app/api/repositories/excluded/route.ts +343 -0
- package/.next/types/app/api/repositories/restore/route.ts +343 -0
- package/README.md +2 -2
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +2 -13
- package/dist/cli/commands/start.d.ts.map +1 -1
- package/dist/cli/commands/start.js +3 -7
- package/dist/cli/config/security-messages.d.ts +11 -0
- package/dist/cli/config/security-messages.d.ts.map +1 -0
- package/dist/cli/config/security-messages.js +29 -0
- package/dist/cli/types/index.d.ts +0 -1
- package/dist/cli/types/index.d.ts.map +1 -1
- package/dist/cli/utils/daemon.d.ts.map +1 -1
- package/dist/cli/utils/daemon.js +3 -7
- package/dist/cli/utils/env-setup.d.ts +0 -4
- package/dist/cli/utils/env-setup.d.ts.map +1 -1
- package/dist/cli/utils/env-setup.js +0 -14
- package/dist/cli/utils/security-logger.d.ts.map +1 -1
- package/dist/cli/utils/security-logger.js +1 -2
- package/dist/server/server.js +25 -2
- package/dist/server/src/lib/auto-yes-manager.js +100 -11
- package/dist/server/src/lib/claude-poller.js +341 -0
- package/dist/server/src/lib/claude-session.js +48 -19
- package/dist/server/src/lib/cli-patterns.js +69 -6
- package/dist/server/src/lib/cli-tools/base.js +7 -1
- package/dist/server/src/lib/cli-tools/codex.js +14 -2
- package/dist/server/src/lib/cli-tools/manager.js +27 -0
- package/dist/server/src/lib/cli-tools/types.js +7 -0
- package/dist/server/src/lib/cli-tools/validation.js +41 -0
- package/dist/server/src/lib/db-repository.js +482 -0
- package/dist/server/src/lib/db.js +23 -0
- package/dist/server/src/lib/env.js +0 -17
- package/dist/server/src/lib/logger.js +0 -4
- package/dist/server/src/lib/prompt-detector.js +297 -109
- package/dist/server/src/lib/response-poller.js +73 -27
- package/dist/server/src/lib/tmux.js +48 -0
- package/dist/server/src/lib/ws-server.js +12 -1
- package/dist/server/src/types/sidebar.js +16 -31
- package/dist/server/src/types/slash-commands.js +2 -0
- package/package.json +1 -1
- package/.next/server/chunks/1318.js +0 -29
- package/.next/server/chunks/2597.js +0 -1
- package/.next/server/chunks/2648.js +0 -1
- package/.next/server/chunks/9703.js +0 -31
- package/.next/server/chunks/9723.js +0 -19
- package/.next/server/edge-runtime-webpack.js +0 -2
- package/.next/server/edge-runtime-webpack.js.map +0 -1
- package/.next/server/src/middleware.js +0 -14
- package/.next/server/src/middleware.js.map +0 -1
- package/.next/static/chunks/2853-d11a80b03c9a1640.js +0 -1
- package/.next/static/chunks/4327.3b84aa049900fdeb.js +0 -60
- package/.next/static/chunks/816-7e340dad784be28c.js +0 -1
- package/.next/static/chunks/9365-733d8c05712d2888.js +0 -1
- package/.next/static/chunks/app/layout-37e55f11dcc8b1bf.js +0 -1
- package/.next/static/chunks/app/page-fe35d61f14b90a51.js +0 -1
- package/.next/static/chunks/app/worktrees/[id]/page-58fcf2e63c056743.js +0 -1
- package/.next/static/chunks/main-a960f4a5e1a2f598.js +0 -1
- package/.next/static/css/376b339640084689.css +0 -3
- /package/.next/static/{564GHwluX5xIv9qpqLJV2 → oUD-A998xeBoez6zsrTH3}/_buildManifest.js +0 -0
- /package/.next/static/{564GHwluX5xIv9qpqLJV2 → oUD-A998xeBoez6zsrTH3}/_ssgManifest.js +0 -0
|
@@ -8,6 +8,8 @@ exports.CLIToolManager = void 0;
|
|
|
8
8
|
const claude_1 = require("./claude");
|
|
9
9
|
const codex_1 = require("./codex");
|
|
10
10
|
const gemini_1 = require("./gemini");
|
|
11
|
+
const response_poller_1 = require("../response-poller");
|
|
12
|
+
const claude_poller_1 = require("../claude-poller");
|
|
11
13
|
/**
|
|
12
14
|
* CLI Tool Manager (Singleton)
|
|
13
15
|
* Provides centralized access to all CLI tools
|
|
@@ -139,5 +141,30 @@ class CLIToolManager {
|
|
|
139
141
|
const allInfo = await this.getAllToolsInfo();
|
|
140
142
|
return allInfo.filter(info => info.installed);
|
|
141
143
|
}
|
|
144
|
+
/**
|
|
145
|
+
* Stop pollers for a specific worktree and CLI tool
|
|
146
|
+
* T2.4: Abstraction for poller stopping (MF1-001 DIP compliance)
|
|
147
|
+
*
|
|
148
|
+
* This method abstracts the poller stopping logic so API layer
|
|
149
|
+
* doesn't need to know about specific poller implementations.
|
|
150
|
+
*
|
|
151
|
+
* @param worktreeId - Worktree ID
|
|
152
|
+
* @param cliToolId - CLI tool ID
|
|
153
|
+
*
|
|
154
|
+
* @example
|
|
155
|
+
* ```typescript
|
|
156
|
+
* const manager = CLIToolManager.getInstance();
|
|
157
|
+
* manager.stopPollers('my-worktree', 'claude');
|
|
158
|
+
* ```
|
|
159
|
+
*/
|
|
160
|
+
stopPollers(worktreeId, cliToolId) {
|
|
161
|
+
// Stop response-poller for all tools
|
|
162
|
+
(0, response_poller_1.stopPolling)(worktreeId, cliToolId);
|
|
163
|
+
// claude-poller is Claude-specific
|
|
164
|
+
if (cliToolId === 'claude') {
|
|
165
|
+
(0, claude_poller_1.stopPolling)(worktreeId);
|
|
166
|
+
}
|
|
167
|
+
// Future: Add other tool-specific pollers here if needed
|
|
168
|
+
}
|
|
142
169
|
}
|
|
143
170
|
exports.CLIToolManager = CLIToolManager;
|
|
@@ -3,3 +3,10 @@
|
|
|
3
3
|
* Type definitions and interfaces for CLI tools
|
|
4
4
|
*/
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.CLI_TOOL_IDS = void 0;
|
|
7
|
+
/**
|
|
8
|
+
* CLI Tool IDs constant array
|
|
9
|
+
* T2.1: Single source of truth for CLI tool IDs
|
|
10
|
+
* CLIToolType is derived from this constant (DRY principle)
|
|
11
|
+
*/
|
|
12
|
+
exports.CLI_TOOL_IDS = ['claude', 'codex', 'gemini'];
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Session name validation module
|
|
4
|
+
* Issue #4: T2.2 - Security validation for session names (MF4-001)
|
|
5
|
+
*
|
|
6
|
+
* This module provides validation functions to prevent command injection
|
|
7
|
+
* attacks through session names used in tmux commands.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.SESSION_NAME_PATTERN = void 0;
|
|
11
|
+
exports.validateSessionName = validateSessionName;
|
|
12
|
+
/**
|
|
13
|
+
* Session name pattern
|
|
14
|
+
* Only allows alphanumeric characters, underscores, and hyphens
|
|
15
|
+
* This prevents command injection through shell special characters
|
|
16
|
+
*
|
|
17
|
+
* Pattern breakdown:
|
|
18
|
+
* - ^ : Start of string
|
|
19
|
+
* - [a-zA-Z0-9_-] : Allowed characters (alphanumeric, underscore, hyphen)
|
|
20
|
+
* - + : One or more characters (empty strings not allowed)
|
|
21
|
+
* - $ : End of string
|
|
22
|
+
*/
|
|
23
|
+
exports.SESSION_NAME_PATTERN = /^[a-zA-Z0-9_-]+$/;
|
|
24
|
+
/**
|
|
25
|
+
* Validate session name format
|
|
26
|
+
* Throws an error if the session name contains invalid characters
|
|
27
|
+
*
|
|
28
|
+
* @param sessionName - Session name to validate
|
|
29
|
+
* @throws Error if session name is invalid
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```typescript
|
|
33
|
+
* validateSessionName('mcbd-claude-test'); // OK
|
|
34
|
+
* validateSessionName('test;rm -rf'); // Throws Error
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
function validateSessionName(sessionName) {
|
|
38
|
+
if (!exports.SESSION_NAME_PATTERN.test(sessionName)) {
|
|
39
|
+
throw new Error(`Invalid session name format: ${sessionName}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,482 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Repository and Clone Job Database Operations
|
|
4
|
+
* Issue #71: Clone URL registration feature
|
|
5
|
+
* Issue #190: Repository exclusion on sync
|
|
6
|
+
*/
|
|
7
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
8
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
9
|
+
};
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.MAX_DISABLED_REPOSITORIES = void 0;
|
|
12
|
+
exports.createRepository = createRepository;
|
|
13
|
+
exports.getRepositoryByNormalizedUrl = getRepositoryByNormalizedUrl;
|
|
14
|
+
exports.getRepositoryById = getRepositoryById;
|
|
15
|
+
exports.getRepositoryByPath = getRepositoryByPath;
|
|
16
|
+
exports.updateRepository = updateRepository;
|
|
17
|
+
exports.getAllRepositories = getAllRepositories;
|
|
18
|
+
exports.resolveRepositoryPath = resolveRepositoryPath;
|
|
19
|
+
exports.validateRepositoryPath = validateRepositoryPath;
|
|
20
|
+
exports.ensureEnvRepositoriesRegistered = ensureEnvRepositoriesRegistered;
|
|
21
|
+
exports.filterExcludedPaths = filterExcludedPaths;
|
|
22
|
+
exports.registerAndFilterRepositories = registerAndFilterRepositories;
|
|
23
|
+
exports.disableRepository = disableRepository;
|
|
24
|
+
exports.getExcludedRepositoryPaths = getExcludedRepositoryPaths;
|
|
25
|
+
exports.getExcludedRepositories = getExcludedRepositories;
|
|
26
|
+
exports.restoreRepository = restoreRepository;
|
|
27
|
+
exports.createCloneJob = createCloneJob;
|
|
28
|
+
exports.getCloneJob = getCloneJob;
|
|
29
|
+
exports.updateCloneJob = updateCloneJob;
|
|
30
|
+
exports.getActiveCloneJobByUrl = getActiveCloneJobByUrl;
|
|
31
|
+
exports.getCloneJobsByStatus = getCloneJobsByStatus;
|
|
32
|
+
const crypto_1 = require("crypto");
|
|
33
|
+
const path_1 = __importDefault(require("path"));
|
|
34
|
+
const system_directories_1 = require("../config/system-directories");
|
|
35
|
+
/**
|
|
36
|
+
* Map repository row to Repository model
|
|
37
|
+
*/
|
|
38
|
+
function mapRepositoryRow(row) {
|
|
39
|
+
return {
|
|
40
|
+
id: row.id,
|
|
41
|
+
name: row.name,
|
|
42
|
+
path: row.path,
|
|
43
|
+
enabled: row.enabled === 1,
|
|
44
|
+
cloneUrl: row.clone_url || undefined,
|
|
45
|
+
normalizedCloneUrl: row.normalized_clone_url || undefined,
|
|
46
|
+
cloneSource: row.clone_source,
|
|
47
|
+
isEnvManaged: row.is_env_managed === 1,
|
|
48
|
+
createdAt: new Date(row.created_at),
|
|
49
|
+
updatedAt: new Date(row.updated_at),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Map clone job row to CloneJobDB model
|
|
54
|
+
*/
|
|
55
|
+
function mapCloneJobRow(row) {
|
|
56
|
+
return {
|
|
57
|
+
id: row.id,
|
|
58
|
+
cloneUrl: row.clone_url,
|
|
59
|
+
normalizedCloneUrl: row.normalized_clone_url,
|
|
60
|
+
targetPath: row.target_path,
|
|
61
|
+
repositoryId: row.repository_id || undefined,
|
|
62
|
+
status: row.status,
|
|
63
|
+
pid: row.pid || undefined,
|
|
64
|
+
progress: row.progress,
|
|
65
|
+
errorCategory: row.error_category || undefined,
|
|
66
|
+
errorCode: row.error_code || undefined,
|
|
67
|
+
errorMessage: row.error_message || undefined,
|
|
68
|
+
startedAt: row.started_at ? new Date(row.started_at) : undefined,
|
|
69
|
+
completedAt: row.completed_at ? new Date(row.completed_at) : undefined,
|
|
70
|
+
createdAt: new Date(row.created_at),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
// ============================================================
|
|
74
|
+
// Repository Operations
|
|
75
|
+
// ============================================================
|
|
76
|
+
/**
|
|
77
|
+
* Create a new repository
|
|
78
|
+
*/
|
|
79
|
+
function createRepository(db, data) {
|
|
80
|
+
const id = (0, crypto_1.randomUUID)();
|
|
81
|
+
const now = Date.now();
|
|
82
|
+
const stmt = db.prepare(`
|
|
83
|
+
INSERT INTO repositories (
|
|
84
|
+
id, name, path, enabled, clone_url, normalized_clone_url,
|
|
85
|
+
clone_source, is_env_managed, created_at, updated_at
|
|
86
|
+
)
|
|
87
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
88
|
+
`);
|
|
89
|
+
stmt.run(id, data.name, data.path, data.enabled !== false ? 1 : 0, data.cloneUrl || null, data.normalizedCloneUrl || null, data.cloneSource, data.isEnvManaged ? 1 : 0, now, now);
|
|
90
|
+
return {
|
|
91
|
+
id,
|
|
92
|
+
name: data.name,
|
|
93
|
+
path: data.path,
|
|
94
|
+
enabled: data.enabled !== false,
|
|
95
|
+
cloneUrl: data.cloneUrl,
|
|
96
|
+
normalizedCloneUrl: data.normalizedCloneUrl,
|
|
97
|
+
cloneSource: data.cloneSource,
|
|
98
|
+
isEnvManaged: data.isEnvManaged || false,
|
|
99
|
+
createdAt: new Date(now),
|
|
100
|
+
updatedAt: new Date(now),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Get repository by normalized clone URL
|
|
105
|
+
*/
|
|
106
|
+
function getRepositoryByNormalizedUrl(db, normalizedCloneUrl) {
|
|
107
|
+
const stmt = db.prepare(`
|
|
108
|
+
SELECT * FROM repositories
|
|
109
|
+
WHERE normalized_clone_url = ?
|
|
110
|
+
`);
|
|
111
|
+
const row = stmt.get(normalizedCloneUrl);
|
|
112
|
+
return row ? mapRepositoryRow(row) : null;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Get repository by ID
|
|
116
|
+
*/
|
|
117
|
+
function getRepositoryById(db, id) {
|
|
118
|
+
const stmt = db.prepare(`
|
|
119
|
+
SELECT * FROM repositories
|
|
120
|
+
WHERE id = ?
|
|
121
|
+
`);
|
|
122
|
+
const row = stmt.get(id);
|
|
123
|
+
return row ? mapRepositoryRow(row) : null;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Get repository by path
|
|
127
|
+
*/
|
|
128
|
+
function getRepositoryByPath(db, path) {
|
|
129
|
+
const stmt = db.prepare(`
|
|
130
|
+
SELECT * FROM repositories
|
|
131
|
+
WHERE path = ?
|
|
132
|
+
`);
|
|
133
|
+
const row = stmt.get(path);
|
|
134
|
+
return row ? mapRepositoryRow(row) : null;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Update repository
|
|
138
|
+
*/
|
|
139
|
+
function updateRepository(db, id, updates) {
|
|
140
|
+
const now = Date.now();
|
|
141
|
+
const assignments = ['updated_at = ?'];
|
|
142
|
+
const params = [now];
|
|
143
|
+
if (updates.name !== undefined) {
|
|
144
|
+
assignments.push('name = ?');
|
|
145
|
+
params.push(updates.name);
|
|
146
|
+
}
|
|
147
|
+
if (updates.enabled !== undefined) {
|
|
148
|
+
assignments.push('enabled = ?');
|
|
149
|
+
params.push(updates.enabled ? 1 : 0);
|
|
150
|
+
}
|
|
151
|
+
if (updates.cloneUrl !== undefined) {
|
|
152
|
+
assignments.push('clone_url = ?');
|
|
153
|
+
params.push(updates.cloneUrl || null);
|
|
154
|
+
}
|
|
155
|
+
if (updates.normalizedCloneUrl !== undefined) {
|
|
156
|
+
assignments.push('normalized_clone_url = ?');
|
|
157
|
+
params.push(updates.normalizedCloneUrl || null);
|
|
158
|
+
}
|
|
159
|
+
params.push(id);
|
|
160
|
+
const stmt = db.prepare(`
|
|
161
|
+
UPDATE repositories
|
|
162
|
+
SET ${assignments.join(', ')}
|
|
163
|
+
WHERE id = ?
|
|
164
|
+
`);
|
|
165
|
+
stmt.run(...params);
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Get all repositories
|
|
169
|
+
*/
|
|
170
|
+
function getAllRepositories(db) {
|
|
171
|
+
const stmt = db.prepare(`
|
|
172
|
+
SELECT * FROM repositories
|
|
173
|
+
ORDER BY name ASC
|
|
174
|
+
`);
|
|
175
|
+
const rows = stmt.all();
|
|
176
|
+
return rows.map(mapRepositoryRow);
|
|
177
|
+
}
|
|
178
|
+
// ============================================================
|
|
179
|
+
// Repository Exclusion Operations (Issue #190)
|
|
180
|
+
// ============================================================
|
|
181
|
+
/**
|
|
182
|
+
* Maximum number of disabled repositories allowed.
|
|
183
|
+
* Prevents unlimited record accumulation from malicious or buggy DELETE requests.
|
|
184
|
+
* SEC-SF-004
|
|
185
|
+
*/
|
|
186
|
+
exports.MAX_DISABLED_REPOSITORIES = 1000;
|
|
187
|
+
/**
|
|
188
|
+
* Resolve and normalize a repository path.
|
|
189
|
+
* All path normalization is centralized here to prevent inconsistencies.
|
|
190
|
+
*
|
|
191
|
+
* NOTE: path.resolve() removes trailing slashes and resolves relative paths
|
|
192
|
+
* but does NOT resolve symlinks. For symlink resolution, use fs.realpathSync().
|
|
193
|
+
* See design policy Section 7 for the symlink handling policy.
|
|
194
|
+
*
|
|
195
|
+
* SF-001: DRY - centralized path normalization
|
|
196
|
+
*/
|
|
197
|
+
function resolveRepositoryPath(repoPath) {
|
|
198
|
+
return path_1.default.resolve(repoPath);
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Validate and resolve a repository path for API requests.
|
|
202
|
+
* Centralizes all validation checks to avoid duplication across route handlers.
|
|
203
|
+
*
|
|
204
|
+
* Checks performed:
|
|
205
|
+
* 1. Presence and type check (must be non-empty string)
|
|
206
|
+
* 2. Null byte check (path traversal prevention, SEC-MF-001)
|
|
207
|
+
* 3. System directory check (prevents operations on /etc, /usr, etc.)
|
|
208
|
+
*
|
|
209
|
+
* DRY: Used by DELETE /api/repositories and PUT /api/repositories/restore
|
|
210
|
+
*/
|
|
211
|
+
function validateRepositoryPath(repositoryPath) {
|
|
212
|
+
if (!repositoryPath || typeof repositoryPath !== 'string') {
|
|
213
|
+
return { valid: false, error: 'repositoryPath is required' };
|
|
214
|
+
}
|
|
215
|
+
if (repositoryPath.includes('\0')) {
|
|
216
|
+
return { valid: false, error: 'Invalid repository path' };
|
|
217
|
+
}
|
|
218
|
+
const resolvedPath = resolveRepositoryPath(repositoryPath);
|
|
219
|
+
if ((0, system_directories_1.isSystemDirectory)(resolvedPath)) {
|
|
220
|
+
return { valid: false, error: 'Invalid repository path' };
|
|
221
|
+
}
|
|
222
|
+
return { valid: true, resolvedPath };
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Register environment variable repositories to the repositories table.
|
|
226
|
+
* Idempotent: already registered repositories are skipped (regardless of enabled status).
|
|
227
|
+
*
|
|
228
|
+
* NOTE (MF-C01): createRepository() treats enabled as follows:
|
|
229
|
+
* - true -> SQLite 1 (enabled)
|
|
230
|
+
* - false -> SQLite 0 (disabled)
|
|
231
|
+
* - undefined -> SQLite 1 (enabled, due to `data.enabled !== false ? 1 : 0` logic)
|
|
232
|
+
* We explicitly pass enabled: true to avoid relying on the implicit default.
|
|
233
|
+
*
|
|
234
|
+
* MF-001: SRP - registration logic separated from sync route
|
|
235
|
+
*/
|
|
236
|
+
function ensureEnvRepositoriesRegistered(db, repositoryPaths) {
|
|
237
|
+
for (const repoPath of repositoryPaths) {
|
|
238
|
+
const resolvedPath = resolveRepositoryPath(repoPath);
|
|
239
|
+
const existing = getRepositoryByPath(db, resolvedPath);
|
|
240
|
+
if (!existing) {
|
|
241
|
+
createRepository(db, {
|
|
242
|
+
name: path_1.default.basename(resolvedPath),
|
|
243
|
+
path: resolvedPath,
|
|
244
|
+
cloneSource: 'local',
|
|
245
|
+
isEnvManaged: true,
|
|
246
|
+
enabled: true, // Explicit: do not rely on undefined -> 1 default
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Filter out excluded repository paths (enabled=0).
|
|
253
|
+
* Exclusion logic is encapsulated here, so changes to exclusion criteria
|
|
254
|
+
* (e.g., pattern-based exclusion, temporary exclusion) only affect this function.
|
|
255
|
+
*
|
|
256
|
+
* @requires ensureEnvRepositoriesRegistered() must be called before this function
|
|
257
|
+
* to ensure all paths exist in the repositories table.
|
|
258
|
+
* Without prior registration, unregistered paths will not be filtered correctly.
|
|
259
|
+
*
|
|
260
|
+
* NOTE (SEC-SF-002): Array.includes() performs case-sensitive string comparison.
|
|
261
|
+
* On macOS (case-insensitive filesystem), paths with different casing would not match.
|
|
262
|
+
* resolveRepositoryPath() normalization on both sides mitigates most cases.
|
|
263
|
+
* On Linux (case-sensitive filesystem), the behavior is consistent.
|
|
264
|
+
*
|
|
265
|
+
* @param db - Database instance
|
|
266
|
+
* @param repositoryPaths - Array of repository paths to filter
|
|
267
|
+
* @returns Filtered array excluding disabled repositories
|
|
268
|
+
*
|
|
269
|
+
* SF-003: OCP - exclusion logic encapsulated
|
|
270
|
+
*/
|
|
271
|
+
function filterExcludedPaths(db, repositoryPaths) {
|
|
272
|
+
const excludedPaths = getExcludedRepositoryPaths(db);
|
|
273
|
+
return repositoryPaths.filter(p => !excludedPaths.includes(resolveRepositoryPath(p)));
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Register environment variable repositories and filter out excluded ones.
|
|
277
|
+
* Encapsulates the ordering constraint: registration MUST happen before filtering.
|
|
278
|
+
*
|
|
279
|
+
* This function combines ensureEnvRepositoriesRegistered() and filterExcludedPaths()
|
|
280
|
+
* into a single atomic operation to prevent callers from accidentally reversing the order.
|
|
281
|
+
*
|
|
282
|
+
* DRY: Used by server.ts initializeWorktrees() and POST /api/repositories/sync
|
|
283
|
+
*
|
|
284
|
+
* @param db - Database instance
|
|
285
|
+
* @param repositoryPaths - Array of repository paths from environment variables
|
|
286
|
+
* @returns ExclusionSummary with filtered paths, excluded paths, and count
|
|
287
|
+
*/
|
|
288
|
+
function registerAndFilterRepositories(db, repositoryPaths) {
|
|
289
|
+
// Step 1: Register (must be before filter - see design policy Section 4)
|
|
290
|
+
ensureEnvRepositoriesRegistered(db, repositoryPaths);
|
|
291
|
+
// Step 2: Filter
|
|
292
|
+
const filteredPaths = filterExcludedPaths(db, repositoryPaths);
|
|
293
|
+
const excludedPaths = repositoryPaths.filter(p => !filteredPaths.includes(p));
|
|
294
|
+
return {
|
|
295
|
+
filteredPaths,
|
|
296
|
+
excludedPaths,
|
|
297
|
+
excludedCount: excludedPaths.length,
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Disable a repository by setting enabled=0.
|
|
302
|
+
* If the repository is not registered, create it with enabled=0.
|
|
303
|
+
* All internal logic (lookup + update/create) is encapsulated.
|
|
304
|
+
*
|
|
305
|
+
* NOTE (MF-C01): Explicitly passes enabled: false to createRepository().
|
|
306
|
+
* The internal mapping `data.enabled !== false ? 1 : 0` will correctly
|
|
307
|
+
* store 0 in SQLite. Do NOT pass undefined for enabled.
|
|
308
|
+
*
|
|
309
|
+
* NOTE (SEC-SF-004): When creating a new record, checks the count of
|
|
310
|
+
* disabled repositories against MAX_DISABLED_REPOSITORIES to prevent
|
|
311
|
+
* unlimited record accumulation from malicious or buggy DELETE requests.
|
|
312
|
+
*
|
|
313
|
+
* SF-002: SRP - disable logic encapsulated
|
|
314
|
+
*/
|
|
315
|
+
function disableRepository(db, repositoryPath) {
|
|
316
|
+
const resolvedPath = resolveRepositoryPath(repositoryPath);
|
|
317
|
+
const repo = getRepositoryByPath(db, resolvedPath);
|
|
318
|
+
if (repo) {
|
|
319
|
+
updateRepository(db, repo.id, { enabled: false });
|
|
320
|
+
}
|
|
321
|
+
else {
|
|
322
|
+
// SEC-SF-004: Check disabled repository count limit before creating new record
|
|
323
|
+
const disabledCount = db.prepare('SELECT COUNT(*) as count FROM repositories WHERE enabled = 0').get();
|
|
324
|
+
if (disabledCount.count >= exports.MAX_DISABLED_REPOSITORIES) {
|
|
325
|
+
throw new Error('Disabled repository limit exceeded');
|
|
326
|
+
}
|
|
327
|
+
createRepository(db, {
|
|
328
|
+
name: path_1.default.basename(resolvedPath),
|
|
329
|
+
path: resolvedPath,
|
|
330
|
+
cloneSource: 'local',
|
|
331
|
+
isEnvManaged: false,
|
|
332
|
+
enabled: false, // Explicit: do not rely on undefined -> 1 default
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Get paths of excluded (enabled=0) repositories
|
|
338
|
+
*/
|
|
339
|
+
function getExcludedRepositoryPaths(db) {
|
|
340
|
+
const stmt = db.prepare('SELECT path FROM repositories WHERE enabled = 0');
|
|
341
|
+
const rows = stmt.all();
|
|
342
|
+
return rows.map(r => r.path);
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Get excluded repositories with full details
|
|
346
|
+
*/
|
|
347
|
+
function getExcludedRepositories(db) {
|
|
348
|
+
const stmt = db.prepare('SELECT * FROM repositories WHERE enabled = 0 ORDER BY name ASC');
|
|
349
|
+
const rows = stmt.all();
|
|
350
|
+
return rows.map(mapRepositoryRow);
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* Restore an excluded repository by setting enabled=1
|
|
354
|
+
*
|
|
355
|
+
* @returns Restored Repository object, or null if not found
|
|
356
|
+
*/
|
|
357
|
+
function restoreRepository(db, repoPath) {
|
|
358
|
+
const resolvedPath = resolveRepositoryPath(repoPath);
|
|
359
|
+
const repo = getRepositoryByPath(db, resolvedPath);
|
|
360
|
+
if (!repo)
|
|
361
|
+
return null;
|
|
362
|
+
updateRepository(db, repo.id, { enabled: true });
|
|
363
|
+
return { ...repo, enabled: true };
|
|
364
|
+
}
|
|
365
|
+
// ============================================================
|
|
366
|
+
// Clone Job Operations
|
|
367
|
+
// ============================================================
|
|
368
|
+
/**
|
|
369
|
+
* Create a new clone job
|
|
370
|
+
*/
|
|
371
|
+
function createCloneJob(db, data) {
|
|
372
|
+
const id = (0, crypto_1.randomUUID)();
|
|
373
|
+
const now = Date.now();
|
|
374
|
+
const stmt = db.prepare(`
|
|
375
|
+
INSERT INTO clone_jobs (
|
|
376
|
+
id, clone_url, normalized_clone_url, target_path,
|
|
377
|
+
status, progress, created_at
|
|
378
|
+
)
|
|
379
|
+
VALUES (?, ?, ?, ?, 'pending', 0, ?)
|
|
380
|
+
`);
|
|
381
|
+
stmt.run(id, data.cloneUrl, data.normalizedCloneUrl, data.targetPath, now);
|
|
382
|
+
return {
|
|
383
|
+
id,
|
|
384
|
+
cloneUrl: data.cloneUrl,
|
|
385
|
+
normalizedCloneUrl: data.normalizedCloneUrl,
|
|
386
|
+
targetPath: data.targetPath,
|
|
387
|
+
status: 'pending',
|
|
388
|
+
progress: 0,
|
|
389
|
+
createdAt: new Date(now),
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Get clone job by ID
|
|
394
|
+
*/
|
|
395
|
+
function getCloneJob(db, id) {
|
|
396
|
+
const stmt = db.prepare(`
|
|
397
|
+
SELECT * FROM clone_jobs
|
|
398
|
+
WHERE id = ?
|
|
399
|
+
`);
|
|
400
|
+
const row = stmt.get(id);
|
|
401
|
+
return row ? mapCloneJobRow(row) : null;
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Update clone job
|
|
405
|
+
*/
|
|
406
|
+
function updateCloneJob(db, id, updates) {
|
|
407
|
+
const assignments = [];
|
|
408
|
+
const params = [];
|
|
409
|
+
if (updates.status !== undefined) {
|
|
410
|
+
assignments.push('status = ?');
|
|
411
|
+
params.push(updates.status);
|
|
412
|
+
}
|
|
413
|
+
if (updates.pid !== undefined) {
|
|
414
|
+
assignments.push('pid = ?');
|
|
415
|
+
params.push(updates.pid);
|
|
416
|
+
}
|
|
417
|
+
if (updates.progress !== undefined) {
|
|
418
|
+
assignments.push('progress = ?');
|
|
419
|
+
params.push(updates.progress);
|
|
420
|
+
}
|
|
421
|
+
if (updates.repositoryId !== undefined) {
|
|
422
|
+
assignments.push('repository_id = ?');
|
|
423
|
+
params.push(updates.repositoryId);
|
|
424
|
+
}
|
|
425
|
+
if (updates.errorCategory !== undefined) {
|
|
426
|
+
assignments.push('error_category = ?');
|
|
427
|
+
params.push(updates.errorCategory);
|
|
428
|
+
}
|
|
429
|
+
if (updates.errorCode !== undefined) {
|
|
430
|
+
assignments.push('error_code = ?');
|
|
431
|
+
params.push(updates.errorCode);
|
|
432
|
+
}
|
|
433
|
+
if (updates.errorMessage !== undefined) {
|
|
434
|
+
assignments.push('error_message = ?');
|
|
435
|
+
params.push(updates.errorMessage);
|
|
436
|
+
}
|
|
437
|
+
if (updates.startedAt !== undefined) {
|
|
438
|
+
assignments.push('started_at = ?');
|
|
439
|
+
params.push(updates.startedAt.getTime());
|
|
440
|
+
}
|
|
441
|
+
if (updates.completedAt !== undefined) {
|
|
442
|
+
assignments.push('completed_at = ?');
|
|
443
|
+
params.push(updates.completedAt.getTime());
|
|
444
|
+
}
|
|
445
|
+
if (assignments.length === 0) {
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
params.push(id);
|
|
449
|
+
const stmt = db.prepare(`
|
|
450
|
+
UPDATE clone_jobs
|
|
451
|
+
SET ${assignments.join(', ')}
|
|
452
|
+
WHERE id = ?
|
|
453
|
+
`);
|
|
454
|
+
stmt.run(...params);
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* Get active clone job by normalized URL
|
|
458
|
+
* Active jobs are those with status 'pending' or 'running'
|
|
459
|
+
*/
|
|
460
|
+
function getActiveCloneJobByUrl(db, normalizedCloneUrl) {
|
|
461
|
+
const stmt = db.prepare(`
|
|
462
|
+
SELECT * FROM clone_jobs
|
|
463
|
+
WHERE normalized_clone_url = ?
|
|
464
|
+
AND status IN ('pending', 'running')
|
|
465
|
+
ORDER BY created_at DESC
|
|
466
|
+
LIMIT 1
|
|
467
|
+
`);
|
|
468
|
+
const row = stmt.get(normalizedCloneUrl);
|
|
469
|
+
return row ? mapCloneJobRow(row) : null;
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* Get clone jobs by status
|
|
473
|
+
*/
|
|
474
|
+
function getCloneJobsByStatus(db, status) {
|
|
475
|
+
const stmt = db.prepare(`
|
|
476
|
+
SELECT * FROM clone_jobs
|
|
477
|
+
WHERE status = ?
|
|
478
|
+
ORDER BY created_at DESC
|
|
479
|
+
`);
|
|
480
|
+
const rows = stmt.all(status);
|
|
481
|
+
return rows.map(mapCloneJobRow);
|
|
482
|
+
}
|
|
@@ -19,6 +19,7 @@ exports.getMessages = getMessages;
|
|
|
19
19
|
exports.getLastUserMessage = getLastUserMessage;
|
|
20
20
|
exports.getLastMessage = getLastMessage;
|
|
21
21
|
exports.deleteAllMessages = deleteAllMessages;
|
|
22
|
+
exports.deleteMessagesByCliTool = deleteMessagesByCliTool;
|
|
22
23
|
exports.getSessionState = getSessionState;
|
|
23
24
|
exports.updateSessionState = updateSessionState;
|
|
24
25
|
exports.setInProgressMessageId = setInProgressMessageId;
|
|
@@ -458,6 +459,28 @@ function deleteAllMessages(db, worktreeId) {
|
|
|
458
459
|
stmt.run(worktreeId);
|
|
459
460
|
console.log(`[deleteAllMessages] Deleted all messages for worktree: ${worktreeId}`);
|
|
460
461
|
}
|
|
462
|
+
/**
|
|
463
|
+
* Delete messages for a specific CLI tool in a worktree
|
|
464
|
+
* Issue #4: T4.2 - Individual CLI tool session termination (MF3-001)
|
|
465
|
+
*
|
|
466
|
+
* Used when killing only a specific CLI tool's session to clear its message history
|
|
467
|
+
* while preserving messages from other CLI tools.
|
|
468
|
+
* Note: Log files are preserved for historical reference
|
|
469
|
+
*
|
|
470
|
+
* @param db - Database instance
|
|
471
|
+
* @param worktreeId - Worktree ID
|
|
472
|
+
* @param cliTool - CLI tool ID to delete messages for
|
|
473
|
+
* @returns Number of deleted messages
|
|
474
|
+
*/
|
|
475
|
+
function deleteMessagesByCliTool(db, worktreeId, cliTool) {
|
|
476
|
+
const stmt = db.prepare(`
|
|
477
|
+
DELETE FROM chat_messages
|
|
478
|
+
WHERE worktree_id = ? AND cli_tool_id = ?
|
|
479
|
+
`);
|
|
480
|
+
const result = stmt.run(worktreeId, cliTool);
|
|
481
|
+
console.log(`[deleteMessagesByCliTool] Deleted ${result.changes} messages for worktree: ${worktreeId}, cliTool: ${cliTool}`);
|
|
482
|
+
return result.changes;
|
|
483
|
+
}
|
|
461
484
|
/**
|
|
462
485
|
* Get session state for a worktree
|
|
463
486
|
*/
|
|
@@ -22,7 +22,6 @@ exports.getDatabasePathWithDeprecationWarning = getDatabasePathWithDeprecationWa
|
|
|
22
22
|
exports.getLogConfig = getLogConfig;
|
|
23
23
|
exports.getEnv = getEnv;
|
|
24
24
|
exports.validateEnv = validateEnv;
|
|
25
|
-
exports.isAuthRequired = isAuthRequired;
|
|
26
25
|
const path_1 = __importDefault(require("path"));
|
|
27
26
|
const db_path_resolver_1 = require("./db-path-resolver");
|
|
28
27
|
// ============================================================
|
|
@@ -37,7 +36,6 @@ exports.ENV_MAPPING = {
|
|
|
37
36
|
CM_ROOT_DIR: 'MCBD_ROOT_DIR',
|
|
38
37
|
CM_PORT: 'MCBD_PORT',
|
|
39
38
|
CM_BIND: 'MCBD_BIND',
|
|
40
|
-
CM_AUTH_TOKEN: 'MCBD_AUTH_TOKEN',
|
|
41
39
|
CM_LOG_LEVEL: 'MCBD_LOG_LEVEL',
|
|
42
40
|
CM_LOG_FORMAT: 'MCBD_LOG_FORMAT',
|
|
43
41
|
CM_LOG_DIR: 'MCBD_LOG_DIR',
|
|
@@ -160,7 +158,6 @@ function getEnv() {
|
|
|
160
158
|
const rootDir = getEnvByKey('CM_ROOT_DIR') || process.cwd();
|
|
161
159
|
const port = parseInt(getEnvByKey('CM_PORT') || '3000', 10);
|
|
162
160
|
const bind = getEnvByKey('CM_BIND') || '127.0.0.1';
|
|
163
|
-
const authToken = getEnvByKey('CM_AUTH_TOKEN');
|
|
164
161
|
// Issue #135: DB path resolution with proper fallback chain
|
|
165
162
|
// Priority: CM_DB_PATH > DATABASE_PATH (deprecated) > getDefaultDbPath()
|
|
166
163
|
const databasePath = getEnvByKey('CM_DB_PATH')
|
|
@@ -176,10 +173,6 @@ function getEnv() {
|
|
|
176
173
|
if (bind !== '127.0.0.1' && bind !== '0.0.0.0' && bind !== 'localhost') {
|
|
177
174
|
throw new Error(`Invalid CM_BIND: ${bind}. Must be '127.0.0.1', '0.0.0.0', or 'localhost'.`);
|
|
178
175
|
}
|
|
179
|
-
// Require auth token for public binding
|
|
180
|
-
if (bind === '0.0.0.0' && !authToken) {
|
|
181
|
-
throw new Error('CM_AUTH_TOKEN (or MCBD_AUTH_TOKEN) is required when CM_BIND=0.0.0.0');
|
|
182
|
-
}
|
|
183
176
|
// Issue #135: Validate DB path for security (SEC-001)
|
|
184
177
|
let validatedDbPath;
|
|
185
178
|
try {
|
|
@@ -195,7 +188,6 @@ function getEnv() {
|
|
|
195
188
|
CM_ROOT_DIR: path_1.default.resolve(rootDir),
|
|
196
189
|
CM_PORT: port,
|
|
197
190
|
CM_BIND: bind,
|
|
198
|
-
CM_AUTH_TOKEN: authToken,
|
|
199
191
|
CM_DB_PATH: validatedDbPath,
|
|
200
192
|
};
|
|
201
193
|
}
|
|
@@ -217,12 +209,3 @@ function validateEnv() {
|
|
|
217
209
|
return { valid: false, errors };
|
|
218
210
|
}
|
|
219
211
|
}
|
|
220
|
-
/**
|
|
221
|
-
* Check if authentication is required based on bind address
|
|
222
|
-
*
|
|
223
|
-
* @returns True if authentication is required
|
|
224
|
-
*/
|
|
225
|
-
function isAuthRequired() {
|
|
226
|
-
const env = getEnv();
|
|
227
|
-
return env.CM_BIND === '0.0.0.0';
|
|
228
|
-
}
|
|
@@ -35,10 +35,6 @@ const SENSITIVE_PATTERNS = [
|
|
|
35
35
|
{ pattern: /(password|passwd|pwd)[=:]\s*\S+/gi, replacement: '$1=[REDACTED]' },
|
|
36
36
|
// Token/secret related
|
|
37
37
|
{ pattern: /(token|secret|api_key|apikey|auth)[=:]\s*\S+/gi, replacement: '$1=[REDACTED]' },
|
|
38
|
-
// CM_AUTH_TOKEN (new name - Issue #76)
|
|
39
|
-
{ pattern: /CM_AUTH_TOKEN=\S+/gi, replacement: 'CM_AUTH_TOKEN=[REDACTED]' },
|
|
40
|
-
// MCBD_AUTH_TOKEN (legacy name)
|
|
41
|
-
{ pattern: /MCBD_AUTH_TOKEN=\S+/gi, replacement: 'MCBD_AUTH_TOKEN=[REDACTED]' },
|
|
42
38
|
// Authorization header
|
|
43
39
|
{ pattern: /Authorization:\s*\S+/gi, replacement: 'Authorization: [REDACTED]' },
|
|
44
40
|
// SSH key
|