|
23 | 23 |
|
24 | 24 | import chameleon_com |
25 | 25 | import chameleon_cmd |
| 26 | +import chameleon_script |
26 | 27 | from chameleon_utils import ArgumentParserNoExit, ArgsParserError, UnexpectedResponseError, execute_tool, \ |
27 | 28 | tqdm_if_exists, print_key_table |
28 | 29 | from chameleon_utils import CLITree |
@@ -4724,3 +4725,251 @@ def on_exec(self, args: argparse.Namespace): |
4724 | 4725 | ) |
4725 | 4726 | else: |
4726 | 4727 | print(f" [*] {color_string((CY, 'No response'))}") |
| 4728 | + |
| 4729 | + |
| 4730 | +# Scripting commands |
| 4731 | +script = root.subgroup('script', 'Script management and execution') |
| 4732 | + |
| 4733 | +@script.command('run') |
| 4734 | +class ScriptRun(BaseCLIUnit): |
| 4735 | + """Run a Python script""" |
| 4736 | + |
| 4737 | + def args_parser(self) -> ArgumentParserNoExit: |
| 4738 | + parser = ArgumentParserNoExit() |
| 4739 | + parser.description = 'Run a Python script' |
| 4740 | + parser.add_argument('filename', help='Script filename') |
| 4741 | + parser.add_argument('--verbose', '-v', action='store_true', help='Verbose output') |
| 4742 | + return parser |
| 4743 | + |
| 4744 | + def on_exec(self, args): |
| 4745 | + try: |
| 4746 | + script_engine = chameleon_script.ScriptEngine(self.device_com) |
| 4747 | + script_content = script_engine.load_script(args.filename) |
| 4748 | + |
| 4749 | + # Show the actual path being used |
| 4750 | + script_path = Path(args.filename) |
| 4751 | + if not script_path.is_absolute(): |
| 4752 | + actual_path = script_engine.scripts_dir / args.filename |
| 4753 | + if actual_path.exists(): |
| 4754 | + script_path = actual_path |
| 4755 | + else: |
| 4756 | + script_path = Path.cwd() / args.filename |
| 4757 | + |
| 4758 | + print(f" [*] Running script: {color_string((CG, str(script_path)))}") |
| 4759 | + |
| 4760 | + result = script_engine.execute_script(script_content, args.filename) |
| 4761 | + |
| 4762 | + if result['success']: |
| 4763 | + print(f" [+] {color_string((CG, 'Script executed successfully'))}") |
| 4764 | + if result['output'].strip(): |
| 4765 | + print(" [*] Output:") |
| 4766 | + print(result['output']) |
| 4767 | + if args.verbose and result['variables']: |
| 4768 | + print(" [*] Variables:") |
| 4769 | + for key, value in result['variables'].items(): |
| 4770 | + print(f" {key}: {value}") |
| 4771 | + else: |
| 4772 | + print(f" [!] {color_string((CR, 'Script execution failed'))}") |
| 4773 | + if result['error']: |
| 4774 | + print(f" [!] Error: {result['error']}") |
| 4775 | + if args.verbose and 'traceback' in result: |
| 4776 | + print(" [*] Traceback:") |
| 4777 | + print(result['traceback']) |
| 4778 | + |
| 4779 | + except FileNotFoundError: |
| 4780 | + print(f" [!] {color_string((CR, f'Script file not found: {args.filename}'))}") |
| 4781 | + print(f" [*] Tried paths:") |
| 4782 | + print(f" - {chameleon_script.ScriptEngine(self.device_com).scripts_dir / args.filename}") |
| 4783 | + print(f" - {Path.cwd() / args.filename}") |
| 4784 | + if Path(args.filename).is_absolute(): |
| 4785 | + print(f" - {args.filename}") |
| 4786 | + except Exception as e: |
| 4787 | + print(f" [!] {color_string((CR, f'Error running script: {e}'))}") |
| 4788 | + |
| 4789 | + |
| 4790 | +@script.command('exec') |
| 4791 | +class ScriptExec(BaseCLIUnit): |
| 4792 | + """Execute Python code directly""" |
| 4793 | + |
| 4794 | + def args_parser(self) -> ArgumentParserNoExit: |
| 4795 | + parser = ArgumentParserNoExit() |
| 4796 | + parser.description = 'Execute Python code directly' |
| 4797 | + parser.add_argument('code', help='Python code to execute') |
| 4798 | + parser.add_argument('--verbose', '-v', action='store_true', help='Verbose output') |
| 4799 | + return parser |
| 4800 | + |
| 4801 | + def on_exec(self, args): |
| 4802 | + try: |
| 4803 | + script_engine = chameleon_script.ScriptEngine(self.device_com) |
| 4804 | + |
| 4805 | + print(f" [*] Executing code...") |
| 4806 | + |
| 4807 | + result = script_engine.execute_script(args.code) |
| 4808 | + |
| 4809 | + if result['success']: |
| 4810 | + print(f" [+] {color_string((CG, 'Code executed successfully'))}") |
| 4811 | + if result['output'].strip(): |
| 4812 | + print(" [*] Output:") |
| 4813 | + print(result['output']) |
| 4814 | + if args.verbose and result['variables']: |
| 4815 | + print(" [*] Variables:") |
| 4816 | + for key, value in result['variables'].items(): |
| 4817 | + print(f" {key}: {value}") |
| 4818 | + else: |
| 4819 | + print(f" [!] {color_string((CR, 'Code execution failed'))}") |
| 4820 | + if result['error']: |
| 4821 | + print(f" [!] Error: {result['error']}") |
| 4822 | + if args.verbose and 'traceback' in result: |
| 4823 | + print(" [*] Traceback:") |
| 4824 | + print(result['traceback']) |
| 4825 | + |
| 4826 | + except Exception as e: |
| 4827 | + print(f" [!] {color_string((CR, f'Error executing code: {e}'))}") |
| 4828 | + |
| 4829 | + |
| 4830 | +@script.command('list') |
| 4831 | +class ScriptList(BaseCLIUnit): |
| 4832 | + """List available scripts""" |
| 4833 | + |
| 4834 | + def args_parser(self) -> ArgumentParserNoExit: |
| 4835 | + parser = ArgumentParserNoExit() |
| 4836 | + parser.description = 'List available scripts' |
| 4837 | + return parser |
| 4838 | + |
| 4839 | + def on_exec(self, args): |
| 4840 | + try: |
| 4841 | + script_engine = chameleon_script.ScriptEngine(self.device_com) |
| 4842 | + scripts = script_engine.list_scripts() |
| 4843 | + |
| 4844 | + if scripts: |
| 4845 | + print(f" [*] Available scripts:") |
| 4846 | + for script in scripts: |
| 4847 | + print(f" - {color_string((CG, script))}") |
| 4848 | + else: |
| 4849 | + print(f" [*] {color_string((CY, 'No scripts found'))}") |
| 4850 | + print(f" [*] Scripts directory: {script_engine.scripts_dir}") |
| 4851 | + |
| 4852 | + except Exception as e: |
| 4853 | + print(f" [!] {color_string((CR, f'Error listing scripts: {e}'))}") |
| 4854 | + |
| 4855 | + |
| 4856 | +@script.command('save') |
| 4857 | +class ScriptSave(BaseCLIUnit): |
| 4858 | + """Save a script to file""" |
| 4859 | + |
| 4860 | + def args_parser(self) -> ArgumentParserNoExit: |
| 4861 | + parser = ArgumentParserNoExit() |
| 4862 | + parser.description = 'Save a script to file' |
| 4863 | + parser.add_argument('filename', help='Script filename') |
| 4864 | + parser.add_argument('--code', '-c', help='Script code content') |
| 4865 | + return parser |
| 4866 | + |
| 4867 | + def on_exec(self, args): |
| 4868 | + try: |
| 4869 | + script_engine = chameleon_script.ScriptEngine(self.device_com) |
| 4870 | + |
| 4871 | + if args.code: |
| 4872 | + script_content = args.code |
| 4873 | + else: |
| 4874 | + print(" [*] Enter script content (Ctrl+D to finish):") |
| 4875 | + script_content = [] |
| 4876 | + try: |
| 4877 | + while True: |
| 4878 | + line = input() |
| 4879 | + script_content.append(line) |
| 4880 | + except EOFError: |
| 4881 | + pass |
| 4882 | + script_content = '\n'.join(script_content) |
| 4883 | + |
| 4884 | + script_engine.save_script(args.filename, script_content) |
| 4885 | + print(f" [+] {color_string((CG, f'Script saved: {args.filename}'))}") |
| 4886 | + |
| 4887 | + except Exception as e: |
| 4888 | + print(f" [!] {color_string((CR, f'Error saving script: {e}'))}") |
| 4889 | + |
| 4890 | + |
| 4891 | +@script.command('show') |
| 4892 | +class ScriptShow(BaseCLIUnit): |
| 4893 | + """Show script content""" |
| 4894 | + |
| 4895 | + def args_parser(self) -> ArgumentParserNoExit: |
| 4896 | + parser = ArgumentParserNoExit() |
| 4897 | + parser.description = 'Show script content' |
| 4898 | + parser.add_argument('filename', help='Script filename') |
| 4899 | + return parser |
| 4900 | + |
| 4901 | + def on_exec(self, args): |
| 4902 | + try: |
| 4903 | + script_engine = chameleon_script.ScriptEngine(self.device_com) |
| 4904 | + script_content = script_engine.load_script(args.filename) |
| 4905 | + |
| 4906 | + print(f" [*] Content of {color_string((CG, args.filename))}:") |
| 4907 | + print("-" * 50) |
| 4908 | + print(script_content) |
| 4909 | + print("-" * 50) |
| 4910 | + |
| 4911 | + except FileNotFoundError: |
| 4912 | + print(f" [!] {color_string((CR, f'Script file not found: {args.filename}'))}") |
| 4913 | + except Exception as e: |
| 4914 | + print(f" [!] {color_string((CR, f'Error showing script: {e}'))}") |
| 4915 | + |
| 4916 | + |
| 4917 | +@script.command('vars') |
| 4918 | +class ScriptVars(BaseCLIUnit): |
| 4919 | + """Show current script variables""" |
| 4920 | + |
| 4921 | + def args_parser(self) -> ArgumentParserNoExit: |
| 4922 | + parser = ArgumentParserNoExit() |
| 4923 | + parser.description = 'Show current script variables' |
| 4924 | + return parser |
| 4925 | + |
| 4926 | + def on_exec(self, args): |
| 4927 | + try: |
| 4928 | + script_engine = chameleon_script.ScriptEngine(self.device_com) |
| 4929 | + |
| 4930 | + if script_engine.env.variables: |
| 4931 | + print(f" [*] Current script variables:") |
| 4932 | + for key, value in script_engine.env.variables.items(): |
| 4933 | + print(f" {key}: {value}") |
| 4934 | + else: |
| 4935 | + print(f" [*] {color_string((CY, 'No variables set'))}") |
| 4936 | + |
| 4937 | + except Exception as e: |
| 4938 | + print(f" [!] {color_string((CR, f'Error showing variables: {e}'))}") |
| 4939 | + |
| 4940 | + |
| 4941 | +@script.command('clear') |
| 4942 | +class ScriptClear(BaseCLIUnit): |
| 4943 | + """Clear script variables""" |
| 4944 | + |
| 4945 | + def args_parser(self) -> ArgumentParserNoExit: |
| 4946 | + parser = ArgumentParserNoExit() |
| 4947 | + parser.description = 'Clear script variables' |
| 4948 | + return parser |
| 4949 | + |
| 4950 | + def on_exec(self, args): |
| 4951 | + try: |
| 4952 | + script_engine = chameleon_script.ScriptEngine(self.device_com) |
| 4953 | + script_engine.env.clear_variables() |
| 4954 | + print(f" [+] {color_string((CG, 'Script variables cleared'))}") |
| 4955 | + |
| 4956 | + except Exception as e: |
| 4957 | + print(f" [!] {color_string((CR, f'Error clearing variables: {e}'))}") |
| 4958 | + |
| 4959 | + |
| 4960 | +# Example script command |
| 4961 | +@root.command('py') |
| 4962 | +class PyCommand(BaseCLIUnit): |
| 4963 | + """Execute Python code (alias for script exec)""" |
| 4964 | + |
| 4965 | + def args_parser(self) -> ArgumentParserNoExit: |
| 4966 | + parser = ArgumentParserNoExit() |
| 4967 | + parser.description = 'Execute Python code (alias for script exec)' |
| 4968 | + parser.add_argument('code', help='Python code to execute') |
| 4969 | + return parser |
| 4970 | + |
| 4971 | + def on_exec(self, args): |
| 4972 | + # Reuse ScriptExec functionality |
| 4973 | + script_exec = ScriptExec() |
| 4974 | + script_exec.device_com = self.device_com |
| 4975 | + script_exec.on_exec(args) |
0 commit comments