Here's 2 versions. The top flow will run the command if not cancelled within 10 seconds, and the bottom version will cancel the command in 10 seconds unless it has been confirmed.
To enable either flow, just reconnect the 'Command' exec node.
I'll add them to the flow library later...
[{"id":"d9792b68.171d18","type":"inject","z":"4487e413.bb781c","name":"Restart node-RED","topic":"nrRestart","payload":"node-red-restart","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":230,"y":1270,"wires":[["3516bc38.3395f4"]],"icon":"font-awesome/fa-refresh"},{"id":"3516bc38.3395f4","type":"function","z":"4487e413.bb781c","name":"Countdown","func":"let input = msg.payload;\nlet cancel = false;\nlet option = msg.topic;\nlet counter = 10;\nvar desc;\n\n// define the status messages\nif (option == \"nrRestart\"){\n desc = \"Restarting node-RED\";\n } else if (option == \"piRestart\"){\n desc = \"Rebooting system\";\n } else if (option == \"update\"){\n desc = \"Updating Pi server\";\n }\n \n// enable user to cancel operation\nif (option == \"cancel\"){\n flow.set(\"cancel\", true);\n return;\n } else {flow.set(\"cancel\", false);}\n\n// Run the countdown\nvar countdown = setInterval(function(){\n node.status({text:(desc+\" in \"+counter+\" seconds\")});\n counter--;\n if (flow.get(\"cancel\")===true) {\n clearInterval(countdown);\n node.status({text:\"Request Cancelled\"});\n flow.set(\"cancel\", false);\n }\n \n// When counter expires, update status & reset \n if (counter === -1) {\n node.status({text:(desc+\" NOW!\")});\n clearInterval(countdown);\n node.send({\"payload\": input});\n }\n}, 1000);","outputs":1,"noerr":0,"x":450,"y":1330,"wires":[[]],"icon":"font-awesome/fa-hourglass-1"},{"id":"609e311a.8302b","type":"inject","z":"4487e413.bb781c","name":"Cancel","topic":"cancel","payload":"cancel","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":"","x":270,"y":1390,"wires":[["3516bc38.3395f4"]],"icon":"font-awesome/fa-exclamation-triangle"},{"id":"f6ab5b46.514e08","type":"exec","z":"4487e413.bb781c","command":"","addpay":true,"append":"","useSpawn":"false","timer":"","oldrc":false,"name":"Command","x":630,"y":1320,"wires":[[],[],[]]},{"id":"ee6338f0.bdcfd8","type":"inject","z":"4487e413.bb781c","name":"Restart Pi","topic":"piRestart","payload":"sudo systemctl reboot","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":260,"y":1310,"wires":[["3516bc38.3395f4"]],"icon":"font-awesome/fa-refresh"},{"id":"d5bb979b.123548","type":"inject","z":"4487e413.bb781c","name":"Update Pi","topic":"update","payload":"sudo ~/update_server.sh","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":260,"y":1350,"wires":[["3516bc38.3395f4"]],"icon":"node-red/db.svg"},{"id":"b988f007.c6dd6","type":"inject","z":"4487e413.bb781c","name":"Restart node-RED","topic":"nrRestart","payload":"node-red-restart","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":240,"y":1450,"wires":[["418bf636.a88bd8"]],"icon":"font-awesome/fa-refresh"},{"id":"f48ec4b0.caab38","type":"inject","z":"4487e413.bb781c","name":"Confirm","topic":"confirm","payload":"confirm","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":"","x":280,"y":1570,"wires":[["418bf636.a88bd8"]],"icon":"font-awesome/fa-exclamation-triangle"},{"id":"97991ced.05158","type":"exec","z":"4487e413.bb781c","command":"","addpay":true,"append":"","useSpawn":"false","timer":"","oldrc":false,"name":"Command","x":630,"y":1500,"wires":[[],[],[]]},{"id":"3ad3fcb4.4d5684","type":"inject","z":"4487e413.bb781c","name":"Restart Pi","topic":"piRestart","payload":"sudo systemctl reboot","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":270,"y":1490,"wires":[["418bf636.a88bd8"]],"icon":"font-awesome/fa-refresh"},{"id":"88a2f64c.47de78","type":"inject","z":"4487e413.bb781c","name":"Update Pi","topic":"update","payload":"sudo ~/update_server.sh","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":270,"y":1530,"wires":[["418bf636.a88bd8"]],"icon":"node-red/db.svg"},{"id":"418bf636.a88bd8","type":"function","z":"4487e413.bb781c","name":"Countdown","func":"let input = msg.payload;\nlet option = msg.topic;\nlet confirm = false;\nlet counter = 10;\nvar desc;\n\n// define the status messages\n if (option == \"nrRestart\"){\n desc = \"Confirm NR restart\";\n } else if (option == \"piRestart\"){\n desc = \"Confirm reboot\";\n } else if (option == \"update\"){\n desc = \"Confirm update\";\n }\n \n// enable user to confirm operation\nif (option == \"confirm\"){\n flow.set(\"confirm\", true);\n return;\n } else {flow.set(\"confirm\", false);}\n \n// Run the countdown\nvar countdown = setInterval(function(){\n node.status({text:(desc+\" within \"+counter+\" seconds\")});\n counter--;\n if (flow.get(\"confirm\")===true) {\n clearInterval(countdown);\n node.status({text:\"Request Confirmed\"});\n node.send({\"payload\": input});\n flow.set(\"confirm\", false);\n }\n \n// When counter expires, update status & reset\n if (counter === 0) {\n node.status({text:(\"Request Cancelled!\")});\n clearInterval(countdown);\n }\n}, 1000);","outputs":1,"noerr":0,"x":460,"y":1510,"wires":[[]]},{"id":"24a0ce1e.4a4c42","type":"comment","z":"4487e413.bb781c","name":"Unless cancelled, command will be actioned","info":"","x":550,"y":1270,"wires":[]},{"id":"48618e90.16136","type":"comment","z":"4487e413.bb781c","name":"Unless confirmed, command will be cancelled","info":"","x":550,"y":1450,"wires":[]}]