From 4ae82749c7695b6479217701d9d0d637ab6c3592 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Figueiredo?= Date: Sun, 14 Sep 2025 22:58:24 +0100 Subject: [PATCH] Add CLI command to run UXP functions --- .gitignore | 3 + cli/.gitignore | 1 + cli/run.js | 189 ++++++++++++++++++++++++++++++++++++++++++++++ package-lock.json | 115 +++++++++++++++++++++++++++- package.json | 7 ++ 5 files changed, 311 insertions(+), 4 deletions(-) create mode 100644 cli/.gitignore create mode 100644 cli/run.js create mode 100644 package.json diff --git a/.gitignore b/.gitignore index fdba4f3..0ee30f7 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,9 @@ Icon # Thumbnails ._* +# Development +node_modules/ + # Files that might appear in the root of a volume .DocumentRevisions-V100 .fseventsd diff --git a/cli/.gitignore b/cli/.gitignore new file mode 100644 index 0000000..8fce603 --- /dev/null +++ b/cli/.gitignore @@ -0,0 +1 @@ +data/ diff --git a/cli/run.js b/cli/run.js new file mode 100644 index 0000000..0745bcb --- /dev/null +++ b/cli/run.js @@ -0,0 +1,189 @@ +#!/usr/bin/env node + +const fs = require('fs'); + +const { io } = require('socket.io-client'); + +// CLI tool that connects to the running proxy server +class ProxyCLI { + constructor() { + this.socket = null; + this.serverUrl = 'http://localhost:3001'; + } + + async connect() { + return new Promise((resolve, reject) => { + console.log(`Connecting to proxy server at ${this.serverUrl}...`); + + this.socket = io(this.serverUrl, { + transports: ['websocket'], + timeout: 5000 + }); + + this.socket.on('connect', () => { + console.log('Connected to proxy server'); + resolve(); + }); + + this.socket.on('connect_error', (error) => { + console.error('Connection failed:', error.message); + reject(error); + }); + }); + } + + async sendCommand(application, command) { + if (!this.socket || !this.socket.connected) { + throw new Error('Not connected to proxy server'); + } + + return new Promise((resolve, reject) => { + console.log(`Sending command to ${application}:`, command); + + const responseHandler = (packet) => { + console.log('Command executed successfully'); + resolve(packet); + }; + + const timeoutId = setTimeout(() => { + this.socket.off('packet_response', responseHandler); + reject(new Error('Command timeout - no response received')); + }, 10000); + + this.socket.once('packet_response', (packet) => { + clearTimeout(timeoutId); + responseHandler(packet); + }); + + this.socket.emit('command_packet', { application, command }); + }); + } + + disconnect() { + if (this.socket) { + this.socket.disconnect(); + this.socket = null; + } + } +} + +function parseArgs() { + const args = process.argv.slice(2); + const ret = { + application: null, + command: {}, + help: false + }; + + function getOptionsFromFile(filePath) { + if (fs.existsSync(filePath)) { + try { + ret.options = JSON.parse(fs.readFileSync(filePath, 'utf8')); + } catch (e) { + console.error('Error parsing options:', e); + process.exit(1); + } + } else { + console.error('Options file does not exist:', filePath); + process.exit(1); + } + return ret.options; + } + + for (let i = 0; i < args.length; i++) { + const arg = args[i]; + + switch (arg) { + case '-a': + case '--application': + ret.application = args[++i]; + break; + case '-x': + case '--action': + ret.command.action = args[++i]; + break; + case '-o': + case '--options': + ret.command.options = getOptionsFromFile(args[++i]); + break; + case '-h': + case '--help': + ret.help = true; + break; + default: + if (!ret.application) { + ret.application = arg; + } else if (!ret.command.action) { + ret.command.action = arg; + } else if (!ret.command.options) { + ret.command.options = getOptionsFromFile(arg); + } + break; + } + } + + return ret; +} + +function showHelp() { + console.log(` +📡 ADB-MCP Proxy CLI Tool + +Usage: node run.js [application] [action] [options] + +Options: + -a, --application Target application (photoshop, illustrator, etc.) + -x, --action Name of the action to execute + -o, --options Path to JSON options file to pass to action + -h, --help Show this help message + +Examples: + node run.js -a photoshop -x getActiveDocument -o options.json + +Positional arguments: + application Target application name + command Name of the action to execute + options Path to JSON options file to pass to action +`); +} + +async function runProxyCLI() { + const options = parseArgs(); + + if (options.help) { + showHelp(); + return; + } + + if (!options.application || !options.command) { + console.error('Both application and command are required. Use --help for usage information.'); + process.exit(1); + } + + const cli = new ProxyCLI(); + + try { + await cli.connect(); + + let command; + try { + command = JSON.parse(options.command); + } catch { + command = options.command; + } + + const response = await cli.sendCommand(options.application, command); + console.log('Command completed successfully'); + console.log('Response:', JSON.stringify(response, null, 2)); + + } catch (error) { + console.error('Error:', error.message); + process.exit(1); + } finally { + cli.disconnect(); + } +} + +if (require.main === module) { + runProxyCLI(); +} diff --git a/package-lock.json b/package-lock.json index a609efc..6fdd97b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,113 @@ { - "name": "adb-mcp", - "lockfileVersion": 3, - "requires": true, - "packages": {} + "name": "adb-mcp", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "adb-mcp", + "dependencies": { + "socket.io-client": "^4.8.1" + } + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==" + }, + "node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/engine.io-client": { + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.3.tgz", + "integrity": "sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1", + "xmlhttprequest-ssl": "~2.1.1" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/socket.io-client": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz", + "integrity": "sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.6.1", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xmlhttprequest-ssl": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz", + "integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==", + "engines": { + "node": ">=0.4.0" + } + } + } } diff --git a/package.json b/package.json new file mode 100644 index 0000000..75132e4 --- /dev/null +++ b/package.json @@ -0,0 +1,7 @@ +{ + "name": "adb-mcp", + "dependencies": { + "socket.io-client": "^4.8.1" + } + } + \ No newline at end of file