vercel / hyper

A terminal built on web technologies
https://hyper.is
MIT License
43.21k stars 3.51k forks source link

Is it possible for a plugin to execute a command in the running shell? #3001

Closed rfgamaral closed 6 years ago

rfgamaral commented 6 years ago

I've took a look at the documentation available and some plugins source code but couldn't find if this is possible to achieve. My question is pretty much the issue title.

Say that you are running /bin/bash on Linux or cmd.exe on Windows and on some action - SESSION_ADD for instance - you execute a command inside the running instance of bash/cmd.exe/whatever and get the output from that command. Is this possible?

My necessity for such thing comes from hharnisc/hypercwd#16, not sure if there's a better way though.

linonetwo commented 6 years ago

I don't know the deeper difference of SESSION_PTY_DATA and SESSION_ADD_DATA, but I do know that SESSION_PTY_DATA is adding text to xterm.js but not the real terminal.

So dispatching this:

({ data, uid }) => ({
  type: 'SESSION_PTY_DATA',
  data,
  uid,
  now: Number(new Date()),
});

will add text to xterm, but it will not go into real terminal, so when hitting return, real terminal just don't know you have inserted some text.

And old ways:

// term is get from decorateTerm's onDecorated
const terminal = term.keyboard.terminal;
terminal.io.sendString('some command');

is not working either, since in hyper2.0 there is no keyboard in term object, term.io is not found too.

I am also waiting for an answer, maybe hyper should export more command exec related xterm.js API to extensions.

linonetwo commented 6 years ago

I get reference in decorateTerm like this:

export default (Term: ComponentType<*>) =>
  class DecorateTerm extends Component<*> {
    render() {
      return (
        <Term
          {...this.props}
          onDecorated={terminal => {
            if (this.props.onDecorated) this.props.onDecorated(terminal);
            window.CLI2GUI_TERMINAL_REFERENCE = terminal;
          }}
        />
      );
    }
  };

And window.CLI2GUI_TERMINAL_REFERENCE.term is: wx20180512-144035 There is no function to insert commands.

linonetwo commented 6 years ago

Another thread discussing this https://github.com/zeit/hyper/issues/2754

linonetwo commented 6 years ago

There is term.write() hidden in the proto chain, It can write text to xterm.js, but not the real terminal.

ivanwonder commented 6 years ago

I think when the user presses a key, the term will dispatch SESSION_USER_DATA action with the key value. https://github.com/zeit/hyper/blob/a14c55640c47c4e294095e50a610c5e77d2fad22/lib/containers/terms.js#L53 after the main process receives the user input, the value will be written into the corresponding session which I think it is a terminal instance. https://github.com/zeit/hyper/blob/a14c55640c47c4e294095e50a610c5e77d2fad22/app/session.js#L89

after the data has been handled. the renderer process will receive the output. and dispatch SESSION_PTY_DATA and SESSION_ADD_DATA, Finally, it will be shown on the canvas. https://github.com/zeit/hyper/blob/a14c55640c47c4e294095e50a610c5e77d2fad22/lib/store/write-middleware.js#L10

above is my understand. If wrong, welcome to correct me.

linonetwo commented 6 years ago

@ivanwonder So you think this RPC ↓ https://github.com/zeit/hyper/blob/canary/lib/actions/sessions.js#L158 will result in this ↓ call? https://github.com/zeit/hyper/blob/a14c55640c47c4e294095e50a610c5e77d2fad22/app/session.js#L89 I think to understand the relation between I should read more code.

But good news! I found that this plugin is still working: https://github.com/curz46/hyper-startup/blob/master/index.js So to execute a command, just

win.sessions.get(uid).write(cmd + '\r');

where win comes from onWindow.

For input recording, you can have a look at hyper-history or my refactored implementation.

But It seems to execute the command in the main thread. I don't know whether it will work in tty like ssh.

linonetwo commented 6 years ago

It worked: https://github.com/linonetwo/hyper-cli2gui/blob/8cd54c5c011a7393d9370f6b7a2a05ca3c108618/src/onWindow.js#L6

RPC to it: https://github.com/linonetwo/hyper-cli2gui/blob/8cd54c5c011a7393d9370f6b7a2a05ca3c108618/src/store/actions.js#L4-L6

So I think this issue can be closed.

rfgamaral commented 6 years ago

I don't see how that solves my issue so I'm hesitant in closing it. Care to provide a basic plugin example on how to achieve what I asked on the first post?

ivanwonder commented 6 years ago

why do you need a plugin to execute a command, and what problem the plugin wants to resolve.

rfgamaral commented 6 years ago

@ivanwonder It's somewhat explained in the original post, I'm trying to solve an issue hharnisc/hypercwd#16 and I can't think of a different way.

linonetwo commented 6 years ago

@rfgamaral Does hyper-startup run in Linux subsystem? It is working at least in Mac OS.

rfgamaral commented 6 years ago

@linonetwo It doesn't, just tried:

  commands: ['echo "hello world"'],

  localPlugins: [
    'hyper-startup'
  ],

After launching Hyper with that configuration:

image

linonetwo commented 6 years ago

win.sessions.get(uid).write(cmd + '\r'); in onWindow maybe broken in windows or Windows Subsystem for Linux.

rfgamaral commented 6 years ago

Even if it worked, I was going to see the output of the command on the Hyper window, right? Because the idea where would be to run a command in the context of the current WSL session, get the output from that command but never show any signs of that to the user.

linonetwo commented 6 years ago

Maybe you can try import { exec } from 'child_process';'s exec. And exec in main process likes onWindow or onApp, if you have hyper running in WSL.

albinekb commented 6 years ago

