@wordpress-flow/cli 1.2.11 → 1.2.13

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.
Files changed (2) hide show
  1. package/dist/index.js +656 -32
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -80975,6 +80975,16 @@ class ConfigManager {
80975
80975
  }
80976
80976
  return result2;
80977
80977
  }
80978
+ getRawConfig() {
80979
+ if (!this.rawConfig) {
80980
+ throw new Error("Configuration not loaded. Call loadConfig() first.");
80981
+ }
80982
+ return this.rawConfig;
80983
+ }
80984
+ getEnvironments() {
80985
+ const raw = this.getRawConfig();
80986
+ return raw.environments || [];
80987
+ }
80978
80988
  getConfig() {
80979
80989
  if (!this.config) {
80980
80990
  throw new Error("Configuration not loaded. Call loadConfig() first.");
@@ -81126,6 +81136,9 @@ class Logger {
81126
81136
  step(step, total, message) {
81127
81137
  console.log(import_chalk2.default.cyan(`[${step}/${total}] ${message}`));
81128
81138
  }
81139
+ substep(message) {
81140
+ console.log(import_chalk2.default.gray(` ↳ ${message}`));
81141
+ }
81129
81142
  }
81130
81143
  var logger = Logger.getInstance();
81131
81144
  // src/commands/pull-command.ts
@@ -114591,6 +114604,7 @@ var $watch = watch;
114591
114604
  // src/dev/dev-mode-orchestrator.ts
114592
114605
  import * as path17 from "path";
114593
114606
  import * as fs15 from "fs";
114607
+ import * as esbuild from "esbuild";
114594
114608
  import { fileURLToPath as fileURLToPath4 } from "url";
114595
114609
 
114596
114610
  // src/build/block-scanner.ts
@@ -114936,10 +114950,6 @@ class BlockHashManager {
114936
114950
  logger.debug(`${blockName}: No stored hash, needs rebuild`);
114937
114951
  return true;
114938
114952
  }
114939
- if (storedHash.cliVersion !== this.cliVersion) {
114940
- logger.debug(`${blockName}: CLI version changed, needs rebuild`);
114941
- return true;
114942
- }
114943
114953
  const currentHash = this.calculateSourceHash(sourcePath);
114944
114954
  if (!currentHash) {
114945
114955
  logger.debug(`${blockName}: Source file missing, needs rebuild`);
@@ -116185,7 +116195,7 @@ class DockerEnvManager {
116185
116195
  // package.json
116186
116196
  var package_default = {
116187
116197
  name: "@wordpress-flow/cli",
116188
- version: "1.2.11",
116198
+ version: "1.2.13",
116189
116199
  type: "module",
116190
116200
  description: "TypeScript-based WordPress block creation system",
116191
116201
  main: "dist/index.js",
@@ -116547,14 +116557,11 @@ class DevModeOrchestrator {
116547
116557
  return;
116548
116558
  const config = this.configManager.getConfig();
116549
116559
  const postTypes = config.sync.postTypes || ["post", "page"];
116550
- console.log(`[DEBUG] Initial push - Post types: ${JSON.stringify(postTypes)}`);
116551
116560
  const contentFiles = [];
116552
116561
  for (const postType of postTypes) {
116553
116562
  const typeDir = path17.join(this.contentDir, postType);
116554
- console.log(`[DEBUG] Checking directory: ${typeDir}, exists: ${fs15.existsSync(typeDir)}`);
116555
116563
  if (fs15.existsSync(typeDir)) {
116556
116564
  const files = this.findMDXFiles(typeDir);
116557
- console.log(`[DEBUG] Found ${files.length} files in ${postType}`);
116558
116565
  contentFiles.push(...files);
116559
116566
  }
116560
116567
  }
@@ -116687,6 +116694,9 @@ class DevModeOrchestrator {
116687
116694
  }
116688
116695
  startWatchers() {
116689
116696
  this.startBlockWatcher();
116697
+ if (this.scriptsPath) {
116698
+ this.startScriptWatcher();
116699
+ }
116690
116700
  if (this.templatesDir && this.options.buildTemplates) {
116691
116701
  this.startTemplateWatcher();
116692
116702
  }
@@ -116730,6 +116740,20 @@ class DevModeOrchestrator {
116730
116740
  logger.debug("Block watcher ready");
116731
116741
  });
116732
116742
  }
116743
+ startScriptWatcher() {
116744
+ if (!this.scriptsPath || !fs15.existsSync(this.scriptsPath))
116745
+ return;
116746
+ logger.debug(`Watching scripts: ${this.scriptsPath}`);
116747
+ const watcher = $watch("**/*.{ts,js}", {
116748
+ cwd: this.scriptsPath,
116749
+ ...this.getWatcherOptions()
116750
+ });
116751
+ this.watchers.push(watcher);
116752
+ watcher.on("add", (filePath2) => this.handleScriptFileEvent("add", filePath2)).on("change", (filePath2) => this.handleScriptFileEvent("change", filePath2)).on("unlink", (filePath2) => this.handleScriptFileEvent("unlink", filePath2)).on("error", (error) => logger.error("Script watcher error:", error));
116753
+ watcher.on("ready", () => {
116754
+ logger.debug("Script watcher ready");
116755
+ });
116756
+ }
116733
116757
  startTemplateWatcher() {
116734
116758
  if (!this.templatesDir || !fs15.existsSync(this.templatesDir))
116735
116759
  return;
@@ -116758,8 +116782,6 @@ class DevModeOrchestrator {
116758
116782
  const config = this.configManager.getConfig();
116759
116783
  const postTypes = config.sync.postTypes || ["post", "page"];
116760
116784
  const watchPatterns = postTypes.map((type) => `${type}/**/*.mdx`);
116761
- console.log(`[DEBUG] Post types from config: ${JSON.stringify(postTypes)}`);
116762
- console.log(`[DEBUG] Watch patterns: ${JSON.stringify(watchPatterns)}`);
116763
116785
  logger.debug(`Watching content: ${this.contentDir} (${postTypes.join(", ")})`);
116764
116786
  const watcher = $watch(watchPatterns, {
116765
116787
  cwd: this.contentDir,
@@ -116792,6 +116814,44 @@ class DevModeOrchestrator {
116792
116814
  this.rescanBlocks();
116793
116815
  }, this.rescanDebounceMs);
116794
116816
  }
116817
+ handleScriptFileEvent(eventType, filePath2) {
116818
+ const fullPath = path17.resolve(this.scriptsPath, filePath2);
116819
+ const scriptName = path17.basename(filePath2, path17.extname(filePath2));
116820
+ if (eventType === "unlink") {
116821
+ const outputPath = path17.join(this.scriptsOutputDir, `${scriptName}.js`);
116822
+ if (fs15.existsSync(outputPath)) {
116823
+ fs15.unlinkSync(outputPath);
116824
+ console.log(`\uD83D\uDDD1️ ${scriptName}.js deleted`);
116825
+ }
116826
+ } else {
116827
+ this.buildScript(fullPath, scriptName);
116828
+ }
116829
+ }
116830
+ async buildScript(scriptPath, scriptName) {
116831
+ const startTime = Date.now();
116832
+ process.stdout.write(`\uD83D\uDCDC ${scriptName} → `);
116833
+ try {
116834
+ const outputPath = path17.join(this.scriptsOutputDir, `${scriptName}.js`);
116835
+ await esbuild.build({
116836
+ entryPoints: [scriptPath],
116837
+ outfile: outputPath,
116838
+ bundle: true,
116839
+ minify: false,
116840
+ platform: "browser",
116841
+ target: "es2015",
116842
+ format: "iife",
116843
+ external: ["react", "react-dom", "@wordpress/*", "jquery"],
116844
+ define: { "process.env.NODE_ENV": '"development"' },
116845
+ logLevel: "warning",
116846
+ allowOverwrite: true,
116847
+ sourcemap: true
116848
+ });
116849
+ const duration = `${((Date.now() - startTime) / 1000).toFixed(1)}s`;
116850
+ console.log(`✅ (${duration})`);
116851
+ } catch (error) {
116852
+ console.log(`❌ ${error.message}`);
116853
+ }
116854
+ }
116795
116855
  handleTemplateFileEvent(eventType, filePath2) {
116796
116856
  const fullPath = path17.resolve(this.templatesDir, filePath2);
116797
116857
  this.changeQueue.queueTemplateChange(fullPath, eventType);
@@ -117108,6 +117168,7 @@ class DevCommand {
117108
117168
 
117109
117169
  // src/commands/build-command.ts
117110
117170
  import * as path22 from "path";
117171
+ import * as fs19 from "fs";
117111
117172
  import { fileURLToPath as fileURLToPath5 } from "url";
117112
117173
 
117113
117174
  // src/build/block-builder.ts
@@ -117210,14 +117271,14 @@ class WebpackRunner {
117210
117271
  // src/build/script-builder.ts
117211
117272
  import * as fs17 from "fs";
117212
117273
  import * as path19 from "path";
117213
- import * as esbuild from "esbuild";
117274
+ import * as esbuild2 from "esbuild";
117214
117275
  class ScriptBuilder {
117215
117276
  async buildScript(scriptPath, outputDir, scriptName) {
117216
117277
  try {
117217
117278
  logger.debug(`Building script: ${scriptName}`);
117218
117279
  fs17.mkdirSync(outputDir, { recursive: true });
117219
117280
  const outputPath = path19.join(outputDir, scriptName.replace(/\.ts$/, ".js"));
117220
- await esbuild.build({
117281
+ await esbuild2.build({
117221
117282
  entryPoints: [scriptPath],
117222
117283
  outfile: outputPath,
117223
117284
  bundle: true,
@@ -117264,7 +117325,7 @@ class ScriptBuilder {
117264
117325
  }
117265
117326
 
117266
117327
  // src/build/block-builder.ts
117267
- import * as esbuild2 from "esbuild";
117328
+ import * as esbuild3 from "esbuild";
117268
117329
  class BlockBuilder {
117269
117330
  webpackRunner;
117270
117331
  scriptBuilder;
@@ -117341,7 +117402,7 @@ if (document.readyState === 'loading') {
117341
117402
  try {
117342
117403
  logger.debug(`Generating SSR version for ${block.name} using esbuild`);
117343
117404
  const ssrPath = path20.join(outputDir, "ssr.js");
117344
- await esbuild2.build({
117405
+ await esbuild3.build({
117345
117406
  entryPoints: [block.filePath],
117346
117407
  outfile: ssrPath,
117347
117408
  platform: "node",
@@ -117437,7 +117498,7 @@ if (document.readyState === 'loading') {
117437
117498
  if (typeof global.React === "undefined") {
117438
117499
  global.React = require_react();
117439
117500
  }
117440
- await esbuild2.build({
117501
+ await esbuild3.build({
117441
117502
  entryPoints: [filePath2],
117442
117503
  outfile: tempFile,
117443
117504
  platform: "node",
@@ -117700,6 +117761,12 @@ class WorkerPool {
117700
117761
  // src/commands/build-command.ts
117701
117762
  var __filename3 = fileURLToPath5(import.meta.url);
117702
117763
  var __dirname3 = path22.dirname(__filename3);
117764
+ var packageJsonPath = path22.join(__dirname3, "..", "..", "..", "package.json");
117765
+ var packageVersion = "1.2.12";
117766
+ try {
117767
+ const packageJson = JSON.parse(fs19.readFileSync(packageJsonPath, "utf8"));
117768
+ packageVersion = packageJson.version;
117769
+ } catch {}
117703
117770
 
117704
117771
  class BuildCommand {
117705
117772
  configManager;
@@ -117708,6 +117775,7 @@ class BuildCommand {
117708
117775
  typeGenerator;
117709
117776
  phpGenerator;
117710
117777
  blockScripts;
117778
+ hashManager = null;
117711
117779
  constructor() {
117712
117780
  this.configManager = ConfigManager.getInstance();
117713
117781
  this.blockScanner = new BlockScanner;
@@ -117758,6 +117826,27 @@ class BuildCommand {
117758
117826
  } else {
117759
117827
  logger.info(`Found ${blocks.length} block(s): ${blocks.map((b) => b.name).join(", ")}`);
117760
117828
  }
117829
+ this.hashManager = new BlockHashManager(outputDir, packageVersion);
117830
+ let blocksToRebuild = blocks;
117831
+ if (!options.noCache) {
117832
+ const stats = this.hashManager.getStats(blocks);
117833
+ if (stats.cached > 0) {
117834
+ logger.info(`\uD83D\uDCE6 Cache: ${stats.cached} block(s) unchanged, ${stats.needsRebuild} need(s) rebuild`);
117835
+ blocksToRebuild = this.hashManager.filterBlocksNeedingRebuild(blocks);
117836
+ const skippedBlocks = blocks.filter((b) => !blocksToRebuild.find((rb) => rb.name === b.name));
117837
+ if (skippedBlocks.length > 0) {
117838
+ logger.info(`⏭️ Skipping: ${skippedBlocks.map((b) => b.name).join(", ")}`);
117839
+ }
117840
+ }
117841
+ } else {
117842
+ logger.info("\uD83D\uDD04 Cache disabled (--no-cache), rebuilding all blocks");
117843
+ this.hashManager.clearAllHashes();
117844
+ }
117845
+ if (blocksToRebuild.length === 0) {
117846
+ logger.success("✨ All blocks are up to date! Nothing to build.");
117847
+ return;
117848
+ }
117849
+ logger.info(`\uD83D\uDD28 Building ${blocksToRebuild.length} block(s): ${blocksToRebuild.map((b) => b.name).join(", ")}`);
117761
117850
  const scriptsOutputDir = this.configManager.resolvePath(config.paths.scriptsDist);
117762
117851
  const workerPool = new WorkerPool({
117763
117852
  concurrency: options.concurrency,
@@ -117766,9 +117855,14 @@ class BuildCommand {
117766
117855
  webpackConfigPath: webpackConfig,
117767
117856
  scriptsPath
117768
117857
  });
117769
- const results = await workerPool.buildAll(blocks, (completed, total, blockName, success, error) => {
117858
+ const results = await workerPool.buildAll(blocksToRebuild, (completed, total, blockName, success, error) => {
117770
117859
  if (success) {
117771
117860
  logger.success(`✅ Built: ${blockName} [${completed}/${total}]`);
117861
+ const block = blocksToRebuild.find((b) => b.name === blockName);
117862
+ if (block && this.hashManager) {
117863
+ const sourceHash = this.hashManager.calculateSourceHash(block.filePath);
117864
+ this.hashManager.storeHash(blockName, sourceHash);
117865
+ }
117772
117866
  } else {
117773
117867
  logger.error(`❌ Failed: ${blockName} [${completed}/${total}]: ${error || "unknown error"}`);
117774
117868
  }
@@ -117797,11 +117891,17 @@ Failed to build ${failures.length} block(s):`);
117797
117891
  }
117798
117892
  await this.generateTypeDefinitions(outputDir);
117799
117893
  const successCount = results.filter((r) => r.success).length;
117894
+ const totalBlocks = blocks.length;
117895
+ const cachedCount = totalBlocks - blocksToRebuild.length;
117800
117896
  if (failures.length > 0) {
117801
- logger.warn(`Build completed with errors. Built ${successCount}/${blocks.length} block(s).`);
117897
+ logger.warn(`Build completed with errors. Built ${successCount}/${blocksToRebuild.length} block(s)${cachedCount > 0 ? `, ${cachedCount} cached` : ""}.`);
117802
117898
  process.exit(1);
117803
117899
  } else {
117804
- logger.success(`Build completed successfully! Built ${successCount} block(s).`);
117900
+ if (cachedCount > 0) {
117901
+ logger.success(`Build completed successfully! Built ${successCount} block(s), ${cachedCount} cached.`);
117902
+ } else {
117903
+ logger.success(`Build completed successfully! Built ${successCount} block(s).`);
117904
+ }
117805
117905
  }
117806
117906
  } catch (error) {
117807
117907
  logger.error("Build operation failed:");
@@ -117830,7 +117930,7 @@ Failed to build ${failures.length} block(s):`);
117830
117930
  }
117831
117931
 
117832
117932
  // src/commands/build-templates-command.ts
117833
- import * as fs19 from "fs";
117933
+ import * as fs20 from "fs";
117834
117934
  import * as path23 from "path";
117835
117935
  class BuildTemplatesCommand {
117836
117936
  configManager;
@@ -117842,11 +117942,11 @@ class BuildTemplatesCommand {
117842
117942
  const config = this.configManager.getConfig();
117843
117943
  const templatesDir = options.templatesDir || this.configManager.resolvePath(config.paths.templates);
117844
117944
  const outputDir = options.outputDir || this.configManager.resolvePath(config.paths.templatesOutput);
117845
- if (!fs19.existsSync(templatesDir)) {
117945
+ if (!fs20.existsSync(templatesDir)) {
117846
117946
  logger.error(`Templates directory not found: ${templatesDir}`);
117847
117947
  return;
117848
117948
  }
117849
- fs19.mkdirSync(outputDir, { recursive: true });
117949
+ fs20.mkdirSync(outputDir, { recursive: true });
117850
117950
  const blocksOutputDir = this.configManager.resolvePath(config.paths.blocksDist);
117851
117951
  const blockRegistry = new BlockRegistry(blocksOutputDir);
117852
117952
  await blockRegistry.loadBuiltBlocks();
@@ -117884,17 +117984,17 @@ class BuildTemplatesCommand {
117884
117984
  }
117885
117985
  }
117886
117986
  async buildTemplate(templatePath, templateName, outputDir, renderer) {
117887
- const mdxContent = fs19.readFileSync(templatePath, "utf8");
117987
+ const mdxContent = fs20.readFileSync(templatePath, "utf8");
117888
117988
  const cleanContent = this.removeFrontmatter(mdxContent);
117889
117989
  const html5 = await renderer.renderMDXToHTML(cleanContent);
117890
117990
  const cleanHTML = renderer.postProcessHTML(html5);
117891
117991
  const outputPath = path23.join(outputDir, `${templateName}.html`);
117892
- fs19.writeFileSync(outputPath, cleanHTML, "utf8");
117992
+ fs20.writeFileSync(outputPath, cleanHTML, "utf8");
117893
117993
  logger.info(`✓ Generated: ${path23.relative(process.cwd(), outputPath)}`);
117894
117994
  }
117895
117995
  findMDXFiles(dir) {
117896
117996
  const files = [];
117897
- const entries = fs19.readdirSync(dir, { withFileTypes: true });
117997
+ const entries = fs20.readdirSync(dir, { withFileTypes: true });
117898
117998
  for (const entry of entries) {
117899
117999
  const fullPath = path23.join(dir, entry.name);
117900
118000
  if (entry.isDirectory()) {
@@ -117912,7 +118012,7 @@ class BuildTemplatesCommand {
117912
118012
  }
117913
118013
 
117914
118014
  // src/commands/env-command.ts
117915
- import * as fs20 from "fs";
118015
+ import * as fs21 from "fs";
117916
118016
  import * as path24 from "path";
117917
118017
  class EnvCommand {
117918
118018
  configManager;
@@ -117921,11 +118021,11 @@ class EnvCommand {
117921
118021
  }
117922
118022
  getDockerEnvManager() {
117923
118023
  const config = this.configManager.getConfig();
117924
- const packageJsonPath = path24.join(this.configManager.getConfigDir(), "package.json");
118024
+ const packageJsonPath2 = path24.join(this.configManager.getConfigDir(), "package.json");
117925
118025
  let projectName = "wordpress-project";
117926
- if (fs20.existsSync(packageJsonPath)) {
118026
+ if (fs21.existsSync(packageJsonPath2)) {
117927
118027
  try {
117928
- const pkg = JSON.parse(fs20.readFileSync(packageJsonPath, "utf8"));
118028
+ const pkg = JSON.parse(fs21.readFileSync(packageJsonPath2, "utf8"));
117929
118029
  projectName = pkg.name || projectName;
117930
118030
  } catch {}
117931
118031
  }
@@ -117974,6 +118074,501 @@ class EnvCommand {
117974
118074
  }
117975
118075
  }
117976
118076
 
118077
+ // src/commands/deploy-command.ts
118078
+ import * as fs23 from "fs";
118079
+ import * as path26 from "path";
118080
+
118081
+ // src/wordpress/ssh-tunnel.ts
118082
+ import * as fs22 from "fs";
118083
+ import * as path25 from "path";
118084
+ import { spawn } from "child_process";
118085
+ class SSHTunnel {
118086
+ configManager;
118087
+ secrets = new Map;
118088
+ tunnelProcess = null;
118089
+ constructor() {
118090
+ this.configManager = ConfigManager.getInstance();
118091
+ this.loadSecrets();
118092
+ }
118093
+ loadSecrets() {
118094
+ const secretsPath = path25.join(process.cwd(), "wordpress-flow.secrets");
118095
+ if (!fs22.existsSync(secretsPath))
118096
+ return;
118097
+ try {
118098
+ const content4 = fs22.readFileSync(secretsPath, "utf8");
118099
+ for (const line of content4.split(`
118100
+ `)) {
118101
+ const trimmed = line.trim();
118102
+ if (!trimmed || trimmed.startsWith("#"))
118103
+ continue;
118104
+ const eqIndex = trimmed.indexOf("=");
118105
+ if (eqIndex === -1)
118106
+ continue;
118107
+ const key = trimmed.slice(0, eqIndex).trim();
118108
+ let value = trimmed.slice(eqIndex + 1).trim();
118109
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
118110
+ value = value.slice(1, -1);
118111
+ }
118112
+ this.secrets.set(key, value);
118113
+ }
118114
+ } catch {}
118115
+ }
118116
+ resolveVariables(value) {
118117
+ return value.replace(/\$\{([^}]+)\}/g, (match2, varName) => {
118118
+ const secret = this.secrets.get(varName);
118119
+ if (secret !== undefined)
118120
+ return secret;
118121
+ const envVar = process.env[varName];
118122
+ if (envVar !== undefined)
118123
+ return envVar;
118124
+ return match2;
118125
+ });
118126
+ }
118127
+ getEnvironment(name2) {
118128
+ const environments = this.configManager.getEnvironments();
118129
+ const env2 = environments.find((e) => e.name === name2);
118130
+ if (!env2) {
118131
+ throw new Error(`Environment "${name2}" not found. Available: ${environments.map((e) => e.name).join(", ")}`);
118132
+ }
118133
+ return env2;
118134
+ }
118135
+ async connect(envName) {
118136
+ const env2 = this.getEnvironment(envName);
118137
+ if (!env2.database) {
118138
+ throw new Error(`No database config for environment "${envName}"`);
118139
+ }
118140
+ const db = env2.database;
118141
+ const dbHost = this.resolveVariables(db.host);
118142
+ const dbPort = db.port || 3306;
118143
+ const dbUser = this.resolveVariables(db.username);
118144
+ const dbPass = this.resolveVariables(db.password);
118145
+ const dbName = this.resolveVariables(db.database);
118146
+ const localPort = 13306 + Math.floor(Math.random() * 1000);
118147
+ const sshPort = env2.ssh.port || 22;
118148
+ const target = `${env2.ssh.user}@${env2.ssh.host}`;
118149
+ let sshFlags = `-p ${sshPort} -o ServerAliveInterval=30 -o StrictHostKeyChecking=no`;
118150
+ let prefix = "";
118151
+ if (env2.ssh.privateKeyPath) {
118152
+ sshFlags += ` -i ${this.resolveVariables(env2.ssh.privateKeyPath)}`;
118153
+ } else if (env2.ssh.password) {
118154
+ const password = this.resolveVariables(env2.ssh.password);
118155
+ const escaped = password.replace(/'/g, "'\\''");
118156
+ prefix = `sshpass -p '${escaped}' `;
118157
+ sshFlags += ` -o PreferredAuthentications=password -o PubkeyAuthentication=no`;
118158
+ }
118159
+ const tunnelCmd = `${prefix}ssh ${sshFlags} -N -L ${localPort}:${dbHost}:${dbPort} ${target}`;
118160
+ logger.substep(`SSH tunnel: localhost:${localPort} → ${envName} (${dbHost}:${dbPort})`);
118161
+ this.tunnelProcess = spawn("bash", ["-c", tunnelCmd], {
118162
+ stdio: "pipe",
118163
+ detached: false
118164
+ });
118165
+ await this.waitForPort(localPort, 15000);
118166
+ this.configManager.setDatabaseConnection({
118167
+ host: "127.0.0.1",
118168
+ port: localPort,
118169
+ database: dbName,
118170
+ username: dbUser,
118171
+ password: dbPass
118172
+ });
118173
+ return () => {
118174
+ this.tunnelProcess?.kill();
118175
+ this.tunnelProcess = null;
118176
+ };
118177
+ }
118178
+ waitForPort(port, timeoutMs) {
118179
+ const net = __require("net");
118180
+ const start2 = Date.now();
118181
+ return new Promise((resolve7, reject) => {
118182
+ const tryConnect = () => {
118183
+ if (Date.now() - start2 > timeoutMs) {
118184
+ return reject(new Error(`SSH tunnel timed out — port ${port} not reachable after ${timeoutMs / 1000}s`));
118185
+ }
118186
+ const socket = net.createConnection({ port, host: "127.0.0.1" });
118187
+ socket.setTimeout(500);
118188
+ socket.on("connect", () => {
118189
+ socket.destroy();
118190
+ resolve7();
118191
+ });
118192
+ socket.on("error", () => {
118193
+ socket.destroy();
118194
+ setTimeout(tryConnect, 300);
118195
+ });
118196
+ socket.on("timeout", () => {
118197
+ socket.destroy();
118198
+ setTimeout(tryConnect, 300);
118199
+ });
118200
+ };
118201
+ tryConnect();
118202
+ });
118203
+ }
118204
+ }
118205
+
118206
+ // src/commands/deploy-command.ts
118207
+ import { execSync as execSync3 } from "child_process";
118208
+
118209
+ class DeployCommand {
118210
+ configManager;
118211
+ secrets = new Map;
118212
+ constructor() {
118213
+ this.configManager = ConfigManager.getInstance();
118214
+ this.loadSecrets();
118215
+ }
118216
+ loadSecrets() {
118217
+ const secretsPath = path26.join(process.cwd(), "wordpress-flow.secrets");
118218
+ if (!fs23.existsSync(secretsPath)) {
118219
+ logger.debug("No secrets file found at wordpress-flow.secrets");
118220
+ return;
118221
+ }
118222
+ try {
118223
+ const content4 = fs23.readFileSync(secretsPath, "utf8");
118224
+ for (const line of content4.split(`
118225
+ `)) {
118226
+ const trimmed = line.trim();
118227
+ if (!trimmed || trimmed.startsWith("#"))
118228
+ continue;
118229
+ const eqIndex = trimmed.indexOf("=");
118230
+ if (eqIndex === -1)
118231
+ continue;
118232
+ const key = trimmed.slice(0, eqIndex).trim();
118233
+ let value = trimmed.slice(eqIndex + 1).trim();
118234
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
118235
+ value = value.slice(1, -1);
118236
+ }
118237
+ this.secrets.set(key, value);
118238
+ }
118239
+ logger.debug(`Loaded ${this.secrets.size} secrets`);
118240
+ } catch (error) {
118241
+ logger.warn(`Failed to load secrets: ${error.message}`);
118242
+ }
118243
+ }
118244
+ resolveVariables(value) {
118245
+ return value.replace(/\$\{([^}]+)\}/g, (match2, varName) => {
118246
+ const secret = this.secrets.get(varName);
118247
+ if (secret !== undefined)
118248
+ return secret;
118249
+ const envVar = process.env[varName];
118250
+ if (envVar !== undefined)
118251
+ return envVar;
118252
+ logger.warn(`Variable ${varName} not found in secrets or environment`);
118253
+ return match2;
118254
+ });
118255
+ }
118256
+ buildSSHConfig(env2) {
118257
+ return `${env2.ssh.user}@${env2.ssh.host}`;
118258
+ }
118259
+ buildSSHParts(env2) {
118260
+ const port = env2.ssh.port || 22;
118261
+ let sshFlags = `-p ${port} -o ServerAliveInterval=30 -o StrictHostKeyChecking=no`;
118262
+ let prefix = "";
118263
+ if (env2.ssh.privateKeyPath) {
118264
+ const keyPath = this.resolveVariables(env2.ssh.privateKeyPath);
118265
+ sshFlags += ` -i ${keyPath}`;
118266
+ } else if (env2.ssh.password) {
118267
+ const password = this.resolveVariables(env2.ssh.password);
118268
+ const escaped = password.replace(/'/g, "'\\''");
118269
+ prefix = `sshpass -p '${escaped}' `;
118270
+ sshFlags += ` -o PreferredAuthentications=password -o PubkeyAuthentication=no`;
118271
+ }
118272
+ return { prefix, sshFlags };
118273
+ }
118274
+ sshExec(env2, remoteCmd, opts) {
118275
+ const { prefix, sshFlags } = this.buildSSHParts(env2);
118276
+ const target = this.buildSSHConfig(env2);
118277
+ const fullCmd = `${prefix}ssh ${sshFlags} ${target} "${remoteCmd.replace(/"/g, "\\\"")}"`;
118278
+ if (!opts?.silent)
118279
+ logger.debug(`SSH: ${remoteCmd}`);
118280
+ try {
118281
+ return execSync3(fullCmd, { encoding: "utf8", stdio: "pipe" }).trim();
118282
+ } catch (error) {
118283
+ const stderr = error.stderr?.toString() || "";
118284
+ const msg = error.message || String(error);
118285
+ if (msg.includes("sshpass: command not found") || msg.includes("sshpass: not found")) {
118286
+ throw new Error(`sshpass is required for password authentication.
118287
+ ` + `Install: brew install sshpass (macOS) / apt install sshpass (Linux)
118288
+ ` + `Or use SSH key authentication instead.`);
118289
+ }
118290
+ throw new Error(`SSH command failed: ${remoteCmd}
118291
+ ${stderr || msg}`);
118292
+ }
118293
+ }
118294
+ scpUpload(env2, localPath, remotePath) {
118295
+ const { prefix, sshFlags } = this.buildSSHParts(env2);
118296
+ const target = this.buildSSHConfig(env2);
118297
+ const port = env2.ssh.port || 22;
118298
+ const scpFlags = sshFlags.replace(`-p ${port}`, "");
118299
+ const fullCmd = `${prefix}scp -P ${port} ${scpFlags} "${localPath}" "${target}:${remotePath}"`;
118300
+ logger.debug(`SCP upload: ${localPath} → ${remotePath}`);
118301
+ try {
118302
+ execSync3(fullCmd, { encoding: "utf8", stdio: "pipe" });
118303
+ } catch (error) {
118304
+ const stderr = error.stderr?.toString() || "";
118305
+ throw new Error(`SCP upload failed: ${stderr || error.message}`);
118306
+ }
118307
+ }
118308
+ async checkWordPressInstalled(env2) {
118309
+ const webroot = this.deriveWebroot(env2);
118310
+ logger.debug(`Checking WordPress at ${webroot}...`);
118311
+ try {
118312
+ const result2 = this.sshExec(env2, `test -f ${webroot}/wp-config.php && echo 'yes' || echo 'no'`, { silent: true });
118313
+ return { installed: result2 === "yes", webroot };
118314
+ } catch {
118315
+ return { installed: false, webroot };
118316
+ }
118317
+ }
118318
+ deriveWebroot(env2) {
118319
+ const themePath = env2.remotePaths.theme;
118320
+ const wpContentIdx = themePath.indexOf("/wp-content/");
118321
+ if (wpContentIdx !== -1) {
118322
+ return themePath.slice(0, wpContentIdx);
118323
+ }
118324
+ return path26.posix.resolve(themePath, "../../..");
118325
+ }
118326
+ askYesNo(question) {
118327
+ return new Promise((resolve7) => {
118328
+ const readline2 = __require("readline");
118329
+ const rl = readline2.createInterface({
118330
+ input: process.stdin,
118331
+ output: process.stdout
118332
+ });
118333
+ rl.question(`${question} (Y/n): `, (answer) => {
118334
+ rl.close();
118335
+ const n = answer.trim().toLowerCase();
118336
+ resolve7(n === "" || n === "y" || n === "yes");
118337
+ });
118338
+ });
118339
+ }
118340
+ askInput(question, defaultValue) {
118341
+ return new Promise((resolve7) => {
118342
+ const readline2 = __require("readline");
118343
+ const rl = readline2.createInterface({
118344
+ input: process.stdin,
118345
+ output: process.stdout
118346
+ });
118347
+ const prompt = defaultValue ? `${question} [${defaultValue}]: ` : `${question}: `;
118348
+ rl.question(prompt, (answer) => {
118349
+ rl.close();
118350
+ resolve7(answer.trim() || defaultValue || "");
118351
+ });
118352
+ });
118353
+ }
118354
+ async installWordPress(env2, webroot, sqlDump) {
118355
+ logger.progress("Installing WordPress on remote server...");
118356
+ logger.info("Checking for WP-CLI...");
118357
+ try {
118358
+ this.sshExec(env2, "wp --version", { silent: true });
118359
+ logger.debug("WP-CLI already available");
118360
+ } catch {
118361
+ logger.info("Installing WP-CLI...");
118362
+ this.sshExec(env2, `curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar && chmod +x wp-cli.phar && mkdir -p ~/bin && mv wp-cli.phar ~/bin/wp`);
118363
+ logger.success("WP-CLI installed to ~/bin/wp");
118364
+ }
118365
+ const wp = "~/bin/wp";
118366
+ this.sshExec(env2, `mkdir -p ${webroot}`);
118367
+ logger.info("Downloading WordPress core...");
118368
+ this.sshExec(env2, `${wp} core download --path=${webroot} --locale=pl_PL --skip-content`);
118369
+ logger.success("WordPress core downloaded");
118370
+ if (env2.database) {
118371
+ const db = env2.database;
118372
+ const dbHost = this.resolveVariables(db.host);
118373
+ const dbPort = db.port || 3306;
118374
+ const dbName = this.resolveVariables(db.database);
118375
+ const dbUser = this.resolveVariables(db.username);
118376
+ const dbPass = this.resolveVariables(db.password);
118377
+ logger.info("Creating wp-config.php...");
118378
+ this.sshExec(env2, `${wp} config create --path=${webroot} --dbname=${dbName} --dbuser=${dbUser} --dbpass='${dbPass.replace(/'/g, "'\\''")}' --dbhost=${dbHost}:${dbPort} --skip-check`);
118379
+ logger.success("wp-config.php created");
118380
+ if (sqlDump && fs23.existsSync(sqlDump)) {
118381
+ logger.info(`Loading database dump from ${sqlDump}...`);
118382
+ await this.loadDump(env2, sqlDump);
118383
+ logger.success("Database imported from dump");
118384
+ } else {
118385
+ if (sqlDump) {
118386
+ logger.warn(`Dump file not found at ${sqlDump}, falling back to fresh install`);
118387
+ }
118388
+ logger.info("Installing WordPress database...");
118389
+ const siteUrl = env2.url || `https://${env2.ssh.host}`;
118390
+ const adminUser = await this.askInput("Admin username", "admin");
118391
+ const adminPassword = await this.askInput("Admin password");
118392
+ const adminEmail = await this.askInput("Admin email");
118393
+ const siteTitle = await this.askInput("Site title", env2.name);
118394
+ this.sshExec(env2, `${wp} core install --path=${webroot} --url='${siteUrl}' --title='${siteTitle.replace(/'/g, "'\\''")}' --admin_user='${adminUser}' --admin_password='${adminPassword.replace(/'/g, "'\\''")}' --admin_email='${adminEmail}' --skip-email`);
118395
+ logger.success("WordPress database installed");
118396
+ }
118397
+ } else {
118398
+ logger.warn(`No database config in environment — skipping wp-config.php creation.
118399
+ ` + "You'll need to create it manually on the server.");
118400
+ }
118401
+ this.sshExec(env2, `mkdir -p ${webroot}/wp-content/themes ${webroot}/wp-content/plugins`);
118402
+ logger.success("WordPress installed on remote server");
118403
+ }
118404
+ async execute(options) {
118405
+ const config = this.configManager.getConfig();
118406
+ const environments = this.configManager.getEnvironments();
118407
+ const env2 = environments.find((e) => e.name === options.environment);
118408
+ if (!env2) {
118409
+ throw new Error(`Environment "${options.environment}" not found in config. ` + `Available: ${environments.map((e) => e.name).join(", ")}`);
118410
+ }
118411
+ console.log("");
118412
+ logger.progress(`Deploying to ${env2.name} (${env2.ssh.host})`);
118413
+ console.log("");
118414
+ try {
118415
+ const { installed, webroot } = await this.checkWordPressInstalled(env2);
118416
+ if (!installed) {
118417
+ logger.warn(`WordPress not found at ${webroot}`);
118418
+ const shouldInstall = await this.askYesNo("WordPress is not installed on the remote server. Install it now?");
118419
+ if (shouldInstall) {
118420
+ await this.installWordPress(env2, webroot, config.dev.sqlDump);
118421
+ } else {
118422
+ throw new Error(`WordPress is required on the remote server before deploying.
118423
+ ` + "Install it manually or re-run deploy and choose to install.");
118424
+ }
118425
+ }
118426
+ const hasDump = options.dump !== undefined;
118427
+ let dumpPath;
118428
+ if (hasDump) {
118429
+ dumpPath = typeof options.dump === "string" ? options.dump : config.dev.sqlDump;
118430
+ if (!dumpPath) {
118431
+ throw new Error("No dump file specified. Use --dump <path> or set dev.sqlDump in config");
118432
+ }
118433
+ }
118434
+ const steps = [];
118435
+ if (!options.skipBuild)
118436
+ steps.push("build");
118437
+ steps.push("sync");
118438
+ if (hasDump) {
118439
+ steps.push("database");
118440
+ } else if (env2.database) {
118441
+ steps.push("content");
118442
+ }
118443
+ let step = 0;
118444
+ const logStep = (msg) => {
118445
+ step++;
118446
+ logger.step(step, steps.length, msg);
118447
+ };
118448
+ if (!options.skipBuild) {
118449
+ logStep("Building blocks and assets...");
118450
+ const buildCommand = new BuildCommand;
118451
+ await buildCommand.execute({ noCache: false });
118452
+ logger.success("Build completed");
118453
+ }
118454
+ logStep("Syncing files to remote server...");
118455
+ await this.syncFiles(env2, config.paths);
118456
+ if (hasDump && dumpPath) {
118457
+ logStep("Importing database...");
118458
+ await this.loadDump(env2, dumpPath);
118459
+ } else if (env2.database) {
118460
+ logStep("Pushing content to remote database...");
118461
+ await this.pushContent(env2);
118462
+ }
118463
+ console.log("");
118464
+ logger.success(`\uD83D\uDE80 Deployment to ${env2.name} completed!`);
118465
+ console.log("");
118466
+ } catch (error) {
118467
+ console.log("");
118468
+ logger.error("Deployment failed:", error);
118469
+ process.exit(1);
118470
+ }
118471
+ }
118472
+ async syncFiles(env2, paths) {
118473
+ const target = this.buildSSHConfig(env2);
118474
+ try {
118475
+ this.sshExec(env2, `mkdir -p ${env2.remotePaths.theme} ${env2.remotePaths.plugins}`, { silent: true });
118476
+ } catch {}
118477
+ logger.substep("theme");
118478
+ this.rsync(paths.theme + "/", `${target}:${env2.remotePaths.theme}/`, env2);
118479
+ logger.substep("plugins");
118480
+ this.rsync(paths.plugins + "/", `${target}:${env2.remotePaths.plugins}/`, env2);
118481
+ logger.success("Files synced");
118482
+ }
118483
+ rsync(source, destination, env2) {
118484
+ const { prefix, sshFlags } = this.buildSSHParts(env2);
118485
+ const rsyncCmd = `${prefix}rsync -avz --delete ` + `--exclude 'node_modules' --exclude '.git' --exclude '.DS_Store' --exclude '*.log' ` + `-e "ssh ${sshFlags}" ` + `"${source}" "${destination}"`;
118486
+ try {
118487
+ execSync3(rsyncCmd, { encoding: "utf8", stdio: "pipe" });
118488
+ } catch (error) {
118489
+ const msg = error.message || String(error);
118490
+ const stderr = error.stderr?.toString() || "";
118491
+ if (msg.includes("Disk quota exceeded") || stderr.includes("Disk quota exceeded")) {
118492
+ throw new Error("Server disk quota exceeded. Free up space and retry.");
118493
+ }
118494
+ if (msg.includes("Permission denied") || stderr.includes("Permission denied")) {
118495
+ throw new Error(`Permission denied writing to ${destination}.`);
118496
+ }
118497
+ if (msg.includes("Connection refused") || msg.includes("Connection timed out")) {
118498
+ throw new Error(`Cannot connect to ${env2.ssh.host}.`);
118499
+ }
118500
+ if (msg.includes("sshpass: command not found") || msg.includes("sshpass: not found")) {
118501
+ throw new Error("sshpass is required for password auth. Install: brew install sshpass");
118502
+ }
118503
+ if (msg.includes("child exited with status 11") || msg.includes("unexpected end of file")) {
118504
+ throw new Error("Connection interrupted. Check disk quota and try again.");
118505
+ }
118506
+ throw new Error(`Rsync failed:
118507
+ ${stderr || msg}`);
118508
+ }
118509
+ }
118510
+ async pushContent(env2) {
118511
+ if (!env2.database) {
118512
+ throw new Error(`Database configuration not found for environment ${env2.name}`);
118513
+ }
118514
+ const tunnel = new SSHTunnel;
118515
+ const closeTunnel = await tunnel.connect(env2.name);
118516
+ try {
118517
+ logger.substep("Pushing MDX content to remote database");
118518
+ const pushCommand = new PushCommand;
118519
+ const result2 = await pushCommand.execute({ force: true });
118520
+ if (!result2.success) {
118521
+ const errors = result2.errors.map((e) => e.error).join(", ");
118522
+ throw new Error(`Failed to push content: ${errors}`);
118523
+ }
118524
+ logger.success(`Content pushed (${result2.filesProcessed} files)`);
118525
+ } finally {
118526
+ closeTunnel();
118527
+ }
118528
+ }
118529
+ async loadDump(env2, dumpPath) {
118530
+ if (!env2.database) {
118531
+ throw new Error(`Database configuration not found for environment ${env2.name}`);
118532
+ }
118533
+ if (!fs23.existsSync(dumpPath)) {
118534
+ throw new Error(`Dump file not found: ${dumpPath}`);
118535
+ }
118536
+ const db = env2.database;
118537
+ const dbHost = this.resolveVariables(db.host);
118538
+ const dbPort = db.port || 3306;
118539
+ const dbName = this.resolveVariables(db.database);
118540
+ const dbUser = this.resolveVariables(db.username);
118541
+ const dbPass = this.resolveVariables(db.password);
118542
+ logger.substep(`Uploading ${dumpPath}`);
118543
+ const remoteDumpPath = `/tmp/wordpress-flow-dump-${Date.now()}.sql`;
118544
+ this.scpUpload(env2, dumpPath, remoteDumpPath);
118545
+ logger.substep("Importing into database");
118546
+ const escapedPass = dbPass.replace(/'/g, "'\\''");
118547
+ const mysqlCmd = `grep -v '^mysqldump:' ${remoteDumpPath} | mysql -h ${dbHost} -P ${dbPort} -u ${dbUser} -p'${escapedPass}' ${dbName} && rm ${remoteDumpPath}`;
118548
+ this.sshExec(env2, mysqlCmd);
118549
+ const webroot = this.deriveWebroot(env2);
118550
+ const wp = "~/bin/wp";
118551
+ if (env2.url) {
118552
+ const config = this.configManager.getConfig();
118553
+ const devPort = config.dev.port || 8888;
118554
+ const oldUrl = `http://localhost:${devPort}`;
118555
+ const newUrl = env2.url.replace(/\/$/, "");
118556
+ logger.substep(`Rewriting URLs: ${oldUrl} → ${newUrl}`);
118557
+ this.sshExec(env2, `${wp} search-replace '${oldUrl}' '${newUrl}' --path=${webroot} --all-tables --skip-columns=guid`);
118558
+ } else {
118559
+ logger.warn("No 'url' in environment config — skipping URL rewrite");
118560
+ }
118561
+ const themeSlug = path26.posix.basename(env2.remotePaths.theme);
118562
+ logger.substep(`Activating theme: ${themeSlug}`);
118563
+ try {
118564
+ this.sshExec(env2, `${wp} theme activate ${themeSlug} --path=${webroot}`);
118565
+ } catch {
118566
+ logger.warn(`Could not activate theme "${themeSlug}"`);
118567
+ }
118568
+ logger.success("Database imported");
118569
+ }
118570
+ }
118571
+
117977
118572
  // src/index.ts
117978
118573
  var program2 = new Command;
117979
118574
  program2.name("wordpress-flow").description("Build WordPress sites with React, TypeScript, and MDX - A content-first development framework").version(package_default.version);
@@ -117994,7 +118589,7 @@ program2.command("setup").description("Run the configuration wizard").action(asy
117994
118589
  process.exit(1);
117995
118590
  }
117996
118591
  });
117997
- program2.command("build").description("Build WordPress blocks for Gutenberg").option("--blocks-dir <dir>", "Source directory for blocks").option("--output-dir <dir>", "Output directory for built blocks").option("--webpack-config <path>", "Path to webpack config file").option("--watch", "Watch mode for development").option("-b, --blocks <names...>", "Specific block names to build").option("-c, --concurrency <number>", "Number of parallel workers (default: CPU cores - 1)").action(async (options) => {
118592
+ program2.command("build").description("Build WordPress blocks for Gutenberg").option("--blocks-dir <dir>", "Source directory for blocks").option("--output-dir <dir>", "Output directory for built blocks").option("--webpack-config <path>", "Path to webpack config file").option("--watch", "Watch mode for development").option("-b, --blocks <names...>", "Specific block names to build").option("-c, --concurrency <number>", "Number of parallel workers (default: CPU cores - 1)").option("--no-cache", "Disable cache and rebuild all blocks").action(async (options) => {
117998
118593
  try {
117999
118594
  await ensureConfiguration();
118000
118595
  const command = new BuildCommand;
@@ -118004,7 +118599,8 @@ program2.command("build").description("Build WordPress blocks for Gutenberg").op
118004
118599
  webpackConfig: options.webpackConfig,
118005
118600
  watch: options.watch || false,
118006
118601
  blocks: options.blocks,
118007
- concurrency: options.concurrency ? parseInt(options.concurrency, 10) : undefined
118602
+ concurrency: options.concurrency ? parseInt(options.concurrency, 10) : undefined,
118603
+ noCache: !options.cache
118008
118604
  });
118009
118605
  logger.info("Build command completed successfully");
118010
118606
  } catch (error) {
@@ -118015,9 +118611,14 @@ program2.command("build").description("Build WordPress blocks for Gutenberg").op
118015
118611
  program2.command("pull").description("Pull content from WordPress and convert to MDX").option("-f, --force", "Force overwrite existing files").option("--post-types <types...>", "Specific post types to pull").option("--post-ids <ids...>", "Specific post IDs to pull").option("--status <statuses...>", "Post statuses to include", [
118016
118612
  "publish",
118017
118613
  "draft"
118018
- ]).option("--output-dir <dir>", "Output directory for MDX files").action(async (options) => {
118614
+ ]).option("--output-dir <dir>", "Output directory for MDX files").option("--env <environment>", "Pull from a remote environment via SSH tunnel").action(async (options) => {
118615
+ let closeTunnel;
118019
118616
  try {
118020
118617
  await ensureConfiguration();
118618
+ if (options.env) {
118619
+ const tunnel = new SSHTunnel;
118620
+ closeTunnel = await tunnel.connect(options.env);
118621
+ }
118021
118622
  const command = new PullCommand;
118022
118623
  await command.execute({
118023
118624
  force: options.force || false,
@@ -118030,17 +118631,26 @@ program2.command("pull").description("Pull content from WordPress and convert to
118030
118631
  } catch (error) {
118031
118632
  logger.error("Pull command failed:", error);
118032
118633
  process.exit(1);
118634
+ } finally {
118635
+ closeTunnel?.();
118033
118636
  }
118034
118637
  });
118035
- program2.command("push").description("Convert MDX files and push to WordPress").option("-f, --files <files...>", "Specific files to push").option("--force", "Force push, overriding conflicts").option("--dry-run", "Preview changes without pushing").action(async (options) => {
118638
+ program2.command("push").description("Convert MDX files and push to WordPress").option("-f, --files <files...>", "Specific files to push").option("--force", "Force push, overriding conflicts").option("--dry-run", "Preview changes without pushing").option("--env <environment>", "Push to a remote environment via SSH tunnel").action(async (options) => {
118639
+ let closeTunnel;
118036
118640
  try {
118037
118641
  await ensureConfiguration();
118642
+ if (options.env) {
118643
+ const tunnel = new SSHTunnel;
118644
+ closeTunnel = await tunnel.connect(options.env);
118645
+ }
118038
118646
  const command = new PushCommand;
118039
118647
  await command.execute(options);
118040
118648
  logger.info("Push command completed successfully");
118041
118649
  } catch (error) {
118042
118650
  logger.error("Push command failed:", error);
118043
118651
  process.exit(1);
118652
+ } finally {
118653
+ closeTunnel?.();
118044
118654
  }
118045
118655
  });
118046
118656
  program2.command("build-templates").description("Build WordPress theme templates from MDX files").option("--templates-dir <dir>", "Source directory for template MDX files").option("--output-dir <dir>", "Output directory for HTML templates").action(async (options) => {
@@ -118086,6 +118696,20 @@ program2.command("destroy").description("Remove Docker containers and volumes fo
118086
118696
  process.exit(1);
118087
118697
  }
118088
118698
  });
118699
+ program2.command("deploy <environment>").description("Deploy to a remote environment (staging, production, etc.)").option("--skip-build", "Skip the build step").option("--dump [path]", "Load SQL dump instead of pushing MDX (uses config sqlDump if no path provided)").action(async (environment, options) => {
118700
+ try {
118701
+ await ensureConfiguration();
118702
+ const command = new DeployCommand;
118703
+ await command.execute({
118704
+ environment,
118705
+ skipBuild: options.skipBuild || false,
118706
+ dump: options.dump
118707
+ });
118708
+ } catch (error) {
118709
+ logger.error("Deploy command failed:", error);
118710
+ process.exit(1);
118711
+ }
118712
+ });
118089
118713
  var databaseCmd = program2.command("database").description("Database management commands");
118090
118714
  databaseCmd.command("import").description("Import SQL dump into Docker database").option("-f, --file <path>", "Path to SQL file (defaults to config sqlDump)").action(async (options) => {
118091
118715
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wordpress-flow/cli",
3
- "version": "1.2.11",
3
+ "version": "1.2.13",
4
4
  "type": "module",
5
5
  "description": "TypeScript-based WordPress block creation system",
6
6
  "main": "dist/index.js",