open-composition-editor-v1.1.0.js•15.2 kB
#!/usr/bin/env node
/**
* Composition Editor Opener Tool v1.1.0 - FAIL-FAST RELIABILITY
* Navigate to saved composition with strict validation and comprehensive error reporting
* @version 1.1.0 (January 20, 2025)
* @status ENHANCED - Fail-fast validation with comprehensive error reporting
* @reference JIT workflow step 7 of 7
* @enhancement Strict validation, no fallbacks, detailed troubleshooting
*/
export class CompositionEditorOpenerV110 {
constructor() {
this.processingStartTime = null;
this.navigationLog = [];
this.validationErrors = [];
this.browserTimeout = 30000; // 30 seconds
this.composerBaseUrl = 'https://composer.euconquisto.com/';
}
/**
* Main navigation entry point with FAIL-FAST validation
* @param {Object} input - Contains compositionUid from save operation
* @returns {Object} Navigation result OR detailed error information
*/
async openCompositionEditor(input) {
this.processingStartTime = Date.now();
this.navigationLog = [];
this.validationErrors = [];
try {
console.error('[OPEN_COMPOSITION_EDITOR_V110] Starting fail-fast navigation');
// FAIL-FAST VALIDATION PHASE
const validationResult = this.validateCompositionUidComprehensively(input);
if (!validationResult.valid) {
throw new Error(`Composition Editor navigation validation failed:\n\n${validationResult.errors.join('\n\n')}\n\nReceived input: ${JSON.stringify(input, null, 2)}`);
}
const { compositionUid } = validationResult;
console.error(`[OPEN_COMPOSITION_EDITOR_V110] Validation passed - Opening composition: ${compositionUid}`);
// BROWSER LAUNCH PHASE
const browserResult = await this.launchBrowserStrict();
if (!browserResult.success) {
throw new Error(`Browser launch failed: ${browserResult.error}`);
}
const { browser, page } = browserResult;
try {
// NAVIGATION PHASE
const navigationResult = await this.navigateToCompositionStrict(page, compositionUid);
if (!navigationResult.success) {
throw new Error(`Navigation failed: ${navigationResult.error}`);
}
// VERIFICATION PHASE
const verificationResult = await this.verifyCompositionLoadedStrict(page, compositionUid);
if (!verificationResult.success) {
throw new Error(`Composition verification failed: ${verificationResult.error}`);
}
const processingTime = Date.now() - this.processingStartTime;
console.error(`[OPEN_COMPOSITION_EDITOR_V110] Success - Composition opened in ${processingTime}ms`);
return {
success: true,
data: {
compositionUid: compositionUid,
editorUrl: navigationResult.finalUrl,
browserStatus: 'opened',
verificationStatus: verificationResult.status,
navigationSummary: {
totalSteps: this.navigationLog.length,
processingTime: processingTime,
browserPersistence: true,
editingReady: true
},
userInstructions: {
status: "Composition editor is now open and ready for editing",
browserWindow: "Keep the browser window open to continue editing",
note: "Browser will remain open until manually closed"
}
},
metadata: {
version: "1.1.0",
timestamp: new Date().toISOString(),
processingTime: processingTime,
failFastValidation: "passed",
navigationSteps: this.navigationLog.length
}
};
} catch (error) {
// Close browser on error
try {
await browser.close();
} catch (closeError) {
console.error('[OPEN_COMPOSITION_EDITOR_V110] Browser close error:', closeError.message);
}
throw error;
}
} catch (error) {
console.error('[OPEN_COMPOSITION_EDITOR_V110] FAIL-FAST ERROR:', error.message);
return {
success: false,
error: {
code: 'COMPOSITION_EDITOR_NAVIGATION_FAILED',
message: error.message,
timestamp: new Date().toISOString(),
processingTime: Date.now() - this.processingStartTime,
failFast: true,
developmentMode: true,
troubleshooting: {
requiredInputStructure: {
compositionUid: "string - unique composition identifier from save operation"
},
navigationPhases: [
"1. Input validation and composition UID extraction",
"2. Browser launch with Playwright",
"3. Navigation to Composer base URL",
"4. Composition-specific URL navigation",
"5. Composer interface verification",
"6. Composition load verification"
],
commonIssues: [
"Missing or invalid composition UID",
"Playwright browser not installed",
"Network connectivity issues",
"Composer website accessibility problems",
"Composition not found or access denied",
"Browser automation restrictions"
],
debugSteps: [
"1. Check if compositionUid exists and is valid",
"2. Verify Playwright installation: 'npx playwright install'",
"3. Test network connectivity to composer.euconquisto.com",
"4. Verify composition was saved successfully",
"5. Check browser automation permissions",
"6. Test manual access to composition URL"
],
playwrightRequirements: {
installation: "Run 'npx playwright install chromium' if browser launch fails",
permissions: "Ensure browser automation is allowed",
network: "Verify access to https://composer.euconquisto.com/"
}
}
}
};
}
}
/**
* COMPREHENSIVE FAIL-FAST VALIDATION for composition UID
*/
validateCompositionUidComprehensively(input) {
const errors = [];
// Basic input validation
if (!input || typeof input !== 'object') {
errors.push("❌ INPUT STRUCTURE ERROR:\nInput must be an object containing compositionUid.\nThis should come from the save_composition_direct_api tool output.\nRequired format: { compositionUid: 'string' }");
return { valid: false, errors };
}
// Extract composition UID from various possible structures
let compositionUid = null;
// Direct compositionUid property
if (input.compositionUid) {
compositionUid = input.compositionUid;
}
// From save operation response structure
else if (input.data && input.data.compositionUid) {
compositionUid = input.data.compositionUid;
}
// From nested save response
else if (input.data && input.data.uploadDetails && input.data.uploadDetails.compositionUid) {
compositionUid = input.data.uploadDetails.compositionUid;
}
// From alternative response structure
else if (input.uploadDetails && input.uploadDetails.compositionUid) {
compositionUid = input.uploadDetails.compositionUid;
}
if (!compositionUid) {
errors.push("❌ MISSING COMPOSITION UID ERROR:\ncompositionUid is required for navigation.\nThis should be provided by the save_composition_direct_api tool.\nCheck if the save operation completed successfully.\nExpected structure: { compositionUid: 'string' }");
return { valid: false, errors };
}
// Validate composition UID format
if (typeof compositionUid !== 'string') {
errors.push(`❌ COMPOSITION UID TYPE ERROR:\ncompositionUid must be a string, received: ${typeof compositionUid}\nValue: ${JSON.stringify(compositionUid)}`);
return { valid: false, errors };
}
if (compositionUid.trim().length === 0) {
errors.push("❌ EMPTY COMPOSITION UID ERROR:\ncompositionUid cannot be empty.\nEnsure the save operation returned a valid composition identifier.");
return { valid: false, errors };
}
// Basic UID format validation (adjust based on actual UID format)
if (compositionUid.length < 10) {
errors.push(`❌ INVALID COMPOSITION UID FORMAT ERROR:\ncompositionUid appears too short: "${compositionUid}"\nExpected: Valid composition identifier from save operation\nLength: ${compositionUid.length} characters (minimum: 10)`);
return { valid: false, errors };
}
console.error(`[OPEN_COMPOSITION_EDITOR_V110] Composition UID validation passed: ${compositionUid}`);
return { valid: true, compositionUid: compositionUid.trim(), errors: [] };
}
/**
* STRICT browser launch with comprehensive error handling
*/
async launchBrowserStrict() {
try {
console.error('[OPEN_COMPOSITION_EDITOR_V110] Launching browser with Playwright');
// Import Playwright
const { chromium } = await import('playwright');
// Launch browser with optimal settings
const browser = await chromium.launch({
headless: false, // Keep visible for user interaction
args: [
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-web-security',
'--disable-blink-features=AutomationControlled'
],
timeout: this.browserTimeout
});
// Create new page
const page = await browser.newPage();
// Set user agent and viewport
await page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36');
await page.setViewportSize({ width: 1366, height: 768 });
console.error('[OPEN_COMPOSITION_EDITOR_V110] Browser launched successfully');
return {
success: true,
browser,
page
};
} catch (error) {
console.error('[OPEN_COMPOSITION_EDITOR_V110] Browser launch error:', error.message);
return {
success: false,
error: `Browser launch failed: ${error.message}. Ensure Playwright is installed: 'npx playwright install chromium'`
};
}
}
/**
* STRICT navigation to composition with comprehensive verification
*/
async navigateToCompositionStrict(page, compositionUid) {
try {
console.error(`[OPEN_COMPOSITION_EDITOR_V110] Navigating to composition: ${compositionUid}`);
// Step 1: Navigate to base URL first
console.error('[OPEN_COMPOSITION_EDITOR_V110] Step 1: Navigating to Composer base URL');
await page.goto(this.composerBaseUrl, {
waitUntil: 'networkidle',
timeout: this.browserTimeout
});
// Wait for initial load
await page.waitForTimeout(2000);
// Step 2: Navigate to specific composition
const compositionUrl = `${this.composerBaseUrl}#/composer/${compositionUid}`;
console.error(`[OPEN_COMPOSITION_EDITOR_V110] Step 2: Navigating to composition URL: ${compositionUrl}`);
await page.goto(compositionUrl, {
waitUntil: 'networkidle',
timeout: this.browserTimeout
});
// Step 3: Wait for Composer interface
console.error('[OPEN_COMPOSITION_EDITOR_V110] Step 3: Waiting for Composer interface');
await page.waitForFunction(() => {
const composerSelectors = [
'[data-testid="composer-app"]',
'.composer-editor',
'#composer-root',
'.rdp-composer',
'[data-composer]',
'.composer-container',
'.composer-interface'
];
return composerSelectors.some(selector =>
document.querySelector(selector) !== null
);
}, { timeout: 15000 });
// Additional wait for composition to load
await page.waitForTimeout(3000);
const finalUrl = page.url();
console.error(`[OPEN_COMPOSITION_EDITOR_V110] Navigation successful - Final URL: ${finalUrl}`);
return {
success: true,
finalUrl: finalUrl
};
} catch (error) {
console.error('[OPEN_COMPOSITION_EDITOR_V110] Navigation error:', error.message);
return {
success: false,
error: `Navigation failed: ${error.message}. Check network connectivity and Composer website accessibility.`
};
}
}
/**
* STRICT composition load verification
*/
async verifyCompositionLoadedStrict(page, compositionUid) {
try {
console.error(`[OPEN_COMPOSITION_EDITOR_V110] Verifying composition loaded: ${compositionUid}`);
// Check URL contains composition UID
const currentUrl = page.url();
if (!currentUrl.includes(compositionUid)) {
throw new Error(`Composition UID not found in current URL: ${currentUrl}`);
}
// Check for error messages
const errorElements = await page.$$('.error, .alert-danger, [data-error], .composition-error');
if (errorElements.length > 0) {
const errorText = await Promise.all(
errorElements.map(el => el.textContent().catch(() => 'Unknown error'))
);
throw new Error(`Composition load errors detected: ${errorText.join(', ')}`);
}
// Check for loading indicators completion
await page.waitForFunction(() => {
const loadingSelectors = [
'.loading',
'.spinner',
'[data-loading]',
'.composition-loading'
];
return !loadingSelectors.some(selector =>
document.querySelector(selector) !== null
);
}, { timeout: 10000 }).catch(() => {
console.error('[OPEN_COMPOSITION_EDITOR_V110] Loading indicators still present - continuing anyway');
});
console.error('[OPEN_COMPOSITION_EDITOR_V110] Composition verification successful');
return {
success: true,
status: 'loaded and ready for editing'
};
} catch (error) {
console.error('[OPEN_COMPOSITION_EDITOR_V110] Verification error:', error.message);
return {
success: false,
error: `Composition verification failed: ${error.message}. Composition may not exist or access may be denied.`
};
}
}
/**
* Log navigation step
*/
logNavigation(step, description, success, details = null) {
this.navigationLog.push({
step,
description,
success,
details,
timestamp: new Date().toISOString()
});
}
}
/**
* Create and export the tool instance for JIT server integration
*/
export function createCompositionEditorOpenerV110() {
const opener = new CompositionEditorOpenerV110();
return {
name: 'open_composition_editor_v110',
description: 'STEP 7 v1.1.0: Navigate to saved composition editor with fail-fast validation. Opens browser to composition with comprehensive error reporting for development.',
inputSchema: {
type: 'object',
properties: {
compositionUid: {
type: 'string',
description: 'Composition UID from save_composition_direct_api tool output'
}
},
required: ['compositionUid']
},
handler: async (input) => {
return await opener.openCompositionEditor(input);
}
};
}