Yes @linonetwo, you need to send the correct CWD too, check: https://github.com/Hyperline/hyperline/blob/master/src/lib/plugins/git-status.js#L56-L58 how hyperline is doing it.

rfgamaral commented 6 years ago

I'm a little bit confused, could you guys provide a very basic plugin example where one could run a command like lsof | grep cwd and get the returned value assigned to a variable that I could use within the plugin?

rfgamaral commented 6 years ago

@linonetwo @albinekb Can you help me out with an example of the above please?

linonetwo commented 6 years ago

Hey @rfgamaral , try https://github.com/linonetwo/hyper-visual/blob/master/src/onWindow.js#L7

rfgamaral commented 6 years ago

@linonetwo I'm trying your plugin to see how ti works but it doesn't even load for me... I've removed all plugins and even started from scratch by removing .hyper_plugins/node_modules and hyper-visual is installed but it' never loaded when I launch Hyper. There's no error message and the dev tools console is empty.

linonetwo commented 6 years ago

@rfgamaral That's true, https://github.com/zeit/hyper/issues/3013 But it really works in dev mode. You can try using that code into your plugin. I don't know why my plugin doesn't load. And hyper's developer is too busy to reply.

rfgamaral commented 6 years ago

I was testing your plugin first to quickly check if your approach worked or not and how it worked. But maybe you can answer me this quickly... Does your plugin write the command (when you click it on the quick suggestion) to the terminal in a visible way to the user and you also see the output on the same window, correct?

linonetwo commented 6 years ago

@rfgamaral Yes. But if you want to exec command silently, you can just replace that line of code to exec('some command') where import exec from 'child_process'.

rfgamaral commented 6 years ago

@linonetwo Unfortunately that doesn't work, exec runs in the context of system running Hyper and not in the running Hyper shell.

It doesn't help me because I run Hyper on Windows and the shell is Windows Subsystem for Linux (WSL). When I use exec it's like running a command inside cmd.exe, which is not what I'm after. I want to run a silent command inside WSL (and read the result), which is running bash.

albinekb commented 6 years ago

You can read how hyper launches bash here: https://github.com/zeit/hyper/blob/9a536722f8b0a43e8f3741a033d519864f55a957/app/session.js#L53-L58 @rfgamaral, it's using require('node-pty').spawn, I think you can do the same in your plugin.

rfgamaral commented 6 years ago

I don't believe that will solve my problem. I don't want to launch bash, I don't want to spawn/exec a new shell, I need to execute a command in the context of the current running shell and read the output of that command. Maybe this is not possible and that's a valid answer for this issue.

Also, I'm no trying to write a plugin but to fix WSL related issues on other plugins. These plugins rely on the current absolute path of the running shell to work. On Windows, they assume Command Prompt/PowerShell and both of these print the absolute path of the current directory into the prompt, which they read from the prompt and do their thing. On Linux, they rely on lsof and the current running shell PID to read the current directory for that shell. None of these methods work on Windows with WSL (which is a Linux environment running on a Windows host).

Until something like Microsoft/WSL#3174 is implemented (and this will take a while...), I can't think of any other way than to run a command (silently) in the context of the running shell and read it's output, but again, this might not be possible at all. If that's true, I can't think of any other possible workaround to fix these particular plugins issues with WSL.

linonetwo commented 6 years ago

Wait @rfgamaral , I have another idea. You can use win.sessions.get(uid).write(command); to execute a command, and prevent the result being printed on the screen by hijacking SESSION_PTY_DATA and SESSION_ADD_DATA action after some EXTCUTE_COMMAND action. Do you think it's an option?

albinekb commented 6 years ago

I think that's the only option if you need to execute it in the exact same session but without printing the result, I think it's better to spawn a new instance where you do your headless queries in 👍

rfgamaral commented 6 years ago

@linonetwo It could be, I'd need to test and see how it works. Thank you for the idea.

I think that's the only option if you need to execute it in the exact same session but without printing the result(...)

I didn't want to need to execute it in the exact same session but to achieve what I need to fix WSL issues on those plugins, I can't think of any other way.

I think it's better to spawn a new instance where you do your headless queries in

What doesn't work for what I'm trying to achieve. These plugins rely on the current directory for the current session to work. Spawning a new instance starts at the default working directory, which is no good.

rfgamaral commented 6 years ago

@linonetwo Just tried to implement your idea but I don't think it will work either. And now that I think about it, writing a command into the running instance is not an option. Some of these plugins rely SESSION_ADD_DATA and they do their thing on this action, meaning that I need to execute my code on these actions too and I can't write something to the window if the user itself is typing there.

albinekb commented 6 years ago

You could get the current working directory and then spawn in there? Would that work?

rfgamaral commented 6 years ago

@albinekb Getting the current working directory is actually the problem that I'm trying to solve.

albinekb commented 6 years ago

Maybe you can use the same way as hyper-statusline finds the current working directory by pid? https://github.com/henrikdahl/hyper-statusline/blob/master/index.js#L329-L358 @rfgamaral

rfgamaral commented 6 years ago

@albinekb That plugin does not work with WSL. It finds the current working directory by PID on Linux, not on Windows (WSL is Linux but runs under Windows), on Windows reads the prompt instead (which works fine for Command Prompt and PowerShell, but no WSL). That plugin is actually one of the plugins I'm trying to fix with this...

lucaspereirasouzat commented 1 year ago

Wait @rfgamaral , I have another idea. You can use win.sessions.get(uid).write(command); to execute a command, and prevent the result being printed on the screen by hijacking SESSION_PTY_DATA and SESSION_ADD_DATA action after some EXTCUTE_COMMAND action. Do you think it's an option?

I'm seen this problem but dont work for now any other solution ?