wp-studio 1.7.7-alpha1

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 (48) hide show
  1. package/LICENSE.md +257 -0
  2. package/README.md +87 -0
  3. package/dist/cli/_events-BeOo0LuG.js +116 -0
  4. package/dist/cli/appdata-07CF2rhg.js +21090 -0
  5. package/dist/cli/archive-xDmkN4wb.js +15942 -0
  6. package/dist/cli/browser-CgWK-yoe.js +44 -0
  7. package/dist/cli/certificate-manager-DdBumKZp.js +250 -0
  8. package/dist/cli/create-BHVhkvTx.js +80 -0
  9. package/dist/cli/create-ZS29BDDi.js +40999 -0
  10. package/dist/cli/delete-BgQn-elT.js +56 -0
  11. package/dist/cli/delete-g8pgaLna.js +132 -0
  12. package/dist/cli/get-wordpress-version-BwSCJujO.js +18 -0
  13. package/dist/cli/index-7pbG_s_U.js +434 -0
  14. package/dist/cli/index-BXRYeCYG.js +1393 -0
  15. package/dist/cli/index-T3F1GwxX.js +2668 -0
  16. package/dist/cli/is-errno-exception-t38xF2pB.js +6 -0
  17. package/dist/cli/list-BE_UBjL5.js +105 -0
  18. package/dist/cli/list-DKz0XxM7.js +1032 -0
  19. package/dist/cli/logger-actions-OaIvl-ai.js +45 -0
  20. package/dist/cli/login-B4PkfKOu.js +82 -0
  21. package/dist/cli/logout-BC9gKlTj.js +48 -0
  22. package/dist/cli/main.js +5 -0
  23. package/dist/cli/mu-plugins-GEfKsl5U.js +530 -0
  24. package/dist/cli/passwords-DyzWd9Xi.js +80 -0
  25. package/dist/cli/process-manager-daemon.js +327 -0
  26. package/dist/cli/process-manager-ipc-AUZeYYDT.js +454 -0
  27. package/dist/cli/proxy-daemon.js +197 -0
  28. package/dist/cli/run-wp-cli-command-BctnMDWG.js +88 -0
  29. package/dist/cli/sequential-BQFuixXz.js +46 -0
  30. package/dist/cli/server-files-C_oy-mnI.js +26 -0
  31. package/dist/cli/set-DknhAZpw.js +327 -0
  32. package/dist/cli/site-utils-CfsabjUn.js +243 -0
  33. package/dist/cli/snapshots-6XE53y_F.js +874 -0
  34. package/dist/cli/sqlite-integration-H4OwSlwR.js +83 -0
  35. package/dist/cli/start-CRJqm09_.js +90 -0
  36. package/dist/cli/status-CWNHIOaY.js +44 -0
  37. package/dist/cli/status-CWWx9jYF.js +110 -0
  38. package/dist/cli/stop-CQosmjqA.js +117 -0
  39. package/dist/cli/update-BgL2HKHW.js +101 -0
  40. package/dist/cli/validation-error-DqLxqQuA.js +40 -0
  41. package/dist/cli/wordpress-server-child.js +514 -0
  42. package/dist/cli/wordpress-server-ipc-Dwsg9jSb.js +140 -0
  43. package/dist/cli/wordpress-server-manager-CtiuJqEb.js +566 -0
  44. package/dist/cli/wordpress-version-utils-B6UVeTh_.js +51 -0
  45. package/dist/cli/wp-UGSnlkN0.js +103 -0
  46. package/package.json +73 -0
  47. package/patches/@wp-playground+wordpress+3.1.12.patch +28 -0
  48. package/scripts/postinstall-npm.mjs +38 -0
