devops-whc 1.0.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/AGENT_MCP_USAGE.md +394 -0
- package/LICENSE +15 -0
- package/README.md +208 -0
- package/WHC_MCP_REQUIREMENTS.md +112 -0
- package/dist/audit/audit-logger.js +57 -0
- package/dist/clients/ssh-client.js +199 -0
- package/dist/clients/whc-uapi-client.js +178 -0
- package/dist/clients/wpcli-client.js +125 -0
- package/dist/config/env.js +132 -0
- package/dist/contracts/deployment.js +2 -0
- package/dist/contracts/envelope.js +2 -0
- package/dist/dispatcher/tool-dispatcher.js +145 -0
- package/dist/handlers/whc-check-health.js +131 -0
- package/dist/handlers/whc-db-backup.js +111 -0
- package/dist/handlers/whc-deploy.js +381 -0
- package/dist/handlers/whc-get-logs.js +108 -0
- package/dist/handlers/whc-pipeline-status.js +96 -0
- package/dist/handlers/whc-prepare.js +127 -0
- package/dist/handlers/whc-rollback.js +141 -0
- package/dist/handlers/whc-setup-remote.js +262 -0
- package/dist/handlers/whc-ssh-exec.js +138 -0
- package/dist/handlers/whc-verify.js +304 -0
- package/dist/idempotency/store.js +13 -0
- package/dist/index.js +109 -0
- package/dist/policy/policy-engine.js +41 -0
- package/dist/probes/connectivity.js +41 -0
- package/dist/registry/tool-registry.js +69 -0
- package/dist/schemas/whc-check-health.js +55 -0
- package/dist/schemas/whc-db-backup.js +29 -0
- package/dist/schemas/whc-deploy.js +66 -0
- package/dist/schemas/whc-get-logs.js +25 -0
- package/dist/schemas/whc-pipeline-status.js +24 -0
- package/dist/schemas/whc-prepare.js +29 -0
- package/dist/schemas/whc-rollback.js +58 -0
- package/dist/schemas/whc-setup-remote.js +60 -0
- package/dist/schemas/whc-ssh-exec.js +117 -0
- package/dist/schemas/whc-verify.js +28 -0
- package/dist/server-entry.js +8 -0
- package/dist/server.js +381 -0
- package/dist/services/deploy-runtime-ops.js +104 -0
- package/dist/services/deployment-locks.js +34 -0
- package/dist/state/workspace-state.js +201 -0
- package/package.json +48 -0
- package/scripts/prepare-first-time.cjs +75 -0
- package/scripts/start-mcp.cjs +42 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildDefaultWhcDeployPayload = buildDefaultWhcDeployPayload;
|
|
4
|
+
exports.validateWhcDeployRequest = validateWhcDeployRequest;
|
|
5
|
+
const zod_1 = require("zod");
|
|
6
|
+
const whc_setup_remote_1 = require("./whc-setup-remote");
|
|
7
|
+
const payloadSchema = zod_1.z.object({
|
|
8
|
+
workflow_mode: zod_1.z.enum(["managed_clone_sync", "git_controlled"]),
|
|
9
|
+
target_environment: zod_1.z.enum(["live", "staging"]),
|
|
10
|
+
release_intent: zod_1.z.enum(["refresh", "deploy", "promote", "migrate", "recover"]),
|
|
11
|
+
pipeline_id: zod_1.z.enum(["P0", "P1", "P2", "P2R", "P3", "P3D", "P4", "P5"]),
|
|
12
|
+
source_profile: whc_setup_remote_1.sourceProfileSchema,
|
|
13
|
+
// managed_clone_sync fields
|
|
14
|
+
direction: zod_1.z.enum(["live_to_staging", "staging_to_live"]).optional(),
|
|
15
|
+
sync_scope: zod_1.z.enum(["files", "database", "everything"]).optional(),
|
|
16
|
+
// git_controlled fields
|
|
17
|
+
repository_root: zod_1.z.string().min(1).optional(),
|
|
18
|
+
branch: zod_1.z.string().min(1).optional(),
|
|
19
|
+
rollback_branch: zod_1.z.string().min(1).optional(),
|
|
20
|
+
// hardening and recovery controls
|
|
21
|
+
backup_reference: zod_1.z.string().min(1).optional(),
|
|
22
|
+
allow_emergency_without_backup: zod_1.z.boolean().optional(),
|
|
23
|
+
verify_after_deploy: zod_1.z.boolean().optional(),
|
|
24
|
+
auto_rollback_on_verify_failure: zod_1.z.boolean().optional(),
|
|
25
|
+
lock_key: zod_1.z.string().min(1).optional(),
|
|
26
|
+
});
|
|
27
|
+
const requestSchema = zod_1.z.object({
|
|
28
|
+
request_id: zod_1.z.string().min(1),
|
|
29
|
+
actor: zod_1.z.object({
|
|
30
|
+
kind: zod_1.z.literal("copilot"),
|
|
31
|
+
user_hint: zod_1.z.string().min(1).optional(),
|
|
32
|
+
}),
|
|
33
|
+
dry_run: zod_1.z.boolean().optional(),
|
|
34
|
+
confirmed: zod_1.z.boolean().optional(),
|
|
35
|
+
idempotency_key: zod_1.z.string().min(1),
|
|
36
|
+
payload: payloadSchema,
|
|
37
|
+
});
|
|
38
|
+
function buildDefaultWhcDeployPayload(config) {
|
|
39
|
+
const releaseIntent = config.sourceProfile.defaultReleaseIntent;
|
|
40
|
+
const sourceProfile = {
|
|
41
|
+
local_project_root: config.sourceProfile.localProjectRoot,
|
|
42
|
+
local_app_path: config.sourceProfile.localAppPath,
|
|
43
|
+
source_kind: config.sourceProfile.sourceKind,
|
|
44
|
+
deploy_unit: config.sourceProfile.deployUnit,
|
|
45
|
+
build_command: config.sourceProfile.buildCommand,
|
|
46
|
+
build_artifact_path: config.sourceProfile.buildArtifactPath,
|
|
47
|
+
};
|
|
48
|
+
const pipelineId = "P3";
|
|
49
|
+
return {
|
|
50
|
+
workflow_mode: config.workflowMode,
|
|
51
|
+
target_environment: "live",
|
|
52
|
+
release_intent: releaseIntent,
|
|
53
|
+
pipeline_id: pipelineId,
|
|
54
|
+
source_profile: sourceProfile,
|
|
55
|
+
direction: "staging_to_live",
|
|
56
|
+
sync_scope: "files",
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function validateWhcDeployRequest(input) {
|
|
60
|
+
const parsed = requestSchema.safeParse(input);
|
|
61
|
+
if (!parsed.success) {
|
|
62
|
+
const issues = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
|
|
63
|
+
throw new Error(`VALIDATION_ERROR: ${issues}`);
|
|
64
|
+
}
|
|
65
|
+
return parsed.data;
|
|
66
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validateWhcGetLogsRequest = validateWhcGetLogsRequest;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const payloadSchema = zod_1.z.object({
|
|
6
|
+
target_environment: zod_1.z.enum(["live", "staging"]),
|
|
7
|
+
log_type: zod_1.z.enum(["apache_error", "apache_access", "cron", "php_error"]).default("apache_error"),
|
|
8
|
+
lines: zod_1.z.number().int().min(1).max(500).default(50),
|
|
9
|
+
});
|
|
10
|
+
const requestSchema = zod_1.z.object({
|
|
11
|
+
request_id: zod_1.z.string().min(1),
|
|
12
|
+
actor: zod_1.z.object({
|
|
13
|
+
kind: zod_1.z.literal("copilot"),
|
|
14
|
+
user_hint: zod_1.z.string().min(1).optional(),
|
|
15
|
+
}),
|
|
16
|
+
payload: payloadSchema,
|
|
17
|
+
});
|
|
18
|
+
function validateWhcGetLogsRequest(input) {
|
|
19
|
+
const parsed = requestSchema.safeParse(input);
|
|
20
|
+
if (!parsed.success) {
|
|
21
|
+
const issues = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
|
|
22
|
+
throw new Error(`VALIDATION_ERROR: ${issues}`);
|
|
23
|
+
}
|
|
24
|
+
return parsed.data;
|
|
25
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validateWhcPipelineStatusRequest = validateWhcPipelineStatusRequest;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const payloadSchema = zod_1.z.object({
|
|
6
|
+
workspace_root: zod_1.z.string().min(1).optional(),
|
|
7
|
+
request_id: zod_1.z.string().min(1).optional(),
|
|
8
|
+
});
|
|
9
|
+
const requestSchema = zod_1.z.object({
|
|
10
|
+
request_id: zod_1.z.string().min(1),
|
|
11
|
+
actor: zod_1.z.object({
|
|
12
|
+
kind: zod_1.z.literal("copilot"),
|
|
13
|
+
user_hint: zod_1.z.string().min(1).optional(),
|
|
14
|
+
}),
|
|
15
|
+
payload: payloadSchema.default({}),
|
|
16
|
+
});
|
|
17
|
+
function validateWhcPipelineStatusRequest(input) {
|
|
18
|
+
const parsed = requestSchema.safeParse(input);
|
|
19
|
+
if (!parsed.success) {
|
|
20
|
+
const issues = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
|
|
21
|
+
throw new Error(`VALIDATION_ERROR: ${issues}`);
|
|
22
|
+
}
|
|
23
|
+
return parsed.data;
|
|
24
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validateWhcPrepareRequest = validateWhcPrepareRequest;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const payloadSchema = zod_1.z.object({
|
|
6
|
+
workspace_root: zod_1.z.string().min(1).optional(),
|
|
7
|
+
target_environment: zod_1.z.enum(["live", "staging"]).optional(),
|
|
8
|
+
ensure_gitignore_rule: zod_1.z.boolean().default(true),
|
|
9
|
+
force_reinitialize: zod_1.z.boolean().default(false),
|
|
10
|
+
});
|
|
11
|
+
const requestSchema = zod_1.z.object({
|
|
12
|
+
request_id: zod_1.z.string().min(1),
|
|
13
|
+
actor: zod_1.z.object({
|
|
14
|
+
kind: zod_1.z.literal("copilot"),
|
|
15
|
+
user_hint: zod_1.z.string().min(1).optional(),
|
|
16
|
+
}),
|
|
17
|
+
dry_run: zod_1.z.boolean().optional(),
|
|
18
|
+
confirmed: zod_1.z.boolean().optional(),
|
|
19
|
+
idempotency_key: zod_1.z.string().min(1),
|
|
20
|
+
payload: payloadSchema,
|
|
21
|
+
});
|
|
22
|
+
function validateWhcPrepareRequest(input) {
|
|
23
|
+
const parsed = requestSchema.safeParse(input);
|
|
24
|
+
if (!parsed.success) {
|
|
25
|
+
const issues = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
|
|
26
|
+
throw new Error(`VALIDATION_ERROR: ${issues}`);
|
|
27
|
+
}
|
|
28
|
+
return parsed.data;
|
|
29
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validateWhcRollbackRequest = validateWhcRollbackRequest;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const payloadSchema = zod_1.z
|
|
6
|
+
.object({
|
|
7
|
+
target_environment: zod_1.z.enum(["live", "staging"]),
|
|
8
|
+
rollback_mode: zod_1.z.enum(["git_branch", "db_backup", "managed_sync_reverse"]),
|
|
9
|
+
rollback_branch: zod_1.z.string().min(1).optional(),
|
|
10
|
+
backup_reference: zod_1.z.string().min(1).optional(),
|
|
11
|
+
direction: zod_1.z.enum(["live_to_staging", "staging_to_live"]).optional(),
|
|
12
|
+
reason: zod_1.z.string().min(1),
|
|
13
|
+
verify_release_id: zod_1.z.string().min(1).optional(),
|
|
14
|
+
verify_report_path: zod_1.z.string().min(1).optional(),
|
|
15
|
+
repository_root: zod_1.z.string().min(1).optional(),
|
|
16
|
+
})
|
|
17
|
+
.superRefine((payload, ctx) => {
|
|
18
|
+
if (payload.rollback_mode === "git_branch" && !payload.rollback_branch) {
|
|
19
|
+
ctx.addIssue({
|
|
20
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
21
|
+
path: ["rollback_branch"],
|
|
22
|
+
message: "rollback_branch is required for rollback_mode=git_branch",
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
if (payload.rollback_mode === "db_backup" && !payload.backup_reference) {
|
|
26
|
+
ctx.addIssue({
|
|
27
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
28
|
+
path: ["backup_reference"],
|
|
29
|
+
message: "backup_reference is required for rollback_mode=db_backup",
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
if (payload.rollback_mode === "managed_sync_reverse" && !payload.direction) {
|
|
33
|
+
ctx.addIssue({
|
|
34
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
35
|
+
path: ["direction"],
|
|
36
|
+
message: "direction is required for rollback_mode=managed_sync_reverse",
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
const requestSchema = zod_1.z.object({
|
|
41
|
+
request_id: zod_1.z.string().min(1),
|
|
42
|
+
actor: zod_1.z.object({
|
|
43
|
+
kind: zod_1.z.literal("copilot"),
|
|
44
|
+
user_hint: zod_1.z.string().min(1).optional(),
|
|
45
|
+
}),
|
|
46
|
+
dry_run: zod_1.z.boolean().optional(),
|
|
47
|
+
confirmed: zod_1.z.boolean().optional(),
|
|
48
|
+
idempotency_key: zod_1.z.string().min(1),
|
|
49
|
+
payload: payloadSchema,
|
|
50
|
+
});
|
|
51
|
+
function validateWhcRollbackRequest(input) {
|
|
52
|
+
const parsed = requestSchema.safeParse(input);
|
|
53
|
+
if (!parsed.success) {
|
|
54
|
+
const issues = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
|
|
55
|
+
throw new Error(`VALIDATION_ERROR: ${issues}`);
|
|
56
|
+
}
|
|
57
|
+
return parsed.data;
|
|
58
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sourceProfileSchema = void 0;
|
|
4
|
+
exports.buildDefaultWhcSetupRemotePayload = buildDefaultWhcSetupRemotePayload;
|
|
5
|
+
exports.validateWhcSetupRemoteRequest = validateWhcSetupRemoteRequest;
|
|
6
|
+
const zod_1 = require("zod");
|
|
7
|
+
exports.sourceProfileSchema = zod_1.z.object({
|
|
8
|
+
local_project_root: zod_1.z.string().min(1).optional(),
|
|
9
|
+
local_app_path: zod_1.z.string().min(1).optional(),
|
|
10
|
+
source_kind: zod_1.z.enum(["full_site", "partial_content", "package_only", "artifact_first", "monorepo_slice"]),
|
|
11
|
+
deploy_unit: zod_1.z.enum(["raw_source", "build_artifact", "package_bundle"]),
|
|
12
|
+
build_command: zod_1.z.string().min(1).optional(),
|
|
13
|
+
build_artifact_path: zod_1.z.string().min(1).optional(),
|
|
14
|
+
});
|
|
15
|
+
const payloadSchema = zod_1.z.object({
|
|
16
|
+
target_environment: zod_1.z.enum(["live", "staging"]),
|
|
17
|
+
release_intent: zod_1.z.enum(["refresh", "deploy", "promote", "migrate", "recover"]),
|
|
18
|
+
pipeline_id: zod_1.z.enum(["P0", "P1", "P2", "P2R", "P3", "P3D", "P4", "P5"]),
|
|
19
|
+
source_profile: exports.sourceProfileSchema,
|
|
20
|
+
deploy_target_path: zod_1.z.string().min(1),
|
|
21
|
+
clone_url: zod_1.z.string().url().optional(),
|
|
22
|
+
});
|
|
23
|
+
const requestSchema = zod_1.z.object({
|
|
24
|
+
request_id: zod_1.z.string().min(1),
|
|
25
|
+
actor: zod_1.z.object({
|
|
26
|
+
kind: zod_1.z.literal("copilot"),
|
|
27
|
+
user_hint: zod_1.z.string().min(1).optional(),
|
|
28
|
+
}),
|
|
29
|
+
dry_run: zod_1.z.boolean().optional(),
|
|
30
|
+
confirmed: zod_1.z.boolean().optional(),
|
|
31
|
+
idempotency_key: zod_1.z.string().min(1),
|
|
32
|
+
payload: payloadSchema,
|
|
33
|
+
});
|
|
34
|
+
function buildDefaultWhcSetupRemotePayload(config, deployTargetPath) {
|
|
35
|
+
const releaseIntent = config.sourceProfile.defaultReleaseIntent;
|
|
36
|
+
const sourceProfile = {
|
|
37
|
+
local_project_root: config.sourceProfile.localProjectRoot,
|
|
38
|
+
local_app_path: config.sourceProfile.localAppPath,
|
|
39
|
+
source_kind: config.sourceProfile.sourceKind,
|
|
40
|
+
deploy_unit: config.sourceProfile.deployUnit,
|
|
41
|
+
build_command: config.sourceProfile.buildCommand,
|
|
42
|
+
build_artifact_path: config.sourceProfile.buildArtifactPath,
|
|
43
|
+
};
|
|
44
|
+
const pipelineId = "P3";
|
|
45
|
+
return {
|
|
46
|
+
target_environment: "live",
|
|
47
|
+
release_intent: releaseIntent,
|
|
48
|
+
pipeline_id: pipelineId,
|
|
49
|
+
source_profile: sourceProfile,
|
|
50
|
+
deploy_target_path: deployTargetPath,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function validateWhcSetupRemoteRequest(input) {
|
|
54
|
+
const parsed = requestSchema.safeParse(input);
|
|
55
|
+
if (!parsed.success) {
|
|
56
|
+
const issues = parsed.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`).join("; ");
|
|
57
|
+
throw new Error(`VALIDATION_ERROR: ${issues}`);
|
|
58
|
+
}
|
|
59
|
+
return parsed.data;
|
|
60
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SSH_EXEC_DENYLIST = exports.SSH_EXEC_SAFE_SYNC_PATTERNS = exports.SSH_EXEC_SAFE_WP_PATTERNS = exports.SSH_EXEC_SAFE_READ_COMMANDS = void 0;
|
|
4
|
+
exports.validateWhcSshExecRequest = validateWhcSshExecRequest;
|
|
5
|
+
exports.isCommandAllowed = isCommandAllowed;
|
|
6
|
+
const zod_1 = require("zod");
|
|
7
|
+
exports.SSH_EXEC_SAFE_READ_COMMANDS = [
|
|
8
|
+
"ls",
|
|
9
|
+
"cat",
|
|
10
|
+
"echo",
|
|
11
|
+
"pwd",
|
|
12
|
+
"df",
|
|
13
|
+
"du",
|
|
14
|
+
"uptime",
|
|
15
|
+
"free",
|
|
16
|
+
"ps",
|
|
17
|
+
"whoami",
|
|
18
|
+
"id",
|
|
19
|
+
"env",
|
|
20
|
+
"printenv",
|
|
21
|
+
"find",
|
|
22
|
+
"grep",
|
|
23
|
+
"tail",
|
|
24
|
+
"head",
|
|
25
|
+
"wc",
|
|
26
|
+
"stat",
|
|
27
|
+
"file",
|
|
28
|
+
"which",
|
|
29
|
+
"test",
|
|
30
|
+
];
|
|
31
|
+
exports.SSH_EXEC_SAFE_WP_PATTERNS = [
|
|
32
|
+
/^wp\s+--info(\s|$)/i,
|
|
33
|
+
/^wp\s+core\s+(is-installed|version)(\s|$)/i,
|
|
34
|
+
/^wp\s+plugin\s+list(\s|$)/i,
|
|
35
|
+
/^wp\s+theme\s+list(\s|$)/i,
|
|
36
|
+
/^wp\s+option\s+get\s+\S+/i,
|
|
37
|
+
/^wp\s+user\s+list(\s|$)/i,
|
|
38
|
+
/^wp\s+post\s+list(\s|$)/i,
|
|
39
|
+
/^wp\s+db\s+size(\s|$)/i,
|
|
40
|
+
/^wp\s+config\s+get\s+\S+/i,
|
|
41
|
+
];
|
|
42
|
+
exports.SSH_EXEC_SAFE_SYNC_PATTERNS = [
|
|
43
|
+
/^rsync(\s|$)/i,
|
|
44
|
+
];
|
|
45
|
+
// Patterns that are never allowed regardless of allowlist.
|
|
46
|
+
exports.SSH_EXEC_DENYLIST = [
|
|
47
|
+
/rm\s+-rf\s+\//i,
|
|
48
|
+
/chmod\s+777\s+\//i,
|
|
49
|
+
/chmod\s+-R\s+777\s+\//i,
|
|
50
|
+
/\/etc\/passwd/i,
|
|
51
|
+
/\/etc\/shadow/i,
|
|
52
|
+
/>\s*\/dev\/sd[a-z]/i,
|
|
53
|
+
/mkfs/i,
|
|
54
|
+
/dd\s+if=\/dev\/zero/i,
|
|
55
|
+
/\|\s*bash/i,
|
|
56
|
+
/\|\s*sh/i,
|
|
57
|
+
/curl\s+.*\|\s*bash/i,
|
|
58
|
+
/wget\s+.*\|\s*bash/i,
|
|
59
|
+
/nc\s+.*-e/i,
|
|
60
|
+
/ncat\s+.*-e/i,
|
|
61
|
+
/--delete(\s|$)/i,
|
|
62
|
+
/--remove-source-files(\s|$)/i,
|
|
63
|
+
];
|
|
64
|
+
const payloadSchema = zod_1.z.object({
|
|
65
|
+
target_environment: zod_1.z.enum(["live", "staging"]),
|
|
66
|
+
raw_command: zod_1.z.string().min(1).max(1024),
|
|
67
|
+
working_dir: zod_1.z.string().min(1).optional(),
|
|
68
|
+
});
|
|
69
|
+
const requestSchema = zod_1.z.object({
|
|
70
|
+
request_id: zod_1.z.string().min(1),
|
|
71
|
+
actor: zod_1.z.object({
|
|
72
|
+
kind: zod_1.z.literal("copilot"),
|
|
73
|
+
user_hint: zod_1.z.string().min(1).optional(),
|
|
74
|
+
}),
|
|
75
|
+
dry_run: zod_1.z.boolean().optional(),
|
|
76
|
+
confirmed: zod_1.z.boolean().optional(),
|
|
77
|
+
idempotency_key: zod_1.z.string().min(1),
|
|
78
|
+
payload: payloadSchema,
|
|
79
|
+
});
|
|
80
|
+
function validateWhcSshExecRequest(input) {
|
|
81
|
+
const parsed = requestSchema.safeParse(input);
|
|
82
|
+
if (!parsed.success) {
|
|
83
|
+
const issues = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
|
|
84
|
+
throw new Error(`VALIDATION_ERROR: ${issues}`);
|
|
85
|
+
}
|
|
86
|
+
return parsed.data;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Returns true if the command is classified as a safe read/sync command.
|
|
90
|
+
*/
|
|
91
|
+
function isCommandAllowed(rawCommand) {
|
|
92
|
+
const trimmed = rawCommand.trim();
|
|
93
|
+
for (const pattern of exports.SSH_EXEC_DENYLIST) {
|
|
94
|
+
if (pattern.test(trimmed)) {
|
|
95
|
+
return { allowed: false, reason: `Command matches denylist pattern: ${pattern.source}` };
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
const firstToken = trimmed.split(/\s+/)[0] ?? "";
|
|
99
|
+
const binaryName = firstToken.split("/").pop() ?? "";
|
|
100
|
+
if (exports.SSH_EXEC_SAFE_READ_COMMANDS.includes(binaryName.toLowerCase())) {
|
|
101
|
+
return { allowed: true };
|
|
102
|
+
}
|
|
103
|
+
for (const pattern of exports.SSH_EXEC_SAFE_WP_PATTERNS) {
|
|
104
|
+
if (pattern.test(trimmed)) {
|
|
105
|
+
return { allowed: true };
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
for (const pattern of exports.SSH_EXEC_SAFE_SYNC_PATTERNS) {
|
|
109
|
+
if (pattern.test(trimmed)) {
|
|
110
|
+
return { allowed: true };
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
allowed: false,
|
|
115
|
+
reason: `Command '${binaryName}' is not in the safe read/sync command set. Use read-only probes or rsync-based sync only.`,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validateWhcVerifyRequest = validateWhcVerifyRequest;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const payloadSchema = zod_1.z.object({
|
|
6
|
+
target_environment: zod_1.z.enum(["live", "staging"]),
|
|
7
|
+
release_id: zod_1.z.string().min(1).optional(),
|
|
8
|
+
manifest_path: zod_1.z.string().min(1).optional(),
|
|
9
|
+
verify_level: zod_1.z.enum(["default", "with_db"]).default("default"),
|
|
10
|
+
random_sample_size: zod_1.z.number().int().min(1).max(1000).optional(),
|
|
11
|
+
random_seed: zod_1.z.string().min(1).optional(),
|
|
12
|
+
});
|
|
13
|
+
const requestSchema = zod_1.z.object({
|
|
14
|
+
request_id: zod_1.z.string().min(1),
|
|
15
|
+
actor: zod_1.z.object({
|
|
16
|
+
kind: zod_1.z.literal("copilot"),
|
|
17
|
+
user_hint: zod_1.z.string().min(1).optional(),
|
|
18
|
+
}),
|
|
19
|
+
payload: payloadSchema,
|
|
20
|
+
});
|
|
21
|
+
function validateWhcVerifyRequest(input) {
|
|
22
|
+
const parsed = requestSchema.safeParse(input);
|
|
23
|
+
if (!parsed.success) {
|
|
24
|
+
const issues = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
|
|
25
|
+
throw new Error(`VALIDATION_ERROR: ${issues}`);
|
|
26
|
+
}
|
|
27
|
+
return parsed.data;
|
|
28
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const server_1 = require("./server");
|
|
4
|
+
(0, server_1.startMcpServer)().catch((error) => {
|
|
5
|
+
const message = error instanceof Error ? error.message : "MCP server startup failed";
|
|
6
|
+
process.stderr.write(message + "\n");
|
|
7
|
+
process.exitCode = 1;
|
|
8
|
+
});
|