# Wrapper-based CLI tools The primary idea here is to develop CLI tools (of any kind) with Polywrap. The benefits they have over traditional CLI tools are: - Security - Wrappers can not access the OS directly. They need to go through plugins which can set permissions in different ways and act as "guards" to different OS components. - Per plugin permissions could be implemented by modifying their configuration (e.g. Only allow current working directory for File System plugin) - Per wrapper permissions could be implemented by monitoring which wrapper tries to access which plugin. - Additional control could be implemented by mimicking tx.origin and msg.sender functionality of smart contracts - Composability - Due to the composable nature of wrappers, they can easily be reused, extend each others functionality or turned into standard interfaces for other similar tools to follow. ## Wrap CLI manager To manage (install, update) and run these CLI tools, we can implement a "Wrap CLI manager". It can take the form of a new CLI tool or can be intergrated as part of our w3 CLI (or both). In the following examples the latter is assumed. ## Developer experience The following examples are based on the need for: https://github.com/polywrap/monorepo/issues/670 ### Web 2 way Install CLI tools from diferent developers/contributors: ``` npm install @contributor1.eth/ens-cli npm install @contributor2.eth/ipfs-cli ``` Use those tools: ``` publish-ens -u "test.eth" -hash "Qm..." publish-ipfs ``` ### Web 3 way #### Deployed wrappers ``` ens/ens-cli.contributor1.eth Apps publish-ens verify-ens ens/ipfs-cli.contributor2.eth Apps publish-ipfs verify-ipfs ens/publish.polywrap.eth Apps publish Commands ens -> "ens/ens-cli.contributor1.eth" -> publish-ens ipfs -> "ens/ipfs-cli.contributor2.eth" -> publish-ipfs verify Commands ens -> "ens/ens-cli.contributor1.eth" -> verify-ens ipfs -> "ens/ipfs-cli.contributor2.eth" -> verify-ipfs ``` #### Usage This installs the "publish" and "verify" apps: ``` w3 install ens/publish.polywrap.eth ``` This updates the "publish" and "verify" apps: ``` w3 update ens/publish.polywrap.eth ``` Use the "ens" command on the "publish" app: ``` w3 publish ens -u "test.eth" -hash "Qm..." ``` Use the "ipfs" command on the "publish" app: ``` w3 publish ipfs ``` #### Update all cli wrappers ``` w3 update ``` #### Install/Update specific apps This installs only the "publish" app: ``` w3 install ens/publish.polywrap.eth -a "publish" ``` This updates only the "verify" app: ``` w3 update ens/publish.polywrap.eth -a "verify" ``` #### Install via IPFS URI ``` w3 install ipfs/Qm... ``` Require: - File System Plugin currentWorkingDirOnly = true - HTTP Plugin URIs = ["http://google.com", ""] Method: GET Method: Post y/n ### Additional features: - Plugin permissions - On install/update, request access to plugins. The user can deny the request, thus aborting the installation/update if he chooses. - If the CLI wrapper tries accessing other plugins (for which it has not been granted access), the wrap cli manager will abort the execution. - Plugin config/env permissions - While the plugin permissions approach is useful, it is also an all or nothing kind of approach. E.g. You either allow file system access to a CLI wrapper or you don't. To allow for more granularity we can implement permission requests for each setting value in the plugin config. - E.g. require File System Plugin access with the setting "currentWorkingDirOnly" set to a value of true. - E.g. require HTTP Plugin access with the setting "baseURI" set to a value of "http://ipfs.io". - Plugin method permissions - File system cache - The wrap cli manager will cache installed CLI wrappers in a local directory - Local storage - Each CLI wrapper (or each app) will have it's own directory it can access and use for any kind of persistance required tasks. ### WIP Example CLI wrapper Schema: ```graphql= type Mutation { run(command: String!) } type Query { getAppInfo(appName: String!): AppInfo! getAppNames: [String!]! getApps: [AppInfo!]! } type AppInfo { name: String! description: String! commands: [CommandInfo!] } type CommandInfo { name: String! description: String } type ArgInfo { name: String! description: String } ```