@@ -0,0 +1,44 @@
1
+ import { spawn } from "child_process";
2
+ async function openBrowser(url) {
3
+ const platform = process.platform;
4
+ let cmd;
5
+ let args;
6
+ switch (platform) {
7
+ case "darwin":
8
+ cmd = "open";
9
+ args = [url];
10
+ break;
11
+ case "win32":
12
+ cmd = "PowerShell";
13
+ args = [
14
+ "-NoProfile",
15
+ "-NonInteractive",
16
+ "-ExecutionPolicy",
17
+ "Bypass",
18
+ "-Command",
19
+ "Start",
20
+ `"${url}"`
21
+ ];
22
+ break;
23
+ case "linux":
24
+ cmd = "xdg-open";
25
+ args = [url];
26
+ break;
27
+ default:
28
+ return Promise.reject(new Error("Unsupported platform"));
29
+ }
30
+ return new Promise((resolve, reject) => {
31
+ const child = spawn(cmd, args);
32
+ child.on("error", reject);
33
+ child.on("exit", (code) => {
34
+ if (code === 0) {
35
+ resolve();
36
+ } else {
37
+ reject(new Error("Failed to open browser"));
38
+ }
39
+ });
40
+ });
41
+ }
42
+ export {
43
+ openBrowser as o
44
+ };
@@ -0,0 +1,250 @@
1
+ import { execFile } from "node:child_process";
2
+ import crypto from "node:crypto";
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import { domainToASCII } from "node:url";
6
+ import { promisify } from "node:util";
7
+ import sudo from "@vscode/sudo-prompt";
8
+ import forge from "node-forge";
9
+ import { Q as getAppdataDirectory } from "./appdata-07CF2rhg.js";
10
+ const execFilePromise = promisify(execFile);
11
+ function createNameConstraintsExtension(domains) {
12
+ const asn1 = forge.asn1;
13
+ const domainsToSequence = (domains2) => domains2.map((domain) => {
14
+ const generalName = asn1.create(asn1.Class.CONTEXT_SPECIFIC, 2, false, domain);
15
+ return asn1.create(
16
+ asn1.Class.UNIVERSAL,
17
+ asn1.Type.SEQUENCE,
18
+ true,
19
+ [generalName]
20
+ // The minimum value is DEFAULT 0, so we can omit it for simplicity
21
+ // The maximum value is OPTIONAL and we don't need it
22
+ );
23
+ });
24
+ const nameConstraintsComponents = [];
25
+ nameConstraintsComponents.push(
26
+ asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, domainsToSequence(domains))
27
+ );
28
+ const nameConstraintsValue = asn1.create(
29
+ asn1.Class.UNIVERSAL,
30
+ asn1.Type.SEQUENCE,
31
+ true,
32
+ nameConstraintsComponents
33
+ );
34
+ return {
35
+ id: "2.5.29.30",
36
+ // nameConstraints OID
37
+ critical: true,
38
+ value: nameConstraintsValue
39
+ };
40
+ }
41
+ const CA_NAME = "WordPress Studio CA";
42
+ const CA_CERT_VALIDITY_DAYS = 3650;
43
+ const SITE_CERT_VALIDITY_DAYS = 825;
44
+ const CERT_DIRECTORY = path.join(getAppdataDirectory(), "certificates");
45
+ const CA_CERT_PATH = path.join(CERT_DIRECTORY, "studio-ca.crt");
46
+ const CA_KEY_PATH = path.join(CERT_DIRECTORY, "studio-ca.key");
47
+ function initializeCertificatesDirectory() {
48
+ if (!fs.existsSync(path.join(CERT_DIRECTORY, "domains"))) {
49
+ fs.mkdirSync(path.join(CERT_DIRECTORY, "domains"), { recursive: true });
50
+ }
51
+ }
52
+ async function ensureRootCA() {
53
+ if (fs.existsSync(CA_CERT_PATH) && fs.existsSync(CA_KEY_PATH)) {
54
+ return {
55
+ cert: fs.readFileSync(CA_CERT_PATH, "utf8"),
56
+ key: fs.readFileSync(CA_KEY_PATH, "utf8")
57
+ };
58
+ }
59
+ console.log("Generating new root CA certificate…");
60
+ const keys = forge.pki.rsa.generateKeyPair(2048);
61
+ const cert = forge.pki.createCertificate();
62
+ cert.publicKey = keys.publicKey;
63
+ cert.serialNumber = crypto.randomBytes(20).toString("hex");
64
+ const now = /* @__PURE__ */ new Date();
65
+ cert.validity.notBefore = now;
66
+ cert.validity.notAfter = new Date(now.getTime());
67
+ cert.validity.notAfter.setDate(now.getDate() + CA_CERT_VALIDITY_DAYS);
68
+ const attrs = [
69
+ { name: "commonName", value: CA_NAME },
70
+ { name: "countryName", value: "US" },
71
+ { name: "organizationName", value: "WordPress Studio" }
72
+ ];
73
+ cert.setSubject(attrs);
74
+ cert.setIssuer(attrs);
75
+ cert.setExtensions([
76
+ {
77
+ name: "basicConstraints",
78
+ cA: true,
79
+ critical: true,
80
+ pathLenConstraint: 0
81
+ // Can only sign end entity certificates, not intermediate CAs
82
+ },
83
+ {
84
+ name: "keyUsage",
85
+ critical: true,
86
+ keyCertSign: true,
87
+ cRLSign: true
88
+ },
89
+ {
90
+ name: "extKeyUsage",
91
+ serverAuth: true,
92
+ clientAuth: true
93
+ },
94
+ createNameConstraintsExtension([".local"])
95
+ ]);
96
+ cert.sign(keys.privateKey, forge.md.sha256.create());
97
+ const certPem = forge.pki.certificateToPem(cert);
98
+ const keyPem = forge.pki.privateKeyToPem(keys.privateKey);
99
+ initializeCertificatesDirectory();
100
+ fs.writeFileSync(CA_CERT_PATH, certPem);
101
+ fs.writeFileSync(CA_KEY_PATH, keyPem);
102
+ fs.chmodSync(CA_CERT_PATH, 448);
103
+ fs.chmodSync(CA_KEY_PATH, 448);
104
+ await trustRootCA();
105
+ return { cert: certPem, key: keyPem };
106
+ }
107
+ async function isRootCATrusted() {
108
+ if (!fs.existsSync(CA_CERT_PATH)) {
109
+ return false;
110
+ }
111
+ if (process.platform === "win32") {
112
+ try {
113
+ const { stdout } = await execFilePromise("certutil", ["-verify", CA_CERT_PATH]);
114
+ const hasValidPolicies = stdout.includes("Verified Application Policies:") && stdout.includes("Server Authentication");
115
+ return hasValidPolicies;
116
+ } catch (error) {
117
+ return false;
118
+ }
119
+ } else if (process.platform === "darwin") {
120
+ try {
121
+ await execFilePromise("security", ["verify-cert", "-r", CA_CERT_PATH, "-p", "ssl"]);
122
+ return true;
123
+ } catch (error) {
124
+ return false;
125
+ }
126
+ }
127
+ return false;
128
+ }
129
+ async function trustRootCA() {
130
+ try {
131
+ if (await isRootCATrusted()) {
132
+ console.log("Root CA is already trusted in the system store");
133
+ return;
134
+ }
135
+ const platform = process.platform;
136
+ if (platform === "win32") {
137
+ await new Promise((resolve, reject) => {
138
+ sudo.exec(
139
+ `certutil -addstore -f "ROOT" "${CA_CERT_PATH}"`,
140
+ { name: "WordPress Studio" },
141
+ (error) => {
142
+ if (error) {
143
+ console.error("Error adding certificate to system trust store:", error);
144
+ reject(error);
145
+ } else {
146
+ console.log("Root CA trusted in Windows certificate store");
147
+ resolve();
148
+ }
149
+ }
150
+ );
151
+ });
152
+ } else {
153
+ console.error("Unsupported platform for automatic certificate trust:", platform);
154
+ }
155
+ } catch (error) {
156
+ console.error("Failed to trust root CA:", error);
157
+ throw error;
158
+ }
159
+ }
160
+ async function generateSiteCertificate(domain) {
161
+ try {
162
+ const punycodeDomain = domainToASCII(domain);
163
+ const siteCertPath = path.join(CERT_DIRECTORY, "domains", `${domain}.crt`);
164
+ const siteKeyPath = path.join(CERT_DIRECTORY, "domains", `${domain}.key`);
165
+ if (fs.existsSync(siteCertPath) && fs.existsSync(siteKeyPath)) {
166
+ return {
167
+ cert: fs.readFileSync(siteCertPath, "utf8"),
168
+ key: fs.readFileSync(siteKeyPath, "utf8")
169
+ };
170
+ }
171
+ const { cert: caCert, key: caKey } = await ensureRootCA();
172
+ const caPrivateKey = forge.pki.privateKeyFromPem(caKey);
173
+ const caCertObj = forge.pki.certificateFromPem(caCert);
174
+ const keys = forge.pki.rsa.generateKeyPair(2048);
175
+ const cert = forge.pki.createCertificate();
176
+ cert.publicKey = keys.publicKey;
177
+ cert.serialNumber = crypto.randomBytes(20).toString("hex");
178
+ const now = /* @__PURE__ */ new Date();
179
+ cert.validity.notBefore = now;
180
+ cert.validity.notAfter = new Date(now.getTime());
181
+ cert.validity.notAfter.setDate(now.getDate() + SITE_CERT_VALIDITY_DAYS);
182
+ const attrs = [
183
+ { name: "commonName", value: punycodeDomain },
184
+ { name: "countryName", value: "US" },
185
+ { name: "organizationName", value: "WordPress Studio" }
186
+ ];
187
+ cert.setSubject(attrs);
188
+ cert.setIssuer(caCertObj.subject.attributes);
189
+ cert.setExtensions([
190
+ {
191
+ name: "basicConstraints",
192
+ cA: false,
193
+ critical: true
194
+ },
195
+ {
196
+ name: "keyUsage",
197
+ critical: true,
198
+ digitalSignature: true,
199
+ keyEncipherment: true
200
+ },
201
+ {
202
+ name: "extKeyUsage",
203
+ serverAuth: true
204
+ },
205
+ {
206
+ name: "subjectAltName",
207
+ altNames: [
208
+ {
209
+ type: 2,
210
+ // DNS
211
+ value: punycodeDomain
212
+ }
213
+ ]
214
+ }
215
+ ]);
216
+ cert.sign(caPrivateKey, forge.md.sha256.create());
217
+ const certPem = forge.pki.certificateToPem(cert);
218
+ const keyPem = forge.pki.privateKeyToPem(keys.privateKey);
219
+ initializeCertificatesDirectory();
220
+ fs.writeFileSync(siteCertPath, certPem);
221
+ fs.writeFileSync(siteKeyPath, keyPem);
222
+ return { cert: certPem, key: keyPem };
223
+ } catch (error) {
224
+ console.error(`Failed to generate certificate for ${domain}:`, error);
225
+ throw error;
226
+ }
227
+ }
228
+ function deleteSiteCertificate(domain) {
229
+ try {
230
+ const siteCertPath = path.join(CERT_DIRECTORY, "domains", `${domain}.crt`);
231
+ const siteKeyPath = path.join(CERT_DIRECTORY, "domains", `${domain}.key`);
232
+ let deletedFiles = false;
233
+ if (fs.existsSync(siteCertPath)) {
234
+ fs.unlinkSync(siteCertPath);
235
+ deletedFiles = true;
236
+ }
237
+ if (fs.existsSync(siteKeyPath)) {
238
+ fs.unlinkSync(siteKeyPath);
239
+ deletedFiles = true;
240
+ }
241
+ return deletedFiles;
242
+ } catch (error) {
243
+ console.error(`Failed to delete certificate for ${domain}:`, error);
244
+ return false;
245
+ }
246
+ }
247
+ export {
248
+ deleteSiteCertificate as d,
249
+ generateSiteCertificate as g
250
+ };
@@ -0,0 +1,80 @@
1
+ import os from "os";
2
+ import path from "path";
3
+ import { g as getWordPressVersion } from "./get-wordpress-version-BwSCJujO.js";
4
+ import { P as PreviewCommandLoggerAction } from "./logger-actions-OaIvl-ai.js";
5
+ import { sprintf, __ } from "@wordpress/i18n";
6
+ import { f as calculateDirectorySizeForArchive, h as DEMO_SITE_SIZE_LIMIT_BYTES, L as LoggerError, j as DEMO_SITE_SIZE_LIMIT_GB, a as Logger, k as getSiteByFolder, g as getAuthToken, m as uploadArchive, w as waitForSiteReady } from "./appdata-07CF2rhg.js";
7
+ import { a as archiveSiteContent, c as cleanup } from "./archive-xDmkN4wb.js";
8
+ import { s as saveSnapshotToAppdata } from "./snapshots-6XE53y_F.js";
9
+ async function validateSiteSize(siteFolder) {
10
+ const wpContentPath = path.join(siteFolder, "wp-content");
11
+ const wpContentSize = await calculateDirectorySizeForArchive(wpContentPath);
12
+ if (wpContentSize > DEMO_SITE_SIZE_LIMIT_BYTES) {
13
+ throw new LoggerError(
14
+ sprintf(
15
+ __(
16
+ "Your site exceeds the %d GB size limit. Please, consider removing unnecessary media files, plugins, or themes from wp-content."
17
+ ),
18
+ DEMO_SITE_SIZE_LIMIT_GB
19
+ )
20
+ );
21
+ }
22
+ return true;
23
+ }
24
+ async function runCommand(siteFolder) {
25
+ const archivePath = path.join(
26
+ os.tmpdir(),
27
+ `${path.basename(siteFolder)}-${Date.now()}.zip`
28
+ );
29
+ const logger = new Logger();
30
+ try {
31
+ logger.reportStart(PreviewCommandLoggerAction.VALIDATE, __("Validating…"));
32
+ await getSiteByFolder(siteFolder);
33
+ await validateSiteSize(siteFolder);
34
+ const token = await getAuthToken();
35
+ logger.reportSuccess(__("Validation successful"), true);
36
+ logger.reportStart(PreviewCommandLoggerAction.ARCHIVE, __("Creating archive…"));
37
+ await archiveSiteContent(siteFolder, archivePath);
38
+ logger.reportSuccess(__("Archive created"));
39
+ logger.reportStart(PreviewCommandLoggerAction.UPLOAD, __("Uploading archive…"));
40
+ const wordpressVersion = getWordPressVersion(siteFolder);
41
+ const uploadResponse = await uploadArchive(archivePath, token.accessToken, wordpressVersion);
42
+ logger.reportSuccess(__("Archive uploaded"));
43
+ logger.reportStart(PreviewCommandLoggerAction.READY, __("Creating preview site…"));
44
+ await waitForSiteReady(uploadResponse.site_id, token.accessToken);
45
+ logger.reportSuccess(
46
+ sprintf(__("Preview site available at: %s"), `https://${uploadResponse.site_url}`)
47
+ );
48
+ logger.reportStart(PreviewCommandLoggerAction.APPDATA, __("Saving preview site to Studio…"));
49
+ const snapshot = await saveSnapshotToAppdata(
50
+ siteFolder,
51
+ uploadResponse.site_id,
52
+ uploadResponse.site_url
53
+ );
54
+ logger.reportSuccess(__("Preview site saved to Studio"));
55
+ logger.reportKeyValuePair("name", snapshot.name ?? "");
56
+ logger.reportKeyValuePair("url", snapshot.url);
57
+ } catch (error) {
58
+ if (error instanceof LoggerError) {
59
+ logger.reportError(error);
60
+ } else {
61
+ const loggerError = new LoggerError(__("Failed to create preview site"), error);
62
+ logger.reportError(loggerError);
63
+ }
64
+ } finally {
65
+ void cleanup(archivePath);
66
+ }
67
+ }
68
+ const registerCommand = (yargs) => {
69
+ return yargs.command({
70
+ command: "create",
71
+ describe: __("Create a preview site"),
72
+ handler: async (argv) => {
73
+ await runCommand(argv.path);
74
+ }
75
+ });
76
+ };
77
+ export {
78
+ registerCommand,
79
+ runCommand
80
+ };