#!/usr/bin/env node /** * Node.js Nushell Plugin Example * Communicates with Nushell via JSON-encoded messages over stdin/stdout * * Register command: `plugin add -s 'C:\Program Files\nodejs\node.exe' ./nu_plugin_node_example.js` * `plugin use ./nu_plugin_node_example.js` * Usage: node_example 2 "3" */ // Configuration constants const NUSHELL_VERSION = '0.103.1'; const PLUGIN_VERSION = '0.1.1'; // Core protocol functions /** * Writes a structured response to stdout * @param {number} id - Call identifier * @param {object} response - Response payload */ function writeResponse (id, response) { const message = JSON.stringify({ CallResponse: [id, response] }); process.stdout.write(`${message}\n`); } /** * Writes an error response * @param {number} id - Call identifier * @param {string} text - Error description * @param {object|null} span - Error location metadata */ function writeError (id, text, span = null) { const error = span ? { Error: { msg: 'Plugin execution error', labels: [{ text, span }] } } : { Error: { msg: 'Plugin configuration error', help: text } }; writeResponse(id, error); } // Plugin capability definitions /** * Generates plugin signature metadata * @returns {object} Structured plugin capabilities */ function getPluginSignature () { return { Signature: [{ sig: { name: 'node_example', description: 'Demonstration plugin for Node.js', extra_description: '', required_positional: [ { name: 'a', desc: 'Required integer parameter', shape: 'Int' }, { name: 'b', desc: 'Required string parameter', shape: 'String' } ], optional_positional: [{ name: 'opt', desc: 'Optional numeric parameter', shape: 'Int' }], rest_positional: { name: 'rest', desc: 'Variable-length string parameters', shape: 'String' }, named: [ { long: 'help', short: 'h', arg: null, required: false, desc: 'Display help information' }, { long: 'flag', short: 'f', arg: null, required: false, desc: 'Example boolean flag' }, { long: 'named', short: 'n', arg: 'String', required: false, desc: 'Example named parameter' } ], input_output_types: [['Any', 'Any']], allow_variants_without_examples: true, search_terms: ['nodejs', 'example'], is_filter: false, creates_scope: false, allows_unknown_args: false, category: 'Experimental' }, examples: [] }] }; } /** * Processes execution calls from Nushell * @param {number} id - Call identifier * @param {object} callData - Execution context metadata */ function processExecutionCall (id, callData) { const span = callData.call.head; // Generate sample tabular data const tableData = Array.from({ length: 10 }, (_, index) => ({ Record: { val: { one: { Int: { val: index * 1, span } }, two: { Int: { val: index * 2, span } }, three: { Int: { val: index * 3, span } } }, span } })); writeResponse(id, { PipelineData: { Value: [{ List: { vals: tableData, span } }, null] } }); } // Protocol handling /** * Handles different types of input messages * @param {object} input - Parsed JSON message from Nushell */ function handleInputMessage (input) { if (input.Hello) { handleHelloMessage(input.Hello); } else if (input === 'Goodbye') { process.exit(0); } else if (input.Call) { handleCallMessage(...input.Call); } else if (input.Signal) { handleSignal(input.Signal); } } function handleHelloMessage ({ version }) { if (version !== NUSHELL_VERSION) { process.stderr.write(`Version mismatch: Expected ${NUSHELL_VERSION}, got ${version}\n`); process.exit(1); } } function handleCallMessage (id, call) { try { if (call === 'Metadata') { writeResponse(id, { Metadata: { version: PLUGIN_VERSION } }); } else if (call === 'Signature') { writeResponse(id, getPluginSignature()); } else if (call.Run) { processExecutionCall(id, call.Run); } else { writeError(id, `Unsupported operation: ${JSON.stringify(call)}`); } } catch (error) { writeError(id, `Processing error: ${error.message}`); } } function handleSignal (signal) { if (signal !== 'Reset') { process.stderr.write(`Unhandled signal: ${signal}\n`); } } // Stream processing setup /** * Initializes plugin communication protocol */ function initializePlugin () { // Set up JSON encoding process.stdout.write('\x04json\n'); // Send handshake message process.stdout.write(JSON.stringify({ Hello: { protocol: 'nu-plugin', version: NUSHELL_VERSION, features: [] } }) + '\n'); // Configure input processing let buffer = ''; process.stdin.setEncoding('utf8') .on('data', chunk => { buffer += chunk; const messages = buffer.split('\n'); buffer = messages.pop() || ''; // Preserve incomplete line for (const message of messages) { if (message.trim()) { try { handleInputMessage(JSON.parse(message)); } catch (error) { process.stderr.write(`Parse error: ${error.message}\n`); process.stderr.write(`Received: ${message}\n`); } } } }) .on('error', error => { process.stderr.write(`Input error: ${error.message}\n`); process.exit(1); }); } // Main execution if (process.argv.includes('--stdio')) { initializePlugin(); } else { console.log('This plugin is intended to be run from within Nushell'); process.exit(2); }