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.
- package/LICENSE.md +257 -0
- package/README.md +87 -0
- package/dist/cli/_events-BeOo0LuG.js +116 -0
- package/dist/cli/appdata-07CF2rhg.js +21090 -0
- package/dist/cli/archive-xDmkN4wb.js +15942 -0
- package/dist/cli/browser-CgWK-yoe.js +44 -0
- package/dist/cli/certificate-manager-DdBumKZp.js +250 -0
- package/dist/cli/create-BHVhkvTx.js +80 -0
- package/dist/cli/create-ZS29BDDi.js +40999 -0
- package/dist/cli/delete-BgQn-elT.js +56 -0
- package/dist/cli/delete-g8pgaLna.js +132 -0
- package/dist/cli/get-wordpress-version-BwSCJujO.js +18 -0
- package/dist/cli/index-7pbG_s_U.js +434 -0
- package/dist/cli/index-BXRYeCYG.js +1393 -0
- package/dist/cli/index-T3F1GwxX.js +2668 -0
- package/dist/cli/is-errno-exception-t38xF2pB.js +6 -0
- package/dist/cli/list-BE_UBjL5.js +105 -0
- package/dist/cli/list-DKz0XxM7.js +1032 -0
- package/dist/cli/logger-actions-OaIvl-ai.js +45 -0
- package/dist/cli/login-B4PkfKOu.js +82 -0
- package/dist/cli/logout-BC9gKlTj.js +48 -0
- package/dist/cli/main.js +5 -0
- package/dist/cli/mu-plugins-GEfKsl5U.js +530 -0
- package/dist/cli/passwords-DyzWd9Xi.js +80 -0
- package/dist/cli/process-manager-daemon.js +327 -0
- package/dist/cli/process-manager-ipc-AUZeYYDT.js +454 -0
- package/dist/cli/proxy-daemon.js +197 -0
- package/dist/cli/run-wp-cli-command-BctnMDWG.js +88 -0
- package/dist/cli/sequential-BQFuixXz.js +46 -0
- package/dist/cli/server-files-C_oy-mnI.js +26 -0
- package/dist/cli/set-DknhAZpw.js +327 -0
- package/dist/cli/site-utils-CfsabjUn.js +243 -0
- package/dist/cli/snapshots-6XE53y_F.js +874 -0
- package/dist/cli/sqlite-integration-H4OwSlwR.js +83 -0
- package/dist/cli/start-CRJqm09_.js +90 -0
- package/dist/cli/status-CWNHIOaY.js +44 -0
- package/dist/cli/status-CWWx9jYF.js +110 -0
- package/dist/cli/stop-CQosmjqA.js +117 -0
- package/dist/cli/update-BgL2HKHW.js +101 -0
- package/dist/cli/validation-error-DqLxqQuA.js +40 -0
- package/dist/cli/wordpress-server-child.js +514 -0
- package/dist/cli/wordpress-server-ipc-Dwsg9jSb.js +140 -0
- package/dist/cli/wordpress-server-manager-CtiuJqEb.js +566 -0
- package/dist/cli/wordpress-version-utils-B6UVeTh_.js +51 -0
- package/dist/cli/wp-UGSnlkN0.js +103 -0
- package/package.json +73 -0
- package/patches/@wp-playground+wordpress+3.1.12.patch +28 -0
- 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
|
+
};
|