diff --git a/cli/commands/ai/generate.ts b/cli/commands/ai/generate.ts new file mode 100644 index 000000000..18bfabf86 --- /dev/null +++ b/cli/commands/ai/generate.ts @@ -0,0 +1,302 @@ +import { input, select, confirm } from '@inquirer/prompts'; +import { __ } from '@wordpress/i18n'; +import { getAuthToken } from 'cli/lib/appdata'; +import ora from 'ora'; +import chalk from 'chalk'; +import type { StudioArgv } from 'cli/types'; +import { TelexClient } from 'cli/lib/telex-client'; +import { parseArtefactXml, getBlockMetadata } from 'cli/lib/artefact-parser'; +import { + installBlockToSite, + listStudioSites, + studioSiteExists, + getPluginActivationUrl, +} from 'cli/lib/block-installer'; +import { getTelexApiUrl } from 'cli/lib/telex-constants'; + +interface GenerateOptions { + prompt?: string; + site?: string; +} + +/** + * Run the 'ai generate' command to create a WordPress block using AI + */ +export async function runCommand( options: GenerateOptions ): Promise< void > { + const spinner = ora(); + + try { + // 1. Check authentication + spinner.start( __( 'Checking authentication...' ) ); + const authToken = await getAuthToken(); + if ( ! authToken ) { + spinner.fail( __( 'Not authenticated with WordPress.com' ) ); + console.log( + chalk.yellow( '\nPlease run:' ), + chalk.cyan( 'studio auth login' ) + ); + return; + } + spinner.succeed( + __( 'Authenticated as %s', chalk.cyan( authToken.displayName ) ) + ); + + // 2. Get prompt from user or flag + const prompt = + options.prompt || + ( await input( { + message: __( 'Describe the block you want to create:' ), + validate: ( value ) => + value.trim().length > 0 || __( 'Please enter a prompt' ), + } ) ); + + console.log( chalk.dim( `\nPrompt: "${ prompt }"\n` ) ); + + // 3. Initialize Telex client + const telexApiUrl = getTelexApiUrl(); + const telex = new TelexClient( telexApiUrl, authToken.accessToken ); + + // 4. Generate block + spinner.start( __( 'Connecting to Telex AI...' ) ); + + let chatText = ''; + let currentLine = ''; + + try { + const result = await telex.generateBlock( prompt, { + onChunk: ( text ) => { + chatText += text; + currentLine += text; + + // Update spinner with last line of AI response + if ( text.includes( '\n' ) ) { + const lines = currentLine.split( '\n' ); + currentLine = lines[ lines.length - 1 ]; + } + + const displayText = + currentLine.length > 60 + ? currentLine.slice( -60 ) + : currentLine; + spinner.text = chalk.cyan( `AI: ${ displayText.trim() }` ); + }, + onArtefact: () => { + spinner.text = __( 'Receiving block files...' ); + }, + } ); + + spinner.succeed( __( 'Block generated successfully!' ) ); + + // Show AI explanation + if ( chatText ) { + console.log( chalk.dim( '\n' + '─'.repeat( 60 ) ) ); + console.log( chalk.cyan( '\nAI Response:\n' ) ); + console.log( chatText.trim() ); + console.log( chalk.dim( '\n' + '─'.repeat( 60 ) + '\n' ) ); + } + + // 5. Parse artefact + const artefact = parseArtefactXml( result.artefact ); + const metadata = getBlockMetadata( artefact ); + + // Show block info + console.log( chalk.bold( '\nGenerated Block:' ) ); + console.log( chalk.gray( ' Name: ' ), chalk.white( artefact.name ) ); + console.log( chalk.gray( ' Slug: ' ), chalk.white( artefact.slug ) ); + console.log( + chalk.gray( ' Files: ' ), + chalk.white( artefact.files.length ) + ); + + if ( metadata?.title ) { + console.log( + chalk.gray( ' Title: ' ), + chalk.white( metadata.title ) + ); + } + + // 6. Ask what to do with the block + const action = await select( { + message: __( 'What would you like to do?' ), + choices: [ + { + value: 'install', + name: __( 'Install to local Studio site' ), + }, + { + value: 'preview', + name: __( 'Open in Telex web editor' ), + }, + { + value: 'exit', + name: __( 'Exit (block saved in Telex)' ), + }, + ], + } ); + + // 7. Handle action + if ( action === 'install' ) { + await handleInstall( artefact, options.site, spinner ); + } else if ( action === 'preview' ) { + const url = telex.getProjectUrl( result.epid ); + console.log( + chalk.green( '\n✓' ), + __( 'Open in your browser:' ), + chalk.cyan( url ) + ); + } else { + console.log( + chalk.green( '\n✓' ), + __( 'Block saved to your Telex account' ) + ); + } + } catch ( error ) { + spinner.fail( __( 'Block generation failed' ) ); + + if ( error instanceof Error ) { + console.error( chalk.red( '\nError:' ), error.message ); + } + + throw error; + } + } catch ( error ) { + if ( spinner.isSpinning ) { + spinner.fail(); + } + + if ( error instanceof Error && error.message !== 'User force closed the prompt' ) { + // Don't show error if user pressed Ctrl+C + console.error( chalk.red( '\nCommand failed:' ), error.message ); + } + + process.exit( 1 ); + } +} + +/** + * Handle block installation to Studio site + */ +async function handleInstall( + artefact: ReturnType< typeof parseArtefactXml >, + siteName: string | undefined, + spinner: ReturnType< typeof ora > +): Promise< void > { + // Get or select site + let selectedSite = siteName; + + if ( ! selectedSite ) { + const sites = await listStudioSites(); + + if ( sites.length === 0 ) { + console.log( + chalk.yellow( '\n⚠ No Studio sites found' ) + ); + console.log( + chalk.gray( 'Create a site with:' ), + chalk.cyan( 'studio site create' ) + ); + return; + } + + selectedSite = await select( { + message: __( 'Select a Studio site:' ), + choices: sites.map( ( site ) => ( { + value: site, + name: site, + } ) ), + } ); + } + + // Verify site exists + if ( ! studioSiteExists( selectedSite ) ) { + console.error( + chalk.red( `\n✗ Site '${ selectedSite }' not found` ) + ); + console.log( + chalk.gray( 'Available sites:' ), + ( await listStudioSites() ).join( ', ' ) + ); + return; + } + + // Check if plugin already exists + const overwrite = await confirm( { + message: __( + 'Install "%s" to %s?', + artefact.name, + selectedSite + ), + default: true, + } ); + + if ( ! overwrite ) { + console.log( chalk.yellow( '\nInstallation cancelled' ) ); + return; + } + + // Install + spinner.start( __( 'Installing block to %s...', selectedSite ) ); + + try { + await installBlockToSite( selectedSite, artefact ); + spinner.succeed( __( 'Block installed to %s', chalk.cyan( selectedSite ) ) ); + + // Show next steps + const pluginUrl = getPluginActivationUrl( selectedSite, artefact.slug ); + + console.log( chalk.bold( '\n📦 Next Steps:' ) ); + console.log( chalk.gray( ' 1.' ), 'Visit', chalk.cyan( pluginUrl ) ); + console.log( + chalk.gray( ' 2.' ), + 'Activate', + chalk.cyan( `"${ artefact.name }"` ) + ); + console.log( + chalk.gray( ' 3.' ), + 'Create a post and add the block' + ); + } catch ( error ) { + spinner.fail( __( 'Installation failed' ) ); + throw error; + } +} + +/** + * Register the 'ai generate' command with Yargs + */ +export const registerCommand = ( yargs: StudioArgv ) => { + return yargs.command( { + command: 'generate [prompt]', + describe: __( 'Generate a WordPress block using AI' ), + builder: ( yargs ) => { + return yargs + .positional( 'prompt', { + type: 'string', + describe: __( 'Describe the block you want to create' ), + } ) + .option( 'site', { + type: 'string', + describe: __( 'Studio site to install the block to' ), + alias: 's', + } ) + .example( + '$0 ai generate', + __( 'Interactively create a block' ) + ) + .example( + '$0 ai generate "testimonial carousel"', + __( 'Create a block from prompt' ) + ) + .example( + '$0 ai generate "hero section" --site mysite', + __( 'Create and install to site' ) + ); + }, + handler: async ( argv ) => { + await runCommand( { + prompt: argv.prompt, + site: argv.site, + } ); + }, + } ); +}; diff --git a/cli/commands/telex/block.ts b/cli/commands/telex/block.ts new file mode 100644 index 000000000..31f586739 --- /dev/null +++ b/cli/commands/telex/block.ts @@ -0,0 +1,119 @@ +import { input } from '@inquirer/prompts'; +import { __ } from '@wordpress/i18n'; +import chalk from 'chalk'; +import type { StudioArgv } from 'cli/types'; +import { runTelexCommand } from 'cli/lib/telex-command-utils'; + +interface BlockOptions { + prompt?: string; +} + +/** + * Run the 'telex block' command to create a WordPress block using AI + */ +export async function runCommand( options: BlockOptions, sitePath: string ): Promise< void > { + // Get prompt from user or flag + const prompt = + options.prompt || + ( await input( { + message: __( 'Describe the block you want to create:' ), + validate: ( value ) => + value.trim().length > 0 || __( 'Please enter a prompt' ), + } ) ); + + console.log( chalk.dim( `\nPrompt: "${ prompt }"\n` ) ); + + // Use shared command flow + await runTelexCommand( options, sitePath, async ( telex, spinner ) => { + // Generate block with AI + spinner.start( __( 'Connecting to Telex AI...' ) ); + + let chatText = ''; + let currentLine = ''; + + const result = await telex.generateBlock( prompt, { + onChunk: ( text ) => { + chatText += text; + currentLine += text; + + // Update spinner with last line of AI response + if ( text.includes( '\n' ) ) { + const lines = currentLine.split( '\n' ); + currentLine = lines[ lines.length - 1 ]; + } + + const displayText = + currentLine.length > 60 + ? currentLine.slice( -60 ) + : currentLine; + spinner.text = chalk.cyan( `AI: ${ displayText.trim() }` ); + }, + onArtefact: () => { + spinner.text = __( 'Receiving block files...' ); + }, + } ); + + spinner.succeed( __( 'Block generated successfully!' ) ); + + // Show AI explanation + if ( chatText ) { + console.log( chalk.dim( '\n' + '─'.repeat( 60 ) ) ); + console.log( chalk.cyan( '\nAI Response:\n' ) ); + console.log( chatText.trim() ); + console.log( chalk.dim( '\n' + '─'.repeat( 60 ) + '\n' ) ); + } + + // Show Telex UI URL + const telexUrl = telex.getProjectUrl( result.epid ); + console.log( + chalk.gray( '\n Telex: ' ), + chalk.cyan( telexUrl ) + ); + + // Wait for build and fetch complete block with built files + spinner.start( __( 'Waiting for build to complete...' ) ); + const artefact = await telex.fetchBlock( result.epid, undefined, ( attempt, max ) => { + spinner.text = __( 'Waiting for build to complete...' ) + ` (${ attempt }/${ max })`; + } ); + spinner.succeed( __( 'Build complete! Block ready for installation.' ) ); + + return artefact; + } ); +} + +/** + * Register the 'telex block' command with Yargs + */ +export const registerCommand = ( yargs: StudioArgv ) => { + return yargs.command( { + command: 'block [prompt]', + describe: __( 'Generate a WordPress block using AI and install it' ), + builder: ( yargs ) => { + return yargs + .positional( 'prompt', { + type: 'string', + describe: __( 'Describe the block you want to create' ), + } ) + .example( + '$0 telex block', + __( 'Interactively create a block' ) + ) + .example( + '$0 telex block "testimonial carousel"', + __( 'Create a testimonial carousel block' ) + ) + .example( + '$0 telex block "hero section" --path ~/sites/mysite', + __( 'Create and install to specific site' ) + ); + }, + handler: async ( argv ) => { + await runCommand( + { + prompt: argv.prompt, + }, + argv.path + ); + }, + } ); +}; diff --git a/cli/commands/telex/install.ts b/cli/commands/telex/install.ts new file mode 100644 index 000000000..cb1b7c907 --- /dev/null +++ b/cli/commands/telex/install.ts @@ -0,0 +1,91 @@ +import { input } from '@inquirer/prompts'; +import { __ } from '@wordpress/i18n'; +import chalk from 'chalk'; +import type { StudioArgv } from 'cli/types'; +import { runTelexCommand } from 'cli/lib/telex-command-utils'; + +interface InstallOptions { + projectId?: string; +} + +/** + * Extract EPID from Telex URL or return as-is if already an EPID + * Examples: + * - https://telex.automattic.ai/projects/v1.abc123 → v1.abc123 + * - v1.abc123 → v1.abc123 + */ +function extractEpid( input: string ): string { + const match = input.match( /\/projects\/([^\/\?#]+)/ ); + return match ? match[ 1 ] : input; +} + +/** + * Run the 'telex install' command to install a block from Telex + */ +export async function runCommand( options: InstallOptions, sitePath: string ): Promise< void > { + // Get project ID from user or flag + const projectInput = + options.projectId || + ( await input( { + message: __( 'Enter Telex project URL or ID:' ), + validate: ( value ) => + value.trim().length > 0 || __( 'Please enter a project URL or ID' ), + } ) ); + + const epid = extractEpid( projectInput ); + console.log( chalk.dim( `\nProject ID: ${ epid }\n` ) ); + + // Use shared command flow + await runTelexCommand( options, sitePath, async ( telex, spinner ) => { + // Fetch block artefact + spinner.start( __( 'Fetching block from Telex...' ) ); + const artefact = await telex.fetchBlock( epid ); + spinner.succeed( __( 'Block fetched successfully!' ) ); + + // Show Telex UI URL + const telexUrl = telex.getProjectUrl( epid ); + console.log( + chalk.gray( ' Telex: ' ), + chalk.cyan( telexUrl ) + ); + + return artefact; + } ); +} + +/** + * Register the 'telex install' command with Yargs + */ +export const registerCommand = ( yargs: StudioArgv ) => { + return yargs.command( { + command: 'install [project-id]', + describe: __( 'Install a block from Telex to your local site' ), + builder: ( yargs ) => { + return yargs + .positional( 'project-id', { + type: 'string', + describe: __( 'Telex project URL or ID' ), + } ) + .example( + '$0 telex install', + __( 'Interactively install a block' ) + ) + .example( + '$0 telex install v1.abc123', + __( 'Install block by ID' ) + ) + .example( + '$0 telex install https://telex.automattic.ai/projects/v1.abc123', + __( 'Install block by URL' ) + ); + }, + handler: async ( argv ) => { + await runCommand( + { + projectId: argv.projectId, + }, + argv.path + ); + }, + } ); +}; diff --git a/cli/index.ts b/cli/index.ts index d544db03b..f1938e812 100644 --- a/cli/index.ts +++ b/cli/index.ts @@ -6,6 +6,8 @@ import yargs from 'yargs'; import { registerCommand as registerAuthLoginCommand } from 'cli/commands/auth/login'; import { registerCommand as registerAuthLogoutCommand } from 'cli/commands/auth/logout'; import { registerCommand as registerAuthStatusCommand } from 'cli/commands/auth/status'; +import { registerCommand as registerTelexBlockCommand } from 'cli/commands/telex/block'; +import { registerCommand as registerTelexInstallCommand } from 'cli/commands/telex/install'; import { registerCommand as registerCreateCommand } from 'cli/commands/preview/create'; import { registerCommand as registerDeleteCommand } from 'cli/commands/preview/delete'; import { registerCommand as registerListCommand } from 'cli/commands/preview/list'; @@ -67,6 +69,11 @@ async function main() { registerAuthStatusCommand( authYargs ); authYargs.version( false ).demandCommand( 1, __( 'You must provide a valid auth command' ) ); } ) + .command( 'telex', __( 'AI-assisted block development with Telex' ), ( telexYargs ) => { + registerTelexBlockCommand( telexYargs ); + registerTelexInstallCommand( telexYargs ); + telexYargs.demandCommand( 1, __( 'You must provide a valid telex command' ) ); + } ) .command( 'preview', __( 'Manage preview sites' ), ( previewYargs ) => { registerCreateCommand( previewYargs ); registerListCommand( previewYargs ); diff --git a/cli/lib/artefact-parser.ts b/cli/lib/artefact-parser.ts new file mode 100644 index 000000000..23b22f04c --- /dev/null +++ b/cli/lib/artefact-parser.ts @@ -0,0 +1,169 @@ +import { DOMParser } from '@xmldom/xmldom'; + +/** + * Parsed artefact file + */ +export interface ArtefactFile { + path: string; // Relative path (e.g., 'src/index.js') + content: string; // File contents + description?: string; // Optional description +} + +/** + * Parsed artefact data + */ +export interface Artefact { + name: string; // Block display name (e.g., 'Confetti Button') + slug: string; // Machine-readable slug (e.g., 'confetti-button') + type: string; // Always 'code-package' + schemaVersion: string; // Schema version (e.g., '2') + files: ArtefactFile[]; // All files in the block +} + +/** + * Parse Telex artefact XML into structured data. + * + * Artefacts are XML files that contain all block files and metadata + * as a single source of truth for WordPress Gutenberg blocks. + * + * Example: + * ```xml + * + * + * Block registration + * + * + * + * ``` + * + * @param xml - Artefact XML string + * @returns Parsed artefact data + * @throws Error if XML is invalid or malformed + */ +export function parseArtefactXml( xml: string ): Artefact { + try { + // Trim whitespace first + xml = xml.trim(); + + // Extract only the artefact XML (from to ) + // to ignore any trailing chat text or extra content + const artefactStart = xml.indexOf( '' ); + + if ( artefactStart === -1 || artefactEnd === -1 ) { + throw new Error( 'Missing tags in XML' ); + } + + const cleanXml = xml.substring( artefactStart, artefactEnd + ''.length ).trim(); + + const parser = new DOMParser(); + const doc = parser.parseFromString( cleanXml, 'text/xml' ); + + // Check for parser errors + const parseError = doc.getElementsByTagName( 'parsererror' ); + if ( parseError.length > 0 ) { + throw new Error( `XML parsing error: ${ parseError[ 0 ].textContent }` ); + } + + // Get root artefact element + const artefactEl = doc.getElementsByTagName( 'artefact' )[ 0 ]; + if ( ! artefactEl ) { + throw new Error( 'Missing root element' ); + } + + // Extract attributes + const name = artefactEl.getAttribute( 'name' ); + const slug = artefactEl.getAttribute( 'slug' ); + const type = artefactEl.getAttribute( 'type' ); + const schemaVersion = artefactEl.getAttribute( 'schemaVersion' ); + + if ( ! name || ! slug ) { + throw new Error( 'Missing required attributes: name and slug' ); + } + + // Parse all elements + const fileElements = artefactEl.getElementsByTagName( 'file' ); + const files: ArtefactFile[] = []; + + for ( let i = 0; i < fileElements.length; i++ ) { + const fileEl = fileElements[ i ]; + const path = fileEl.getAttribute( 'path' ); + + if ( ! path ) { + console.warn( `File element ${ i } missing path attribute, skipping` ); + continue; + } + + // Get description (optional) + const descriptionEl = fileEl.getElementsByTagName( 'description' )[ 0 ]; + const description = descriptionEl?.textContent?.trim() || undefined; + + // Get content (required) + const contentEl = fileEl.getElementsByTagName( 'content' )[ 0 ]; + const content = contentEl?.textContent || ''; + + files.push( { + path, + content, + description, + } ); + } + + return { + name, + slug, + type: type || 'code-package', + schemaVersion: schemaVersion || '2', + files, + }; + } catch ( error ) { + if ( error instanceof Error ) { + throw new Error( `Failed to parse artefact XML: ${ error.message }` ); + } + throw error; + } +} + +/** + * Get a specific file from an artefact by path + * + * @param artefact - Parsed artefact + * @param filePath - File path to search for + * @returns File content or null if not found + */ +export function getArtefactFile( + artefact: Artefact, + filePath: string +): string | null { + const file = artefact.files.find( ( f ) => f.path === filePath ); + return file?.content || null; +} + +/** + * Get the main plugin file path for an artefact + * + * @param artefact - Parsed artefact + * @returns Main plugin file path (e.g., 'my-block.php') + */ +export function getMainPluginFile( artefact: Artefact ): string { + return `${ artefact.slug }.php`; +} + +/** + * Extract block metadata from block.json file + * + * @param artefact - Parsed artefact + * @returns Parsed block.json or null if not found/invalid + */ +export function getBlockMetadata( artefact: Artefact ): Record< string, unknown > | null { + const blockJsonContent = getArtefactFile( artefact, 'src/block.json' ); + if ( ! blockJsonContent ) { + return null; + } + + try { + return JSON.parse( blockJsonContent ); + } catch { + return null; + } +} diff --git a/cli/lib/block-installer.ts b/cli/lib/block-installer.ts new file mode 100644 index 000000000..29076ed23 --- /dev/null +++ b/cli/lib/block-installer.ts @@ -0,0 +1,184 @@ +import { writeFile, mkdir, readdir } from 'fs/promises'; +import { join, dirname } from 'path'; +import { homedir } from 'os'; +import { existsSync } from 'fs'; +import type { Artefact } from './artefact-parser'; + +/** + * Get the path to a Studio site's directory + * + * @param siteName - Name of the Studio site (e.g., 'mysite') + * @returns Absolute path to site directory + */ +export function getStudioSitePath( siteName: string ): string { + // Studio sites are stored in ~/Studio/sites/{siteName} + return join( homedir(), 'Studio', 'sites', siteName ); +} + +/** + * Check if a Studio site exists + * + * @param siteName - Name of the Studio site + * @returns true if site exists + */ +export function studioSiteExists( siteName: string ): boolean { + const sitePath = getStudioSitePath( siteName ); + return existsSync( join( sitePath, 'wp-config.php' ) ); +} + +/** + * List all Studio sites + * + * @returns Array of site names + */ +export async function listStudioSites(): Promise< string[] > { + const studiosPath = join( homedir(), 'Studio', 'sites' ); + + if ( ! existsSync( studiosPath ) ) { + return []; + } + + try { + const entries = await readdir( studiosPath, { withFileTypes: true } ); + const sites: string[] = []; + + for ( const entry of entries ) { + if ( entry.isDirectory() ) { + const wpConfigPath = join( studiosPath, entry.name, 'wp-config.php' ); + if ( existsSync( wpConfigPath ) ) { + sites.push( entry.name ); + } + } + } + + return sites; + } catch ( error ) { + console.warn( 'Failed to list Studio sites:', error ); + return []; + } +} + +/** + * Install a block artefact to a site's plugins directory by path. + * + * Creates the plugin directory and writes all files from the artefact. + * The block will appear in WordPress plugins but needs to be activated manually. + * + * @param sitePath - Full path to the WordPress site directory + * @param artefact - Parsed artefact containing block files + * @throws Error if site doesn't exist or installation fails + */ +export async function installBlockToSitePath( + sitePath: string, + artefact: Artefact +): Promise< void > { + // Verify site exists + const wpConfigPath = join( sitePath, 'wp-config.php' ); + if ( ! existsSync( wpConfigPath ) ) { + throw new Error( + `WordPress installation not found at '${ sitePath }'. Missing wp-config.php.` + ); + } + + // Plugin will be installed to wp-content/plugins/{slug} + const pluginPath = join( sitePath, 'wp-content', 'plugins', artefact.slug ); + + // Create plugin directory + await mkdir( pluginPath, { recursive: true } ); + + // Write all files from artefact + let filesWritten = 0; + for ( const file of artefact.files ) { + const filePath = join( pluginPath, file.path ); + const fileDir = dirname( filePath ); + + // Ensure directory exists + await mkdir( fileDir, { recursive: true } ); + + // Write file + await writeFile( filePath, file.content, 'utf-8' ); + filesWritten++; + } + + if ( filesWritten === 0 ) { + throw new Error( 'No files were written - artefact may be empty' ); + } +} + +/** + * Install a block artefact to a Studio site's plugins directory by name. + * + * This is a convenience wrapper around installBlockToSitePath() for sites + * in the default Studio location (~/Studio/sites/). + * + * @param siteName - Name of the Studio site (e.g., 'mysite') + * @param artefact - Parsed artefact containing block files + * @throws Error if site doesn't exist or installation fails + */ +export async function installBlockToSite( + siteName: string, + artefact: Artefact +): Promise< void > { + const sitePath = getStudioSitePath( siteName ); + + // Verify site exists + if ( ! studioSiteExists( siteName ) ) { + throw new Error( + `Studio site '${ siteName }' not found. Run 'studio site list' to see available sites.` + ); + } + + return installBlockToSitePath( sitePath, artefact ); +} + +/** + * Get the WordPress plugin activation URL for a block + * + * @param siteName - Name of the Studio site + * @param pluginSlug - Plugin slug (e.g., 'my-block') + * @returns URL to WordPress plugins page + */ +export function getPluginActivationUrl( siteName: string, pluginSlug: string ): string { + // Studio sites typically use {siteName}.local domain + return `http://${ siteName }.local/wp-admin/plugins.php`; +} + +/** + * Install options for blocks + */ +export interface InstallOptions { + /** Overwrite existing plugin if it exists (default: false) */ + overwrite?: boolean; + /** Verbose output (default: false) */ + verbose?: boolean; +} + +/** + * Install a block with options + * + * @param siteName - Name of the Studio site + * @param artefact - Parsed artefact + * @param options - Install options + */ +export async function installBlock( + siteName: string, + artefact: Artefact, + options: InstallOptions = {} +): Promise< void > { + const sitePath = getStudioSitePath( siteName ); + const pluginPath = join( sitePath, 'wp-content', 'plugins', artefact.slug ); + + // Check if plugin already exists + if ( existsSync( pluginPath ) && ! options.overwrite ) { + throw new Error( + `Plugin '${ artefact.slug }' already exists. Use --overwrite to replace it.` + ); + } + + // Install the block + await installBlockToSite( siteName, artefact ); + + if ( options.verbose ) { + console.log( `Installed ${ artefact.files.length } files to ${ pluginPath }` ); + } +} diff --git a/cli/lib/telex-client.ts b/cli/lib/telex-client.ts new file mode 100644 index 000000000..da14f4ec8 --- /dev/null +++ b/cli/lib/telex-client.ts @@ -0,0 +1,532 @@ +import { EventEmitter } from 'events'; +import { DOMParser } from '@xmldom/xmldom'; +import { TELEX_DEFAULTS } from 'cli/lib/telex-constants'; + +/** + * Artefact file + */ +export interface ArtefactFile { + path: string; // Relative path (e.g., 'src/index.js' or 'build/index.js') + content: string; // File contents + description?: string; // Optional description +} + +/** + * Artefact data + */ +export interface Artefact { + name: string; // Block display name + slug: string; // Machine-readable slug + type: string; // Always 'code-package' + schemaVersion: string; // Schema version (e.g., '2') + files: ArtefactFile[]; // All files in the block +} + +/** + * Block metadata from block.json + */ +export interface BlockMetadata { + title?: string; + name?: string; + description?: string; + category?: string; + supports?: Record< string, unknown >; +} + +/** + * Telex AI generation result + */ +export interface TelexGenerateResult { + artefact: string; // Full artefact XML + epid: string; // Encoded project ID + chatText: string; // AI explanation text +} + +/** + * Options for block generation + */ +export interface TelexGenerateOptions { + onChunk?: ( text: string ) => void; // Called for each chat text chunk + onArtefact?: () => void; // Called when artefact marker detected + onArtefactChunk?: ( xml: string ) => void; // Called for each XML chunk +} + +/** + * Telex API client for AI-powered WordPress block generation. + * + * Uses WordPress.com OAuth tokens for authentication (no separate login needed). + * Compatible with Studio CLI authentication. + */ +export class TelexClient extends EventEmitter { + private apiUrl: string; + private wpcomToken: string; + + /** + * Create a new Telex API client + * + * @param apiUrl - Base URL of Telex API (e.g., 'https://telex.automattic.ai/api') + * @param wpcomToken - WordPress.com OAuth access token + */ + constructor( apiUrl: string, wpcomToken: string ) { + super(); + this.apiUrl = apiUrl.replace( /\/$/, '' ); // Remove trailing slash + this.wpcomToken = wpcomToken; + } + + /** + * Generate a WordPress block from a natural language prompt. + * + * Uses Claude AI to generate a complete block with all files (PHP, JS, CSS, etc.) + * Returns streaming response with real-time chat updates. + * + * @param prompt - Natural language description of the block to generate + * @param options - Optional callbacks for streaming updates + * @returns Promise - Generated artefact and metadata + */ + async generateBlock( + prompt: string, + options: TelexGenerateOptions = {} + ): Promise< TelexGenerateResult > { + const response = await fetch( `${ this.apiUrl }/assistant`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${ this.wpcomToken }`, + }, + body: JSON.stringify( { + prompt, + mode: 'chat', + stream: true, + // Note: No euid/epid needed - Bearer token provides identity + } ), + } ); + + if ( ! response.ok ) { + const errorText = await response.text(); + throw new Error( + `Telex API error (${ response.status }): ${ errorText.slice( 0, 200 ) }` + ); + } + + // Parse Server-Sent Events (SSE) stream + return this.parseStreamResponse( response, options ); + } + + /** + * Update an existing block with additional instructions. + * + * @param epid - Encoded project ID of existing block + * @param prompt - Instructions for updating the block + * @param options - Optional callbacks for streaming updates + * @returns Promise - Updated artefact and metadata + */ + async updateBlock( + epid: string, + prompt: string, + options: TelexGenerateOptions = {} + ): Promise< TelexGenerateResult > { + const response = await fetch( `${ this.apiUrl }/assistant`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${ this.wpcomToken }`, + }, + body: JSON.stringify( { + prompt, + mode: 'chat', + stream: true, + epid, // Provide existing project ID + } ), + } ); + + if ( ! response.ok ) { + const errorText = await response.text(); + throw new Error( + `Telex API error (${ response.status }): ${ errorText.slice( 0, 200 ) }` + ); + } + + return this.parseStreamResponse( response, options ); + } + + /** + * Parse SSE stream response from Telex API + */ + private async parseStreamResponse( + response: Response, + options: TelexGenerateOptions + ): Promise< TelexGenerateResult > { + const reader = response.body?.getReader(); + if ( ! reader ) { + throw new Error( 'Response body is not readable' ); + } + + const decoder = new TextDecoder(); + let buffer = ''; + let chatText = ''; + let artefactXml = ''; + let epid = ''; + let inArtefact = false; + let error: string | null = null; + let currentEvent = ''; + + try { + while ( true ) { + const { done, value } = await reader.read(); + if ( done ) break; + + buffer += decoder.decode( value, { stream: true } ); + const lines = buffer.split( '\n' ); + buffer = lines.pop() || ''; // Keep incomplete line in buffer + + for ( const line of lines ) { + // Track event type from "event:" lines + if ( line.startsWith( 'event: ' ) ) { + currentEvent = line.slice( 7 ).trim(); + continue; + } + + if ( ! line.startsWith( 'data: ' ) ) continue; + + const data = line.slice( 6 ).trim(); + if ( data === '[DONE]' ) continue; + + + try { + const eventData = JSON.parse( data ); + + switch ( currentEvent ) { + case 'chunk': + // Chat text streaming + const chunkContent = eventData.content || ''; + chatText += chunkContent; + options.onChunk?.( chunkContent ); + break; + + case 'new_artefact': + // Artefact marker detected + inArtefact = true; + options.onArtefact?.(); + break; + + case 'artefact_content': + // Artefact XML streaming + const xmlContent = eventData.content || ''; + artefactXml += xmlContent; + options.onArtefactChunk?.( xmlContent ); + break; + + case 'artefact_ready': + // Generation complete + epid = eventData.epid || ''; + break; + + case 'error': + // Error occurred + error = eventData.message || 'Unknown error'; + break; + + case 'retry': + // Retry attempt (informational) + break; + + case 'end': + // End of stream + break; + } + } catch ( e ) { + // Ignore JSON parse errors for malformed events + console.warn( 'Failed to parse SSE event:', data.slice( 0, 100 ) ); + } + } + } + } finally { + reader.releaseLock(); + } + + // Check for errors + if ( error ) { + throw new Error( `Telex generation failed: ${ error }` ); + } + + if ( ! artefactXml ) { + throw new Error( 'No artefact generated - response incomplete' ); + } + + return { + artefact: artefactXml, + epid, + chatText, + }; + } + + /** + * Fetch an existing block's artefact by project ID. + * Fetches BUILT files (compiled JS/CSS) ready for WordPress installation. + * Polls for build completion if block is still building. + * + * @param projectId - Can be either encoded project ID (epid) or public ID + * @param maxRetries - Maximum number of polling attempts (default: 60, ~2 minutes) + * @param onRetry - Optional callback called on each polling retry + * @returns Promise - Parsed artefact data with built files + */ + async fetchBlock( + projectId: string, + maxRetries: number = TELEX_DEFAULTS.BUILD_MAX_RETRIES, + onRetry?: ( attempt: number, maxRetries: number ) => void + ): Promise< Artefact > { + const isEncodedId = projectId.startsWith( TELEX_DEFAULTS.ENCODED_ID_PREFIX ); + + // 1. Wait for build to complete + await this.waitForBuildCompletion( projectId, isEncodedId, maxRetries, onRetry ); + + // 2. Fetch built and source files + const builtFiles = await this.fetchBuiltFiles( projectId, isEncodedId ); + const sourceFiles = await this.fetchSourceFiles( projectId, isEncodedId ); + + // 3. Combine all files + const allFiles = [ ...builtFiles, ...sourceFiles ]; + + // 4. Extract metadata and create artefact + return this.createArtefact( allFiles ); + } + + /** + * Wait for build to complete by polling the build endpoint + * + * @param projectId - Project ID + * @param isEncodedId - Whether ID is encoded or public + * @param maxRetries - Maximum polling attempts + * @param onRetry - Optional callback called on each retry with attempt number + */ + private async waitForBuildCompletion( + projectId: string, + isEncodedId: boolean, + maxRetries: number, + onRetry?: ( attempt: number, maxRetries: number ) => void + ): Promise< void > { + let retries = 0; + + while ( retries < maxRetries ) { + const buildUrl = this.getBuildUrl( projectId, isEncodedId ); + const response = await fetch( buildUrl.toString(), { + method: 'GET', + headers: { + Authorization: `Bearer ${ this.wpcomToken }`, + }, + } ); + + // If 204, build is not ready - wait and retry + if ( response.status === 204 ) { + retries++; + if ( retries >= maxRetries ) { + throw new Error( 'Build timeout: Block is taking too long to build.' ); + } + + // Notify about retry + onRetry?.( retries, maxRetries ); + + await new Promise( ( resolve ) => setTimeout( resolve, TELEX_DEFAULTS.BUILD_POLL_INTERVAL_MS ) ); + continue; + } + + // Check for errors + if ( ! response.ok ) { + const errorText = await response.text(); + throw new Error( + `Telex API error (${ response.status }): ${ errorText.slice( 0, 200 ) }` + ); + } + + // Build is ready + break; + } + } + + /** + * Fetch built files (compiled JS/CSS) from build endpoint + * + * @param projectId - Project ID + * @param isEncodedId - Whether ID is encoded or public + * @returns Array of built files + */ + private async fetchBuiltFiles( projectId: string, isEncodedId: boolean ): Promise< ArtefactFile[] > { + const buildUrl = this.getBuildUrl( projectId, isEncodedId ); + const response = await fetch( buildUrl.toString(), { + method: 'GET', + headers: { + Authorization: `Bearer ${ this.wpcomToken }`, + }, + } ); + + if ( ! response.ok ) { + return []; + } + + const contentType = response.headers.get( 'Content-Type' ); + if ( ! contentType?.includes( 'application/xml' ) ) { + return []; + } + + const xmlText = await response.text(); + return this.parseBuiltFilesXml( xmlText ); + } + + /** + * Parse build XML to extract files + * + * @param xmlText - XML string with build files + * @returns Array of built files + */ + private parseBuiltFilesXml( xmlText: string ): ArtefactFile[] { + const parser = new DOMParser(); + const doc = parser.parseFromString( xmlText, 'text/xml' ); + + // Check for parse errors + const parseError = doc.getElementsByTagName( 'parsererror' ); + if ( parseError.length > 0 ) { + throw new Error( `XML parsing error: ${ parseError[ 0 ].textContent }` ); + } + + const files: ArtefactFile[] = []; + const fileElements = doc.getElementsByTagName( 'file' ); + + for ( let i = 0; i < fileElements.length; i++ ) { + const fileEl = fileElements[ i ]; + const name = fileEl.getAttribute( 'name' ); + const content = fileEl.textContent || ''; + + if ( name ) { + files.push( { + path: `build/${ name }`, + content, + } ); + } + } + + return files; + } + + /** + * Fetch source files (PHP, block.json, etc.) from artefact endpoint + * + * @param projectId - Project ID + * @param isEncodedId - Whether ID is encoded or public + * @returns Array of source files + */ + private async fetchSourceFiles( projectId: string, isEncodedId: boolean ): Promise< ArtefactFile[] > { + const sourceUrl = this.getSourceUrl( projectId, isEncodedId ); + const response = await fetch( sourceUrl.toString(), { + method: 'GET', + headers: { + Authorization: `Bearer ${ this.wpcomToken }`, + }, + } ); + + if ( ! response.ok ) { + return []; + } + + const sourceData = await response.json(); + if ( sourceData.files && Array.isArray( sourceData.files ) ) { + return sourceData.files; + } + + return []; + } + + /** + * Create artefact object with metadata extracted from files + * + * @param files - All block files (built + source) + * @returns Complete artefact object + */ + private createArtefact( files: ArtefactFile[] ): Artefact { + const blockJsonFile = files.find( ( f ) => f.path === 'src/block.json' ); + const packageJsonFile = files.find( ( f ) => f.path === 'package.json' ); + + let name = 'Untitled Block'; + let slug = 'untitled-block'; + + // Extract from package.json + if ( packageJsonFile ) { + try { + const pkg = JSON.parse( packageJsonFile.content ); + name = pkg.name || name; + slug = pkg.name || slug; + } catch ( e ) { + console.warn( 'Warning: Could not parse package.json:', e instanceof Error ? e.message : 'Unknown error' ); + } + } + + // Extract from block.json (takes precedence) + if ( blockJsonFile ) { + try { + const blockMeta = JSON.parse( blockJsonFile.content ); + if ( blockMeta.title ) { + name = blockMeta.title; + } + if ( blockMeta.name ) { + const parts = blockMeta.name.split( '/' ); + const lastPart = parts[ parts.length - 1 ]; + slug = lastPart.startsWith( 'block-' ) ? lastPart.substring( 6 ) : lastPart; + } + } catch ( e ) { + console.warn( 'Warning: Could not parse block.json:', e instanceof Error ? e.message : 'Unknown error' ); + } + } + + return { + name, + slug, + type: 'code-package', + schemaVersion: '2', + files, + }; + } + + /** + * Get build URL for a project + * + * @param projectId - Project ID + * @param isEncodedId - Whether ID is encoded or public + * @returns Build URL + */ + private getBuildUrl( projectId: string, isEncodedId: boolean ): URL { + if ( isEncodedId ) { + const url = new URL( `${ this.apiUrl }/artefact` ); + url.searchParams.set( 'epid', projectId ); + return url; + } else { + return new URL( `${ this.apiUrl }/project/${ projectId }/artefact` ); + } + } + + /** + * Get source URL for a project + * + * @param projectId - Project ID + * @param isEncodedId - Whether ID is encoded or public + * @returns Source URL + */ + private getSourceUrl( projectId: string, isEncodedId: boolean ): URL { + if ( isEncodedId ) { + const url = new URL( `${ this.apiUrl }/artefact` ); + url.searchParams.set( 'epid', projectId ); + url.searchParams.set( 'mode', 'artefact' ); + return url; + } else { + const url = new URL( `${ this.apiUrl }/project/${ projectId }/artefact` ); + url.searchParams.set( 'mode', 'artefact' ); + return url; + } + } + + /** + * Get the full URL for a project in the Telex web UI + */ + getProjectUrl( epid: string ): string { + const webUrl = this.apiUrl.replace( /\/api$/, '' ); + return `${ webUrl }/projects/${ epid }`; + } +} diff --git a/cli/lib/telex-command-utils.ts b/cli/lib/telex-command-utils.ts new file mode 100644 index 000000000..f2760024e --- /dev/null +++ b/cli/lib/telex-command-utils.ts @@ -0,0 +1,111 @@ +import { __, sprintf } from '@wordpress/i18n'; +import { getAuthToken, getSiteByFolder } from 'cli/lib/appdata'; +import ora from 'ora'; +import chalk from 'chalk'; +import { TelexClient, type Artefact } from 'cli/lib/telex-client'; +import { getBlockMetadata } from 'cli/lib/artefact-parser'; +import { + installBlockToSitePath, + getPluginActivationUrl, +} from 'cli/lib/block-installer'; +import { getTelexApiUrl } from 'cli/lib/telex-constants'; + +interface TelexCommandOptions { + // Reserved for future options +} + +/** + * Shared command execution flow for Telex block commands + * + * Handles authentication, site detection, installation, and next steps. + * The only difference between commands is how they fetch the artefact. + * + * @param options - Command options + * @param sitePath - Path to WordPress site + * @param fetchArtefact - Callback to fetch the artefact (differs per command) + */ +export async function runTelexCommand( + options: TelexCommandOptions, + sitePath: string, + fetchArtefact: ( telex: TelexClient, spinner: ora.Ora ) => Promise< Artefact > +): Promise< void > { + const spinner = ora(); + + try { + // 1. Check authentication + spinner.start( __( 'Checking authentication...' ) ); + const authToken = await getAuthToken(); + if ( ! authToken ) { + spinner.fail( __( 'Not authenticated with WordPress.com' ) ); + console.log( + chalk.yellow( '\nPlease run:' ), + chalk.cyan( 'studio auth login' ) + ); + return; + } + spinner.succeed( + sprintf( __( 'Authenticated as %s' ), chalk.cyan( authToken.displayName ) ) + ); + + // 2. Detect current site + spinner.start( __( 'Loading site...' ) ); + const site = await getSiteByFolder( sitePath ); + spinner.succeed( sprintf( __( 'Site: %s' ), chalk.cyan( site.name ) ) ); + + // 3. Initialize Telex client + const telexApiUrl = getTelexApiUrl(); + const telex = new TelexClient( telexApiUrl, authToken.accessToken ); + + // 4. Fetch artefact (command-specific logic via callback) + const artefact = await fetchArtefact( telex, spinner ); + + // 5. Get block metadata + const metadata = getBlockMetadata( artefact ); + + // 6. Show block info + console.log( chalk.bold( '\nBlock Information:' ) ); + console.log( chalk.gray( ' Name: ' ), chalk.white( artefact.name ) ); + console.log( chalk.gray( ' Slug: ' ), chalk.white( artefact.slug ) ); + console.log( + chalk.gray( ' Files: ' ), + chalk.white( artefact.files.length ) + ); + + if ( metadata?.title ) { + console.log( + chalk.gray( ' Title: ' ), + chalk.white( metadata.title ) + ); + } + + // 7. Install to current site + spinner.start( sprintf( __( 'Installing block to %s...' ), site.name ) ); + await installBlockToSitePath( site.path, artefact ); + spinner.succeed( sprintf( __( 'Block installed to %s' ), chalk.cyan( site.name ) ) ); + + // 8. Show next steps + const pluginUrl = getPluginActivationUrl( site.name, artefact.slug ); + + console.log( chalk.bold( '\n📦 Next Steps:' ) ); + console.log( chalk.gray( ' 1.' ), 'Visit', chalk.cyan( pluginUrl ) ); + console.log( + chalk.gray( ' 2.' ), + 'Activate', + chalk.cyan( `"${ artefact.name }"` ) + ); + console.log( + chalk.gray( ' 3.' ), + 'Create a post and add the block' + ); + } catch ( error ) { + if ( spinner.isSpinning ) { + spinner.fail(); + } + + if ( error instanceof Error && error.message !== 'User force closed the prompt' ) { + console.error( chalk.red( '\nCommand failed:' ), error.message ); + } + + process.exit( 1 ); + } +} diff --git a/cli/lib/telex-constants.ts b/cli/lib/telex-constants.ts new file mode 100644 index 000000000..48f963cc6 --- /dev/null +++ b/cli/lib/telex-constants.ts @@ -0,0 +1,29 @@ +/** + * Telex API configuration constants + */ +export const TELEX_DEFAULTS = { + API_URL: 'https://telex.automattic.ai/api', + BUILD_POLL_INTERVAL_MS: 2000, + BUILD_MAX_RETRIES: 60, // ~2 minutes + ENCODED_ID_PREFIX: 'v1.', +} as const; + +/** + * Get Telex API URL from environment variable or default + * + * Set STUDIO_TELEX_URL environment variable to use a custom Telex deployment + * (useful for development or testing against staging environments) + * + * @returns Resolved Telex API URL + */ +export function getTelexApiUrl(): string { + return process.env.STUDIO_TELEX_URL || TELEX_DEFAULTS.API_URL; +} + +/** + * Studio site path conventions + */ +export const STUDIO_PATHS = { + SITES_DIR: 'Studio/sites', + DOMAIN_SUFFIX: '.local', +} as const; diff --git a/cli/package-lock.json b/cli/package-lock.json index 2aa0fe160..3d672fba9 100644 --- a/cli/package-lock.json +++ b/cli/package-lock.json @@ -14,6 +14,7 @@ "@wp-playground/cli": "^3.0.22", "@wp-playground/common": "^3.0.22", "@wp-playground/storage": "^3.0.22", + "@xmldom/xmldom": "^0.9.5", "http-proxy": "^1.18.1", "pm2": "^6.0.13", "trash": "^10.0.1" @@ -1539,6 +1540,15 @@ } } }, + "node_modules/@xmldom/xmldom": { + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.9.8.tgz", + "integrity": "sha512-p96FSY54r+WJ50FIOsCOjyj/wavs8921hG5+kVMmZgKcvIKxMXHTrjNJvRgWa/zuX3B6t2lijLNFaOyuxUH+2A==", + "license": "MIT", + "engines": { + "node": ">=14.6" + } + }, "node_modules/@zip.js/zip.js": { "version": "2.7.57", "resolved": "https://registry.npmjs.org/@zip.js/zip.js/-/zip.js-2.7.57.tgz", diff --git a/cli/package.json b/cli/package.json index e81185477..8f5daa01c 100644 --- a/cli/package.json +++ b/cli/package.json @@ -12,6 +12,7 @@ "@wp-playground/cli": "^3.0.22", "@wp-playground/common": "^3.0.22", "@wp-playground/storage": "^3.0.22", + "@xmldom/xmldom": "^0.9.5", "http-proxy": "^1.18.1", "pm2": "^6.0.13", "trash": "^10.0.1" diff --git a/package-lock.json b/package-lock.json index 70146c823..898854fc5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -119,6 +119,7 @@ "jest-watch-typeahead": "^3.0.1", "nock": "^13.5.6", "patch-package": "^8.0.0", + "pm2": "^6.0.14", "postcss": "^8.4.32", "prettier": "npm:wp-prettier@3.0.3", "react": "^18.2.0", @@ -8301,6 +8302,334 @@ "node": ">=18" } }, + "node_modules/@pm2/agent": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@pm2/agent/-/agent-2.1.1.tgz", + "integrity": "sha512-0V9ckHWd/HSC8BgAbZSoq8KXUG81X97nSkAxmhKDhmF8vanyaoc1YXwc2KVkbWz82Rg4gjd2n9qiT3i7bdvGrQ==", + "dev": true, + "license": "AGPL-3.0", + "dependencies": { + "async": "~3.2.0", + "chalk": "~3.0.0", + "dayjs": "~1.8.24", + "debug": "~4.3.1", + "eventemitter2": "~5.0.1", + "fast-json-patch": "^3.1.0", + "fclone": "~1.0.11", + "pm2-axon": "~4.0.1", + "pm2-axon-rpc": "~0.7.0", + "proxy-agent": "~6.4.0", + "semver": "~7.5.0", + "ws": "~7.5.10" + } + }, + "node_modules/@pm2/agent/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@pm2/agent/node_modules/dayjs": { + "version": "1.8.36", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.8.36.tgz", + "integrity": "sha512-3VmRXEtw7RZKAf+4Tv1Ym9AGeo8r8+CjDi26x+7SYQil1UqtqdaokhzoEJohqlzt0m5kacJSDhJQkG/LWhpRBw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@pm2/agent/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@pm2/agent/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@pm2/agent/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@pm2/agent/node_modules/ws": { + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/@pm2/agent/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/@pm2/blessed": { + "version": "0.1.81", + "resolved": "https://registry.npmjs.org/@pm2/blessed/-/blessed-0.1.81.tgz", + "integrity": "sha512-ZcNHqQjMuNRcQ7Z1zJbFIQZO/BDKV3KbiTckWdfbUaYhj7uNmUwb+FbdDWSCkvxNr9dBJQwvV17o6QBkAvgO0g==", + "dev": true, + "license": "MIT", + "bin": { + "blessed": "bin/tput.js" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/@pm2/io": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@pm2/io/-/io-6.1.0.tgz", + "integrity": "sha512-IxHuYURa3+FQ6BKePlgChZkqABUKFYH6Bwbw7V/pWU1pP6iR1sCI26l7P9ThUEB385ruZn/tZS3CXDUF5IA1NQ==", + "dev": true, + "license": "Apache-2", + "dependencies": { + "async": "~2.6.1", + "debug": "~4.3.1", + "eventemitter2": "^6.3.1", + "require-in-the-middle": "^5.0.0", + "semver": "~7.5.4", + "shimmer": "^1.2.0", + "signal-exit": "^3.0.3", + "tslib": "1.9.3" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/@pm2/io/node_modules/async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash": "^4.17.14" + } + }, + "node_modules/@pm2/io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@pm2/io/node_modules/eventemitter2": { + "version": "6.4.9", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.9.tgz", + "integrity": "sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@pm2/io/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@pm2/io/node_modules/require-in-the-middle": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-5.2.0.tgz", + "integrity": "sha512-efCx3b+0Z69/LGJmm9Yvi4cqEdxnoGnxYxGxBghkkTTFeXRtTCmmhO0AnAfHz59k957uTSuy8WaHqOs8wbYUWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "module-details-from-path": "^1.0.3", + "resolve": "^1.22.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@pm2/io/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@pm2/io/node_modules/tslib": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", + "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@pm2/io/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/@pm2/js-api": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@pm2/js-api/-/js-api-0.8.0.tgz", + "integrity": "sha512-nmWzrA/BQZik3VBz+npRcNIu01kdBhWL0mxKmP1ciF/gTcujPTQqt027N9fc1pK9ERM8RipFhymw7RcmCyOEYA==", + "dev": true, + "license": "Apache-2", + "dependencies": { + "async": "^2.6.3", + "debug": "~4.3.1", + "eventemitter2": "^6.3.1", + "extrareqp2": "^1.0.0", + "ws": "^7.0.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/@pm2/js-api/node_modules/async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash": "^4.17.14" + } + }, + "node_modules/@pm2/js-api/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@pm2/js-api/node_modules/eventemitter2": { + "version": "6.4.9", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.9.tgz", + "integrity": "sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@pm2/js-api/node_modules/ws": { + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/@pm2/pm2-version-check": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@pm2/pm2-version-check/-/pm2-version-check-1.0.4.tgz", + "integrity": "sha512-SXsM27SGH3yTWKc2fKR4SYNxsmnvuBQ9dd6QHtEWmiZ/VqaOYPAIlS8+vMcn27YLtAEBGvNRSh3TPNvtjZgfqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.1" + } + }, "node_modules/@prisma/instrumentation": { "version": "6.11.1", "resolved": "https://registry.npmjs.org/@prisma/instrumentation/-/instrumentation-6.11.1.tgz", @@ -9855,6 +10184,13 @@ "node": ">= 10" } }, + "node_modules/@tootallnate/quickjs-emscripten": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", + "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", + "dev": true, + "license": "MIT" + }, "node_modules/@tsconfig/node10": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", @@ -12425,6 +12761,33 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true }, + "node_modules/amp": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/amp/-/amp-0.3.1.tgz", + "integrity": "sha512-OwIuC4yZaRogHKiuU5WlMR5Xk/jAcpPtawWL05Gj8Lvm2F6mwoJt4O/bHI+DHwG79vWd+8OFYM4/BzYqyRd3qw==", + "dev": true, + "license": "MIT" + }, + "node_modules/amp-message": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/amp-message/-/amp-message-0.1.2.tgz", + "integrity": "sha512-JqutcFwoU1+jhv7ArgW38bqrE+LQdcRv4NxNw0mp0JHQyB6tXesWRjtYKlDgHRY2o3JE5UTaBGUK8kSWUdxWUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "amp": "0.3.1" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -12474,6 +12837,16 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/ansis": { + "version": "4.0.0-node10", + "resolved": "https://registry.npmjs.org/ansis/-/ansis-4.0.0-node10.tgz", + "integrity": "sha512-BRrU0Bo1X9dFGw6KgGz6hWrqQuOlVEDOzkb0QSLZY9sXHqA7pNj7yHPVJRz7y/rj4EOJ3d/D5uxH+ee9leYgsg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, "node_modules/any-promise": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", @@ -12789,6 +13162,19 @@ "node": ">= 6" } }, + "node_modules/ast-types": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", + "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/async": { "version": "3.2.5", "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", @@ -13071,6 +13457,16 @@ "baseline-browser-mapping": "dist/cli.js" } }, + "node_modules/basic-ftp": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz", + "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/before-after-hook": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", @@ -13103,6 +13499,13 @@ "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", "license": "MIT" }, + "node_modules/bodec": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/bodec/-/bodec-0.1.0.tgz", + "integrity": "sha512-Ylo+MAo5BDUq1KA3f3R/MFhh+g8cnHmo8bz3YPGhI1znrMaf77ol1sfvYJzsw3nTE+Y2GryfDxBaR+AqpAkEHQ==", + "dev": true, + "license": "MIT" + }, "node_modules/body-parser": { "version": "1.20.3", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", @@ -13698,6 +14101,13 @@ "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" }, + "node_modules/charm": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/charm/-/charm-0.1.2.tgz", + "integrity": "sha512-syedaZ9cPe7r3hoQA9twWYKu5AIyCswN5+szkmPBe9ccdLrj4bYaCnLVPTLd2kgVRc7+zoX4tyPgRnFKCj5YjQ==", + "dev": true, + "license": "MIT/X11" + }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -13839,7 +14249,33 @@ "@colors/colors": "1.5.0" } }, - "node_modules/cli-truncate": { + "node_modules/cli-tableau": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/cli-tableau/-/cli-tableau-2.0.1.tgz", + "integrity": "sha512-he+WTicka9cl0Fg/y+YyxcN6/bfQ/1O3QmgxRXDhABKqLzvoOSM4fMzp39uMyLBulAFuywD2N7UaoQE7WaADxQ==", + "dev": true, + "dependencies": { + "chalk": "3.0.0" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/cli-tableau/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-truncate": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz", "integrity": "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==", @@ -14321,6 +14757,13 @@ "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" }, + "node_modules/croner": { + "version": "4.1.97", + "resolved": "https://registry.npmjs.org/croner/-/croner-4.1.97.tgz", + "integrity": "sha512-/f6gpQuxDaqXu+1kwQYSckUglPaOrHdbIlBAu0YuW8/Cdb45XwXYNUBXg3r/9Mo6n540Kn/smKcZWko5x99KrQ==", + "dev": true, + "license": "MIT" + }, "node_modules/cross-dirname": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/cross-dirname/-/cross-dirname-0.1.0.tgz", @@ -14430,12 +14873,29 @@ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", "license": "MIT" }, + "node_modules/culvert": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/culvert/-/culvert-0.1.2.tgz", + "integrity": "sha512-yi1x3EAWKjQTreYWeSd98431AV+IEE0qoDyOoaHJ7KJ21gv6HtBXHVLX74opVSGqcR8/AbjJBHAHpcOy2bj5Gg==", + "dev": true, + "license": "MIT" + }, "node_modules/custom-error-instance": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/custom-error-instance/-/custom-error-instance-2.1.1.tgz", "integrity": "sha512-p6JFxJc3M4OTD2li2qaHkDCw9SfMw82Ldr6OC9Je1aXiGfhx2W8p3GaoeaGrPJTUN9NirTM/KTxHWMUdR1rsUg==", "license": "ISC" }, + "node_modules/data-uri-to-buffer": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", + "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, "node_modules/data-urls": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", @@ -14519,6 +14979,13 @@ "integrity": "sha512-hTIP/z+t+qKwBDcmmsnmjWTduxCg+5KfdqWQvb2X/8C9+knYY6epN/pfxdDuyVlSVeFz0sM5eEfwIUQ70U4ckg==", "license": "MIT" }, + "node_modules/dayjs": { + "version": "1.11.15", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.15.tgz", + "integrity": "sha512-MC+DfnSWiM9APs7fpiurHGCoeIx0Gdl6QZBy+5lu8MbYKN5FZEXqOgrundfibdfhGZ15o9hzmZ2xJjZnbvgKXQ==", + "dev": true, + "license": "MIT" + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -14666,6 +15133,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/degenerator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", + "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ast-types": "^0.13.4", + "escodegen": "^2.1.0", + "esprima": "^4.0.1" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -15552,6 +16034,19 @@ "node": ">=10.13.0" } }, + "node_modules/enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "^4.1.1" + }, + "engines": { + "node": ">=8.6" + } + }, "node_modules/entities": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", @@ -15833,6 +16328,38 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/escodegen/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, "node_modules/eslint": { "version": "9.39.2", "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz", @@ -16373,6 +16900,13 @@ "node": ">= 0.6" } }, + "node_modules/eventemitter2": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-5.0.1.tgz", + "integrity": "sha512-5EM1GHXycJBS6mauYAbVKT1cVs7POKWb2NXD4Vyt8dDqeZa7LaDK1/sjtL+Zb0lzTpSNil4596Dyu97hz37QLg==", + "dev": true, + "license": "MIT" + }, "node_modules/eventemitter3": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", @@ -16711,6 +17245,16 @@ "fd-slicer": "~1.1.0" } }, + "node_modules/extrareqp2": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/extrareqp2/-/extrareqp2-1.0.0.tgz", + "integrity": "sha512-Gum0g1QYb6wpPJCVypWP3bbIuaibcFiJcpuPM10YSXp/tzqi84x9PJageob+eN4xVRIOto4wjSGNLyMD54D2xA==", + "dev": true, + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.14.0" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -16756,6 +17300,13 @@ "node": ">= 6" } }, + "node_modules/fast-json-patch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-3.1.1.tgz", + "integrity": "sha512-vf6IHUX2SBcA+5/+4883dsIjpBTqmfBjmYiWK1savxQmFk4JfBMLa7ynTYOs1Rolp/T1betJxHiGD3g1Mn8lUQ==", + "dev": true, + "license": "MIT" + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -16827,6 +17378,13 @@ "bser": "2.1.1" } }, + "node_modules/fclone": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/fclone/-/fclone-1.0.11.tgz", + "integrity": "sha512-GDqVQezKzRABdeqflsgMr7ktzgF9CyS+p2oe0jJqUY6izSSbhPIQJDpoU4PtGcD7VPM9xh/dVrTu6z1nwgmEGw==", + "dev": true, + "license": "MIT" + }, "node_modules/fd-slicer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", @@ -17490,6 +18048,21 @@ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, + "node_modules/get-uri": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.5.tgz", + "integrity": "sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "basic-ftp": "^5.0.2", + "data-uri-to-buffer": "^6.0.2", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/gettext-parser": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/gettext-parser/-/gettext-parser-1.4.0.tgz", @@ -17499,6 +18072,20 @@ "safe-buffer": "^5.1.1" } }, + "node_modules/git-node-fs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/git-node-fs/-/git-node-fs-1.0.0.tgz", + "integrity": "sha512-bLQypt14llVXBg0S0u8q8HmU7g9p3ysH+NvVlae5vILuUvs759665HvmR5+wb04KjHyjFcDRxdYb4kyNnluMUQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/git-sha1": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/git-sha1/-/git-sha1-0.1.2.tgz", + "integrity": "sha512-2e/nZezdVlyCopOCYHeW0onkbZg7xP1Ad6pndPy1rCygeRykefUS6r7oA5cJRGEFvseiaz5a/qUHFVX1dd6Isg==", + "dev": true, + "license": "MIT" + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -20698,16 +21285,37 @@ "integrity": "sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==", "license": "BSD-3-Clause" }, + "node_modules/js-git": { + "version": "0.7.8", + "resolved": "https://registry.npmjs.org/js-git/-/js-git-0.7.8.tgz", + "integrity": "sha512-+E5ZH/HeRnoc/LW0AmAyhU+mNcWBzAKE+30+IDMLSLbbK+Tdt02AdkOKq9u15rlJsDEGFqtgckc8ZM59LhhiUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bodec": "^0.1.0", + "culvert": "^0.1.2", + "git-sha1": "^0.1.2", + "pako": "^0.2.5" + } + }, + "node_modules/js-git/node_modules/pako": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", + "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==", + "dev": true, + "license": "MIT" + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, + "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -22972,6 +23580,34 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node_modules/needle": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/needle/-/needle-2.4.0.tgz", + "integrity": "sha512-4Hnwzr3mi5L97hMYeNl8wRW/Onhy4nUKR/lVemJ8gJedxxUyBLm9kkrDColJvoSfwi0jCNhD+xCdOtiGDQiRZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.6", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + }, + "bin": { + "needle": "bin/needle" + }, + "engines": { + "node": ">= 4.4.x" + } + }, + "node_modules/needle/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -22986,6 +23622,16 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, + "node_modules/netmask": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", + "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", @@ -23601,6 +24247,93 @@ "node": ">=6" } }, + "node_modules/pac-proxy-agent": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz", + "integrity": "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tootallnate/quickjs-emscripten": "^0.23.0", + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "get-uri": "^6.0.1", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.6", + "pac-resolver": "^7.0.1", + "socks-proxy-agent": "^8.0.5" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-proxy-agent/node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-proxy-agent/node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-proxy-agent/node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-proxy-agent/node_modules/socks-proxy-agent": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", + "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-resolver": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", + "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", + "dev": true, + "license": "MIT", + "dependencies": { + "degenerator": "^5.0.0", + "netmask": "^2.0.2" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/package-json-from-dist": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", @@ -23960,6 +24693,40 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pidusage": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pidusage/-/pidusage-3.0.2.tgz", + "integrity": "sha512-g0VU+y08pKw5M8EZ2rIGiEBaB8wrQMjYGFfW2QVIfyT8V+fq8YFLkvlz4bz5ljvFDJYNFCWT3PWqcRr2FKO81w==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/pidusage/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/pify": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", @@ -24104,6 +24871,194 @@ "node": ">=10.4.0" } }, + "node_modules/pm2": { + "version": "6.0.14", + "resolved": "https://registry.npmjs.org/pm2/-/pm2-6.0.14.tgz", + "integrity": "sha512-wX1FiFkzuT2H/UUEA8QNXDAA9MMHDsK/3UHj6Dkd5U7kxyigKDA5gyDw78ycTQZAuGCLWyUX5FiXEuVQWafukA==", + "dev": true, + "license": "AGPL-3.0", + "dependencies": { + "@pm2/agent": "~2.1.1", + "@pm2/blessed": "0.1.81", + "@pm2/io": "~6.1.0", + "@pm2/js-api": "~0.8.0", + "@pm2/pm2-version-check": "^1.0.4", + "ansis": "4.0.0-node10", + "async": "3.2.6", + "chokidar": "3.6.0", + "cli-tableau": "2.0.1", + "commander": "2.15.1", + "croner": "4.1.97", + "dayjs": "1.11.15", + "debug": "4.4.3", + "enquirer": "2.3.6", + "eventemitter2": "5.0.1", + "fclone": "1.0.11", + "js-yaml": "4.1.1", + "mkdirp": "1.0.4", + "needle": "2.4.0", + "pidusage": "3.0.2", + "pm2-axon": "~4.0.1", + "pm2-axon-rpc": "~0.7.1", + "pm2-deploy": "~1.0.2", + "pm2-multimeter": "^0.1.2", + "promptly": "2.2.0", + "semver": "7.7.2", + "source-map-support": "0.5.21", + "sprintf-js": "1.1.2", + "vizion": "~2.2.1" + }, + "bin": { + "pm2": "bin/pm2", + "pm2-dev": "bin/pm2-dev", + "pm2-docker": "bin/pm2-docker", + "pm2-runtime": "bin/pm2-runtime" + }, + "engines": { + "node": ">=16.0.0" + }, + "optionalDependencies": { + "pm2-sysmonit": "^1.2.8" + } + }, + "node_modules/pm2-axon": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pm2-axon/-/pm2-axon-4.0.1.tgz", + "integrity": "sha512-kES/PeSLS8orT8dR5jMlNl+Yu4Ty3nbvZRmaAtROuVm9nYYGiaoXqqKQqQYzWQzMYWUKHMQTvBlirjE5GIIxqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "amp": "~0.3.1", + "amp-message": "~0.1.1", + "debug": "^4.3.1", + "escape-string-regexp": "^4.0.0" + }, + "engines": { + "node": ">=5" + } + }, + "node_modules/pm2-axon-rpc": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/pm2-axon-rpc/-/pm2-axon-rpc-0.7.1.tgz", + "integrity": "sha512-FbLvW60w+vEyvMjP/xom2UPhUN/2bVpdtLfKJeYM3gwzYhoTEEChCOICfFzxkxuoEleOlnpjie+n1nue91bDQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.1" + }, + "engines": { + "node": ">=5" + } + }, + "node_modules/pm2-deploy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pm2-deploy/-/pm2-deploy-1.0.2.tgz", + "integrity": "sha512-YJx6RXKrVrWaphEYf++EdOOx9EH18vM8RSZN/P1Y+NokTKqYAca/ejXwVLyiEpNju4HPZEk3Y2uZouwMqUlcgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "run-series": "^1.1.8", + "tv4": "^1.3.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pm2-multimeter": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/pm2-multimeter/-/pm2-multimeter-0.1.2.tgz", + "integrity": "sha512-S+wT6XfyKfd7SJIBqRgOctGxaBzUOmVQzTAS+cg04TsEUObJVreha7lvCfX8zzGVr871XwCSnHUU7DQQ5xEsfA==", + "dev": true, + "license": "MIT/X11", + "dependencies": { + "charm": "~0.1.1" + } + }, + "node_modules/pm2-sysmonit": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/pm2-sysmonit/-/pm2-sysmonit-1.2.8.tgz", + "integrity": "sha512-ACOhlONEXdCTVwKieBIQLSi2tQZ8eKinhcr9JpZSUAL8Qy0ajIgRtsLxG/lwPOW3JEKqPyw/UaHmTWhUzpP4kA==", + "dev": true, + "license": "Apache", + "optional": true, + "dependencies": { + "async": "^3.2.0", + "debug": "^4.3.1", + "pidusage": "^2.0.21", + "systeminformation": "^5.7", + "tx2": "~1.0.4" + } + }, + "node_modules/pm2-sysmonit/node_modules/pidusage": { + "version": "2.0.21", + "resolved": "https://registry.npmjs.org/pidusage/-/pidusage-2.0.21.tgz", + "integrity": "sha512-cv3xAQos+pugVX+BfXpHsbyz/dLzX+lr44zNMsYiGxUw+kV5sgQCIcLd1z+0vq+KyC7dJ+/ts2PsfgWfSC3WXA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pm2-sysmonit/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "optional": true + }, + "node_modules/pm2/node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" + }, + "node_modules/pm2/node_modules/commander": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/pm2/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/pm2/node_modules/sprintf-js": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", + "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/possible-typed-array-names": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", @@ -24440,6 +25395,16 @@ "node": ">=10" } }, + "node_modules/promptly": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/promptly/-/promptly-2.2.0.tgz", + "integrity": "sha512-aC9j+BZsRSSzEsXBNBwDnAxujdx19HycZoKgRgzWnS8eOHg1asuf9heuLprfbe739zY3IdUQx+Egv6Jn135WHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "read": "^1.0.4" + } + }, "node_modules/propagate": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", @@ -24492,6 +25457,89 @@ "node": ">= 0.10" } }, + "node_modules/proxy-agent": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.4.0.tgz", + "integrity": "sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "http-proxy-agent": "^7.0.1", + "https-proxy-agent": "^7.0.3", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "^7.0.1", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^8.0.2" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/proxy-agent/node_modules/socks-proxy-agent": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", + "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", @@ -24776,6 +25824,19 @@ "node": ">=0.10.0" } }, + "node_modules/read": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", + "integrity": "sha512-rSOKNYUmaxy0om1BNjMN4ezNT6VKK+2xF4GBhc81mkH7L60i6dp8qPYrkndNLT3QPphoII3maL9PVC9XmhHwVQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "mute-stream": "~0.0.4" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/read-binary-file-arch": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/read-binary-file-arch/-/read-binary-file-arch-1.0.6.tgz", @@ -25519,6 +26580,27 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/run-series": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/run-series/-/run-series-1.1.9.tgz", + "integrity": "sha512-Arc4hUN896vjkqCYrUXquBFtRZdv1PfLbTYP71efP6butxyQ0kWpiNJyAgsxscmQg1cqvHY32/UCBzXedTpU2g==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/rungen": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/rungen/-/rungen-0.3.2.tgz", @@ -26854,6 +27936,34 @@ "url": "https://opencollective.com/synckit" } }, + "node_modules/systeminformation": { + "version": "5.27.16", + "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.27.16.tgz", + "integrity": "sha512-aimHO/bE7QFtu3uB3vtpwn7V2DXXGX7NyTY7V1g+hPa7in2k10Bp3AL+Enmg3X71n7HbgLfwy/bbf+2cBSKURQ==", + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin", + "linux", + "win32", + "freebsd", + "openbsd", + "netbsd", + "sunos", + "android" + ], + "bin": { + "systeminformation": "lib/cli.js" + }, + "engines": { + "node": ">=8.0.0" + }, + "funding": { + "type": "Buy me a coffee", + "url": "https://www.buymeacoffee.com/systeminfo" + } + }, "node_modules/tailwindcss": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.1.tgz", @@ -27656,6 +28766,36 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/tv4": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/tv4/-/tv4-1.3.0.tgz", + "integrity": "sha512-afizzfpJgvPr+eDkREK4MxJ/+r8nEEHcmitwgnPUqpaP+FpwQyadnxNoSACbgc/b1LsZYtODGoPiFxQrgJgjvw==", + "dev": true, + "license": [ + { + "type": "Public Domain", + "url": "http://geraintluff.github.io/tv4/LICENSE.txt" + }, + { + "type": "MIT", + "url": "http://jsonary.com/LICENSE.txt" + } + ], + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/tx2": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tx2/-/tx2-1.0.5.tgz", + "integrity": "sha512-sJ24w0y03Md/bxzK4FU8J8JveYYUbSs2FViLJ2D/8bytSiyPRbuE3DyL/9UKYXTZlV3yXq0L8GLlhobTnekCVg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "json-stringify-safe": "^5.0.1" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -29009,6 +30149,39 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/vizion": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/vizion/-/vizion-2.2.1.tgz", + "integrity": "sha512-sfAcO2yeSU0CSPFI/DmZp3FsFE9T+8913nv1xWBOyzODv13fwkn6Vl7HqxGpkr9F608M+8SuFId3s+BlZqfXww==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "async": "^2.6.3", + "git-node-fs": "^1.0.0", + "ini": "^1.3.5", + "js-git": "^0.7.8" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/vizion/node_modules/async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash": "^4.17.14" + } + }, + "node_modules/vizion/node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC" + }, "node_modules/w3c-xmlserializer": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", diff --git a/package.json b/package.json index 3a9a28cbd..502fe6c2d 100644 --- a/package.json +++ b/package.json @@ -89,6 +89,7 @@ "jest-watch-typeahead": "^3.0.1", "nock": "^13.5.6", "patch-package": "^8.0.0", + "pm2": "^6.0.14", "postcss": "^8.4.32", "prettier": "npm:wp-prettier@3.0.3", "react": "^18.2.0",