@instawp/cli 0.0.1-beta.1 → 0.0.1-beta.11

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 (57) hide show
  1. package/CHANGELOG.md +138 -0
  2. package/README.md +58 -10
  3. package/dist/commands/db.d.ts +2 -0
  4. package/dist/commands/db.js +331 -0
  5. package/dist/commands/db.js.map +1 -0
  6. package/dist/commands/exec.js +62 -3
  7. package/dist/commands/exec.js.map +1 -1
  8. package/dist/commands/local.d.ts +2 -0
  9. package/dist/commands/local.js +750 -0
  10. package/dist/commands/local.js.map +1 -0
  11. package/dist/commands/login.js +23 -7
  12. package/dist/commands/login.js.map +1 -1
  13. package/dist/commands/logs.d.ts +2 -0
  14. package/dist/commands/logs.js +239 -0
  15. package/dist/commands/logs.js.map +1 -0
  16. package/dist/commands/open.d.ts +2 -0
  17. package/dist/commands/open.js +114 -0
  18. package/dist/commands/open.js.map +1 -0
  19. package/dist/commands/sites.js +266 -10
  20. package/dist/commands/sites.js.map +1 -1
  21. package/dist/commands/sync.js +6 -3
  22. package/dist/commands/sync.js.map +1 -1
  23. package/dist/commands/teams.js +52 -3
  24. package/dist/commands/teams.js.map +1 -1
  25. package/dist/index.js +54 -8
  26. package/dist/index.js.map +1 -1
  27. package/dist/lib/api.js +6 -1
  28. package/dist/lib/api.js.map +1 -1
  29. package/dist/lib/config.d.ts +10 -1
  30. package/dist/lib/config.js +47 -0
  31. package/dist/lib/config.js.map +1 -1
  32. package/dist/lib/local-env.d.ts +35 -0
  33. package/dist/lib/local-env.js +299 -0
  34. package/dist/lib/local-env.js.map +1 -0
  35. package/dist/lib/output.js +14 -1
  36. package/dist/lib/output.js.map +1 -1
  37. package/dist/lib/paths.d.ts +22 -0
  38. package/dist/lib/paths.js +41 -0
  39. package/dist/lib/paths.js.map +1 -0
  40. package/dist/lib/sftp-sync.d.ts +29 -0
  41. package/dist/lib/sftp-sync.js +266 -0
  42. package/dist/lib/sftp-sync.js.map +1 -0
  43. package/dist/lib/site-resolver.js +32 -11
  44. package/dist/lib/site-resolver.js.map +1 -1
  45. package/dist/lib/ssh-connection.d.ts +17 -0
  46. package/dist/lib/ssh-connection.js +103 -5
  47. package/dist/lib/ssh-connection.js.map +1 -1
  48. package/dist/lib/ssh-keys.js +12 -5
  49. package/dist/lib/ssh-keys.js.map +1 -1
  50. package/dist/lib/windows-binaries.d.ts +10 -0
  51. package/dist/lib/windows-binaries.js +34 -0
  52. package/dist/lib/windows-binaries.js.map +1 -0
  53. package/dist/types.d.ts +8 -0
  54. package/package.json +11 -3
  55. package/scripts/mysql2sqlite +289 -0
  56. package/vendor/win32/NOTICE.md +31 -0
  57. package/vendor/win32/busybox.exe +0 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,138 @@
