33# ----------------------------------------------------
44function run_disembark() {
55 local target_url="$1"
6+ local debug_flag="$2"
67
78 echo "🚀 Starting Disembark process for ${target_url}..."
89
@@ -59,31 +60,34 @@ const path = require('path');
5960const PLUGIN_ZIP_URL = 'https://github.com/DisembarkHost/disembark-connector/releases/latest/download/disembark-connector.zip';
6061
6162async function main() {
62- const [, , targetUrl, username, password] = process.argv;
63+ const [, , targetUrl, username, password, debugFlag ] = process.argv;
6364
6465 if (!targetUrl || !username || !password) {
65- console.error('Usage: node disembark-browser.js <url> <username> <password>');
66+ console.error('Usage: node disembark-browser.js <url> <username> <password> [debug] ');
6667 process.exit(1);
6768 }
6869
69- const browser = await chromium.launch({ headless: true });
70+ const isHeadless = debugFlag !== 'true';
71+ const browser = await chromium.launch({ headless: isHeadless });
72+
7073 const context = await browser.newContext({
7174 userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36'
7275 });
7376 const page = await context.newPage();
7477 try {
7578 // 1. LOGIN
7679 process.stdout.write(' - Step 1/5: Authenticating with WordPress...');
77- await page.goto(`${targetUrl}/wp-login.php`, { waitUntil: 'domcontentloaded' });
80+ await page.goto(`${targetUrl}/wp-login.php`, { waitUntil: 'domcontentloaded' });
81+ await page.waitForSelector('#user_login', { state: 'visible', timeout: 30000 });
7882 await page.fill('#user_login', username);
83+ await page.waitForSelector('#user_pass', { state: 'visible', timeout: 30000 });
7984 await page.fill('#user_pass', password);
80- // Click first, then wait for a specific element on the next page. This is more reliable.
81- await page.click('#wp-submit');
82- await page.waitForSelector('#wpadminbar', { timeout: 60000 }); // Wait 60s for admin bar
85+ await page.waitForSelector('#wp-submit', { state: 'visible', timeout: 30000 });
86+ await page.click('#wp-submit');
87+ await page.waitForSelector('#wpadminbar', { timeout: 60000 });
8388
84- // Verify login by checking for the admin bar.
8589 if (!(await page.isVisible('#wpadminbar'))) {
86- throw new Error('Authentication failed. Please check credentials or for 2FA/CAPTCHA.');
90+ throw new Error('Authentication failed. Please check credentials or for 2FA/CAPTCHA.');
8791 }
8892 console.log(' Success!');
8993
@@ -93,8 +97,7 @@ async function main() {
9397
9498 const pluginRow = page.locator('tr[data-slug="disembark-connector"]');
9599 if (await pluginRow.count() > 0) {
96- console.log(' Plugin found.');
97- // More robustly check if the 'Activate' link exists.
100+ console.log(' Plugin found.');
98101 const activateLink = pluginRow.locator('a.edit:has-text("Activate")');
99102 if (await activateLink.count() > 0) {
100103 process.stdout.write(' - Activating existing plugin...');
@@ -104,15 +107,14 @@ async function main() {
104107 ]);
105108 console.log(' Activated!');
106109 } else {
107- console.log(' - Plugin already active.');
110+ console.log(' - Plugin already active.');
108111 }
109112 } else {
110113 // 3. UPLOAD AND INSTALL PLUGIN
111- console.log(' Plugin not found, proceeding with installation.');
114+ console.log(' Plugin not found, proceeding with installation.');
112115 process.stdout.write(' - Step 3/5: Downloading plugin...');
113116 const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'disembark-'));
114117 const pluginZipPath = path.join(tempDir, 'disembark-connector.zip');
115- // This promise-based downloader now correctly handles HTTP redirects.
116118 await new Promise((resolve, reject) => {
117119 const file = fs.createWriteStream(pluginZipPath);
118120 const request = (url) => {
@@ -139,15 +141,16 @@ async function main() {
139141
140142 await page.click('input#install-plugin-submit');
141143
142- // Define selectors for possible outcomes after installation attempt.
144+ // Define selectors for all possible outcomes after installation attempt.
143145 const activationLinkSelector = 'a:has-text("Activate Plugin"), a.activate-now, .button.activate-now';
144146 const alreadyInstalledSelector = 'body:has-text("Destination folder already exists.")';
145- const genericErrorSelector = '.wrap > .error, .wrap > #message.error'; // Common WP error notice selectors
147+ const mixedSuccessSelector = 'body:has-text("Plugin installed successfully.")'; // For your specific error case
148+ const genericErrorSelector = '.wrap > .error, .wrap > #message.error';
146149
147150 try {
148- // Wait for any of the outcomes to appear on the page.
151+ // Wait for ANY of the outcomes to appear on the page.
149152 await page.waitForSelector(
150- `${activationLinkSelector}, ${alreadyInstalledSelector}, ${genericErrorSelector}`,
153+ `${activationLinkSelector}, ${alreadyInstalledSelector}, ${mixedSuccessSelector}, ${ genericErrorSelector}`,
151154 { timeout: 90000 }
152155 );
153156 } catch (e) {
@@ -167,9 +170,23 @@ async function main() {
167170 } else if (await page.locator(alreadyInstalledSelector).count() > 0) {
168171 // Outcome 2: The plugin was already installed.
169172 console.log(' Plugin already installed.');
170- // No action needed here, the script will proceed to the token retrieval step.
173+ } else if (await page.locator(mixedSuccessSelector).count() > 0) {
174+ // Outcome 3: Install was successful, but the page crashed.
175+ console.log(' Install succeeded, but page reported an error. Navigating to plugins page to activate...');
176+ await page.goto(`${targetUrl}/wp-admin/plugins.php`, { waitUntil: 'networkidle' });
177+ const pluginRow = page.locator('tr[data-slug="disembark-connector"]');
178+ const activateLink = pluginRow.locator('a.edit:has-text("Activate")');
179+ if (await activateLink.count() > 0) {
180+ await Promise.all([
181+ page.waitForNavigation({ waitUntil: 'networkidle' }),
182+ activateLink.click()
183+ ]);
184+ console.log(' Activated!');
185+ } else {
186+ console.log(' - Plugin was already active on the plugins page.');
187+ }
171188 } else {
172- // Outcome 3 : A generic WordPress error occurred.
189+ // Outcome 4 : A generic WordPress error occurred.
173190 const errorText = await page.locator(genericErrorSelector).first().textContent();
174191 throw new Error(`Plugin installation failed with a WordPress error: ${errorText.trim()}`);
175192 }
@@ -185,12 +202,12 @@ async function main() {
185202
186203 const tokenElement = page.locator('div#section-description > code');
187204 if (await tokenElement.count() === 0) {
188- throw new Error('Could not find the connection token element on the page.');
205+ throw new Error('Could not find the connection token element on the page.');
189206 }
190207 const token = await tokenElement.first().textContent();
191208 console.log(' Token found!');
192209 // 5. OUTPUT TOKEN FOR BASH SCRIPT
193- process.stdout.write(' - Step 5/5: Sending token back to script...');
210+ process.stdout.write(' - Step 5/5: Sending token back to script...');
194211 console.log(token.trim());
195212
196213 } catch (error) {
211228 # Enable pipefail to catch errors from the node script before the pipe to tee
212229 set -o pipefail
213230 local full_output
214- full_output=$(echo "$PLAYWRIGHT_SCRIPT" | node - "$target_url" "$username" "$password" | tee /dev/tty)
231+ full_output=$(echo "$PLAYWRIGHT_SCRIPT" | node - "$target_url" "$username" "$password" "$debug_flag" | tee /dev/tty)
215232 local exit_code=$?
216233
217234 # Disable pipefail after the command to avoid affecting other parts of the script
0 commit comments