@instawp/cli 0.0.1-beta.2 → 0.0.1-beta.20
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/CHANGELOG.md +197 -0
- package/README.md +151 -17
- package/dist/commands/db.d.ts +2 -0
- package/dist/commands/db.js +305 -0
- package/dist/commands/db.js.map +1 -0
- package/dist/commands/exec.js +62 -3
- package/dist/commands/exec.js.map +1 -1
- package/dist/commands/local.js +483 -123
- package/dist/commands/local.js.map +1 -1
- package/dist/commands/login.js +23 -7
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/logs.d.ts +2 -0
- package/dist/commands/logs.js +239 -0
- package/dist/commands/logs.js.map +1 -0
- package/dist/commands/open.d.ts +2 -0
- package/dist/commands/open.js +114 -0
- package/dist/commands/open.js.map +1 -0
- package/dist/commands/sites.js +240 -2
- package/dist/commands/sites.js.map +1 -1
- package/dist/commands/sync.js +6 -3
- package/dist/commands/sync.js.map +1 -1
- package/dist/commands/versions.d.ts +2 -0
- package/dist/commands/versions.js +324 -0
- package/dist/commands/versions.js.map +1 -0
- package/dist/index.js +49 -8
- package/dist/index.js.map +1 -1
- package/dist/lib/local-env.d.ts +31 -0
- package/dist/lib/local-env.js +60 -13
- package/dist/lib/local-env.js.map +1 -1
- package/dist/lib/local-instance.d.ts +43 -0
- package/dist/lib/local-instance.js +60 -0
- package/dist/lib/local-instance.js.map +1 -0
- package/dist/lib/output.js +14 -1
- package/dist/lib/output.js.map +1 -1
- package/dist/lib/paths.d.ts +22 -0
- package/dist/lib/paths.js +41 -0
- package/dist/lib/paths.js.map +1 -0
- package/dist/lib/sftp-sync.d.ts +35 -0
- package/dist/lib/sftp-sync.js +290 -0
- package/dist/lib/sftp-sync.js.map +1 -0
- package/dist/lib/site-resolver.js +25 -3
- package/dist/lib/site-resolver.js.map +1 -1
- package/dist/lib/sqlite-to-mysql.d.ts +47 -0
- package/dist/lib/sqlite-to-mysql.js +133 -0
- package/dist/lib/sqlite-to-mysql.js.map +1 -0
- package/dist/lib/ssh-connection.d.ts +11 -0
- package/dist/lib/ssh-connection.js +99 -3
- package/dist/lib/ssh-connection.js.map +1 -1
- package/dist/lib/ssh-keys.js +12 -5
- package/dist/lib/ssh-keys.js.map +1 -1
- package/dist/lib/windows-binaries.d.ts +10 -0
- package/dist/lib/windows-binaries.js +34 -0
- package/dist/lib/windows-binaries.js.map +1 -0
- package/dist/types.d.ts +14 -0
- package/package.json +12 -3
- package/vendor/win32/NOTICE.md +31 -0
- package/vendor/win32/busybox.exe +0 -0
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { requireAuth, getClient } from '../lib/api.js';
|
|
3
|
+
import { resolveSite } from '../lib/site-resolver.js';
|
|
4
|
+
import { success, error, table, spinner, info, isJsonMode } from '../lib/output.js';
|
|
5
|
+
/**
|
|
6
|
+
* Site versions: restorable point-in-time copies of a site's files + database.
|
|
7
|
+
* Unlike backups, a version can be rolled back to in-place. The intended
|
|
8
|
+
* workflow (and the reason this exists for the AI era): create a version
|
|
9
|
+
* BEFORE letting an agent run a batch of changes, then roll back the one
|
|
10
|
+
* change that broke it — in one command.
|
|
11
|
+
*
|
|
12
|
+
* Named `versions` (not "snapshot") to avoid confusion with InstaWP's separate
|
|
13
|
+
* "Snapshots" product.
|
|
14
|
+
*/
|
|
15
|
+
const POLL_INTERVAL = 3000; // 3s
|
|
16
|
+
const MAX_WAIT = 10 * 60 * 1000; // 10 min — create/restore scale with site size
|
|
17
|
+
/** Poll a CloudTask until it completes, errors, or times out. */
|
|
18
|
+
async function pollTask(client, taskId, label) {
|
|
19
|
+
const spin = spinner(`${label}...`);
|
|
20
|
+
spin.start();
|
|
21
|
+
const start = Date.now();
|
|
22
|
+
while (Date.now() - start < MAX_WAIT) {
|
|
23
|
+
try {
|
|
24
|
+
const res = await client.get(`/tasks/${taskId}/status`);
|
|
25
|
+
const task = res.data?.data;
|
|
26
|
+
const pct = parseFloat(task?.percentage_complete) || 0;
|
|
27
|
+
const status = task?.status;
|
|
28
|
+
if (status === 'completed') {
|
|
29
|
+
spin.stop();
|
|
30
|
+
return 'completed';
|
|
31
|
+
}
|
|
32
|
+
if (status === 'error' || status === 'failed') {
|
|
33
|
+
spin.fail(`${label} failed`);
|
|
34
|
+
if (task?.comment)
|
|
35
|
+
error(task.comment);
|
|
36
|
+
return 'error';
|
|
37
|
+
}
|
|
38
|
+
spin.text = pct > 0 ? `${label}... ${chalk.dim(`(${Math.round(pct)}%)`)}` : `${label}...`;
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
// Task status endpoint may not be ready yet — keep polling.
|
|
42
|
+
}
|
|
43
|
+
await new Promise((r) => setTimeout(r, POLL_INTERVAL));
|
|
44
|
+
}
|
|
45
|
+
spin.fail(`${label} timed out`);
|
|
46
|
+
return 'timeout';
|
|
47
|
+
}
|
|
48
|
+
function formatDate(value) {
|
|
49
|
+
if (!value)
|
|
50
|
+
return '';
|
|
51
|
+
const d = new Date(value);
|
|
52
|
+
if (Number.isNaN(d.getTime()))
|
|
53
|
+
return value;
|
|
54
|
+
return d.toISOString().slice(0, 16).replace('T', ' ');
|
|
55
|
+
}
|
|
56
|
+
export function registerVersionsCommand(program) {
|
|
57
|
+
const versionsCmd = program
|
|
58
|
+
.command('versions')
|
|
59
|
+
.aliases(['version'])
|
|
60
|
+
.description('Manage site versions (restorable point-in-time copies) — create one before risky changes, roll back in one command');
|
|
61
|
+
// versions create <site>
|
|
62
|
+
versionsCmd
|
|
63
|
+
.command('create <site>')
|
|
64
|
+
.description('Create a version of a site (a restorable point-in-time copy)')
|
|
65
|
+
.option('--name <name>', 'Label for the version (max 25 chars), e.g. "before plugin update"')
|
|
66
|
+
.option('--no-wait', 'Return immediately instead of waiting for the version to finish')
|
|
67
|
+
.action(async (siteIdentifier, opts) => {
|
|
68
|
+
requireAuth();
|
|
69
|
+
const client = getClient();
|
|
70
|
+
const rspin = spinner('Resolving site...');
|
|
71
|
+
rspin.start();
|
|
72
|
+
let site;
|
|
73
|
+
try {
|
|
74
|
+
site = await resolveSite(siteIdentifier);
|
|
75
|
+
rspin.stop();
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
rspin.fail('Site resolution failed');
|
|
79
|
+
process.exit(1);
|
|
80
|
+
}
|
|
81
|
+
const label = site.name || site.sub_domain || String(site.id);
|
|
82
|
+
const spin = spinner(`Creating version of ${label}...`);
|
|
83
|
+
spin.start();
|
|
84
|
+
let versionId;
|
|
85
|
+
let taskId;
|
|
86
|
+
try {
|
|
87
|
+
const res = await client.post('/site-versions', { site_id: site.id });
|
|
88
|
+
versionId = res.data?.data?.id;
|
|
89
|
+
taskId = res.data?.data?.task_id;
|
|
90
|
+
spin.stop();
|
|
91
|
+
if (!versionId) {
|
|
92
|
+
error('Version creation failed', res.data?.message || res.data);
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
catch (err) {
|
|
97
|
+
spin.fail('Failed to create version');
|
|
98
|
+
error('Could not create version', err.response?.data?.message || err.message);
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
// Optional name — the create endpoint doesn't accept one, so set it via
|
|
102
|
+
// update. The server caps names at 25 chars (longer → 422), so truncate.
|
|
103
|
+
const versionName = opts.name ? String(opts.name).slice(0, 25) : undefined;
|
|
104
|
+
if (versionName) {
|
|
105
|
+
try {
|
|
106
|
+
await client.put(`/site-versions/${versionId}`, { name: versionName });
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
info('Version created, but naming it failed (you can rename it later).');
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (!opts.wait) {
|
|
113
|
+
if (isJsonMode()) {
|
|
114
|
+
console.log(JSON.stringify({ success: true, data: { id: versionId, status: 'progress', task_id: taskId ?? null } }));
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
success('Version started', { id: versionId, status: 'progress' });
|
|
118
|
+
info('It will be restorable once complete. Check with: instawp versions list ' + label);
|
|
119
|
+
}
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if (taskId) {
|
|
123
|
+
const result = await pollTask(client, taskId, 'Creating version');
|
|
124
|
+
if (result !== 'completed') {
|
|
125
|
+
info(`Version (ID ${versionId}) is still processing. Check with: instawp versions list ${label}`);
|
|
126
|
+
process.exit(result === 'error' ? 1 : 0);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
if (isJsonMode()) {
|
|
130
|
+
console.log(JSON.stringify({ success: true, data: { id: versionId, status: 'completed', name: versionName || null } }));
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
success('Version ready', { id: versionId, ...(versionName ? { name: versionName } : {}) });
|
|
134
|
+
info(`Roll back any time with: instawp versions restore ${label} ${versionId}`);
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
// versions list <site>
|
|
138
|
+
versionsCmd
|
|
139
|
+
.command('list <site>')
|
|
140
|
+
.description('List a site\'s versions (most recent first)')
|
|
141
|
+
.action(async (siteIdentifier) => {
|
|
142
|
+
requireAuth();
|
|
143
|
+
const client = getClient();
|
|
144
|
+
const rspin = spinner('Resolving site...');
|
|
145
|
+
rspin.start();
|
|
146
|
+
let site;
|
|
147
|
+
try {
|
|
148
|
+
site = await resolveSite(siteIdentifier);
|
|
149
|
+
rspin.stop();
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
rspin.fail('Site resolution failed');
|
|
153
|
+
process.exit(1);
|
|
154
|
+
}
|
|
155
|
+
const spin = spinner('Fetching versions...');
|
|
156
|
+
spin.start();
|
|
157
|
+
try {
|
|
158
|
+
const res = await client.get('/site-versions', { params: { site_id: site.id, per_page: 100 } });
|
|
159
|
+
const versions = res.data?.data || [];
|
|
160
|
+
spin.stop();
|
|
161
|
+
if (versions.length === 0) {
|
|
162
|
+
if (isJsonMode()) {
|
|
163
|
+
console.log(JSON.stringify([]));
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
info('No versions yet. Create one with: instawp versions create ' + (site.name || site.id));
|
|
167
|
+
}
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
const rows = versions.map((v) => ({
|
|
171
|
+
id: v.id,
|
|
172
|
+
name: v.name || chalk.dim('(unnamed)'),
|
|
173
|
+
size: v.size_mb != null ? `${v.size_mb} MB` : '',
|
|
174
|
+
status: v.status === 'completed' ? chalk.green('completed') : v.status === 'progress' ? chalk.yellow('in progress') : (v.status || ''),
|
|
175
|
+
created: formatDate(v.created_at),
|
|
176
|
+
}));
|
|
177
|
+
table(['ID', 'Name', 'Size', 'Status', 'Created'], rows);
|
|
178
|
+
}
|
|
179
|
+
catch (err) {
|
|
180
|
+
spin.fail('Failed to fetch versions');
|
|
181
|
+
error('Could not list versions', err.response?.data?.message || err.message);
|
|
182
|
+
process.exit(1);
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
// versions restore <site> <version-id>
|
|
186
|
+
versionsCmd
|
|
187
|
+
.command('restore <site> <version-id>')
|
|
188
|
+
.description('Roll a site back to a version — OVERWRITES current files and database')
|
|
189
|
+
.option('--force', 'Skip confirmation')
|
|
190
|
+
.option('--no-wait', 'Return immediately instead of waiting for the restore to finish')
|
|
191
|
+
.action(async (siteIdentifier, versionId, opts) => {
|
|
192
|
+
requireAuth();
|
|
193
|
+
const client = getClient();
|
|
194
|
+
const rspin = spinner('Resolving site...');
|
|
195
|
+
rspin.start();
|
|
196
|
+
let site;
|
|
197
|
+
try {
|
|
198
|
+
site = await resolveSite(siteIdentifier);
|
|
199
|
+
rspin.stop();
|
|
200
|
+
}
|
|
201
|
+
catch {
|
|
202
|
+
rspin.fail('Site resolution failed');
|
|
203
|
+
process.exit(1);
|
|
204
|
+
}
|
|
205
|
+
const label = site.name || site.sub_domain || String(site.id);
|
|
206
|
+
if (!opts.force) {
|
|
207
|
+
if (isJsonMode()) {
|
|
208
|
+
error('Use --force to restore in JSON mode (this overwrites the live site)');
|
|
209
|
+
process.exit(1);
|
|
210
|
+
}
|
|
211
|
+
const readline = await import('node:readline');
|
|
212
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
213
|
+
const answer = await new Promise((resolve) => {
|
|
214
|
+
rl.question(`Restore "${label}" to version ${versionId}? This OVERWRITES the current files and database and cannot be undone. (y/N) `, resolve);
|
|
215
|
+
});
|
|
216
|
+
rl.close();
|
|
217
|
+
if (answer.toLowerCase() !== 'y') {
|
|
218
|
+
info('Cancelled.');
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
const spin = spinner(`Starting restore of ${label}...`);
|
|
223
|
+
spin.start();
|
|
224
|
+
let taskId;
|
|
225
|
+
try {
|
|
226
|
+
const res = await client.put(`/sites/${site.id}/restore-versions/${versionId}`);
|
|
227
|
+
taskId = res.data?.data?.task_id;
|
|
228
|
+
spin.stop();
|
|
229
|
+
}
|
|
230
|
+
catch (err) {
|
|
231
|
+
spin.fail('Failed to start restore');
|
|
232
|
+
error('Could not restore version', err.response?.data?.message || err.message);
|
|
233
|
+
process.exit(1);
|
|
234
|
+
}
|
|
235
|
+
if (!opts.wait) {
|
|
236
|
+
if (isJsonMode()) {
|
|
237
|
+
console.log(JSON.stringify({ success: true, data: { site_id: site.id, version_id: Number(versionId), status: 'restoring', task_id: taskId ?? null } }));
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
success('Restore started', { site: label, version: versionId });
|
|
241
|
+
info('The site will be back shortly. Check with: instawp sites list');
|
|
242
|
+
}
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
if (taskId) {
|
|
246
|
+
const result = await pollTask(client, taskId, 'Restoring version');
|
|
247
|
+
if (result !== 'completed') {
|
|
248
|
+
info('Restore is still processing. The site will update once it finishes.');
|
|
249
|
+
process.exit(result === 'error' ? 1 : 0);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
if (isJsonMode()) {
|
|
253
|
+
console.log(JSON.stringify({ success: true, data: { site_id: site.id, version_id: Number(versionId), status: 'completed' } }));
|
|
254
|
+
}
|
|
255
|
+
else {
|
|
256
|
+
success(`"${label}" restored to version ${versionId}`);
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
// versions delete <site> <version-id...>
|
|
260
|
+
versionsCmd
|
|
261
|
+
.command('delete <site> <version-ids...>')
|
|
262
|
+
.description('Delete one or more versions')
|
|
263
|
+
.option('--force', 'Skip confirmation')
|
|
264
|
+
.action(async (siteIdentifier, versionIds, opts) => {
|
|
265
|
+
requireAuth();
|
|
266
|
+
const client = getClient();
|
|
267
|
+
const rspin = spinner('Resolving site...');
|
|
268
|
+
rspin.start();
|
|
269
|
+
let site;
|
|
270
|
+
try {
|
|
271
|
+
site = await resolveSite(siteIdentifier);
|
|
272
|
+
rspin.stop();
|
|
273
|
+
}
|
|
274
|
+
catch {
|
|
275
|
+
rspin.fail('Site resolution failed');
|
|
276
|
+
process.exit(1);
|
|
277
|
+
}
|
|
278
|
+
const label = site.name || site.sub_domain || String(site.id);
|
|
279
|
+
const ids = versionIds.map((v) => parseInt(v, 10)).filter((n) => !Number.isNaN(n));
|
|
280
|
+
if (ids.length === 0) {
|
|
281
|
+
error('No valid version IDs provided');
|
|
282
|
+
process.exit(1);
|
|
283
|
+
}
|
|
284
|
+
if (!opts.force) {
|
|
285
|
+
if (isJsonMode()) {
|
|
286
|
+
error('Use --force to delete in JSON mode');
|
|
287
|
+
process.exit(1);
|
|
288
|
+
}
|
|
289
|
+
const readline = await import('node:readline');
|
|
290
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
291
|
+
const answer = await new Promise((resolve) => {
|
|
292
|
+
rl.question(`Delete ${ids.length} version(s) [${ids.join(', ')}] of "${label}"? (y/N) `, resolve);
|
|
293
|
+
});
|
|
294
|
+
rl.close();
|
|
295
|
+
if (answer.toLowerCase() !== 'y') {
|
|
296
|
+
info('Cancelled.');
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
const spin = spinner('Deleting version(s)...');
|
|
301
|
+
spin.start();
|
|
302
|
+
try {
|
|
303
|
+
const res = await client.delete('/site-versions', { data: { ids } });
|
|
304
|
+
const data = res.data?.data || {};
|
|
305
|
+
const successIds = data.success_ids || [];
|
|
306
|
+
const failedIds = data.failed_ids || [];
|
|
307
|
+
spin.stop();
|
|
308
|
+
if (isJsonMode()) {
|
|
309
|
+
console.log(JSON.stringify({ success: true, data: { success_ids: successIds, failed_ids: failedIds } }));
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
if (successIds.length)
|
|
313
|
+
success(`Deleted version(s): ${successIds.join(', ')}`);
|
|
314
|
+
if (failedIds.length)
|
|
315
|
+
error(`Failed to delete: ${failedIds.join(', ')}`);
|
|
316
|
+
}
|
|
317
|
+
catch (err) {
|
|
318
|
+
spin.fail('Failed to delete version(s)');
|
|
319
|
+
error('Could not delete versions', err.response?.data?.message || err.message);
|
|
320
|
+
process.exit(1);
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
//# sourceMappingURL=versions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"versions.js","sourceRoot":"","sources":["../../src/commands/versions.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -6,11 +6,15 @@ import { setJsonMode } from './lib/output.js';
|
|
|
6
6
|
import { registerLoginCommand } from './commands/login.js';
|
|
7
7
|
import { registerWhoamiCommand } from './commands/whoami.js';
|
|
8
8
|
import { registerSitesCommand, registerCreateAlias } from './commands/sites.js';
|
|
9
|
+
import { registerVersionsCommand } from './commands/versions.js';
|
|
9
10
|
import { registerSyncCommand } from './commands/sync.js';
|
|
10
11
|
import { registerSshCommand } from './commands/ssh.js';
|
|
11
12
|
import { registerExecCommand, registerWpCommand } from './commands/exec.js';
|
|
12
13
|
import { registerTeamsCommand } from './commands/teams.js';
|
|
13
14
|
import { registerLocalCommand } from './commands/local.js';
|
|
15
|
+
import { registerDbCommand } from './commands/db.js';
|
|
16
|
+
import { registerOpenCommand } from './commands/open.js';
|
|
17
|
+
import { registerLogsCommand } from './commands/logs.js';
|
|
14
18
|
const require = createRequire(import.meta.url);
|
|
15
19
|
const { version } = require('../package.json');
|
|
16
20
|
// Early --json detection so it works in any argv position
|
|
@@ -32,15 +36,37 @@ registerWhoamiCommand(program);
|
|
|
32
36
|
// -- Sites --
|
|
33
37
|
registerSitesCommand(program);
|
|
34
38
|
registerCreateAlias(program);
|
|
39
|
+
registerVersionsCommand(program);
|
|
35
40
|
// -- Remote access --
|
|
36
|
-
registerExecCommand(program);
|
|
37
41
|
registerWpCommand(program);
|
|
42
|
+
registerExecCommand(program);
|
|
38
43
|
registerSshCommand(program);
|
|
39
44
|
registerSyncCommand(program);
|
|
45
|
+
registerDbCommand(program);
|
|
46
|
+
registerLogsCommand(program);
|
|
47
|
+
registerOpenCommand(program);
|
|
40
48
|
// -- Teams --
|
|
41
49
|
registerTeamsCommand(program);
|
|
42
50
|
// -- Local dev --
|
|
43
51
|
registerLocalCommand(program);
|
|
52
|
+
// -- Changelog --
|
|
53
|
+
program
|
|
54
|
+
.command('changelog')
|
|
55
|
+
.description('Show recent changes')
|
|
56
|
+
.action(() => {
|
|
57
|
+
const changelogPath = new URL('../CHANGELOG.md', import.meta.url);
|
|
58
|
+
try {
|
|
59
|
+
const fs = require('fs');
|
|
60
|
+
const content = fs.readFileSync(changelogPath, 'utf-8');
|
|
61
|
+
// Show only the latest version
|
|
62
|
+
const sections = content.split(/\n## /);
|
|
63
|
+
const latest = sections[1] ? `## ${sections[1]}` : content;
|
|
64
|
+
console.log(latest.trim());
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
console.log(`Changelog: https://github.com/InstaWP/cli/blob/main/CHANGELOG.md`);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
44
70
|
// Custom help layout
|
|
45
71
|
program.configureHelp({
|
|
46
72
|
sortSubcommands: false,
|
|
@@ -57,13 +83,24 @@ ${d('Auth')}
|
|
|
57
83
|
${d('Sites')}
|
|
58
84
|
${c('create')} Create a new WordPress site
|
|
59
85
|
${c('sites list')} List all sites
|
|
86
|
+
${c('sites creds')} Show WP admin credentials + Magic Login URL
|
|
87
|
+
${c('sites php')} View or update PHP version/settings
|
|
60
88
|
${c('sites delete')} Delete a site
|
|
89
|
+
${c('open')} ${d('<site>')} Open site (or --admin / --magic) in browser
|
|
90
|
+
|
|
91
|
+
${d('Versions')} ${d('(restorable point-in-time site copies)')}
|
|
92
|
+
${c('versions create')} Create a version before risky changes
|
|
93
|
+
${c('versions list')} List a site's versions
|
|
94
|
+
${c('versions restore')} Roll a site back to a version
|
|
95
|
+
${c('versions delete')} Delete versions
|
|
61
96
|
|
|
62
97
|
${d('Remote Access')}
|
|
63
|
-
${c('
|
|
64
|
-
${c('
|
|
65
|
-
${c('
|
|
66
|
-
${c('
|
|
98
|
+
${c('wp')} ${d('<site>')} ${d('<args>')} WP-CLI on a remote site (primary)
|
|
99
|
+
${c('ssh')} ${d('<site>')} Interactive SSH session
|
|
100
|
+
${c('sync')} ${d('push|pull')} Sync wp-content via rsync
|
|
101
|
+
${c('db')} ${d('push|pull')} Push/pull MySQL database (auto-backup)
|
|
102
|
+
${c('logs')} ${d('<site>')} Tail WP / PHP / nginx logs
|
|
103
|
+
${c('exec')} ${d('<site>')} ${d('<cmd>')} Run arbitrary shell (escape hatch for non-WP)
|
|
67
104
|
|
|
68
105
|
${d('Local Development')}
|
|
69
106
|
${c('local create')} Create and start a local WordPress site
|
|
@@ -85,9 +122,13 @@ ${d('Examples')}
|
|
|
85
122
|
$ instawp create --name my-site
|
|
86
123
|
$ instawp local create --name blog
|
|
87
124
|
$ instawp wp my-site plugin list
|
|
88
|
-
$ instawp
|
|
89
|
-
$ instawp
|
|
90
|
-
$ instawp
|
|
125
|
+
$ instawp wp my-site -- post list --post_type=page
|
|
126
|
+
$ instawp versions create my-site --name "before plugin update"
|
|
127
|
+
$ instawp versions restore my-site 1234
|
|
128
|
+
$ instawp open my-site --admin
|
|
129
|
+
$ instawp db pull my-site
|
|
130
|
+
$ instawp logs my-site --follow
|
|
131
|
+
$ instawp sites creds my-site
|
|
91
132
|
`;
|
|
92
133
|
});
|
|
93
134
|
// Override default help to only show options (commands are in custom section)
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAChF,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC5E,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/local-env.d.ts
CHANGED
|
@@ -1,4 +1,16 @@
|
|
|
1
1
|
import type { LocalInstance } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Returns [command, prefixArgs, usingNpx] for running wp-playground-cli.
|
|
4
|
+
* Prefers the global binary (faster) over npx (slower, downloads on first run).
|
|
5
|
+
*/
|
|
6
|
+
export declare function getPlaygroundCommand(): [string, string[], boolean];
|
|
7
|
+
/** Test-only: reset the once-per-process hint guard. */
|
|
8
|
+
export declare function _resetNpxHint(): void;
|
|
9
|
+
/**
|
|
10
|
+
* One-time, dim hint shown when falling back to npx (no global binary). Explains
|
|
11
|
+
* the first-run download and how to skip it. Suppressed in --json mode.
|
|
12
|
+
*/
|
|
13
|
+
export declare function maybeShowNpxHint(usingNpx: boolean): void;
|
|
2
14
|
export declare function getLocalBaseDir(): string;
|
|
3
15
|
export declare function getInstanceDir(name: string): string;
|
|
4
16
|
/**
|
|
@@ -13,6 +25,25 @@ export declare function createInstanceDir(name: string): string;
|
|
|
13
25
|
*/
|
|
14
26
|
export declare function ensureAutoLogin(instance: LocalInstance): void;
|
|
15
27
|
export declare function deleteInstanceDir(name: string): void;
|
|
28
|
+
/**
|
|
29
|
+
* Build the wp-playground-cli argument(s) to mount a host path at a VFS path.
|
|
30
|
+
*
|
|
31
|
+
* `--mount` / `--mount-before-install` take a single `host:vfs` value, which
|
|
32
|
+
* wp-playground-cli splits on `:`. On Windows the host path contains a
|
|
33
|
+
* drive-letter colon (e.g. `C:\Users\...\wp-content`), so the split yields 3+
|
|
34
|
+
* parts and Playground rejects it with "Invalid mount format". On Windows we
|
|
35
|
+
* instead use `--mount-dir` / `--mount-dir-before-install`, which take the host
|
|
36
|
+
* and vfs paths as two separate args (`nargs: 2`) and avoid the colon entirely.
|
|
37
|
+
* Both forms resolve to the same `{ hostPath, vfsPath }` mount in Playground
|
|
38
|
+
* (no file-vs-directory distinction), so this works for files and directories.
|
|
39
|
+
*
|
|
40
|
+
* macOS/Linux keep the long-standing colon form unchanged (their absolute paths
|
|
41
|
+
* have no drive-letter colon). `platform` is injectable for testing.
|
|
42
|
+
*/
|
|
43
|
+
export declare function buildMountArgs(hostPath: string, vfsPath: string, opts?: {
|
|
44
|
+
beforeInstall?: boolean;
|
|
45
|
+
platform?: NodeJS.Platform;
|
|
46
|
+
}): string[];
|
|
16
47
|
/**
|
|
17
48
|
* Starts the Playground server in the foreground.
|
|
18
49
|
* Watches stdout for the ready URL and calls onReady when detected.
|
package/dist/lib/local-env.js
CHANGED
|
@@ -1,21 +1,42 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
// cross-spawn handles Windows quirks: `npx`/`wp-playground-cli` are `.cmd`
|
|
3
|
+
// shims, and Node won't spawn .cmd without shell:true (CVE-2024-27980). It also
|
|
4
|
+
// quotes args (e.g. mount paths) safely, which shell:true does not.
|
|
5
|
+
import spawn from 'cross-spawn';
|
|
2
6
|
import { closeSync, existsSync, mkdirSync, openSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from 'node:fs';
|
|
3
7
|
import { join } from 'node:path';
|
|
4
8
|
import { homedir } from 'node:os';
|
|
5
9
|
import net from 'node:net';
|
|
10
|
+
import chalk from 'chalk';
|
|
11
|
+
import { isJsonMode } from './output.js';
|
|
6
12
|
const LOCAL_BASE_DIR = join(homedir(), '.instawp', 'local');
|
|
7
13
|
const DEFAULT_PORT_START = 9400;
|
|
8
14
|
/**
|
|
9
|
-
* Returns [command, prefixArgs] for running wp-playground-cli.
|
|
10
|
-
* Prefers the global binary (faster) over npx (slower).
|
|
15
|
+
* Returns [command, prefixArgs, usingNpx] for running wp-playground-cli.
|
|
16
|
+
* Prefers the global binary (faster) over npx (slower, downloads on first run).
|
|
11
17
|
*/
|
|
12
|
-
function getPlaygroundCommand() {
|
|
18
|
+
export function getPlaygroundCommand() {
|
|
13
19
|
// Check for globally installed binary first (0.7s vs 1.4s npx overhead)
|
|
14
|
-
const
|
|
20
|
+
const cmd = process.platform === 'win32' ? 'where' : 'which';
|
|
21
|
+
const result = spawnSync(cmd, ['wp-playground-cli'], { stdio: 'pipe' });
|
|
15
22
|
if (result.status === 0) {
|
|
16
|
-
return ['wp-playground-cli', []];
|
|
23
|
+
return ['wp-playground-cli', [], false];
|
|
17
24
|
}
|
|
18
|
-
return ['npx', ['--yes', '@wp-playground/cli']];
|
|
25
|
+
return ['npx', ['--yes', '@wp-playground/cli'], true];
|
|
26
|
+
}
|
|
27
|
+
let npxHintShown = false;
|
|
28
|
+
/** Test-only: reset the once-per-process hint guard. */
|
|
29
|
+
export function _resetNpxHint() { npxHintShown = false; }
|
|
30
|
+
/**
|
|
31
|
+
* One-time, dim hint shown when falling back to npx (no global binary). Explains
|
|
32
|
+
* the first-run download and how to skip it. Suppressed in --json mode.
|
|
33
|
+
*/
|
|
34
|
+
export function maybeShowNpxHint(usingNpx) {
|
|
35
|
+
if (!usingNpx || npxHintShown || isJsonMode())
|
|
36
|
+
return;
|
|
37
|
+
npxHintShown = true;
|
|
38
|
+
process.stderr.write(chalk.dim('# WordPress Playground not found globally — using npx (downloads once, may take ~30s).\n') +
|
|
39
|
+
chalk.dim('# Tip: npm i -g @wp-playground/cli to skip this on future runs.\n'));
|
|
19
40
|
}
|
|
20
41
|
export function getLocalBaseDir() {
|
|
21
42
|
return LOCAL_BASE_DIR;
|
|
@@ -122,6 +143,28 @@ export function deleteInstanceDir(name) {
|
|
|
122
143
|
rmSync(dir, { recursive: true, force: true });
|
|
123
144
|
}
|
|
124
145
|
}
|
|
146
|
+
/**
|
|
147
|
+
* Build the wp-playground-cli argument(s) to mount a host path at a VFS path.
|
|
148
|
+
*
|
|
149
|
+
* `--mount` / `--mount-before-install` take a single `host:vfs` value, which
|
|
150
|
+
* wp-playground-cli splits on `:`. On Windows the host path contains a
|
|
151
|
+
* drive-letter colon (e.g. `C:\Users\...\wp-content`), so the split yields 3+
|
|
152
|
+
* parts and Playground rejects it with "Invalid mount format". On Windows we
|
|
153
|
+
* instead use `--mount-dir` / `--mount-dir-before-install`, which take the host
|
|
154
|
+
* and vfs paths as two separate args (`nargs: 2`) and avoid the colon entirely.
|
|
155
|
+
* Both forms resolve to the same `{ hostPath, vfsPath }` mount in Playground
|
|
156
|
+
* (no file-vs-directory distinction), so this works for files and directories.
|
|
157
|
+
*
|
|
158
|
+
* macOS/Linux keep the long-standing colon form unchanged (their absolute paths
|
|
159
|
+
* have no drive-letter colon). `platform` is injectable for testing.
|
|
160
|
+
*/
|
|
161
|
+
export function buildMountArgs(hostPath, vfsPath, opts = {}) {
|
|
162
|
+
const platform = opts.platform ?? process.platform;
|
|
163
|
+
if (platform === 'win32') {
|
|
164
|
+
return [opts.beforeInstall ? '--mount-dir-before-install' : '--mount-dir', hostPath, vfsPath];
|
|
165
|
+
}
|
|
166
|
+
return [`${opts.beforeInstall ? '--mount-before-install' : '--mount'}=${hostPath}:${vfsPath}`];
|
|
167
|
+
}
|
|
125
168
|
function buildServerArgs(instance, blueprint) {
|
|
126
169
|
const wpContentDir = join(instance.path, 'wp-content');
|
|
127
170
|
const isClone = existsSync(join(instance.path, 'sqlite-import.sql')) ||
|
|
@@ -140,7 +183,7 @@ function buildServerArgs(instance, blueprint) {
|
|
|
140
183
|
for (const subdir of subdirs) {
|
|
141
184
|
const hostDir = join(wpContentDir, subdir);
|
|
142
185
|
if (existsSync(hostDir)) {
|
|
143
|
-
args.push(
|
|
186
|
+
args.push(...buildMountArgs(hostDir, `/wordpress/wp-content/${subdir}`));
|
|
144
187
|
}
|
|
145
188
|
}
|
|
146
189
|
// Mount non-core root files (CLAUDE.md, .htaccess, etc.)
|
|
@@ -151,7 +194,7 @@ function buildServerArgs(instance, blueprint) {
|
|
|
151
194
|
const filePath = join(instance.path, file);
|
|
152
195
|
const stat = statSync(filePath);
|
|
153
196
|
if (stat.isFile()) {
|
|
154
|
-
args.push(
|
|
197
|
+
args.push(...buildMountArgs(filePath, `/wordpress/${file}`));
|
|
155
198
|
}
|
|
156
199
|
}
|
|
157
200
|
// Use clone blueprint if it exists (has AST driver + login step)
|
|
@@ -164,7 +207,7 @@ function buildServerArgs(instance, blueprint) {
|
|
|
164
207
|
}
|
|
165
208
|
else {
|
|
166
209
|
// For fresh sites: mount entire wp-content before install for persistence
|
|
167
|
-
args.push(
|
|
210
|
+
args.push(...buildMountArgs(wpContentDir, '/wordpress/wp-content', { beforeInstall: true }));
|
|
168
211
|
}
|
|
169
212
|
if (blueprint) {
|
|
170
213
|
args.push(`--blueprint=${blueprint}`);
|
|
@@ -178,7 +221,8 @@ function buildServerArgs(instance, blueprint) {
|
|
|
178
221
|
*/
|
|
179
222
|
export function startServer(instance, opts) {
|
|
180
223
|
const args = buildServerArgs(instance, opts?.blueprint);
|
|
181
|
-
const [cmd, prefixArgs] = getPlaygroundCommand();
|
|
224
|
+
const [cmd, prefixArgs, usingNpx] = getPlaygroundCommand();
|
|
225
|
+
maybeShowNpxHint(usingNpx);
|
|
182
226
|
return new Promise((resolve, reject) => {
|
|
183
227
|
const child = spawn(cmd, [...prefixArgs, ...args], {
|
|
184
228
|
stdio: ['inherit', 'pipe', 'pipe'],
|
|
@@ -209,7 +253,9 @@ export function startServer(instance, opts) {
|
|
|
209
253
|
if (process.stdin.isTTY) {
|
|
210
254
|
process.stdin.setRawMode?.(false);
|
|
211
255
|
}
|
|
212
|
-
|
|
256
|
+
if (process.platform !== 'win32') {
|
|
257
|
+
spawnSync('stty', ['sane'], { stdio: 'inherit' });
|
|
258
|
+
}
|
|
213
259
|
resolve(code ?? 0);
|
|
214
260
|
});
|
|
215
261
|
});
|
|
@@ -221,7 +267,8 @@ export function startServer(instance, opts) {
|
|
|
221
267
|
*/
|
|
222
268
|
export async function startServerBackground(instance, blueprint) {
|
|
223
269
|
const args = buildServerArgs(instance, blueprint);
|
|
224
|
-
const [cmd, prefixArgs] = getPlaygroundCommand();
|
|
270
|
+
const [cmd, prefixArgs, usingNpx] = getPlaygroundCommand();
|
|
271
|
+
maybeShowNpxHint(usingNpx);
|
|
225
272
|
const logFile = join(instance.path, 'server.log');
|
|
226
273
|
const pidFile = join(instance.path, 'server.pid');
|
|
227
274
|
// Spawn fully detached with output to log file
|