1
+ # Changelog
2
+
3
+ ## 0.0.1-beta.11 (2026-05-26)
4
+
5
+ ### Improved — parallel SFTP transfers on Windows
6
+ - Windows file sync now transfers files across a **pool of parallel SSH connections** instead of one-at-a-time. Measured ~2.9× speedup (a 238-file wp-content pull dropped from ~369s to ~129s).
7
+ - Concurrency defaults to 4, configurable via `INSTAWP_SFTP_CONCURRENCY` (capped at 8).
8
+ - Two-phase design: a single control connection walks the tree and pre-creates directories, then files transfer in parallel. Per-file errors are collected and reported without aborting the whole sync.
9
+
10
+ ## 0.0.1-beta.10 (2026-05-26)
11
+
12
+ ### Fixed — Windows file sync now works
13
+ - `instawp sync push/pull`, `local push/pull`, and `local clone` failed on Windows with `rsync: connection unexpectedly closed (0 bytes)` + `sigpacket: Suppressing signal 30 to win32 process`. Root cause: the bundled **msys2 rsync.exe couldn't drive native Windows OpenSSH** (incompatible pipe/signal semantics). The DLL "entry point" fix in beta.6 got rsync.exe to *load*, but the SSH transport still died instantly.
14
+ - **Fix**: Windows now transfers files over a **pure-JS SFTP client** (`ssh2-sftp-client`) instead of rsync-over-ssh. macOS/Linux are unchanged (still rsync, with delta sync). New `syncFiles()` dispatcher picks the transport per-platform.
15
+
16
+ ### Changed
17
+ - **Removed `rsync.exe` + all msys2 runtime DLLs from the bundle** (~11 MB). The Windows bundle is now just `busybox.exe` (660 KB, statically linked, for the `mysql2sqlite` awk step in `local clone`). Total package shrinks accordingly.
18
+ - SFTP transfer honors the same exclude/include patterns as the rsync paths (`.git`, `node_modules`, `cache`, `backup*`, etc.).
19
+
20
+ ### Trade-off
21
+ - SFTP does full-file copy (no rsync delta algorithm). Fine for typical wp-content; repeat syncs of large sites are slower than rsync on macOS/Linux. We chose this over bundling an msys ssh (which would have dragged in the ~3.5 MB Heimdal/Kerberos DLL chain).
22
+
23
+ ## 0.0.1-beta.9 (2026-05-23)
24
+
25
+ ### Internals
26
+ - CI smoke test now verifies the bundle by extracting the packed tarball directly (via `tar -xzf`) and running `rsync.exe` from the extract dir. Replaces the `npm install -g` step, which was failing on the GHA Windows runner due to Defender quarantine interactions (tamper protection prevented our exclusion settings from taking effect). Real-user installs are not affected — Defender on individual developer machines is configurable and the first reported Windows install showed the bundle landing at the correct path.
27
+
28
+ ## 0.0.1-beta.8 (2026-05-23)
29
+
30
+ ### Bug Fixes (Windows)
31
+ - Moved bundled Windows binaries from `bin/win32/` to `vendor/win32/`. With `bin/` and the `bin` field in package.json both set, npm's global install on Windows dropped the `bin/win32/` subdirectory — leaving the CLI unable to find rsync.exe at runtime. macOS/Linux installs were unaffected. Renaming sidesteps the collision entirely.
32
+
33
+ ## 0.0.1-beta.7 (2026-05-23)
34
+
35
+ ### Internals
36
+ - Smoke-windows CI job now runs the bundled `rsync.exe` and `busybox.exe` directly from the workspace bundle **before** the npm-install step, so a passing smoke test proves the DLL chain is correct independent of whether antivirus interferes with the global install path.
37
+ - Adds Windows Defender exclusions before `npm i -g` to prevent msys DLLs from being quarantined during install.
38
+ - Publish job now skips on `workflow_dispatch` (manual triggers), so maintainers can re-test the smoke job without bumping the version.
39
+
40
+ ## 0.0.1-beta.6 (2026-05-23)
41
+
42
+ ### Bug Fixes (Windows)
43
+ - Bundled `rsync.exe` now actually loads. beta.4/beta.5 shipped with `msys-2.0.dll` from the legacy `msys2-runtime-3.3` fork, which is missing the `fallocate` symbol that rsync 3.4 needs — produced `Entry Point Not Found: fallocate` on launch and exit code `3221225785` (`STATUS_DLL_INIT_FAILED`) when invoked indirectly via `sync push/pull` or `local clone`.
44
+ - Rebuilt the Windows bundle against current MSYS2 packages: `msys2-runtime-3.6.9-1`, `libopenssl-3.6.2-1`, `libiconv-1.19-1`, `libxxhash-0.8.3-1`, `libzstd-1.5.7-1`, `popt-1.19-1`, `libintl-0.22.5-1`. Includes `msys-popt-0.dll` and `msys-intl-8.dll` which the newer rsync now requires.
45
+ - Upgraded bundled rsync from 3.4.0 → 3.4.2-2.
46
+
47
+ ### Internals
48
+ - `scripts/fetch-windows-binaries.sh` now verifies DLL closure (every referenced `msys-*.dll` is present) and asserts `fallocate` is exported from `msys-2.0.dll` before declaring the bundle valid. Catches "wrong runtime fork" regressions at build time.
49
+ - Added `smoke-windows` job to the publish workflow — runs on `windows-latest` and actually executes the bundled `rsync.exe` and `busybox.exe` before npm publish. Publish is now gated on this passing.
50
+
51
+ ## 0.0.1-beta.5 (2026-05-23)
52
+
53
+ ### New Commands
54
+ - `db push <site> <file>` — Push a local SQL dump (`.sql` or `.sql.gz`) to the remote MySQL database. Always backs up the remote DB to `~/db-backup-{ISO}.sql.gz` first (skip with `--no-backup`). Confirmation prompt unless `--force`. Closes the #1 gap blocking full-site deploys from the CLI.
55
+ - `db pull <site>` — Stream the remote MySQL database to a local gzipped dump. `--output <path>` and `--no-compress` flags.
56
+ - `open <site>` — Open the site URL in the default browser. `--admin` opens `/wp-admin`, `--magic` opens the Magic Login URL, `--print` pipes the URL to stdout instead.
57
+ - `logs <site>` — Tail logs via SSH. `--wp` (default, debug.log), `--php` (PHP-FPM error log), `--nginx` (nginx error log), `--follow` / `-f`, `--lines <n>`. Multiple flags multi-tail. Probes HestiaCP path variations automatically.
58
+ - `sites creds <site>` — Re-fetch WP admin credentials + Magic Login URL for an existing site (previously only available in the `create` output).
59
+
60
+ ### Improvements
61
+ - `wp <site>` is now positioned as the primary remote-access command; `exec` is documented as the escape hatch for non-WP shell commands.
62
+ - `wp` / `exec` accept POSIX `--` to forward raw args verbatim: `instawp wp my-site -- post list --post_type=page`.
63
+ - Spinners are suppressed in non-TTY contexts, CI environments (`CI` env var), `--json` mode, `NO_COLOR`, and `INSTAWP_QUIET` — fixes "Resolving site..." leaking into piped output.
64
+
65
+ ### Bug Fixes
66
+ - `instawp wp <site> eval '...'` no longer breaks on parens, quotes, or other shell metacharacters. Each arg is now POSIX shell-quoted before being piped to the remote shell's stdin (previously `args.join(' ')` left metacharacters unescaped, causing remote `bash: syntax error near unexpected token '('`).
67
+
68
+ ### Docs
69
+ - New `ROADMAP.md` capturing 15 forward-looking improvement areas (multi-site bulk ops, cost transparency, CI/CD deploy command, shell completion, `doctor`, config file, snapshot/migration CLI, self-update, etc.) ranked by ROI.
70
+ - README + CLAUDE.md updated with `wp`-primary positioning and examples for all new commands.
71
+
72
+ ## 0.0.1-beta.4 (2026-05-22)
73
+
74
+ ### Windows — Zero-Install Support
75
+ - Bundled `rsync.exe` (with msys2 runtime DLLs) and BusyBox-w64 (`awk` provider) in `bin/win32/`. No more "install Git for Windows / cwRsync" prerequisite — `instawp local clone`, `local push/pull`, and `sync push/pull` work out of the box on Windows.
76
+ - Replaced the external `sqlite3` CLI dependency with the `better-sqlite3` Node module.
77
+ - New `src/lib/windows-binaries.ts` resolves bundled binaries; falls back to PATH then common Git-for-Windows install dirs.
78
+
79
+ ### Bug Fixes (Windows)
80
+ - `instawp local clone` now resolves the bundled `mysql2sqlite` script correctly (was broken by `new URL(import.meta.url).pathname` returning `/C:/...`).
81
+ - `mysql2sqlite` is invoked as `awk -f script` explicitly; no longer relies on shebang interpretation.
82
+ - `rsync` no longer treats Windows drive paths (`C:\...`) as remote hostnames — paths are converted to msys style (`/c/...`) inside `rsyncViaSsh`.
83
+ - `-e ssh -i <key>` argument uses forward slashes + quoted paths so msys/cygwin sh inside rsync parses the key path correctly.
84
+ - Eliminated the SQL injection risk in `local clone`'s URL search-replace (now uses bound parameters via better-sqlite3).
85
+
86
+ ### Internals
87
+ - New `scripts/fetch-windows-binaries.sh` (maintainer-only) refreshes the Windows bundle from MSYS2 + frippery.org.
88
+ - 32 new tests covering path conversion and bundled-binary resolution.
89
+
90
+ ## 0.0.1-beta.3 (2026-04-12)
91
+
92
+ ### New Commands
93
+ - `local create` — Create local WordPress sites (powered by WordPress Playground, no Docker needed)
94
+ - `local clone <site>` — Clone an InstaWP cloud site to local (files + database)
95
+ - `local start/stop` — Start in foreground or `--background` mode
96
+ - `local push/pull` — Sync wp-content between local and cloud (incremental rsync)
97
+ - `local list` — Show local sites with running/stopped status
98
+ - `local delete` — Remove local sites
99
+ - `sites php <site>` — View or update PHP version and settings
100
+ - `sites update <site>` — Update site label, description, or expiration
101
+ - `teams switch <team>` — Switch active team context
102
+
103
+ ### Improvements
104
+ - `create --wp <version>` — Specify WordPress version when creating sites
105
+ - `sites list` — 50 per page default, `--all` flag, pagination hints
106
+ - Login now shows user name and team after success
107
+ - Site resolver caches name-to-ID lookups for 10 minutes
108
+ - rsync only shows actually changed files (`--itemize-changes`)
109
+ - Magic login URL fixed to use correct `/wordpress-auto-login` endpoint
110
+
111
+ ### Bug Fixes
112
+ - Windows: SSH key generation now works (removed Unix-specific shell commands)
113
+ - Windows: command detection uses `where` instead of `which`
114
+ - `exec/wp --api` flag now works at any position in the command
115
+ - Terminal restored after local site Ctrl+C (`stty sane`)
116
+
117
+ ## 0.0.1-beta.2 (2026-03-23)
118
+
119
+ ### New Commands
120
+ - `local create/clone/start/stop/push/pull/list/delete` — Full local development workflow
121
+ - `teams switch` — Client-side team context
122
+
123
+ ### Improvements
124
+ - Site resolver caching
125
+ - Incremental rsync output
126
+
127
+ ## 0.0.1-beta.1 (2026-03-02)
128
+
129
+ ### Initial Release
130
+ - `login` — OAuth browser flow or `--token`
131
+ - `whoami` — Show current session
132
+ - `create` — Create WordPress sites with provisioning progress
133
+ - `sites list/delete` — Manage sites
134
+ - `exec/wp` — Run commands via SSH or API
135
+ - `ssh` — Interactive SSH sessions
136
+ - `sync push/pull` — rsync wp-content via SSH
137
+ - `teams list/members` — View teams
138
+ - `--json` mode for all commands
package/README.md CHANGED
@@ -33,26 +33,42 @@ instawp sites list
33
33
  instawp create --name my-site
34
34
  instawp create --name my-site --php 8.3
35
35
 
36
+ # Re-fetch admin credentials + Magic Login URL
37
+ instawp sites creds <site>
38
+
39
+ # Open the site (or admin / magic login) in your browser
40
+ instawp open <site>
41
+ instawp open <site> --admin
42
+ instawp open <site> --magic
43
+
36
44
  # Delete a site
37
45
  instawp sites delete <site>
38
46
  instawp sites delete <site> --force
39
47
  ```
40
48
 
41
- ### Run Commands
49
+ ### Run Commands (WP-CLI + shell)
50
+
51
+ `wp` is the **primary** command for interacting with a remote site. `exec` is the escape hatch for non-WP shell commands.
42
52
 
43
53
  ```bash
44
- # Run any command on a site (via SSH, default)
54
+ # WP-CLI on a remote site
55
+ instawp wp <site> plugin list
56
+ instawp wp <site> option get siteurl
57
+ instawp wp <site> user list --api
58
+
59
+ # Pass raw args to WP-CLI with --
60
+ instawp wp <site> -- post list --post_type=page --format=json
61
+
62
+ # eval / PHP payloads — wrap in single quotes; args are shell-escaped for you
63
+ instawp wp <site> eval '\MyClass::init(["force" => true]);'
64
+
65
+ # Escape hatch for non-WP commands
45
66
  instawp exec <site> ls -la
46
67
  instawp exec <site> php -v
47
68
  instawp exec <site> cat wp-config.php
48
69
 
49
- # Run via API instead of SSH (no SSH setup needed)
50
- instawp exec <site> ls -la --api
51
-
52
- # WP-CLI shorthand (prepends `wp` automatically)
53
- instawp wp <site> plugin list
54
- instawp wp <site> option get siteurl
55
- instawp wp <site> user list --api
70
+ # --api transport (no SSH setup required)
71
+ instawp exec <site> php -v --api
56
72
  ```
57
73
 
58
74
  `<site>` can be a site **ID**, **name**, or **domain** — the CLI resolves it automatically.
@@ -79,6 +95,37 @@ instawp sync pull <site>
79
95
  instawp sync push <site> --dry-run
80
96
  ```
81
97
 
98
+ ### Database (mysqldump)
99
+
100
+ `db push` always backs up the remote database before overwriting (use `--no-backup` to skip).
101
+
102
+ ```bash
103
+ # Pull remote DB to a gzipped SQL dump
104
+ instawp db pull <site>
105
+ instawp db pull <site> --output ./backup.sql.gz
106
+ instawp db pull <site> --no-compress # write .sql instead of .sql.gz
107
+
108
+ # Push a local dump back (auto-backs up the remote first)
109
+ instawp db push <site> ./backup.sql.gz
110
+ instawp db push <site> ./backup.sql --force # skip confirmation
111
+ ```
112
+
113
+ ### Logs
114
+
115
+ ```bash
116
+ # Tail the WP debug.log (default)
117
+ instawp logs <site>
118
+ instawp logs <site> --follow # tail -f
119
+
120
+ # Tail PHP-FPM or nginx error logs
121
+ instawp logs <site> --php
122
+ instawp logs <site> --nginx
123
+ instawp logs <site> --php --nginx -f # multi-tail
124
+
125
+ # Custom line count
126
+ instawp logs <site> --lines 500
127
+ ```
128
+
82
129
  ### Teams
83
130
 
84
131
  ```bash
@@ -93,7 +140,8 @@ Add `--json` to any command for machine-readable output:
93
140
  ```bash
94
141
  instawp sites list --json
95
142
  instawp create --name test-site --json
96
- instawp exec <site> wp option get siteurl --json
143
+ instawp sites creds <site> --json
144
+ instawp wp <site> option get siteurl --json
97
145
  ```
98
146
 
99
147
  ## Environment Variables
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function registerDbCommand(program: Command): void;
@@ -0,0 +1,331 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { join, dirname, basename } from 'node:path';
3
+ import { homedir } from 'node:os';
4
+ import { existsSync, mkdirSync, statSync, createReadStream, createWriteStream, unlinkSync } from 'node:fs';
5
+ import { createGunzip } from 'node:zlib';
6
+ import { pipeline } from 'node:stream/promises';
7
+ import { randomBytes } from 'node:crypto';
8
+ import chalk from 'chalk';
9
+ import { requireAuth } from '../lib/api.js';
10
+ import { resolveSite } from '../lib/site-resolver.js';
11
+ import { ensureSshAccess } from '../lib/ssh-keys.js';
12
+ import { execViaSsh, execViaSshToFile } from '../lib/ssh-connection.js';
13
+ import { success, error, spinner, info, isJsonMode } from '../lib/output.js';
14
+ const KNOWN_HOSTS = join(homedir(), '.instawp', 'known_hosts');
15
+ /**
16
+ * Build scp args matching the ssh-connection.ts ssh args (key, known-hosts,
17
+ * StrictHostKeyChecking). Note `scp` uses uppercase `-P` for port.
18
+ */
19
+ function scpArgs(conn) {
20
+ return [
21
+ '-i', conn.privateKeyPath,
22
+ '-P', String(conn.port),
23
+ '-o', 'StrictHostKeyChecking=accept-new',
24
+ '-o', `UserKnownHostsFile=${KNOWN_HOSTS}`,
25
+ ];
26
+ }
27
+ function scpUpload(conn, localPath, remotePath) {
28
+ const target = `${conn.username}@${conn.host}:${remotePath}`;
29
+ const result = spawnSync('scp', [...scpArgs(conn), localPath, target], {
30
+ stdio: ['ignore', 'pipe', 'pipe'],
31
+ encoding: 'utf-8',
32
+ });
33
+ if ((result.status ?? 1) !== 0 && result.stderr) {
34
+ console.error(result.stderr);
35
+ }
36
+ return result.status ?? 1;
37
+ }
38
+ /** Timestamp like `2026-05-23T12-34-56` (filename-safe — `:` is illegal on Windows). */
39
+ function isoTimestamp() {
40
+ return new Date().toISOString().replace(/[:.]/g, '-').replace(/-\d{3}Z$/, '');
41
+ }
42
+ function sanitizeForFilename(s) {
43
+ return s.replace(/[^a-zA-Z0-9_.-]/g, '-');
44
+ }
45
+ function formatBytes(bytes) {
46
+ if (bytes < 1024)
47
+ return `${bytes} B`;
48
+ if (bytes < 1024 * 1024)
49
+ return `${(bytes / 1024).toFixed(1)} KB`;
50
+ if (bytes < 1024 * 1024 * 1024)
51
+ return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
52
+ return `${(bytes / 1024 / 1024 / 1024).toFixed(2)} GB`;
53
+ }
54
+ async function gunzipFile(src, dest) {
55
+ await pipeline(createReadStream(src), createGunzip(), createWriteStream(dest));
56
+ }
57
+ async function promptYesNo(question) {
58
+ const readline = await import('node:readline');
59
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
60
+ const answer = await new Promise((resolve) => {
61
+ rl.question(question, resolve);
62
+ });
63
+ rl.close();
64
+ return answer.trim().toLowerCase() === 'y' || answer.trim().toLowerCase() === 'yes';
65
+ }
66
+ export function registerDbCommand(program) {
67
+ const db = program
68
+ .command('db')
69
+ .description('Push/pull MySQL database dumps to/from a remote site');
70
+ // db pull <site>
71
+ db
72
+ .command('pull <site>')
73
+ .description('Pull remote MySQL database to a local SQL dump')
74
+ .option('--output <path>', 'Output file path (default: ./db-<site>-<timestamp>.sql.gz)')
75
+ .option('--no-compress', 'Write uncompressed .sql instead of .sql.gz')
76
+ .action(async (siteIdentifier, opts) => {
77
+ requireAuth();
78
+ const resolveSpin = spinner('Resolving site...');
79
+ resolveSpin.start();
80
+ let site;
81
+ try {
82
+ site = await resolveSite(siteIdentifier);
83
+ resolveSpin.succeed(`Site: ${site.name || site.sub_domain} (ID: ${site.id})`);
84
+ }
85
+ catch {
86
+ resolveSpin.fail('Site resolution failed');
87
+ process.exit(1);
88
+ }
89
+ const conn = await ensureSshAccess(site.id);
90
+ const wpPath = `/home/${conn.username}/web/${conn.domain}/public_html`;
91
+ const compress = opts.compress !== false;
92
+ const siteLabel = sanitizeForFilename(site.name || site.sub_domain || `site-${site.id}`);
93
+ const ext = compress ? 'sql.gz' : 'sql';
94
+ const outputPath = opts.output || `./db-${siteLabel}-${isoTimestamp()}.${ext}`;
95
+ // Make sure the output directory exists
96
+ const outDir = dirname(outputPath);
97
+ if (outDir && outDir !== '.' && !existsSync(outDir)) {
98
+ mkdirSync(outDir, { recursive: true });
99
+ }
100
+ const dumpSpin = spinner(`Exporting database from ${conn.domain}...`);
101
+ dumpSpin.start();
102
+ // Stream `wp db export -` from remote. If --compress, pipe through gzip on
103
+ // the remote side so we never materialize the uncompressed dump locally.
104
+ const remoteCmd = compress
105
+ ? `cd ${wpPath} && wp db export --single-transaction - | gzip`
106
+ : `cd ${wpPath} && wp db export --single-transaction -`;
107
+ try {
108
+ const { exitCode, stderr } = execViaSshToFile(conn, remoteCmd, outputPath);
109
+ if (exitCode !== 0) {
110
+ dumpSpin.fail('Database export failed');
111
+ if (stderr)
112
+ error(stderr.trim());
113
+ // Clean up empty/partial file
114
+ try {
115
+ if (existsSync(outputPath))
116
+ unlinkSync(outputPath);
117
+ }
118
+ catch { /* ignore */ }
119
+ process.exit(1);
120
+ }
121
+ const sizeBytes = statSync(outputPath).size;
122
+ if (sizeBytes === 0) {
123
+ dumpSpin.fail('Database export produced an empty file');
124
+ try {
125
+ unlinkSync(outputPath);
126
+ }
127
+ catch { /* ignore */ }
128
+ process.exit(1);
129
+ }
130
+ dumpSpin.succeed(`Database exported (${formatBytes(sizeBytes)})`);
131
+ success('Pull complete', {
132
+ file: outputPath,
133
+ size_bytes: sizeBytes,
134
+ site_id: site.id,
135
+ });
136
+ }
137
+ catch (err) {
138
+ dumpSpin.fail('Database export failed');
139
+ error(err.message || String(err));
140
+ try {
141
+ if (existsSync(outputPath))
142
+ unlinkSync(outputPath);
143
+ }
144
+ catch { /* ignore */ }
145
+ process.exit(1);
146
+ }
147
+ });
148
+ // db push <site> <file>
149
+ db
150
+ .command('push <site> <file>')
151
+ .description('Push local SQL dump to remote site database (creates a backup first)')
152
+ .option('--force', 'Skip confirmation prompt')
153
+ .option('--no-backup', 'Skip taking a remote backup before overwrite (DANGEROUS)')
154
+ .addHelpText('after', `
155
+ Notes:
156
+ - Always takes a remote backup first unless --no-backup is passed.
157
+ - After pushing to a site on a different domain, you may want to run:
158
+ instawp wp <site> search-replace <old-url> <new-url>
159
+ Auto-replacing site URLs is out of scope for this command.
160
+ `)
161
+ .action(async (siteIdentifier, file, opts) => {
162
+ requireAuth();
163
+ // Validate input file
164
+ if (!existsSync(file)) {
165
+ error(`File not found: ${file}`);
166
+ process.exit(1);
167
+ }
168
+ const localSize = statSync(file).size;
169
+ if (localSize === 0) {
170
+ error(`File is empty: ${file}`);
171
+ process.exit(1);
172
+ }
173
+ // In JSON mode, can't prompt — require --force
174
+ if (isJsonMode() && !opts.force) {
175
+ error('--force is required when using --json (cannot prompt for confirmation)');
176
+ process.exit(1);
177
+ }
178
+ const resolveSpin = spinner('Resolving site...');
179
+ resolveSpin.start();
180
+ let site;
181
+ try {
182
+ site = await resolveSite(siteIdentifier);
183
+ resolveSpin.succeed(`Site: ${site.name || site.sub_domain} (ID: ${site.id})`);
184
+ }
185
+ catch {
186
+ resolveSpin.fail('Site resolution failed');
187
+ process.exit(1);
188
+ }
189
+ const conn = await ensureSshAccess(site.id);
190
+ const wpPath = `/home/${conn.username}/web/${conn.domain}/public_html`;
191
+ const remoteHome = `/home/${conn.username}`;
192
+ const timestamp = isoTimestamp();
193
+ const backupFilename = `db-backup-${timestamp}.sql.gz`;
194
+ const backupRemotePath = `${remoteHome}/${backupFilename}`;
195
+ const takeBackup = opts.backup !== false;
196
+ // Confirmation
197
+ if (!opts.force) {
198
+ const backupLine = takeBackup
199
+ ? `A backup will be saved to ~/${backupFilename} on the remote.`
200
+ : chalk.red('NO BACKUP will be taken (--no-backup). This is irreversible.');
201
+ console.log(`\nThis will ${chalk.bold.red('OVERWRITE')} the database on ${chalk.bold(conn.domain)}.`);
202
+ console.log(backupLine);
203
+ const ok = await promptYesNo('Continue? (y/N) ');
204
+ if (!ok) {
205
+ info('Cancelled.');
206
+ return;
207
+ }
208
+ }
209
+ // Step 1: Backup
210
+ if (takeBackup) {
211
+ const backupSpin = spinner(`Backing up remote database to ~/${backupFilename}...`);
212
+ backupSpin.start();
213
+ const backupCmd = `cd ${wpPath} && wp db export --single-transaction - | gzip > ${backupRemotePath}`;
214
+ const backupResult = execViaSsh(conn, backupCmd);
215
+ if (backupResult.exitCode !== 0) {
216
+ backupSpin.fail('Backup failed — aborting push');
217
+ if (backupResult.stderr)
218
+ error(backupResult.stderr.trim());
219
+ process.exit(1);
220
+ }
221
+ backupSpin.succeed(`Backup saved: ~/${backupFilename}`);
222
+ }
223
+ else {
224
+ info('Skipping backup (--no-backup)');
225
+ }
226
+ // Step 2: Prepare local SQL (gunzip if needed)
227
+ const isGzipped = file.endsWith('.gz') || file.endsWith('.gzip');
228
+ let uploadSource = file;
229
+ let tempLocalDecompressed = null;
230
+ if (isGzipped) {
231
+ const decompressSpin = spinner('Decompressing local dump...');
232
+ decompressSpin.start();
233
+ try {
234
+ const tmpName = `instawp-db-push-${randomBytes(6).toString('hex')}.sql`;
235
+ tempLocalDecompressed = join(process.env.TMPDIR || '/tmp', tmpName);
236
+ await gunzipFile(file, tempLocalDecompressed);
237
+ uploadSource = tempLocalDecompressed;
238
+ const decompressedSize = statSync(uploadSource).size;
239
+ decompressSpin.succeed(`Decompressed (${formatBytes(decompressedSize)})`);
240
+ }
241
+ catch (err) {
242
+ decompressSpin.fail('Decompression failed');
243
+ error(err.message || String(err));
244
+ if (tempLocalDecompressed) {
245
+ try {
246
+ unlinkSync(tempLocalDecompressed);
247
+ }
248
+ catch { /* ignore */ }
249
+ }
250
+ if (takeBackup) {
251
+ info(`Remote backup preserved: ~/${backupFilename}`);
252
+ }
253
+ process.exit(1);
254
+ }
255
+ }
256
+ // Step 3: Upload via scp to /tmp on remote
257
+ const remoteTempName = `db-import-${randomBytes(6).toString('hex')}.sql`;
258
+ const remoteTempPath = `/tmp/${remoteTempName}`;
259
+ const uploadSpin = spinner(`Uploading ${basename(uploadSource)} to remote...`);
260
+ uploadSpin.start();
261
+ const scpExit = scpUpload(conn, uploadSource, remoteTempPath);
262
+ if (scpExit !== 0) {
263
+ uploadSpin.fail(`Upload failed (scp exit ${scpExit})`);
264
+ if (tempLocalDecompressed) {
265
+ try {
266
+ unlinkSync(tempLocalDecompressed);
267
+ }
268
+ catch { /* ignore */ }
269
+ }
270
+ if (takeBackup) {
271
+ info(`Remote backup preserved: ~/${backupFilename}`);
272
+ }
273
+ process.exit(1);
274
+ }
275
+ uploadSpin.succeed('Upload complete');
276
+ // Clean up local temp file (we have it on remote now)
277
+ if (tempLocalDecompressed) {
278
+ try {
279
+ unlinkSync(tempLocalDecompressed);
280
+ }
281
+ catch { /* ignore */ }
282
+ }
283
+ // Step 4: Import on remote
284
+ const importSpin = spinner(`Importing database on ${conn.domain}...`);
285
+ importSpin.start();
286
+ const importResult = execViaSsh(conn, `cd ${wpPath} && wp db import ${remoteTempPath}`);
287
+ if (importResult.exitCode !== 0) {
288
+ importSpin.fail('Import failed');
289
+ if (importResult.stderr)
290
+ error(importResult.stderr.trim());
291
+ else if (importResult.stdout)
292
+ error(importResult.stdout.trim());
293
+ // Clean up temp file on remote even on failure (best effort)
294
+ execViaSsh(conn, `rm -f ${remoteTempPath}`);
295
+ if (takeBackup) {
296
+ console.log('');
297
+ info(`Remote backup preserved at: ~/${backupFilename}`);
298
+ info('To restore:');
299
+ console.log(` ssh ${conn.username}@${conn.host} 'cd ${wpPath} && gunzip -c ${backupRemotePath} | wp db import -'`);
300
+ console.log(` ${chalk.dim('# or pull the backup down and re-push:')}`);
301
+ console.log(` scp ${conn.username}@${conn.host}:${backupRemotePath} ./`);
302
+ console.log(` instawp db push ${siteIdentifier} ./${backupFilename}`);
303
+ }
304
+ else {
305
+ error('No backup was taken — database state may be inconsistent.');
306
+ }
307
+ process.exit(1);
308
+ }
309
+ importSpin.succeed('Database imported');
310
+ // Step 5: Cleanup remote temp file
311
+ const cleanupSpin = spinner('Cleaning up...');
312
+ cleanupSpin.start();
313
+ const cleanupResult = execViaSsh(conn, `rm -f ${remoteTempPath}`);
314
+ if (cleanupResult.exitCode !== 0) {
315
+ cleanupSpin.fail(`Could not remove ${remoteTempPath} (non-fatal)`);
316
+ }
317
+ else {
318
+ cleanupSpin.succeed('Cleanup complete');
319
+ }
320
+ success('Push complete', {
321
+ site_id: site.id,
322
+ backup_path: takeBackup ? backupRemotePath : null,
323
+ restored_from: file,
324
+ size_bytes: localSize,
325
+ });
326
+ if (!isJsonMode() && takeBackup) {
327
+ console.log(`\n ${chalk.dim('Backup:')} ~/${backupFilename} ${chalk.dim('(on remote)')}`);
328
+ }
329
+ });
330
+ }
331
+ //# sourceMappingURL=db.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db.js","sourceRoot":"","sources":["../../src/commands/db.ts"],"names":[],"mappings":"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"}