devops-whc 1.0.1 → 1.0.2

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/dist/index.js CHANGED
@@ -1,7 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
3
6
  Object.defineProperty(exports, "__esModule", { value: true });
4
7
  const node_crypto_1 = require("node:crypto");
8
+ const node_fs_1 = require("node:fs");
9
+ const node_path_1 = __importDefault(require("node:path"));
10
+ const dotenv_1 = __importDefault(require("dotenv"));
5
11
  const env_1 = require("./config/env");
6
12
  const tool_dispatcher_1 = require("./dispatcher/tool-dispatcher");
7
13
  const whc_check_health_1 = require("./handlers/whc-check-health");
@@ -11,14 +17,16 @@ const tool_registry_1 = require("./registry/tool-registry");
11
17
  const store_1 = require("./idempotency/store");
12
18
  const audit_logger_1 = require("./audit/audit-logger");
13
19
  const server_1 = require("./server");
14
- const packageJson = require("../package.json");
15
- const APP_VERSION = packageJson.version;
20
+ const source_compatibility_1 = require("./probes/source-compatibility");
21
+ const workspace_state_1 = require("./state/workspace-state");
22
+ const APP_VERSION = process.env.npm_package_version ?? "0.0.0";
16
23
  const HELP_TEXT = [
17
- `WHC MCP CLI Help v${APP_VERSION}`,
24
+ `WHC CLI Help v${APP_VERSION}`,
18
25
  "",
19
26
  "Modes:",
20
- " --serve Start MCP stdio server (registers all tools)",
27
+ " --serve Start stdio tool server (registers all tools)",
21
28
  " --probe Run connectivity probe (UAPI, SSH, WP-CLI)",
29
+ " --check-generic Run one-shot deploy readiness check for a specific project folder",
22
30
  " --check-health Execute whc_check_health once and print JSON",
23
31
  " --version Print CLI version",
24
32
  " --help Show this help",
@@ -26,12 +34,13 @@ const HELP_TEXT = [
26
34
  "Quick start:",
27
35
  " 1) npm install",
28
36
  " 2) copy .env.example to .env and fill secrets",
29
- " 3) npm run mcp:prepare",
37
+ " 3) npm run prepare",
30
38
  " 4) npm run serve",
31
39
  "",
32
40
  "Published package use:",
33
41
  " npx -y devops-whc --serve",
34
42
  " npx -y devops-whc --probe",
43
+ " npx -y devops-whc --check-generic --project-root D:/Source/mytho/source/wordpress --workflow-mode ssh_scp_wpcli",
35
44
  " npx -y devops-whc --check-health",
36
45
  "",
37
46
  "Safe release flow:",
@@ -42,13 +51,17 @@ const HELP_TEXT = [
42
51
  " 5) smoke gate",
43
52
  " 6) promote live only when smoke is green",
44
53
  "",
45
- "Managed clone/sync payload reminders:",
46
- " - direction is required: live_to_staging or staging_to_live",
47
- " - sync_scope is required for clear safety intent",
48
- " - staging_to_live + database/everything needs backup policy",
54
+ "Git deploy payload reminders:",
55
+ " - repository_root is required",
56
+ " - branch is optional; omitted means deploy current configured HEAD",
57
+ " - run whc_setup_remote + local git push before whc_deploy",
58
+ "",
59
+ "ssh_scp_wpcli reminders:",
60
+ " - check-generic validates mytho/source-style doctor prerequisites",
61
+ " - whc_deploy currently returns guidance/dry-run only for this mode",
49
62
  "",
50
63
  "Flow log:",
51
- " - default: .mcp/whc-mcp/logs/flow-events.jsonl",
64
+ " - default: .whc/logs/flow-events.jsonl",
52
65
  " - override: set WHC_FLOW_LOG_PATH",
53
66
  "",
54
67
  "Secrets safety:",
@@ -56,7 +69,7 @@ const HELP_TEXT = [
56
69
  " - npm tarball excludes local env files by design",
57
70
  "",
58
71
  "Reference:",
59
- " - AGENT_MCP_USAGE.md",
72
+ " - AGENT_USAGE.md",
60
73
  ].join("\n");
61
74
  async function main() {
62
75
  if (process.argv.includes("--help")) {
@@ -67,13 +80,13 @@ async function main() {
67
80
  console.log(APP_VERSION);
68
81
  return;
69
82
  }
70
- // MCP stdio server mode — all 6 tools registered, reads from stdin/stdout
83
+ // Stdio server mode — all tools registered, reads from stdin/stdout
71
84
  if (process.argv.includes("--serve")) {
72
- await (0, server_1.startMcpServer)();
85
+ await (0, server_1.startWhcToolServer)();
73
86
  return;
74
87
  }
75
- const config = (0, env_1.loadConfig)();
76
88
  if (process.argv.includes("--probe")) {
89
+ const config = (0, env_1.loadConfig)();
77
90
  const report = await (0, connectivity_1.runConnectivityProbe)(config);
78
91
  const stagingWpCliOk = report.wpcli.staging.reachable;
79
92
  console.log(JSON.stringify({
@@ -84,7 +97,187 @@ async function main() {
84
97
  }, null, 2));
85
98
  return;
86
99
  }
100
+ if (process.argv.includes("--check-generic")) {
101
+ const projectRootArg = readCliOption("--project-root");
102
+ if (!projectRootArg) {
103
+ console.log(JSON.stringify({
104
+ ok: false,
105
+ ready_for_deploy: false,
106
+ status: "blocked",
107
+ next_actions: [
108
+ "Re-run with --project-root <folder> so WHC can bootstrap hidden state for that specific project.",
109
+ ],
110
+ action_id: (0, node_crypto_1.randomUUID)(),
111
+ tool: "whc_source_compatibility_check",
112
+ data: {
113
+ ok: false,
114
+ ready_for_deploy: false,
115
+ status: "blocked",
116
+ blockers: ["Missing required CLI argument: --project-root"],
117
+ warnings: [],
118
+ next_actions: [
119
+ "Use --project-root <folder> to tell WHC which deploy workspace to initialize and check.",
120
+ ],
121
+ checks: [
122
+ {
123
+ id: "project-root-argument",
124
+ status: "fail",
125
+ message: "Project root folder was not provided.",
126
+ hint: "Example: npx -y devops-whc --check-generic --project-root D:/Source/mytho/source/wordpress --workflow-mode ssh_scp_wpcli",
127
+ },
128
+ ],
129
+ },
130
+ }, null, 2));
131
+ return;
132
+ }
133
+ const projectRoot = node_path_1.default.resolve(projectRootArg);
134
+ const appPathArg = readCliOption("--app-path");
135
+ const effectiveAppPath = appPathArg ?? ".";
136
+ const appRoot = node_path_1.default.resolve(projectRoot, effectiveAppPath);
137
+ if (!(0, node_fs_1.existsSync)(appRoot)) {
138
+ console.log(JSON.stringify({
139
+ ok: false,
140
+ ready_for_deploy: false,
141
+ status: "blocked",
142
+ next_actions: [
143
+ "Fix --app-path so it points to an existing application directory, then rerun check-generic.",
144
+ ],
145
+ action_id: (0, node_crypto_1.randomUUID)(),
146
+ tool: "whc_source_compatibility_check",
147
+ data: {
148
+ ok: false,
149
+ ready_for_deploy: false,
150
+ status: "blocked",
151
+ paths: {
152
+ local_project_root: projectRoot,
153
+ app_root: appRoot,
154
+ wp_content_path: node_path_1.default.resolve(appRoot, "wp-content"),
155
+ },
156
+ blockers: [`Application path does not exist: ${appRoot}`],
157
+ warnings: [],
158
+ next_actions: [
159
+ "Fix --app-path so it points to an existing application directory, then rerun check-generic.",
160
+ ],
161
+ checks: [
162
+ {
163
+ id: "app-root",
164
+ status: "fail",
165
+ message: "Application root path is missing.",
166
+ details: appRoot,
167
+ hint: "User must point WHC at the correct project-root/app-path. Hidden WHC files are not initialized until the effective app root is valid.",
168
+ },
169
+ ],
170
+ },
171
+ }, null, 2));
172
+ return;
173
+ }
174
+ const bootstrap = (0, workspace_state_1.ensureWorkspaceBootstrap)(appRoot, {
175
+ localProjectRoot: projectRoot,
176
+ localAppPath: appPathArg,
177
+ });
178
+ const projectEnv = readProjectEnv(bootstrap.envFile);
179
+ const mergedEnv = {
180
+ ...withoutWhcEnv(process.env),
181
+ ...projectEnv,
182
+ WHC_LOCAL_PROJECT_ROOT: projectRoot,
183
+ };
184
+ if (appPathArg) {
185
+ mergedEnv.WHC_LOCAL_APP_PATH = appPathArg;
186
+ }
187
+ else {
188
+ mergedEnv.WHC_LOCAL_APP_PATH = "";
189
+ }
190
+ const workflowModeArg = readCliOption("--workflow-mode");
191
+ if (workflowModeArg) {
192
+ mergedEnv.WHC_WORKFLOW_MODE = workflowModeArg;
193
+ }
194
+ let config;
195
+ try {
196
+ config = (0, env_1.loadConfig)(mergedEnv);
197
+ }
198
+ catch (error) {
199
+ const message = error instanceof Error ? error.message : "Unknown config error";
200
+ const missingKeys = extractMissingEnvKeys(message);
201
+ console.log(JSON.stringify({
202
+ ok: false,
203
+ ready_for_deploy: false,
204
+ status: "blocked",
205
+ next_actions: [
206
+ `Fill the missing credentials in ${bootstrap.envFile}.`,
207
+ "Re-run check-generic after updating the project-local WHC env file.",
208
+ ],
209
+ action_id: (0, node_crypto_1.randomUUID)(),
210
+ tool: "whc_source_compatibility_check",
211
+ data: {
212
+ ok: false,
213
+ ready_for_deploy: false,
214
+ status: "blocked",
215
+ paths: {
216
+ local_project_root: projectRoot,
217
+ app_root: appRoot,
218
+ wp_content_path: node_path_1.default.resolve(appRoot, "wp-content"),
219
+ },
220
+ bootstrap: {
221
+ state_root: bootstrap.stateRootRelative,
222
+ created_paths: bootstrap.createdPaths,
223
+ pipeline_status_file: bootstrap.pipelineStatusFile,
224
+ env_file: bootstrap.envFile,
225
+ env_example_file: bootstrap.envExampleFile,
226
+ gitignore_updated: bootstrap.gitignoreUpdated,
227
+ },
228
+ summary: {
229
+ pass: 1,
230
+ warn: 0,
231
+ fail: missingKeys.length,
232
+ },
233
+ blockers: missingKeys.map((key) => `Set ${key} in ${bootstrap.envFile}`),
234
+ warnings: [],
235
+ next_actions: [
236
+ `Fill the missing credentials in ${bootstrap.envFile}.`,
237
+ "Re-run check-generic after updating the project-local WHC env file.",
238
+ ],
239
+ checks: [
240
+ {
241
+ id: "workspace-bootstrap",
242
+ status: "pass",
243
+ message: "App-specific WHC hidden files are initialized.",
244
+ details: bootstrap.stateRootAbsolute,
245
+ },
246
+ ...missingKeys.map((key) => ({
247
+ id: `env:${key}`,
248
+ status: "fail",
249
+ message: `${key} is missing for this project workspace.`,
250
+ hint: `Fill ${key} in ${bootstrap.envFile}`,
251
+ })),
252
+ ],
253
+ },
254
+ }, null, 2));
255
+ return;
256
+ }
257
+ const report = await (0, source_compatibility_1.runSourceCompatibilityCheck)(config);
258
+ console.log(JSON.stringify({
259
+ ok: report.ok,
260
+ ready_for_deploy: report.ready_for_deploy,
261
+ status: report.status,
262
+ next_actions: report.next_actions,
263
+ action_id: (0, node_crypto_1.randomUUID)(),
264
+ tool: "whc_source_compatibility_check",
265
+ data: {
266
+ ...report,
267
+ bootstrap: {
268
+ state_root: bootstrap.stateRootRelative,
269
+ created_paths: bootstrap.createdPaths,
270
+ pipeline_status_file: bootstrap.pipelineStatusFile,
271
+ env_file: bootstrap.envFile,
272
+ env_example_file: bootstrap.envExampleFile,
273
+ gitignore_updated: bootstrap.gitignoreUpdated,
274
+ },
275
+ },
276
+ }, null, 2));
277
+ return;
278
+ }
87
279
  if (process.argv.includes("--check-health")) {
280
+ const config = (0, env_1.loadConfig)();
88
281
  const request = (0, whc_check_health_1.buildDefaultWhcCheckHealthRequest)(config);
89
282
  const toolDef = tool_registry_1.TOOL_REGISTRY["whc_check_health"];
90
283
  const result = await (0, tool_dispatcher_1.dispatch)({
@@ -102,6 +295,57 @@ async function main() {
102
295
  }
103
296
  console.log(HELP_TEXT);
104
297
  }
298
+ function readCliOption(flag) {
299
+ const index = process.argv.indexOf(flag);
300
+ if (index === -1 || index + 1 >= process.argv.length) {
301
+ const normalized = flag.replace(/^--/, "").replace(/-/g, "_");
302
+ const npmConfigValue = process.env[`npm_config_${normalized}`];
303
+ if (npmConfigValue && npmConfigValue.length > 0) {
304
+ return npmConfigValue;
305
+ }
306
+ return readCheckGenericPositional(flag);
307
+ }
308
+ return process.argv[index + 1];
309
+ }
310
+ function readProjectEnv(filePath) {
311
+ if (!(0, node_fs_1.existsSync)(filePath)) {
312
+ return {};
313
+ }
314
+ const parsed = dotenv_1.default.parse((0, node_fs_1.readFileSync)(filePath, "utf8"));
315
+ return parsed;
316
+ }
317
+ function extractMissingEnvKeys(message) {
318
+ const prefix = "VALIDATION_ERROR: Missing or invalid env vars:";
319
+ if (!message.startsWith(prefix)) {
320
+ return ["project-specific WHC env values"];
321
+ }
322
+ return message
323
+ .slice(prefix.length)
324
+ .split(",")
325
+ .map((item) => item.trim())
326
+ .filter((item) => item.length > 0);
327
+ }
328
+ function withoutWhcEnv(env) {
329
+ return Object.fromEntries(Object.entries(env).filter(([key]) => !key.startsWith("WHC_")));
330
+ }
331
+ function readCheckGenericPositional(flag) {
332
+ const checkIndex = process.argv.indexOf("--check-generic");
333
+ if (checkIndex === -1) {
334
+ return undefined;
335
+ }
336
+ const trailing = process.argv.slice(checkIndex + 1);
337
+ const positionalMap = {
338
+ "--project-root": 0,
339
+ "--app-path": 1,
340
+ "--workflow-mode": 2,
341
+ };
342
+ const position = positionalMap[flag];
343
+ if (position === undefined) {
344
+ return undefined;
345
+ }
346
+ const value = trailing[position];
347
+ return value && !value.startsWith("--") ? value : undefined;
348
+ }
105
349
  main().catch((error) => {
106
350
  const message = error instanceof Error ? error.message : "Unknown startup error";
107
351
  console.error(message);