You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openwhisk.apache.org by st...@apache.org on 2021/01/13 03:56:16 UTC

[openwhisk-intellij-plugin] branch master updated: Initial import of openwhisk-intellij-plugin donation from NAVER (#5)

This is an automated email from the ASF dual-hosted git repository.

style95 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/openwhisk-intellij-plugin.git


The following commit(s) were added to refs/heads/master by this push:
     new ec86984  Initial import of openwhisk-intellij-plugin donation from NAVER (#5)
ec86984 is described below

commit ec869843d97601a152c3cc191d10bfbf23408f2d
Author: 김건희 <ki...@gmail.com>
AuthorDate: Wed Jan 13 12:55:52 2021 +0900

    Initial import of openwhisk-intellij-plugin donation from NAVER (#5)
---
 .gitignore                                         |   6 +
 LICENSE                                            | 203 +++++++++
 README.md                                          |  53 +++
 build.gradle                                       |  61 +++
 config/checkstyle/checkstyle.xml                   | 202 +++++++++
 gradle/wrapper/gradle-wrapper.jar                  | Bin 0 -> 56172 bytes
 gradle/wrapper/gradle-wrapper.properties           |  19 +
 gradlew                                            | 186 +++++++++
 gradlew.bat                                        |  98 +++++
 settings.gradle                                    |  18 +
 .../intellij/common/DialogWrapperWithApply.java    |  71 ++++
 .../navercorp/openwhisk/intellij/common/Icons.java |  72 ++++
 .../common/error/NotExistFileException.java        |  25 ++
 .../common/notification/SimpleNotifier.java        |  51 +++
 .../intellij/common/service/WhiskService.java      |  57 +++
 .../intellij/common/service/WskDeployService.java  |  66 +++
 .../intellij/common/utils/CommandUtils.java        |  60 +++
 .../intellij/common/utils/EventUtils.java          |  57 +++
 .../openwhisk/intellij/common/utils/FileUtils.java |  88 ++++
 .../intellij/common/utils/JsonParserUtils.java     | 214 ++++++++++
 .../intellij/common/utils/ParameterUtils.java      |  66 +++
 .../intellij/common/utils/ParametricRunnable.java  |  25 ++
 .../intellij/common/utils/ValidationUtils.java     |  52 +++
 .../intellij/common/utils/WhiskUtils.java          |  62 +++
 .../intellij/common/whisk/model/Binding.java       |  60 +++
 .../intellij/common/whisk/model/Limits.java        |  88 ++++
 .../intellij/common/whisk/model/Runtime.java       | 188 +++++++++
 .../intellij/common/whisk/model/WhiskAuth.java     |  54 +++
 .../common/whisk/model/WhiskAuthWithName.java      |  48 +++
 .../intellij/common/whisk/model/WhiskEndpoint.java |  69 +++
 .../common/whisk/model/WhiskNamespace.java         | 113 +++++
 .../whisk/model/action/CompactWhiskAction.java     | 136 ++++++
 .../whisk/model/action/ExecutableWhiskAction.java  |  67 +++
 .../common/whisk/model/action/WhiskAction.java     | 292 +++++++++++++
 .../whisk/model/action/WhiskActionMetaData.java    |  46 ++
 .../whisk/model/activation/WhiskActivation.java    | 183 ++++++++
 .../model/activation/WhiskActivationMetaData.java  |  75 ++++
 .../model/activation/WhiskActivationWithLogs.java  |  90 ++++
 .../intellij/common/whisk/model/exec/CodeExec.java | 121 ++++++
 .../whisk/model/exec/CodeExecSerializer.java       |  63 +++
 .../intellij/common/whisk/model/exec/Exec.java     |  52 +++
 .../common/whisk/model/exec/ExecMetaData.java      |  26 ++
 .../common/whisk/model/pkg/WhiskPackage.java       | 137 ++++++
 .../whisk/model/pkg/WhiskPackageWithActions.java   |  92 ++++
 .../model/trigger/ExecutableWhiskTrigger.java      |  93 +++++
 .../model/trigger/SimplifiedEntityMetaData.java    |  72 ++++
 .../whisk/model/trigger/SimplifiedWhiskRule.java   |  65 +++
 .../common/whisk/model/trigger/WhiskRule.java      |  71 ++++
 .../whisk/model/trigger/WhiskRuleMetaData.java     | 126 ++++++
 .../common/whisk/model/trigger/WhiskTrigger.java   | 117 ++++++
 .../whisk/model/trigger/WhiskTriggerMetaData.java  |  37 ++
 .../whisk/model/trigger/WhiskTriggerRoot.java      |  20 +
 .../whisk/model/wskdeploy/NullWskDeployBinary.java |  21 +
 .../whisk/model/wskdeploy/WskDeployBinary.java     |  43 ++
 .../common/whisk/model/wskdeploy/WskDeployCmd.java |  31 ++
 .../whisk/model/wskdeploy/WskDeployCmdDeploy.java  |  74 ++++
 .../model/wskdeploy/WskDeployCmdResponse.java      |  53 +++
 .../model/wskdeploy/WskDeployCmdUndeploy.java      |  74 ++++
 .../whisk/model/wskdeploy/WskDeployFile.java       |  20 +
 .../whisk/model/wskdeploy/WskDeployManifest.java   |  77 ++++
 .../common/whisk/service/WhiskActionService.java   | 128 ++++++
 .../whisk/service/WhiskActivationService.java      |  70 ++++
 .../whisk/service/WhiskNamespaceService.java       |  65 +++
 .../common/whisk/service/WhiskPackageService.java  | 140 +++++++
 .../common/whisk/service/WhiskRuleService.java     |  72 ++++
 .../common/whisk/service/WhiskTriggerService.java  | 129 ++++++
 .../dialog/action/ActionManagerDialog.java         |  67 +++
 .../explorer/dialog/action/DeleteActionDialog.java |  46 ++
 .../dialog/action/listener/DownActionListener.java |  21 +
 .../action/listener/RemoveActionListener.java      |  21 +
 .../dialog/action/listener/UpActionListener.java   |  21 +
 .../dialog/action/ui/ActionManagerDialogForm.form  | 328 +++++++++++++++
 .../dialog/action/ui/ActionManagerDialogForm.java  | 413 ++++++++++++++++++
 .../explorer/dialog/action/ui/CodeTypeForm.form    |  39 ++
 .../explorer/dialog/action/ui/CodeTypeForm.java    |  85 ++++
 .../dialog/action/ui/DefaultParameterForm.form     |  46 ++
 .../dialog/action/ui/DefaultParameterForm.java     |  40 ++
 .../explorer/dialog/action/ui/DockerImageForm.form |  39 ++
 .../explorer/dialog/action/ui/DockerImageForm.java |  40 ++
 .../dialog/action/ui/LinkedActionsEntryForm.form   |  61 +++
 .../dialog/action/ui/LinkedActionsEntryForm.java   |  98 +++++
 .../dialog/action/ui/LinkedActionsForm.form        |  87 ++++
 .../dialog/action/ui/LinkedActionsForm.java        | 180 ++++++++
 .../dialog/endpoint/AddEndpointDialog.java         |  63 +++
 .../dialog/endpoint/DeleteEndpointDialog.java      |  50 +++
 .../dialog/endpoint/EditEndpointDialog.java        |  64 +++
 .../dialog/endpoint/ui/AddEndpointDialogForm.form  |  97 +++++
 .../dialog/endpoint/ui/AddEndpointDialogForm.java  | 102 +++++
 .../dialog/endpoint/ui/EditEndpointDialogForm.form |  97 +++++
 .../dialog/endpoint/ui/EditEndpointDialogForm.java | 115 +++++
 .../dialog/namespace/AddNamespaceDialog.java       |  64 +++
 .../dialog/namespace/DeleteNamespaceDialog.java    |  50 +++
 .../dialog/namespace/EditNamespaceDialog.java      |  65 +++
 .../namespace/ui/AddNamespaceDialogForm.form       |  97 +++++
 .../namespace/ui/AddNamespaceDialogForm.java       | 145 +++++++
 .../namespace/ui/EditNamespaceDialogForm.form      | 175 ++++++++
 .../namespace/ui/EditNamespaceDialogForm.java      | 156 +++++++
 .../explorer/dialog/pkg/DeletePackageDialog.java   |  66 +++
 .../dialog/pkg/PackageCreationManagerDialog.java   |  64 +++
 .../explorer/dialog/pkg/PackageManagerDialog.java  |  65 +++
 .../pkg/ui/PackageCreationManagerDialogForm.form   |  59 +++
 .../pkg/ui/PackageCreationManagerDialogForm.java   |  75 ++++
 .../dialog/pkg/ui/PackageManagerDialogForm.form    | 287 +++++++++++++
 .../dialog/pkg/ui/PackageManagerDialogForm.java    | 131 ++++++
 .../dialog/trigger/DeleteTriggerDialog.java        |  50 +++
 .../trigger/TriggerCreationManagerDialog.java      |  64 +++
 .../dialog/trigger/TriggerManagerDialog.java       |  67 +++
 .../trigger/listener/RemoveLinkedRuleListener.java |  22 +
 .../dialog/trigger/ui/LinkedActionsForm.form       |  53 +++
 .../dialog/trigger/ui/LinkedActionsForm.java       |  62 +++
 .../ui/TriggerCreationManagerDialogForm.form       | 104 +++++
 .../ui/TriggerCreationManagerDialogForm.java       | 113 +++++
 .../trigger/ui/TriggerManagerDialogForm.form       | 215 ++++++++++
 .../trigger/ui/TriggerManagerDialogForm.java       | 247 +++++++++++
 .../explorer/editor/ActivationViewEditor.java      | 191 +++++++++
 .../editor/ActivationViewEditorProvider.java       |  54 +++
 .../explorer/editor/ActivationViewVirtualFile.java | 147 +++++++
 .../editor/ActivationViewVirtualFileSystem.java    | 114 +++++
 .../explorer/editor/model/ComboBoxEntityEntry.java |  80 ++++
 .../explorer/editor/model/ComboBoxEntityType.java  |  21 +
 .../editor/ui/ActivationViewEditorForm.form        |  85 ++++
 .../editor/ui/ActivationViewEditorForm.java        | 276 ++++++++++++
 .../toolwindow/WhiskExplorerWindowFactory.java     | 153 +++++++
 .../toolwindow/action/AddEndpointAction.java       |  43 ++
 .../toolwindow/action/AddNamespaceAction.java      |  48 +++
 .../toolwindow/action/CreatePackageAction.java     |  49 +++
 .../toolwindow/action/CreateTriggerAction.java     |  49 +++
 .../toolwindow/action/DeleteActionAction.java      |  71 ++++
 .../toolwindow/action/DeleteEndpointAction.java    |  97 +++++
 .../toolwindow/action/DeleteNamespaceAction.java   | 104 +++++
 .../toolwindow/action/DeletePackageAction.java     | 129 ++++++
 .../toolwindow/action/DeleteTriggerAction.java     |  70 ++++
 .../toolwindow/action/EditActionAction.java        |  73 ++++
 .../toolwindow/action/EditEndpointAction.java      |  44 ++
 .../toolwindow/action/EditNamespaceAction.java     |  52 +++
 .../toolwindow/action/EditPackageAction.java       |  67 +++
 .../toolwindow/action/EditTriggerAction.java       |  82 ++++
 .../action/OpenActivationViewAction.java           |  66 +++
 .../action/OpenAndFireTriggerAction.java           |  66 +++
 .../toolwindow/action/OpenAndRunActionAction.java  |  72 ++++
 .../toolwindow/action/RefreshTreeAction.java       |  40 ++
 .../toolwindow/actiongroup/WhiskActionGroup.java   |  39 ++
 .../toolwindow/actiongroup/WhiskEndpointGroup.java |  32 ++
 .../actiongroup/WhiskNamespaceGroup.java           |  39 ++
 .../toolwindow/actiongroup/WhiskPackageGroup.java  |  31 ++
 .../toolwindow/actiongroup/WhiskTriggerGroup.java  |  39 ++
 .../actiongroup/WhiskTriggerRootGroup.java         |  28 ++
 .../listener/OpenActionControlActionListener.java  |  31 ++
 .../listener/OpenTriggerControlActionListener.java |  31 ++
 .../listener/RefreshWhiskTreeListener.java         |  27 ++
 .../explorer/toolwindow/tree/WhiskTree.java        | 178 ++++++++
 .../toolwindow/tree/WhiskTreeCellRenderer.java     |  74 ++++
 .../toolwindow/ui/WhiskExplorerWindowForm.form     |  29 ++
 .../toolwindow/ui/WhiskExplorerWindowForm.java     | 462 +++++++++++++++++++++
 .../run/toolwindow/WhiskRunWindowFactory.java      | 173 ++++++++
 .../action/OpenActionManagerDialogAction.java      |  37 ++
 .../toolwindow/action/RefreshMetaDataAction.java   |  37 ++
 .../run/toolwindow/action/RunActionAction.java     |  36 ++
 .../listener/OpenActionManagerListener.java        |  29 ++
 .../OpenAndFireTriggerControlActionListener.java   |  31 ++
 .../OpenAndRunActionControlActionListener.java     |  31 ++
 .../listener/RefreshActionOrTriggerListener.java   |  29 ++
 .../run/toolwindow/listener/RunActionListener.java |  28 ++
 .../run/toolwindow/ui/WhiskRunWindowForm.form      |  73 ++++
 .../run/toolwindow/ui/WhiskRunWindowForm.java      | 281 +++++++++++++
 .../wskdeploy/dialog/WskDeployCmdDialog.java       |  67 +++
 .../wskdeploy/dialog/WskDeployTempleteDialog.java  |  65 +++
 .../dialog/ui/WskDeployCmdDialogForm.form          |  54 +++
 .../dialog/ui/WskDeployCmdDialogForm.java          | 100 +++++
 .../toolwindow/WskDeployWindowFactory.java         |  40 ++
 .../action/ChooseWskDeployBinAction.java           |  59 +++
 .../action/CreateManifestTemplateAction.java       |  90 ++++
 .../toolwindow/action/RefreshWskDeployAction.java  |  40 ++
 .../listener/ChooseWskDeployBinaryListener.java    |  28 ++
 .../listener/RefreshWskDeployManifestListener.java |  27 ++
 .../toolwindow/tree/WskDeployTreeCellRenderer.java |  59 +++
 .../toolwindow/tree/WskDeployTreeModel.java        | 106 +++++
 .../toolwindow/ui/WskDeployWindowForm.form         |  45 ++
 .../toolwindow/ui/WskDeployWindowForm.java         | 213 ++++++++++
 src/main/resources/META-INF/plugin.xml             | 121 ++++++
 src/main/resources/icons/arrowDown.svg             |   1 +
 src/main/resources/icons/arrowUp.svg               |   1 +
 src/main/resources/icons/event.svg                 |   1 +
 src/main/resources/icons/eventGroup.svg            |   1 +
 .../icons/language/docker/DockerCompose.svg        |   1 +
 src/main/resources/icons/language/go/go.svg        |  27 ++
 .../resources/icons/language/go/go_template.svg    |   1 +
 .../icons/language/go/go_template_dark.svg         |   1 +
 src/main/resources/icons/language/java/java 2.svg  |   1 +
 src/main/resources/icons/language/java/java.svg    |   1 +
 .../icons/language/javascript/javascript.svg       |   1 +
 .../resources/icons/language/javascript/js.svg     |   1 +
 .../resources/icons/language/javascript/nodejs.svg |   1 +
 .../icons/language/php/localPHP_interpreter.svg    |   1 +
 src/main/resources/icons/language/php/php-icon.svg |   1 +
 src/main/resources/icons/language/php/php.svg      |  28 ++
 src/main/resources/icons/language/python/py.svg    |  26 ++
 .../resources/icons/language/python/python.svg     |   1 +
 .../resources/icons/language/python/pythonFile.svg |   1 +
 src/main/resources/icons/language/ruby/rb.svg      |  28 ++
 src/main/resources/icons/language/ruby/ruby.svg    |   1 +
 .../resources/icons/language/ruby/ruby_file.svg    |   1 +
 src/main/resources/icons/language/ruby/rxml.svg    |   1 +
 .../resources/icons/language/swift/SwiftLang.svg   |   1 +
 src/main/resources/icons/language/swift/sw.svg     |  27 ++
 src/main/resources/icons/openDiskHover.svg         |   1 +
 src/main/resources/icons/run_anything.svg          |   1 +
 src/main/resources/icons/seq.svg                   |  30 ++
 src/main/resources/icons/sh.svg                    |  27 ++
 src/main/resources/icons/ssh.svg                   |   1 +
 src/main/resources/icons/yaml.svg                  |   1 +
 src/main/resources/template/HOW-TO-DEPLOY.md       |  75 ++++
 src/main/resources/template/manifest.yaml          |  46 ++
 src/main/resources/template/src/index.js           |   7 +
 src/main/resources/template/src/index.test.js      |  12 +
 src/main/resources/template/src/package.json       |  18 +
 .../utils/json/EmptyJsonParserUtilsTests.java      | 225 ++++++++++
 .../common/utils/whisk/WhiskUtilsTest.java         |  34 ++
 .../common/whisk/model/WhiskActionJsonTest.java    | 239 +++++++++++
 .../common/whisk/model/WhiskActionTest.java        | 220 ++++++++++
 .../common/whisk/model/WhiskActivationTest.java    | 124 ++++++
 .../intellij/common/whisk/model/WhiskAuthTest.java |  47 +++
 .../common/whisk/model/WhiskEndpointTest.java      |  60 +++
 .../common/whisk/model/WhiskPackageJsonTest.java   |  85 ++++
 .../common/whisk/model/WhiskPackageTest.java       |  90 ++++
 .../common/whisk/model/WhiskTriggerJsonTest.java   |  75 ++++
 .../openwhisk/intellij/utils/AnnotationHelper.java | 115 +++++
 .../openwhisk/intellij/utils/FileHelper.java       |  22 +
 src/test/resources/action.json                     |  34 ++
 src/test/resources/actions.json                    | 148 +++++++
 src/test/resources/activation_with_log.json        |  55 +++
 src/test/resources/activations.json                |  86 ++++
 src/test/resources/binary-action.json              |  34 ++
 src/test/resources/blackbox-action.json            |  34 ++
 src/test/resources/java-action.json                |  35 ++
 src/test/resources/package.json                    |  57 +++
 src/test/resources/packages.json                   |  54 +++
 src/test/resources/sequence-action.json            |  35 ++
 src/test/resources/trigger.json                    |  24 ++
 src/test/resources/triggers.json                   |  36 ++
 240 files changed, 17751 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..312b385
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+gradle.properties
+.idea
+.gradle
+build
+out
+
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..809ba07
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,203 @@
+  
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright 2020-present NAVER Corp.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..f59a67a
--- /dev/null
+++ b/README.md
@@ -0,0 +1,53 @@
+# OpenWhisk Intellij Plugin
+**OpenWhisk-intellij-support**  is an open source Intellij Plugin for [Apache OpenWhisk](https://github.com/apache/openwhisk). It assists users to develop/deploy/manage OpenWhisk functions in Intellij.
+ 
+## Prerequisites
+Install the dependencies below to use full features:
+* Intellij >= 2018.1.8
+* [wskdeploy](https://github.com/apache/openwhisk-wskdeploy/releases)
+
+This extension finds the `.wskprops` configuration file located in the home path and connects to the Openwhisk server automatically. Set up your configuration referred to the [cli docs](https://github.com/apache/openwhisk/blob/master/docs/cli.md#openwhisk-cli).
+
+## Feature
+### OpenWhisk Explorer
+* Explore all entities in your endpoints/namespaces.
+    * The `.wskprops` file is automatically registered.
+    * You can add the API host manually.
+    * You can add namespace manually by API auth key.
+* Show the action code with syntax highlighting.
+* [Soon] Edit the action code on the remote server.
+* Invoke the action remotely and get the activation result.
+* Show a list of actions related to the sequence action.
+* Show information about the trigger and related rules.
+* Show activations related to the action (Same as `wsk activation list <action>`).
+* Show detailed information of the activation (Same `as wsk activation get <activation_id>`).
+* Update parameters of the action, package, and trigger.
+
+### Manifest View
+* List up manifest YAML files in the workspace.
+* Deploy/Undeploy OpenWhisk packages with manifest (via wskdeploy).
+    * Deploy with the deployment file.
+    * Deploy with multiple credentials.
+
+## How to debug in your local
+```bash
+./gradlew runIde
+```
+
+## License
+
+```
+Copyright 2020-present NAVER Corp.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+```
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..c823c69
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,61 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+plugins {
+    id 'java'
+    id 'org.jetbrains.intellij' version '0.4.18'
+    id 'checkstyle'
+}
+
+group 'com.navercorp.openwhisk.intellij'
+version '1.1.4'
+
+sourceCompatibility = 11
+
+repositories {
+    mavenCentral()
+}
+
+dependencies {
+    implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.9.8'
+    testImplementation group: 'junit', name: 'junit', version: '4.12'
+}
+
+intellij {
+    version '2020.1.2'
+}
+
+patchPluginXml {
+    sinceBuild '181.*'
+    untilBuild '203.*'
+}
+
+publishPlugin {
+    token = System.getenv("ORG_GRADLE_PROJECT_intellijPublishToken")
+    channels 'beta'
+}
+
+checkstyle {
+    configFile = file("${rootDir}/config/checkstyle/checkstyle.xml")
+    toolVersion = 8.35
+}
+
+tasks.withType(Checkstyle) {
+    reports {
+        xml.enabled false
+        html.enabled true
+    }
+}
diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml
new file mode 100644
index 0000000..51b20b3
--- /dev/null
+++ b/config/checkstyle/checkstyle.xml
@@ -0,0 +1,202 @@
+<?xml version="1.0"?>
+<!DOCTYPE module PUBLIC
+        "-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
+        "https://checkstyle.org/dtds/configuration_1_3.dtd">
+
+<!--
+  Checkstyle configuration that checks the sun coding conventions from:
+    - the Java Language Specification at
+      https://docs.oracle.com/javase/specs/jls/se11/html/index.html
+    - the Sun Code Conventions at https://www.oracle.com/java/technologies/javase/codeconventions-contents.html
+    - the Javadoc guidelines at
+      https://www.oracle.com/technical-resources/articles/java/javadoc-tool.html
+    - the JDK Api documentation https://docs.oracle.com/en/java/javase/11/
+    - some best practices
+  Checkstyle is very configurable. Be sure to read the documentation at
+  https://checkstyle.org (or in your downloaded distribution).
+  Most Checks are configurable, be sure to consult the documentation.
+  To completely disable a check, just comment it out or delete it from the file.
+  To suppress certain violations please review suppression filters.
+  Finally, it is worth reading the documentation.
+-->
+
+<module name="Checker">
+    <!--
+        If you set the basedir property below, then all reported file
+        names will be relative to the specified directory. See
+        https://checkstyle.org/config.html#Checker
+        <property name="basedir" value="${basedir}"/>
+    -->
+    <property name="severity" value="error"/>
+
+    <property name="fileExtensions" value="java, properties, xml"/>
+
+    <!-- Excludes all 'module-info.java' files              -->
+    <!-- See https://checkstyle.org/config_filefilters.html -->
+    <module name="BeforeExecutionExclusionFileFilter">
+        <property name="fileNamePattern" value="module\-info\.java$"/>
+    </module>
+
+    <!-- https://checkstyle.org/config_filters.html#SuppressionFilter -->
+    <module name="SuppressionFilter">
+        <property name="file" value="${org.checkstyle.sun.suppressionfilter.config}"
+                  default="checkstyle-suppressions.xml" />
+        <property name="optional" value="true"/>
+    </module>
+
+    <!-- Checks that a package-info.java file exists for each package.     -->
+    <!-- See https://checkstyle.org/config_javadoc.html#JavadocPackage -->
+    <!--<module name="JavadocPackage"/>-->
+
+    <!-- Checks whether files end with a new line.                        -->
+    <!-- See https://checkstyle.org/config_misc.html#NewlineAtEndOfFile -->
+    <module name="NewlineAtEndOfFile"/>
+
+    <!-- Checks that property files contain the same keys.         -->
+    <!-- See https://checkstyle.org/config_misc.html#Translation -->
+    <module name="Translation"/>
+
+    <!-- Checks for Size Violations.                    -->
+    <!-- See https://checkstyle.org/config_sizes.html -->
+    <module name="FileLength"/>
+    <module name="LineLength">
+        <property name="fileExtensions" value="java"/>
+        <property name="max" value="160"/>
+    </module>
+
+    <!-- Checks for whitespace                               -->
+    <!-- See https://checkstyle.org/config_whitespace.html -->
+    <module name="FileTabCharacter"/>
+
+    <!-- Miscellaneous other checks.                   -->
+    <!-- See https://checkstyle.org/config_misc.html -->
+    <module name="RegexpSingleline">
+        <property name="format" value="\s+$"/>
+        <property name="minimum" value="0"/>
+        <property name="maximum" value="0"/>
+        <property name="message" value="Line has trailing spaces."/>
+    </module>
+
+    <!-- Checks for Headers                                -->
+    <!-- See https://checkstyle.org/config_header.html   -->
+    <!-- <module name="Header"> -->
+    <!--   <property name="headerFile" value="${checkstyle.header.file}"/> -->
+    <!--   <property name="fileExtensions" value="java"/> -->
+    <!-- </module> -->
+
+    <module name="TreeWalker">
+
+        <!-- Checks for Javadoc comments.                     -->
+        <!-- See https://checkstyle.org/config_javadoc.html -->
+        <!--<module name="InvalidJavadocPosition"/>-->
+        <!--<module name="JavadocMethod"/>-->
+        <module name="JavadocType"/>
+        <!--<module name="JavadocVariable"/>-->
+        <module name="JavadocStyle"/>
+        <!--<module name="MissingJavadocMethod"/>-->
+
+        <!-- Checks for Naming Conventions.                  -->
+        <!-- See https://checkstyle.org/config_naming.html -->
+        <module name="ConstantName"/>
+        <module name="LocalFinalVariableName"/>
+        <module name="LocalVariableName"/>
+        <module name="MemberName"/>
+        <module name="MethodName"/>
+        <module name="PackageName"/>
+        <module name="ParameterName"/>
+        <module name="StaticVariableName"/>
+        <module name="TypeName"/>
+
+        <!-- Checks for imports                              -->
+        <!-- See https://checkstyle.org/config_imports.html -->
+        <module name="AvoidStarImport">
+            <property name="allowClassImports" value="true"/>
+            <property name="allowStaticMemberImports" value="true"/>
+        </module>
+        <module name="IllegalImport"/> <!-- defaults to sun.* packages -->
+        <module name="RedundantImport"/>
+        <module name="UnusedImports">
+            <property name="processJavadoc" value="false"/>
+        </module>
+
+        <!-- Checks for Size Violations.                    -->
+        <!-- See https://checkstyle.org/config_sizes.html -->
+        <module name="MethodLength">
+            <property name="max" value="300"/>
+        </module>
+        <module name="ParameterNumber">
+            <property name="max" value="20"/>
+        </module>
+
+        <!-- Checks for whitespace                               -->
+        <!-- See https://checkstyle.org/config_whitespace.html -->
+        <module name="EmptyForIteratorPad"/>
+        <module name="GenericWhitespace"/>
+        <module name="MethodParamPad"/>
+        <module name="NoWhitespaceAfter"/>
+        <module name="NoWhitespaceBefore"/>
+        <module name="OperatorWrap"/>
+        <module name="ParenPad"/>
+        <module name="TypecastParenPad"/>
+        <module name="WhitespaceAfter"/>
+        <module name="WhitespaceAround"/>
+
+        <!-- Modifier Checks                                    -->
+        <!-- See https://checkstyle.org/config_modifiers.html -->
+        <module name="ModifierOrder"/>
+        <module name="RedundantModifier"/>
+
+        <!-- Checks for blocks. You know, those {}'s         -->
+        <!-- See https://checkstyle.org/config_blocks.html -->
+        <module name="AvoidNestedBlocks"/>
+        <module name="EmptyBlock"/>
+        <module name="LeftCurly"/>
+        <module name="NeedBraces">
+            <property name="allowSingleLineStatement" value="true"/>
+        </module>
+        <module name="RightCurly"/>
+
+        <!-- Checks for common coding problems               -->
+        <!-- See https://checkstyle.org/config_coding.html -->
+        <module name="EmptyStatement"/>
+        <module name="EqualsHashCode"/>
+        <module name="HiddenField">
+            <property name="ignoreConstructorParameter" value="true"/>
+            <property name="ignoreSetter" value="true"/>
+        </module>
+        <module name="IllegalInstantiation"/>
+        <module name="InnerAssignment"/>
+        <!--<module name="MagicNumber"/>-->
+        <module name="MissingSwitchDefault"/>
+        <module name="MultipleVariableDeclarations"/>
+        <module name="SimplifyBooleanExpression"/>
+        <module name="SimplifyBooleanReturn"/>
+
+        <!-- Checks for class design                         -->
+        <!-- See https://checkstyle.org/config_design.html -->
+        <!--<module name="DesignForExtension"/>-->
+        <!--<module name="FinalClass"/>-->
+        <module name="HideUtilityClassConstructor"/>
+        <module name="InterfaceIsType"/>
+        <module name="VisibilityModifier">
+            <property name="protectedAllowed" value="true"/>
+            <property name="allowPublicFinalFields" value="true"/>
+        </module>
+
+        <!-- Miscellaneous other checks.                   -->
+        <!-- See https://checkstyle.org/config_misc.html -->
+        <module name="ArrayTypeStyle"/>
+        <!--<module name="FinalParameters">-->
+        <module name="TodoComment"/>
+        <module name="UpperEll"/>
+
+        <!-- https://checkstyle.org/config_filters.html#SuppressionXpathFilter -->
+        <module name="SuppressionXpathFilter">
+            <property name="file" value="${org.checkstyle.sun.suppressionxpathfilter.config}"
+                      default="checkstyle-xpath-suppressions.xml" />
+            <property name="optional" value="true"/>
+        </module>
+
+    </module>
+
+</module>
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..28861d2
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..0517884
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,19 @@
+# Copyright 2020-present NAVER Corp.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.6-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
new file mode 100755
index 0000000..d9508bb
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,186 @@
+#!/usr/bin/env sh
+
+# Copyright 2020-present NAVER Corp.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+    echo "$*"
+}
+
+die () {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+  NONSTOP* )
+    nonstop=true
+    ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+    JAVACMD=`cygpath --unix "$JAVACMD"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=$((i+1))
+    done
+    case $i in
+        (0) set -- ;;
+        (1) set -- "$args0" ;;
+        (2) set -- "$args0" "$args1" ;;
+        (3) set -- "$args0" "$args1" "$args2" ;;
+        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Escape application args
+save () {
+    for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+    echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+  cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..d88ae3b
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,98 @@
+@rem Copyright 2020-present NAVER Corp.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem    http://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem  Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 0000000..7ff0013
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1,18 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+rootProject.name = 'intellij-plugin'
+
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/DialogWrapperWithApply.java b/src/main/java/com/navercorp/openwhisk/intellij/common/DialogWrapperWithApply.java
new file mode 100644
index 0000000..7d56d5a
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/DialogWrapperWithApply.java
@@ -0,0 +1,71 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.DialogWrapper;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.event.ActionEvent;
+
+public abstract class DialogWrapperWithApply extends DialogWrapper {
+
+    protected ApplyAction myApplyAction;
+
+    protected DialogWrapperWithApply(@Nullable Project project, boolean canBeParent) {
+        super(project, canBeParent);
+        myApplyAction = new DialogWrapperWithApply.ApplyAction("Apply");
+    }
+
+    /**
+     * This method is invoked by default implementation of "Apply" action.
+     * This is convenient place to override functionality of "Apply" action.
+     */
+    protected void doApplyAction() {
+        // do noting
+    }
+
+    @NotNull
+    @Override
+    protected Action[] createActions() {
+        return new Action[]{
+                myCancelAction, myOKAction, myApplyAction
+        };
+    }
+
+    protected class ApplyAction extends DialogWrapperAction {
+
+        private boolean isApplied;
+
+        protected ApplyAction(@NotNull String name) {
+            super(name);
+            isApplied = false;
+        }
+
+        @Override
+        protected void doAction(ActionEvent e) {
+            doApplyAction();
+            isApplied = true;
+        }
+
+        public boolean isApplied() {
+            return isApplied;
+        }
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/Icons.java b/src/main/java/com/navercorp/openwhisk/intellij/common/Icons.java
new file mode 100644
index 0000000..eaa869e
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/Icons.java
@@ -0,0 +1,72 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.openapi.util.IconLoader;
+import gnu.trove.THashMap;
+
+import javax.swing.*;
+import java.util.Map;
+
+public class Icons {
+
+    protected Icons() {
+        throw new UnsupportedOperationException("Utility classes should not have a public or default constructor.");
+    }
+
+    private static final Map<String, Icon> REGISTERED_ICONS = new THashMap<>();
+
+    public static final Icon ENTITY_TRIGGER = load("/icons/event.svg");
+    public static final Icon ENTITY_TRIGGER_ROOT = load("/icons/eventGroup.svg");
+
+    public static final Icon ARROW_UP = load("/icons/arrowUp.svg");
+    public static final Icon ARROW_DOWN = load("/icons/arrowDown.svg");
+
+    public static final Icon OPEN_DISK_HOVER = load("/icons/openDiskHover.svg");
+    public static final Icon RUN_ANYTHING = load("/icons/run_anything.svg");
+    public static final Icon YAML = load("/icons/yaml.svg");
+
+    public static final Icon KIND_JAVA = load("/icons/language/java/java.svg");
+    public static final Icon KIND_JS = load("/icons/language/javascript/javascript.svg");
+    public static final Icon KIND_GO = load("/icons/language/go/go.svg");
+    public static final Icon KIND_PYTHON = load("/icons/language/python/py.svg");
+    public static final Icon KIND_PHP = load("/icons/language/php/php.svg");
+    public static final Icon KIND_RUBY = load("/icons/language/ruby/rb.svg");
+    public static final Icon KIND_SWIFT = load("/icons/language/swift/sw.svg");
+    public static final Icon KIND_SEQUENCE = load("/icons/seq.svg");
+    public static final Icon KIND_DOCKER = load("/icons/language/docker/DockerCompose.svg");
+
+    private static Icon load(String path) {
+        try {
+            return IconLoader.getIcon(path);
+        } catch (Throwable t) {
+            return AllIcons.General.Warning;
+        }
+    }
+
+    private static Icon load(String key, String path) {
+        Icon icon = load(path);
+        REGISTERED_ICONS.put(key, icon);
+        return icon;
+    }
+
+    public static Icon getIcon(String key) {
+        return REGISTERED_ICONS.get(key);
+    }
+
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/error/NotExistFileException.java b/src/main/java/com/navercorp/openwhisk/intellij/common/error/NotExistFileException.java
new file mode 100644
index 0000000..04c94f4
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/error/NotExistFileException.java
@@ -0,0 +1,25 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.error;
+
+public class NotExistFileException extends RuntimeException {
+
+    public NotExistFileException(String msg) {
+        super(msg);
+    }
+
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/notification/SimpleNotifier.java b/src/main/java/com/navercorp/openwhisk/intellij/common/notification/SimpleNotifier.java
new file mode 100644
index 0000000..a0cc5e4
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/notification/SimpleNotifier.java
@@ -0,0 +1,51 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.notification;
+
+import com.intellij.notification.Notification;
+import com.intellij.notification.NotificationDisplayType;
+import com.intellij.notification.NotificationGroup;
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.project.Project;
+import org.jetbrains.annotations.NotNull;
+
+public class SimpleNotifier {
+
+    private SimpleNotifier() {
+    }
+
+    private static class LazyHolder {
+        private static final SimpleNotifier INSTANCE = new SimpleNotifier();
+    }
+
+    public static SimpleNotifier getInstance() {
+        return LazyHolder.INSTANCE;
+    }
+
+    private static final NotificationGroup NOTIFICATION_GROUP =
+            new NotificationGroup("Simple Notification Group", NotificationDisplayType.STICKY_BALLOON, true);
+
+    public Notification notify(@NotNull String content, @NotNull final NotificationType type) {
+        return notify(null, content, type);
+    }
+
+    public Notification notify(Project project, @NotNull String content, @NotNull final NotificationType type) {
+        final Notification notification = NOTIFICATION_GROUP.createNotification(content, type);
+        notification.notify(project);
+        return notification;
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/service/WhiskService.java b/src/main/java/com/navercorp/openwhisk/intellij/common/service/WhiskService.java
new file mode 100644
index 0000000..cd58f06
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/service/WhiskService.java
@@ -0,0 +1,57 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.service;
+
+import com.intellij.openapi.components.PersistentStateComponent;
+import com.intellij.openapi.components.State;
+import com.intellij.openapi.components.Storage;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.util.xmlb.XmlSerializerUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+@State(
+        name = "whisk",
+        storages = {
+                @Storage("whisk.xml")
+        }
+)
+public class WhiskService implements PersistentStateComponent<WhiskService> {
+    private static final Logger LOG = Logger.getInstance(WhiskService.class);
+
+    private String endpoints;
+
+    @Nullable
+    @Override
+    public WhiskService getState() {
+        return this;
+    }
+
+    @Override
+    public void loadState(@NotNull WhiskService state) {
+        LOG.info("Save state");
+        XmlSerializerUtil.copyBean(state, this);
+    }
+
+    public String getEndpoints() {
+        return endpoints;
+    }
+
+    public void setEndpoints(String endpoints) {
+        this.endpoints = endpoints;
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/service/WskDeployService.java b/src/main/java/com/navercorp/openwhisk/intellij/common/service/WskDeployService.java
new file mode 100644
index 0000000..9627678
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/service/WskDeployService.java
@@ -0,0 +1,66 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.service;
+
+import com.intellij.openapi.components.PersistentStateComponent;
+import com.intellij.openapi.components.State;
+import com.intellij.openapi.components.Storage;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.util.xmlb.XmlSerializerUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+@State(
+        name = "wskdeploy",
+        storages = {
+                @Storage("wskdeploy.xml")
+        }
+)
+public class WskDeployService implements PersistentStateComponent<WskDeployService> {
+    private static final Logger LOG = Logger.getInstance(WskDeployService.class);
+
+    private String wskdeployPath;
+    private String wskdeployName;
+
+    @Nullable
+    @Override
+    public WskDeployService getState() {
+        return this;
+    }
+
+    @Override
+    public void loadState(@NotNull WskDeployService state) {
+        LOG.info("Save wskdeploy");
+        XmlSerializerUtil.copyBean(state, this);
+    }
+
+    public String getWskdeployPath() {
+        return wskdeployPath;
+    }
+
+    public void setWskdeployPath(String wskdeployPath) {
+        this.wskdeployPath = wskdeployPath;
+    }
+
+    public String getWskdeployName() {
+        return wskdeployName;
+    }
+
+    public void setWskdeployName(String wskdeployName) {
+        this.wskdeployName = wskdeployName;
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/utils/CommandUtils.java b/src/main/java/com/navercorp/openwhisk/intellij/common/utils/CommandUtils.java
new file mode 100644
index 0000000..4f35ec5
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/utils/CommandUtils.java
@@ -0,0 +1,60 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.utils;
+
+import com.intellij.openapi.diagnostic.Logger;
+import com.navercorp.openwhisk.intellij.common.whisk.model.wskdeploy.WskDeployCmdResponse;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+public class CommandUtils {
+
+    private static final Logger LOG = Logger.getInstance(CommandUtils.class);
+
+    protected CommandUtils() {
+        throw new UnsupportedOperationException("Utility classes should not have a public or default constructor.");
+    }
+
+    public static WskDeployCmdResponse runCommand(String[] command)
+            throws IOException, InterruptedException {
+        ProcessBuilder builder = new ProcessBuilder(command);
+        Process process = builder.start();
+
+        // read std output
+        BufferedReader reader =
+                new BufferedReader(new InputStreamReader(process.getInputStream()));
+        StringBuilder output = new StringBuilder();
+        String line;
+        while ((line = reader.readLine()) != null) {
+            output.append(line + "\n");
+        }
+
+        // read error output
+        BufferedReader errReader =
+                new BufferedReader(new InputStreamReader(process.getErrorStream()));
+        StringBuilder errOutput = new StringBuilder();
+        String errLine;
+        while ((errLine = errReader.readLine()) != null) {
+            errOutput.append(errLine + "\n");
+        }
+
+        int exitCode = process.waitFor();
+        return new WskDeployCmdResponse(exitCode, output.toString(), errOutput.toString());
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/utils/EventUtils.java b/src/main/java/com/navercorp/openwhisk/intellij/common/utils/EventUtils.java
new file mode 100644
index 0000000..df936da
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/utils/EventUtils.java
@@ -0,0 +1,57 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.utils;
+
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.progress.ProcessCanceledException;
+import com.intellij.openapi.project.Project;
+import com.intellij.util.messages.MessageBus;
+import com.intellij.util.messages.MessageBusConnection;
+import com.intellij.util.messages.Topic;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public class EventUtils {
+
+    protected EventUtils() {
+        throw new UnsupportedOperationException("Utility classes should not have a public or default constructor.");
+    }
+
+    @NotNull
+    private static MessageBusConnection connect(@NotNull Project project, @Nullable Disposable parentDisposable) {
+        MessageBus messageBus = project.getMessageBus();
+        return parentDisposable == null ? messageBus.connect(project) : messageBus.connect(parentDisposable);
+    }
+
+    public static <T> void subscribe(Project project, @Nullable Disposable parentDisposable, Topic<T> topic, T handler) {
+        MessageBusConnection messageBusConnection = connect(project, parentDisposable);
+        messageBusConnection.subscribe(topic, handler);
+
+    }
+
+    public static <T> void publish(@Nullable Project project, Topic<T> topic, ParametricRunnable.Basic<T> callback) {
+        if (project != null) {
+            try {
+                MessageBus messageBus = project.getMessageBus();
+                T publisher = messageBus.syncPublisher(topic);
+                callback.run(publisher);
+            } catch (ProcessCanceledException ignore) {
+            }
+        }
+
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/utils/FileUtils.java b/src/main/java/com/navercorp/openwhisk/intellij/common/utils/FileUtils.java
new file mode 100644
index 0000000..e396c7d
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/utils/FileUtils.java
@@ -0,0 +1,88 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.utils;
+
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.ExecutableWhiskAction;
+import com.navercorp.openwhisk.intellij.common.whisk.model.activation.WhiskActivationWithLogs;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.Optional;
+
+import static com.navercorp.openwhisk.intellij.common.utils.JsonParserUtils.writeWhiskActivationToJson;
+
+public class FileUtils {
+
+    private static final Logger LOG = Logger.getInstance(FileUtils.class);
+
+    protected FileUtils() {
+        throw new UnsupportedOperationException("Utility classes should not have a public or default constructor.");
+    }
+
+    /**
+     * It accumulates in the .gradle/caches subfolder and files are not automatically cleared unless you delete the cache directory.
+     * TODO Delete the cache file globally.
+     * TODO Use LightVirtualFile instead of local file.
+     */
+    public static VirtualFile writeActionToFile(String basePath, ExecutableWhiskAction action) throws IOException {
+        File dir = new File(basePath);
+        if (!dir.exists()) {
+            dir.mkdir();
+        }
+
+        final String path = basePath + "/" + action.getName() + action.getKindExtension();
+        File f = new File(path);
+        LOG.info("Open action file: " + f.getAbsolutePath());
+
+        FileWriter fw = new FileWriter(f);
+
+        if (action.getExec().isBinary()) {
+            fw.write("This action was created as a zip file, and you can't see the code here.");
+        } else {
+            String code = Optional.ofNullable(action.getExec().getCode()).orElse("This action is empty");
+            fw.write(code);
+        }
+        fw.close();
+
+        return VfsUtil.findFileByIoFile(f, true);
+    }
+
+    /**
+     * It accumulates in the .gradle/caches subfolder and files are not automatically cleared unless you delete the cache directory.
+     * TODO Delete the cache file globally.
+     * TODO Use LightVirtualFile instead of local file.
+     */
+    public static VirtualFile writeActivationToFile(String basePath, WhiskActivationWithLogs activation) throws IOException {
+        File dir = new File(basePath);
+        if (!dir.exists()) {
+            dir.mkdir();
+        }
+
+        File f = new File(basePath + "/" + activation.getName() + "-" + activation.getActivationId() + ".json");
+        LOG.info("Open activation file: " + f.getAbsolutePath());
+
+        FileWriter fw = new FileWriter(f);
+        fw.write(writeWhiskActivationToJson(activation));
+        fw.close();
+
+        return VfsUtil.findFileByIoFile(f, true);
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/utils/JsonParserUtils.java b/src/main/java/com/navercorp/openwhisk/intellij/common/utils/JsonParserUtils.java
new file mode 100644
index 0000000..656482c
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/utils/JsonParserUtils.java
@@ -0,0 +1,214 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.utils;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+import org.apache.commons.lang.StringUtils;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskEndpoint;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.ExecutableWhiskAction;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.WhiskActionMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.model.activation.WhiskActivationMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.model.activation.WhiskActivationWithLogs;
+import com.navercorp.openwhisk.intellij.common.whisk.model.exec.CodeExec;
+import com.navercorp.openwhisk.intellij.common.whisk.model.exec.CodeExecSerializer;
+import com.navercorp.openwhisk.intellij.common.whisk.model.pkg.WhiskPackage;
+import com.navercorp.openwhisk.intellij.common.whisk.model.pkg.WhiskPackageWithActions;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.ExecutableWhiskTrigger;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.WhiskRule;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.WhiskTriggerMetaData;
+
+import java.io.IOException;
+import java.util.*;
+
+public class JsonParserUtils {
+    private static ObjectMapper mapper = new ObjectMapper();
+    private static SimpleModule simpleModule = new SimpleModule();
+
+    protected JsonParserUtils() {
+        throw new UnsupportedOperationException("Utility classes should not have a public or default constructor.");
+    }
+
+    static {
+        simpleModule.addSerializer(CodeExec.class, new CodeExecSerializer());
+        mapper.registerModule(simpleModule);
+    }
+
+    public static List<WhiskActionMetaData> parseWhiskActions(String actions) throws IOException {
+        if (StringUtils.isNotEmpty(actions)) {
+            return Arrays.asList(mapper.readValue(actions, WhiskActionMetaData[].class));
+        } else {
+            return new ArrayList<>();
+        }
+    }
+
+    public static Optional<ExecutableWhiskAction> parseWhiskAction(String action) throws IOException {
+        if (StringUtils.isNotEmpty(action)) {
+            return Optional.of(mapper.readValue(action, ExecutableWhiskAction.class));
+        } else {
+            return Optional.empty();
+        }
+    }
+
+    public static List<WhiskPackage> parseWhiskPackages(String packages) throws IOException {
+        if (StringUtils.isNotEmpty(packages)) {
+            return Arrays.asList(mapper.readValue(packages, WhiskPackage[].class));
+        } else {
+            return new ArrayList<>();
+        }
+    }
+
+    public static Optional<WhiskPackageWithActions> parseWhiskPackage(String pkg) throws IOException {
+        if (StringUtils.isNotEmpty(pkg)) {
+            return Optional.of(mapper.readValue(pkg, WhiskPackageWithActions.class));
+        } else {
+            return Optional.empty();
+        }
+    }
+
+    // TODO test
+    public static List<WhiskEndpoint> parseWhiskEndpoints(String endpoints) throws IOException {
+        if (StringUtils.isNotEmpty(endpoints)) {
+            return Arrays.asList(mapper.readValue(endpoints, WhiskEndpoint[].class));
+        } else {
+            return new ArrayList<>();
+        }
+    }
+
+    // TODO test
+    public static String writeEndpointsToJson(List<WhiskEndpoint> ep) throws JsonProcessingException {
+        return mapper.writeValueAsString(ep);
+    }
+
+    // TODO test
+    public static String[] parseWhiskNamespace(String namespaces) throws IOException {
+        if (StringUtils.isNotEmpty(namespaces)) {
+            return mapper.readValue(namespaces, String[].class);
+        } else {
+            return new String[]{};
+        }
+    }
+
+    // TODO test
+    public static List<WhiskActivationMetaData> parseWhiskActivations(String actions) throws IOException {
+        if (StringUtils.isNotEmpty(actions)) {
+            return Arrays.asList(mapper.readValue(actions, WhiskActivationMetaData[].class));
+        } else {
+            return new ArrayList<>();
+        }
+    }
+
+    // TODO test
+    public static Optional<WhiskActivationWithLogs> parseWhiskActivation(String actions) throws IOException {
+        if (StringUtils.isNotEmpty(actions)) {
+            return Optional.of(mapper.readValue(actions, WhiskActivationWithLogs.class));
+        } else {
+            return Optional.empty();
+        }
+    }
+
+    // TODO test
+    public static String writeWhiskActivationToJson(WhiskActivationWithLogs activation) throws JsonProcessingException {
+        return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(activation);
+    }
+
+    public static List<WhiskTriggerMetaData> parseWhiskTriggers(String triggers) throws IOException {
+        if (StringUtils.isNotEmpty(triggers)) {
+            return Arrays.asList(mapper.readValue(triggers, WhiskTriggerMetaData[].class));
+        } else {
+            return new ArrayList<>();
+        }
+    }
+
+    public static Optional<ExecutableWhiskTrigger> parseWhiskTrigger(String triggers) throws IOException {
+        if (StringUtils.isNotEmpty(triggers)) {
+            return Optional.of(mapper.readValue(triggers, ExecutableWhiskTrigger.class));
+        } else {
+            return Optional.empty();
+        }
+    }
+
+    // TODO test
+    public static Optional<WhiskRule> parseWhiskRule(String rule) throws IOException {
+        if (StringUtils.isNotEmpty(rule)) {
+            return Optional.of(mapper.readValue(rule, WhiskRule.class));
+        } else {
+            return Optional.empty();
+        }
+    }
+
+    // TODO test
+    public static String writeParameterToJson(List<Map<String, Object>> params) throws JsonProcessingException {
+        return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(ParameterUtils.listMapToMap(params));
+    }
+
+    // TODO test
+    public static List<Map<String, Object>> parseListMap(String json) throws IOException {
+        if (StringUtils.isNotEmpty(json)) {
+            return mapper.readValue(json, new TypeReference<List<Map<String, Object>>>() {
+            });
+        } else {
+            return new ArrayList<>();
+        }
+    }
+
+    // TODO test
+    public static String writeListMapToJson(List<Map<String, Object>> maps) throws JsonProcessingException {
+        return mapper.writer().writeValueAsString(maps);
+    }
+
+    // TODO test
+    public static Map<String, Object> parseMap(String json) {
+        if (StringUtils.isNotEmpty(json)) {
+            try {
+                return mapper.readValue(json, Map.class);
+            } catch (IOException e) {
+                return new LinkedHashMap<>();
+            }
+        } else {
+            return new LinkedHashMap<>();
+        }
+    }
+
+    // TODO test
+    public static String writeMapToJson(Map<String, Object> map) throws JsonProcessingException {
+        return mapper.writeValueAsString(map);
+    }
+
+    // TODO test
+    public static boolean isValidJson(final String json) throws IOException {
+        boolean valid = true;
+        try {
+            mapper.readTree(json);
+        } catch (JsonProcessingException e) {
+            valid = false;
+        }
+        return valid;
+    }
+
+    // TODO test
+    public static String beautifyJson(String json) throws IOException {
+        if (StringUtils.isNotEmpty(json)) {
+            Map<String, Object> j = mapper.readValue(json, Map.class);
+            return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(j);
+        } else {
+            return "";
+        }
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/utils/ParameterUtils.java b/src/main/java/com/navercorp/openwhisk/intellij/common/utils/ParameterUtils.java
new file mode 100644
index 0000000..c6de207
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/utils/ParameterUtils.java
@@ -0,0 +1,66 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.utils;
+
+import org.apache.commons.lang.StringUtils;
+
+import java.io.IOException;
+import java.util.*;
+
+public class ParameterUtils {
+
+    protected ParameterUtils() {
+        throw new UnsupportedOperationException("Utility classes should not have a public or default constructor.");
+    }
+
+    // TODO test
+    public static Map<String, Object> listMapToMap(List<Map<String, Object>> params) {
+        Map<String, Object> map = new LinkedHashMap<>();
+        for (Map<String, Object> p : params) {
+            map.put((String) p.get("key"), p.get("value"));
+        }
+        return map;
+    }
+
+    public static List<Map<String, Object>> mapToListMap(Map<String, Object> params) {
+        List<Map<String, Object>> list = new ArrayList<>();
+        for (String key : params.keySet()) {
+            Object v = params.get(key);
+            Map<String, Object> m = new LinkedHashMap<>();
+            m.put("key", key);
+            m.put("value", v);
+            list.add(m);
+        }
+        return list;
+    }
+
+    public static Optional<String> validateParams(String params) {
+        if (StringUtils.isEmpty(params)) {
+            return Optional.of("{}");
+        } else {
+            try {
+                if (JsonParserUtils.isValidJson(params)) {
+                    return Optional.of(params);
+                } else {
+                    return Optional.empty();
+                }
+            } catch (IOException e) {
+                return Optional.empty();
+            }
+        }
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/utils/ParametricRunnable.java b/src/main/java/com/navercorp/openwhisk/intellij/common/utils/ParametricRunnable.java
new file mode 100644
index 0000000..5964db8
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/utils/ParametricRunnable.java
@@ -0,0 +1,25 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.utils;
+
+@FunctionalInterface
+public interface ParametricRunnable<P, E extends Throwable> {
+    void run(P parameter) throws E;
+
+    interface Basic<P> extends ParametricRunnable<P, RuntimeException> {
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/utils/ValidationUtils.java b/src/main/java/com/navercorp/openwhisk/intellij/common/utils/ValidationUtils.java
new file mode 100644
index 0000000..e243226
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/utils/ValidationUtils.java
@@ -0,0 +1,52 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.utils;
+
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.navercorp.openwhisk.intellij.common.whisk.model.wskdeploy.WskDeployCmdResponse;
+
+import java.io.IOException;
+import java.util.Optional;
+
+import static com.navercorp.openwhisk.intellij.common.utils.CommandUtils.runCommand;
+
+public class ValidationUtils {
+    private static final Logger LOG = Logger.getInstance(ValidationUtils.class);
+
+    protected ValidationUtils() {
+        throw new UnsupportedOperationException("Utility classes should not have a public or default constructor.");
+    }
+
+    public static Optional<VirtualFile> validateWskDeploy(Optional<VirtualFile> wskdeploy) {
+        return wskdeploy.flatMap(wskdeployFile -> {
+            String[] cmd = new String[]{wskdeployFile.getPath(), "version"};
+            try {
+                WskDeployCmdResponse response = runCommand(cmd);
+                if (response.getExistCode() == 0) {
+                    return Optional.of(wskdeployFile);
+                } else {
+                    LOG.error(response.getErrorOutput());
+                    return Optional.empty();
+                }
+            } catch (IOException | InterruptedException e) {
+                LOG.error(e);
+                return Optional.empty();
+            }
+        });
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/utils/WhiskUtils.java b/src/main/java/com/navercorp/openwhisk/intellij/common/utils/WhiskUtils.java
new file mode 100644
index 0000000..d35c368
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/utils/WhiskUtils.java
@@ -0,0 +1,62 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.utils;
+
+import com.intellij.openapi.diagnostic.Logger;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskEndpoint;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskNamespace;
+
+import java.util.List;
+import java.util.Optional;
+
+public class WhiskUtils {
+    private static final Logger LOG = Logger.getInstance(WhiskUtils.class);
+
+    protected WhiskUtils() {
+        throw new UnsupportedOperationException("Utility classes should not have a public or default constructor.");
+    }
+
+    public static Optional<WhiskAuth> findWhiskAuth(List<WhiskEndpoint> endpoints, WhiskNamespace namespace) {
+        for (WhiskEndpoint ep : endpoints) {
+            for (WhiskNamespace np : ep.getNamespaces()) {
+                if (np.getAuth().equals(namespace.getAuth()) && np.getPath().equals(namespace.getPath())) {
+                    return Optional.of(new WhiskAuth(np.getAuth(), ep.getApihost()));
+                }
+            }
+        }
+        return Optional.empty();
+    }
+
+    public static Optional<WhiskNamespace> findWhiskNamespace(List<WhiskEndpoint> endpoints, String auth) {
+        for (WhiskEndpoint ep : endpoints) {
+            for (WhiskNamespace np : ep.getNamespaces()) {
+                if (np.getAuth().equals(auth)) {
+                    return Optional.of(np);
+                }
+            }
+        }
+        return Optional.empty();
+    }
+
+    public static String getApihHostWithProtocol(String apiHost) {
+        if (!apiHost.toLowerCase().matches("^\\w+://.*")) {
+            apiHost = "https://" + apiHost;
+        }
+        return apiHost;
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/Binding.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/Binding.java
new file mode 100644
index 0000000..3baef86
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/Binding.java
@@ -0,0 +1,60 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model;
+
+import java.util.Objects;
+
+public class Binding {
+
+    private String namespace;
+    private String name;
+
+    public Binding(String namespace, String name) {
+        this.namespace = namespace;
+        this.name = name;
+    }
+
+    public String getNamespace() {
+        return namespace;
+    }
+
+    public void setNamespace(String namespace) {
+        this.namespace = namespace;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        Binding binding = (Binding) o;
+        return Objects.equals(namespace, binding.namespace)
+                && Objects.equals(name, binding.name);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(namespace, name);
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/Limits.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/Limits.java
new file mode 100644
index 0000000..7f82578
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/Limits.java
@@ -0,0 +1,88 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model;
+
+public class Limits {
+    private int concurrency;
+    private int logs;
+    private int memory;
+    private int timeout;
+
+    public Limits() {
+    }
+
+    public Limits(int concurrency, int logs, int memory, int timeout) {
+        this.concurrency = concurrency;
+        this.logs = logs;
+        this.memory = memory;
+        this.timeout = timeout;
+    }
+
+    public int getConcurrency() {
+        return concurrency;
+    }
+
+    public void setConcurrency(int concurrency) {
+        this.concurrency = concurrency;
+    }
+
+    public int getLogs() {
+        return logs;
+    }
+
+    public void setLogs(int logs) {
+        this.logs = logs;
+    }
+
+    public int getMemory() {
+        return memory;
+    }
+
+    public void setMemory(int memory) {
+        this.memory = memory;
+    }
+
+    public int getTimeout() {
+        return timeout;
+    }
+
+    public void setTimeout(int timeout) {
+        this.timeout = timeout;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        Limits limits = (Limits) o;
+
+        if (concurrency != limits.concurrency) return false;
+        if (logs != limits.logs) return false;
+        if (memory != limits.memory) return false;
+        return timeout == limits.timeout;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = concurrency;
+        result = 31 * result + logs;
+        result = 31 * result + memory;
+        result = 31 * result + timeout;
+        return result;
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/Runtime.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/Runtime.java
new file mode 100644
index 0000000..8de09df
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/Runtime.java
@@ -0,0 +1,188 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model;
+
+/**
+ *  Set the type of runtime based on upstream
+ *    - https://github.com/apache/openwhisk/blob/master/ansible/files/runtimes.json
+ *
+ *  The runtime used for each endpoint may be different, so use it according to the policy of the endpoint users are using.
+ */
+public enum Runtime {
+    NODE_6("nodejs", ".js", "6", false, true),
+    NODE_8("nodejs", ".js", "8", false, true),
+    NODE_10("nodejs", ".js", "10", true, false),
+    NODE_12("nodejs", ".js", "12", false, false),
+    NODE_14("nodejs", ".js", "14", false, false),
+    PYTHON_2("python", ".py", "2", false, false),
+    PYTHON_3("python", ".py", "3", true, false),
+    JAVA("java", ".java", "8", true, false),
+    SWIFT_3_1_1("swift", ".swift", "3.1.1", false, true),
+    SWIFT_4_2("swift", ".swift", "4.2", true, false),
+    SWIFT_5_1("swift", ".swift", "5.1", false, false),
+    PHP_7_3("php", ".php", "7.3", false, false),
+    PHP_7_4("php", ".php", "7.4", true, false),
+    GO_1_11("go", ".go", "1.11", true, false),
+    RUBY_2_5("ruby", ".rb", "2.5", true, false),
+    SEQUENCE("sequence", "", "", true, false),
+    DOCKER("blackbox", "", "", true, false);
+
+    private String name;
+    private String extension;
+    private String version;
+    private boolean defaultRuntime;
+    private boolean deprecated;
+
+    Runtime(String name, String extension, String version, boolean defaultRuntime, boolean deprecated) {
+        this.name = name;
+        this.extension = extension;
+        this.version = version;
+        this.defaultRuntime = defaultRuntime;
+        this.deprecated = deprecated;
+    }
+
+    @Override
+    public String toString() {
+        switch (name) {
+            case "java":
+                return name;
+            default:
+                String runtime = name;
+                if (version.length() > 0) {
+                    runtime += ":" + version;
+                }
+
+                if (deprecated) {
+                    runtime += " (Deprecated)";
+                }
+                return runtime;
+        }
+    }
+
+    public static Runtime toRuntime(String kind) {
+        switch (kind) {
+            case "nodejs:6":
+                return NODE_6;
+            case "nodejs:8":
+                return NODE_8;
+            case "nodejs:10":
+                return NODE_10;
+            case "nodejs:12":
+                return NODE_12;
+            case "nodejs:14":
+                return NODE_14;
+            case "python:2":
+                return PYTHON_2;
+            case "python:3":
+                return PYTHON_3;
+            case "java":
+                return JAVA;
+            case "swift:3.1.1":
+                return SWIFT_3_1_1;
+            case "swift:4.2":
+                return SWIFT_4_2;
+            case "swift:5.1":
+                return SWIFT_5_1;
+            case "php:7.3":
+                return PHP_7_3;
+            case "php:7.4":
+                return PHP_7_4;
+            case "go:1.11":
+                return GO_1_11;
+            case "ruby:2.5":
+                return RUBY_2_5;
+            case "sequence":
+                return SEQUENCE;
+            default:
+                return DOCKER;
+        }
+    }
+
+    public static Runtime toRuntime(int index) {
+        switch (index) {
+            case 0:
+                return NODE_6;
+            case 1:
+                return NODE_8;
+            case 2:
+                return NODE_10;
+            case 3:
+                return NODE_12;
+            case 4:
+                return NODE_14;
+            case 5:
+                return PYTHON_2;
+            case 6:
+                return PYTHON_3;
+            case 7:
+                return JAVA;
+            case 8:
+                return SWIFT_3_1_1;
+            case 9:
+                return SWIFT_4_2;
+            case 10:
+                return SWIFT_5_1;
+            case 11:
+                return PHP_7_3;
+            case 12:
+                return PHP_7_4;
+            case 13:
+                return GO_1_11;
+            case 14:
+                return RUBY_2_5;
+            case 15:
+                return SEQUENCE;
+            default:
+                return DOCKER;
+        }
+    }
+
+    public static Runtime toCodeType(int index) {
+        switch (index) {
+            case 0:
+                return NODE_6;
+            case 1:
+                return NODE_8;
+            case 2:
+                return NODE_10;
+            case 3:
+                return NODE_12;
+            case 4:
+                return NODE_14;
+            case 5:
+                return PYTHON_2;
+            case 6:
+                return PYTHON_3;
+            case 7:
+                return JAVA;
+            case 8:
+                return SWIFT_3_1_1;
+            case 9:
+                return SWIFT_4_2;
+            case 10:
+                return SWIFT_5_1;
+            case 11:
+                return PHP_7_3;
+            case 12:
+                return PHP_7_4;
+            case 13:
+                return GO_1_11;
+            default:
+                return RUBY_2_5;
+        }
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskAuth.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskAuth.java
new file mode 100644
index 0000000..a044de8
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskAuth.java
@@ -0,0 +1,54 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model;
+
+import com.navercorp.openwhisk.intellij.common.utils.WhiskUtils;
+
+import java.util.Base64;
+
+public class WhiskAuth {
+    private String auth;
+    private String apihost;
+
+    public WhiskAuth() {
+    }
+
+    public WhiskAuth(String auth, String apihost) {
+        this.auth = auth;
+        this.apihost = WhiskUtils.getApihHostWithProtocol(apihost);
+    }
+
+    public String getAuth() {
+        return auth;
+    }
+
+    public void setAuth(String auth) {
+        this.auth = auth;
+    }
+
+    public String getApihost() {
+        return apihost;
+    }
+
+    public void setApihost(String apihost) {
+        this.apihost = WhiskUtils.getApihHostWithProtocol(apihost);
+    }
+
+    public String getBasicAuthHeader() {
+        return "Basic " + Base64.getEncoder().encodeToString(auth.getBytes());
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskAuthWithName.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskAuthWithName.java
new file mode 100644
index 0000000..72b176e
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskAuthWithName.java
@@ -0,0 +1,48 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model;
+
+public class WhiskAuthWithName {
+    private WhiskAuth auth;
+    private String name;
+
+    public WhiskAuthWithName(WhiskAuth auth, String name) {
+        this.auth = auth;
+        this.name = name;
+    }
+
+    public WhiskAuth getAuth() {
+        return auth;
+    }
+
+    public void setAuth(WhiskAuth auth) {
+        this.auth = auth;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    @Override
+    public String toString() {
+        return name + " (" + auth.getApihost() + ")";
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskEndpoint.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskEndpoint.java
new file mode 100644
index 0000000..d36fb12
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskEndpoint.java
@@ -0,0 +1,69 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model;
+
+import com.navercorp.openwhisk.intellij.common.utils.WhiskUtils;
+
+import java.util.List;
+
+public class WhiskEndpoint {
+    private String alias;
+    private String apihost;
+    private List<WhiskNamespace> namespaces;
+
+    public WhiskEndpoint() {
+    }
+
+    public WhiskEndpoint(String alias, String apihost, List<WhiskNamespace> namespaces) {
+        this.alias = alias;
+        this.apihost = WhiskUtils.getApihHostWithProtocol(apihost);
+        this.namespaces = namespaces;
+    }
+
+    public String getAlias() {
+        return alias;
+    }
+
+    public void setAlias(String alias) {
+        this.alias = alias;
+    }
+
+    public String getApihost() {
+        return apihost;
+    }
+
+    public void setApihost(String apihost) {
+        this.apihost = WhiskUtils.getApihHostWithProtocol(apihost);
+    }
+
+    public List<WhiskNamespace> getNamespaces() {
+        return namespaces;
+    }
+
+    public void setNamespaces(List<WhiskNamespace> namespaces) {
+        this.namespaces = namespaces;
+    }
+
+    public void addNamespaces(WhiskNamespace namespace) {
+        this.namespaces.add(namespace);
+    }
+
+    public String displayName() {
+        return this.alias + " (" + this.apihost + ")";
+    }
+
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskNamespace.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskNamespace.java
new file mode 100644
index 0000000..46c9376
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskNamespace.java
@@ -0,0 +1,113 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.WhiskActionMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.model.pkg.WhiskPackage;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.WhiskTriggerMetaData;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+@JsonIgnoreProperties(value = {"packages", "actions"})
+public class WhiskNamespace {
+    private String auth;
+    private String path;
+    private List<WhiskPackage> packages = new ArrayList<>();
+    private List<WhiskActionMetaData> actions = new ArrayList<>();
+    private List<WhiskTriggerMetaData> triggers = new ArrayList<>();
+
+    public WhiskNamespace() {
+    }
+
+    public WhiskNamespace(String auth, String path) {
+        this.auth = auth;
+        this.path = path;
+    }
+
+    public WhiskNamespace(String auth, String path, List<WhiskPackage> packages, List<WhiskActionMetaData> actions, List<WhiskTriggerMetaData> triggers) {
+        this.auth = auth;
+        this.path = path;
+        this.packages = packages;
+        this.actions = actions;
+        this.triggers = triggers;
+    }
+
+    public String getPath() {
+        return path;
+    }
+
+    public void setPath(String path) {
+        this.path = path;
+    }
+
+    public String getAuth() {
+        return auth;
+    }
+
+    public void setAuth(String auth) {
+        this.auth = auth;
+    }
+
+    public List<WhiskPackage> getPackages() {
+        return packages;
+    }
+
+    public void setPackages(List<WhiskPackage> packages) {
+        this.packages = packages;
+    }
+
+    public List<WhiskActionMetaData> getActions() {
+        return actions;
+    }
+
+    public void setActions(List<WhiskActionMetaData> actions) {
+        this.actions = actions;
+    }
+
+    public List<WhiskTriggerMetaData> getTriggers() {
+        return triggers;
+    }
+
+    public void setTriggers(List<WhiskTriggerMetaData> triggers) {
+        this.triggers = triggers;
+    }
+
+    @Override
+    public String toString() {
+        return path;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        WhiskNamespace that = (WhiskNamespace) o;
+        return Objects.equals(auth, that.auth)
+                && Objects.equals(path, that.path)
+                && Objects.equals(packages, that.packages)
+                && Objects.equals(actions, that.actions)
+                && Objects.equals(triggers, that.triggers);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(auth, path, packages, actions, triggers);
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/action/CompactWhiskAction.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/action/CompactWhiskAction.java
new file mode 100644
index 0000000..e0b2434
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/action/CompactWhiskAction.java
@@ -0,0 +1,136 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.action;
+
+import com.navercorp.openwhisk.intellij.common.Icons;
+
+import javax.swing.*;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class CompactWhiskAction {
+    private String name;
+    private String version;
+    private List<Map<String, Object>> annotations = new ArrayList<>();
+
+    public CompactWhiskAction() {
+    }
+
+    public CompactWhiskAction(String name, String version, List<Map<String, Object>> annotations) {
+        this.name = name;
+        this.version = version;
+        this.annotations = annotations;
+    }
+
+    public String getKind() {
+        for (Map<String, Object> a : this.annotations) {
+            if ("exec".equals(a.get("key"))) {
+                return (String) a.get("value");
+            }
+        }
+        return "";
+    }
+
+    public String getKindExtension() {
+        String kind = getKind();
+        if (kind.contains("java")) {
+            return ".java";
+        } else if (kind.contains("nodejs")) {
+            return ".js";
+        } else if (kind.contains("python")) {
+            return ".py";
+        } else if (kind.contains("swift")) {
+            return ".swift";
+        } else if (kind.contains("php")) {
+            return ".php";
+        } else if (kind.contains("go")) {
+            return ".go";
+        } else if (kind.contains("ruby")) {
+            return ".rb";
+        } else {
+            return "";
+        }
+    }
+
+    public Icon getKindIcon() {
+        String kind = getKind();
+        if (kind.contains("java")) {
+            return Icons.KIND_JAVA;
+        } else if (kind.contains("nodejs")) {
+            return Icons.KIND_JS;
+        } else if (kind.contains("python")) {
+            return Icons.KIND_PYTHON;
+        } else if (kind.contains("swift")) {
+            return Icons.KIND_SWIFT;
+        } else if (kind.contains("php")) {
+            return Icons.KIND_PHP;
+        } else if (kind.contains("go")) {
+            return Icons.KIND_GO;
+        } else if (kind.contains("ruby")) {
+            return Icons.KIND_RUBY;
+        } else if (kind.contains("sequence")) {
+            return Icons.KIND_SEQUENCE;
+        } else {
+            return Icons.KIND_DOCKER;
+        }
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getVersion() {
+        return version;
+    }
+
+    public void setVersion(String version) {
+        this.version = version;
+    }
+
+    public List<Map<String, Object>> getAnnotations() {
+        return annotations;
+    }
+
+    public void setAnnotations(List<Map<String, Object>> annotations) {
+        this.annotations = annotations;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        CompactWhiskAction that = (CompactWhiskAction) o;
+
+        if (name != null ? !name.equals(that.name) : that.name != null) return false;
+        if (version != null ? !version.equals(that.version) : that.version != null) return false;
+        return annotations != null ? annotations.equals(that.annotations) : that.annotations == null;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = name != null ? name.hashCode() : 0;
+        result = 31 * result + (version != null ? version.hashCode() : 0);
+        result = 31 * result + (annotations != null ? annotations.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/action/ExecutableWhiskAction.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/action/ExecutableWhiskAction.java
new file mode 100644
index 0000000..6c6a41e
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/action/ExecutableWhiskAction.java
@@ -0,0 +1,67 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.action;
+
+import com.navercorp.openwhisk.intellij.common.whisk.model.Limits;
+import com.navercorp.openwhisk.intellij.common.whisk.model.exec.CodeExec;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+public class ExecutableWhiskAction extends WhiskAction<CodeExec> {
+    private List<Map<String, Object>> parameters = new ArrayList<>();
+
+    public ExecutableWhiskAction() {
+    }
+
+    public ExecutableWhiskAction(List<Map<String, Object>> parameters) {
+        this.parameters = parameters;
+    }
+
+    public ExecutableWhiskAction(String name, String namespace, String version, long updated, boolean publish,
+                                 List<Map<String, Object>> annotations,
+                                 Limits limits,
+                                 CodeExec exec,
+                                 List<Map<String, Object>> parameters) {
+        super(name, namespace, version, updated, publish, annotations, limits, exec);
+        this.parameters = parameters;
+    }
+
+    public List<Map<String, Object>> getParameters() {
+        return parameters;
+    }
+
+    public void setParameters(List<Map<String, Object>> parameters) {
+        this.parameters = parameters;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        if (!super.equals(o)) return false;
+        ExecutableWhiskAction that = (ExecutableWhiskAction) o;
+        return Objects.equals(parameters, that.parameters);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(super.hashCode(), parameters);
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/action/WhiskAction.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/action/WhiskAction.java
new file mode 100644
index 0000000..feff4e2
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/action/WhiskAction.java
@@ -0,0 +1,292 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.action;
+
+import com.navercorp.openwhisk.intellij.common.Icons;
+import com.navercorp.openwhisk.intellij.common.whisk.model.Limits;
+import com.navercorp.openwhisk.intellij.common.whisk.model.exec.Exec;
+
+import javax.swing.*;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+public abstract class WhiskAction<E extends Exec> {
+    private String name;
+    private String namespace;
+    private String version;
+    private long updated;
+    private boolean publish;
+    private List<Map<String, Object>> annotations = new ArrayList<>();
+    private Limits limits;
+    private E exec;
+
+    public WhiskAction() {
+    }
+
+    public WhiskAction(String name, String namespace, String version, long updated, boolean publish,
+                       List<Map<String, Object>> annotations,
+                       Limits limits,
+                       E exec) {
+        this.name = name;
+        this.namespace = namespace;
+        this.version = version;
+        this.updated = updated;
+        this.publish = publish;
+        this.annotations = annotations;
+        this.limits = limits;
+        this.exec = exec;
+    }
+
+    public Optional<String> getWhiskPackage() {
+        if (namespace == null) {
+            return Optional.empty();
+        } else {
+            String[] ns = namespace.split("/");
+            if (ns.length == 1) {
+                return Optional.empty();
+            } else {
+                return Optional.of(ns[1]);
+            }
+        }
+    }
+
+    public String getNamespacePath() {
+        return namespace.split("/")[0];
+    }
+
+    public String getFullyQualifiedName() {
+        return namespace + "/" + name;
+    }
+
+    public String getKind() {
+        for (Map<String, Object> a : this.annotations) {
+            if ("exec".equals(a.get("key"))) {
+                return (String) a.get("value");
+            }
+        }
+        return "";
+    }
+
+    public Optional<String> getCodeType() {
+        for (Map<String, Object> a : this.annotations) {
+            if ("code-type".equals(a.get("key"))) {
+                return Optional.ofNullable((String) a.get("value"));
+            }
+        }
+        return Optional.empty();
+    }
+
+    public String getKindExtension() {
+        String kind = getKind();
+        if (kind.contains("java")) {
+            return ".java";
+        } else if (kind.contains("nodejs")) {
+            return ".js";
+        } else if (kind.contains("python")) {
+            return ".py";
+        } else if (kind.contains("swift")) {
+            return ".swift";
+        } else if (kind.contains("php")) {
+            return ".php";
+        } else if (kind.contains("go")) {
+            return ".go";
+        } else if (kind.contains("ruby")) {
+            return ".rb";
+        } else if (kind.contains("blackbox")) {
+            return getCodeType().map(codeType -> {
+                if (codeType.contains("java")) {
+                    return ".java";
+                } else if (codeType.contains("nodejs")) {
+                    return ".js";
+                } else if (codeType.contains("python")) {
+                    return ".py";
+                } else if (codeType.contains("swift")) {
+                    return ".swift";
+                } else if (codeType.contains("php")) {
+                    return ".php";
+                } else if (codeType.contains("go")) {
+                    return ".go";
+                } else if (codeType.contains("ruby")) {
+                    return ".rb";
+                }
+                return "";
+            }).orElse("");
+        }
+        return "";
+    }
+
+    public Icon getKindIcon() {
+        String kind = getKind();
+        if (kind.contains("java")) {
+            return Icons.KIND_JAVA;
+        } else if (kind.contains("nodejs")) {
+            return Icons.KIND_JS;
+        } else if (kind.contains("python")) {
+            return Icons.KIND_PYTHON;
+        } else if (kind.contains("swift")) {
+            return Icons.KIND_SWIFT;
+        } else if (kind.contains("php")) {
+            return Icons.KIND_PHP;
+        } else if (kind.contains("go")) {
+            return Icons.KIND_GO;
+        } else if (kind.contains("ruby")) {
+            return Icons.KIND_RUBY;
+        } else if (kind.contains("sequence")) {
+            return Icons.KIND_SEQUENCE;
+        } else {
+            return Icons.KIND_DOCKER;
+        }
+    }
+
+
+    public boolean isSequenceAction() {
+        String kind = getKind();
+        return kind.equals("sequence");
+    }
+
+    public boolean isWebAction() {
+        for (Map<String, Object> a : this.annotations) {
+            if ("web-export".equals(a.get("key"))) {
+                return (Boolean) a.get("value");
+            }
+        }
+        return false;
+    }
+
+    public boolean isCustomOptions() {
+        for (Map<String, Object> a : this.annotations) {
+            if ("web-custom-options".equals(a.get("key"))) {
+                return (Boolean) a.get("value");
+            }
+        }
+        return false;
+    }
+
+    public boolean isRawHttp() {
+        for (Map<String, Object> a : this.annotations) {
+            if ("raw-http".equals(a.get("key"))) {
+                return (Boolean) a.get("value");
+            }
+        }
+        return false;
+    }
+
+    public boolean isFinalDefaultParameter() {
+        for (Map<String, Object> a : this.annotations) {
+            if ("final".equals(a.get("key"))) {
+                return (Boolean) a.get("value");
+            }
+        }
+        return false;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getNamespace() {
+        return namespace;
+    }
+
+    public void setNamespace(String namespace) {
+        this.namespace = namespace;
+    }
+
+    public String getVersion() {
+        return version;
+    }
+
+    public void setVersion(String version) {
+        this.version = version;
+    }
+
+    public long getUpdated() {
+        return updated;
+    }
+
+    public void setUpdated(long updated) {
+        this.updated = updated;
+    }
+
+    public boolean isPublish() {
+        return publish;
+    }
+
+    public void setPublish(boolean publish) {
+        this.publish = publish;
+    }
+
+    public List<Map<String, Object>> getAnnotations() {
+        return annotations;
+    }
+
+    public void setAnnotations(List<Map<String, Object>> annotations) {
+        this.annotations = annotations;
+    }
+
+    public Limits getLimits() {
+        return limits;
+    }
+
+    public void setLimits(Limits limits) {
+        this.limits = limits;
+    }
+
+    public E getExec() {
+        return exec;
+    }
+
+    public void setExec(E exec) {
+        this.exec = exec;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        WhiskAction<?> that = (WhiskAction<?>) o;
+
+        if (updated != that.updated) return false;
+        if (publish != that.publish) return false;
+        if (name != null ? !name.equals(that.name) : that.name != null) return false;
+        if (namespace != null ? !namespace.equals(that.namespace) : that.namespace != null) return false;
+        if (version != null ? !version.equals(that.version) : that.version != null) return false;
+        if (annotations != null ? !annotations.equals(that.annotations) : that.annotations != null) return false;
+        if (limits != null ? !limits.equals(that.limits) : that.limits != null) return false;
+        return exec != null ? exec.equals(that.exec) : that.exec == null;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = name != null ? name.hashCode() : 0;
+        result = 31 * result + (namespace != null ? namespace.hashCode() : 0);
+        result = 31 * result + (version != null ? version.hashCode() : 0);
+        result = 31 * result + (int) (updated ^ (updated >>> 32));
+        result = 31 * result + (publish ? 1 : 0);
+        result = 31 * result + (annotations != null ? annotations.hashCode() : 0);
+        result = 31 * result + (limits != null ? limits.hashCode() : 0);
+        result = 31 * result + (exec != null ? exec.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/action/WhiskActionMetaData.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/action/WhiskActionMetaData.java
new file mode 100644
index 0000000..674995e
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/action/WhiskActionMetaData.java
@@ -0,0 +1,46 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.action;
+
+import com.navercorp.openwhisk.intellij.common.whisk.model.Limits;
+import com.navercorp.openwhisk.intellij.common.whisk.model.exec.ExecMetaData;
+import com.navercorp.openwhisk.intellij.explorer.editor.model.ComboBoxEntityEntry;
+import com.navercorp.openwhisk.intellij.explorer.editor.model.ComboBoxEntityType;
+
+import java.util.List;
+import java.util.Map;
+
+public class WhiskActionMetaData extends WhiskAction<ExecMetaData> {
+    public WhiskActionMetaData() {
+    }
+
+    public WhiskActionMetaData(String name, String namespace, String version, long updated, boolean publish,
+                               List<Map<String, Object>> annotations,
+                               Limits limits,
+                               ExecMetaData exec) {
+        super(name, namespace, version, updated, publish, annotations, limits, exec);
+    }
+
+    @Override
+    public String toString() {
+        return getWhiskPackage().map(pkg -> pkg + "/" + getName()).orElse(getName());
+    }
+
+    public ComboBoxEntityEntry toCombBoxEntityEntry() {
+        return new ComboBoxEntityEntry(toString(), ComboBoxEntityType.ACTION);
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/activation/WhiskActivation.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/activation/WhiskActivation.java
new file mode 100644
index 0000000..4c0be99
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/activation/WhiskActivation.java
@@ -0,0 +1,183 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.activation;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public abstract class WhiskActivation {
+
+    private String activationId;
+    private String name;
+    private String namespace;
+    private String version;
+    private String cause;
+    private long start;
+    private long end;
+    private long duration;
+    private boolean publish;
+    private List<Map<String, Object>> annotations = new ArrayList<>();
+
+    public WhiskActivation() {
+    }
+
+    public WhiskActivation(String activationId, String name, String namespace, String version, String cause,
+                           long start, long end, long duration, boolean publish,
+                           List<Map<String, Object>> annotations) {
+        this.activationId = activationId;
+        this.name = name;
+        this.namespace = namespace;
+        this.version = version;
+        this.cause = cause;
+        this.start = start;
+        this.end = end;
+        this.duration = duration;
+        this.publish = publish;
+        this.annotations = annotations;
+    }
+
+    public String getActivationId() {
+        return activationId;
+    }
+
+    public void setActivationId(String activationId) {
+        this.activationId = activationId;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getNamespace() {
+        return namespace;
+    }
+
+    public void setNamespace(String namespace) {
+        this.namespace = namespace;
+    }
+
+    public String getVersion() {
+        return version;
+    }
+
+    public String getCause() {
+        return cause;
+    }
+
+    public void setCause(String cause) {
+        this.cause = cause;
+    }
+
+    public void setVersion(String version) {
+        this.version = version;
+    }
+
+    public long getStart() {
+        return start;
+    }
+
+    public void setStart(long start) {
+        this.start = start;
+    }
+
+    public long getEnd() {
+        return end;
+    }
+
+    public void setEnd(long end) {
+        this.end = end;
+    }
+
+    public long getDuration() {
+        return duration;
+    }
+
+    public void setDuration(long duration) {
+        this.duration = duration;
+    }
+
+    public boolean isPublish() {
+        return publish;
+    }
+
+    public void setPublish(boolean publish) {
+        this.publish = publish;
+    }
+
+    public List<Map<String, Object>> getAnnotations() {
+        return annotations;
+    }
+
+    public void setAnnotations(List<Map<String, Object>> annotations) {
+        this.annotations = annotations;
+    }
+
+    public String getKind() {
+        for (Map<String, Object> a : annotations) {
+            if (a.get("key").equals("kind")) {
+                return (String) a.get("value");
+            }
+        }
+        return "unknown";
+    }
+
+    public String getStartType() {
+        for (Map<String, Object> a : annotations) {
+            if (a.get("key").equals("initTime")) {
+                return "cold";
+            }
+        }
+        return "warm";
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        WhiskActivation that = (WhiskActivation) o;
+
+        if (start != that.start) return false;
+        if (end != that.end) return false;
+        if (duration != that.duration) return false;
+        if (publish != that.publish) return false;
+        if (activationId != null ? !activationId.equals(that.activationId) : that.activationId != null) return false;
+        if (name != null ? !name.equals(that.name) : that.name != null) return false;
+        if (namespace != null ? !namespace.equals(that.namespace) : that.namespace != null) return false;
+        if (version != null ? !version.equals(that.version) : that.version != null) return false;
+        return annotations != null ? annotations.equals(that.annotations) : that.annotations == null;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = activationId != null ? activationId.hashCode() : 0;
+        result = 31 * result + (name != null ? name.hashCode() : 0);
+        result = 31 * result + (namespace != null ? namespace.hashCode() : 0);
+        result = 31 * result + (version != null ? version.hashCode() : 0);
+        result = 31 * result + (int) (start ^ (start >>> 32));
+        result = 31 * result + (int) (end ^ (end >>> 32));
+        result = 31 * result + (int) (duration ^ (duration >>> 32));
+        result = 31 * result + (publish ? 1 : 0);
+        result = 31 * result + (annotations != null ? annotations.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/activation/WhiskActivationMetaData.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/activation/WhiskActivationMetaData.java
new file mode 100644
index 0000000..9c210a1
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/activation/WhiskActivationMetaData.java
@@ -0,0 +1,75 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.activation;
+
+import java.util.List;
+import java.util.Map;
+
+public class WhiskActivationMetaData extends WhiskActivation {
+
+    private int statusCode;
+
+    public WhiskActivationMetaData() {
+    }
+
+    public WhiskActivationMetaData(String activationId, String name, String namespace, String version, String cause,
+                                   long start, long end, long duration, boolean publish,
+                                   List<Map<String, Object>> annotations,
+                                   int statusCode) {
+        super(activationId, name, namespace, version, cause, start, end, duration, publish, annotations);
+        this.statusCode = statusCode;
+    }
+
+    public int getStatusCode() {
+        return statusCode;
+    }
+
+    public void setStatusCode(int statusCode) {
+        this.statusCode = statusCode;
+    }
+
+    public String getStatus() {
+        switch (this.statusCode) {
+            case 0:
+                return "success";
+            case 1:
+                return "application error";
+            case 2:
+                return "developer error";
+            default:
+                return "internal error";
+        }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        if (!super.equals(o)) return false;
+
+        WhiskActivationMetaData that = (WhiskActivationMetaData) o;
+
+        return statusCode == that.statusCode;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = super.hashCode();
+        result = 31 * result + statusCode;
+        return result;
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/activation/WhiskActivationWithLogs.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/activation/WhiskActivationWithLogs.java
new file mode 100644
index 0000000..b780b6f
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/activation/WhiskActivationWithLogs.java
@@ -0,0 +1,90 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.activation;
+
+import org.codehaus.groovy.util.ListHashMap;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class WhiskActivationWithLogs extends WhiskActivation {
+    private String subject;
+    private List<String> logs = new ArrayList<>();
+    private Map<String, Object> response = new ListHashMap<>();
+
+    public WhiskActivationWithLogs() {
+    }
+
+    public WhiskActivationWithLogs(String activationId, String name, String namespace, String version, String cause,
+                                   long start, long end, long duration, boolean publish,
+                                   List<Map<String, Object>> annotations,
+                                   String subject,
+                                   List<String> logs,
+                                   Map<String, Object> response) {
+        super(activationId, name, namespace, version, cause, start, end, duration, publish, annotations);
+        this.subject = subject;
+        this.logs = logs;
+        this.response = response;
+    }
+
+    public String getSubject() {
+        return subject;
+    }
+
+    public void setSubject(String subject) {
+        this.subject = subject;
+    }
+
+    public List<String> getLogs() {
+        return logs;
+    }
+
+    public void setLogs(List<String> logs) {
+        this.logs = logs;
+    }
+
+    public Map<String, Object> getResponse() {
+        return response;
+    }
+
+    public void setResponse(Map<String, Object> response) {
+        this.response = response;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        if (!super.equals(o)) return false;
+
+        WhiskActivationWithLogs that = (WhiskActivationWithLogs) o;
+
+        if (subject != null ? !subject.equals(that.subject) : that.subject != null) return false;
+        if (logs != null ? !logs.equals(that.logs) : that.logs != null) return false;
+        return response != null ? response.equals(that.response) : that.response == null;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = super.hashCode();
+        result = 31 * result + (subject != null ? subject.hashCode() : 0);
+        result = 31 * result + (logs != null ? logs.hashCode() : 0);
+        result = 31 * result + (response != null ? response.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/exec/CodeExec.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/exec/CodeExec.java
new file mode 100644
index 0000000..1d4788b
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/exec/CodeExec.java
@@ -0,0 +1,121 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.exec;
+
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class CodeExec extends Exec {
+    private String kind;
+    @Nullable
+    private String main;
+    private String code;
+    private String image; // for docker image
+    private List<String> components = new ArrayList<>(); // for sequence action
+
+    public CodeExec() {
+    }
+
+    public CodeExec(String kind, @Nullable String main, String code, String image, List<String> components) {
+        this.kind = kind;
+        this.main = main;
+        this.code = code;
+        this.image = image;
+        this.components = components;
+    }
+
+    public CodeExec(boolean binary, String kind, @Nullable String main, String code, String image, List<String> components) {
+        super(binary);
+        this.kind = kind;
+        this.main = main;
+        this.code = code;
+        this.image = image;
+        this.components = components;
+    }
+
+    public String getKind() {
+        return kind;
+    }
+
+    public void setKind(String kind) {
+        this.kind = kind;
+    }
+
+    public String getCode() {
+        return code;
+    }
+
+    public void setCode(String code) {
+        this.code = code;
+    }
+
+    public String getMain() {
+        return main;
+    }
+
+    public void setMain(String main) {
+        this.main = main;
+    }
+
+    public String getImage() {
+        return image;
+    }
+
+    public void setImage(String image) {
+        this.image = image;
+    }
+
+    public List<String> getComponents() {
+        return components;
+    }
+
+    public void setComponents(List<String> components) {
+        this.components = components;
+    }
+
+    public ExecMetaData toExecMetaData() {
+        return new ExecMetaData(isBinary());
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        if (!super.equals(o)) return false;
+
+        CodeExec codeExec = (CodeExec) o;
+
+        if (kind != null ? !kind.equals(codeExec.kind) : codeExec.kind != null) return false;
+        if (main != null ? !main.equals(codeExec.main) : codeExec.main != null) return false;
+        if (code != null ? !code.equals(codeExec.code) : codeExec.code != null) return false;
+        if (image != null ? !image.equals(codeExec.image) : codeExec.image != null) return false;
+        return components != null ? components.equals(codeExec.components) : codeExec.components == null;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = super.hashCode();
+        result = 31 * result + (kind != null ? kind.hashCode() : 0);
+        result = 31 * result + (main != null ? main.hashCode() : 0);
+        result = 31 * result + (code != null ? code.hashCode() : 0);
+        result = 31 * result + (image != null ? image.hashCode() : 0);
+        result = 31 * result + (components != null ? components.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/exec/CodeExecSerializer.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/exec/CodeExecSerializer.java
new file mode 100644
index 0000000..c7072ec
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/exec/CodeExecSerializer.java
@@ -0,0 +1,63 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.exec;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializerProvider;
+
+import java.io.IOException;
+import java.util.Optional;
+
+public class CodeExecSerializer extends JsonSerializer<CodeExec> {
+    @Override
+    public void serialize(CodeExec codeExec, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
+        jsonGenerator.writeStartObject();
+
+        jsonGenerator.writeFieldName("binary");
+        jsonGenerator.writeBoolean(codeExec.isBinary());
+
+        jsonGenerator.writeFieldName("kind");
+        jsonGenerator.writeString(codeExec.getKind());
+
+        if (Optional.ofNullable(codeExec.getCode()).isPresent()) {
+            jsonGenerator.writeFieldName("code");
+            jsonGenerator.writeString(codeExec.getCode());
+        }
+
+        if (Optional.ofNullable(codeExec.getMain()).isPresent()) {
+            jsonGenerator.writeFieldName("main");
+            jsonGenerator.writeString(codeExec.getMain());
+        }
+
+        if (Optional.ofNullable(codeExec.getImage()).isPresent()) {
+            jsonGenerator.writeFieldName("image");
+            jsonGenerator.writeString(codeExec.getImage());
+        }
+
+        if (!codeExec.getComponents().isEmpty()) {
+            jsonGenerator.writeFieldName("components");
+            jsonGenerator.writeStartArray();
+            for (String c : codeExec.getComponents()) {
+                jsonGenerator.writeString(c);
+            }
+            jsonGenerator.writeEndArray();
+        }
+
+        jsonGenerator.writeEndObject();
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/exec/Exec.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/exec/Exec.java
new file mode 100644
index 0000000..2e48fb5
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/exec/Exec.java
@@ -0,0 +1,52 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.exec;
+
+public abstract class Exec {
+    private boolean binary;
+
+    public Exec() {
+    }
+
+    public Exec(boolean binary) {
+        this.binary = binary;
+    }
+
+
+    public boolean isBinary() {
+        return binary;
+    }
+
+    public void setBinary(boolean binary) {
+        this.binary = binary;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        Exec exec = (Exec) o;
+
+        return binary == exec.binary;
+    }
+
+    @Override
+    public int hashCode() {
+        return (binary ? 1 : 0);
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/exec/ExecMetaData.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/exec/ExecMetaData.java
new file mode 100644
index 0000000..6a11506
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/exec/ExecMetaData.java
@@ -0,0 +1,26 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.exec;
+
+public class ExecMetaData extends Exec {
+    public ExecMetaData() {
+    }
+
+    public ExecMetaData(boolean binary) {
+        super(binary);
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/pkg/WhiskPackage.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/pkg/WhiskPackage.java
new file mode 100644
index 0000000..41a7680
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/pkg/WhiskPackage.java
@@ -0,0 +1,137 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.pkg;
+
+import com.navercorp.openwhisk.intellij.common.whisk.model.Binding;
+
+import java.util.*;
+
+public class WhiskPackage {
+    private String name;
+    private String namespace;
+    private boolean publish;
+    private long updated;
+    private String version;
+    private List<Map<String, Object>> annotations = new ArrayList<>();
+    private Object binding;
+
+    public WhiskPackage() {
+    }
+
+    public WhiskPackage(String name, String namespace, boolean publish, long updated, String version, List<Map<String, Object>> annotations, Object binding) {
+        this.name = name;
+        this.namespace = namespace;
+        this.publish = publish;
+        this.updated = updated;
+        this.version = version;
+        this.annotations = annotations;
+        this.binding = binding;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getNamespace() {
+        return namespace;
+    }
+
+    public void setNamespace(String namespace) {
+        this.namespace = namespace;
+    }
+
+    public boolean isPublish() {
+        return publish;
+    }
+
+    public void setPublish(boolean publish) {
+        this.publish = publish;
+    }
+
+    public long getUpdated() {
+        return updated;
+    }
+
+    public void setUpdated(long updated) {
+        this.updated = updated;
+    }
+
+    public String getVersion() {
+        return version;
+    }
+
+    public void setVersion(String version) {
+        this.version = version;
+    }
+
+    public List<Map<String, Object>> getAnnotations() {
+        return annotations;
+    }
+
+    public void setAnnotations(List<Map<String, Object>> annotations) {
+        this.annotations = annotations;
+    }
+
+    public Optional<Binding> getBinding() {
+        if (binding instanceof Map) {
+            Map<String, String> b = (LinkedHashMap) binding;
+            if (b.containsKey("namespace") && b.containsKey("name")) {
+                return Optional.of(new Binding(b.get("namespace"), b.get("name")));
+            } else {
+                return Optional.empty();
+            }
+        } else {
+            return Optional.empty();
+        }
+    }
+
+    public void setBinding(Object binding) {
+        this.binding = binding;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        WhiskPackage that = (WhiskPackage) o;
+
+        if (publish != that.publish) return false;
+        if (updated != that.updated) return false;
+        if (name != null ? !name.equals(that.name) : that.name != null) return false;
+        if (namespace != null ? !namespace.equals(that.namespace) : that.namespace != null) return false;
+        if (version != null ? !version.equals(that.version) : that.version != null) return false;
+        if (annotations != null ? !annotations.equals(that.annotations) : that.annotations != null) return false;
+        return binding != null ? binding.equals(that.binding) : that.binding == null;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = name != null ? name.hashCode() : 0;
+        result = 31 * result + (namespace != null ? namespace.hashCode() : 0);
+        result = 31 * result + (publish ? 1 : 0);
+        result = 31 * result + (int) (updated ^ (updated >>> 32));
+        result = 31 * result + (version != null ? version.hashCode() : 0);
+        result = 31 * result + (annotations != null ? annotations.hashCode() : 0);
+        result = 31 * result + (binding != null ? binding.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/pkg/WhiskPackageWithActions.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/pkg/WhiskPackageWithActions.java
new file mode 100644
index 0000000..5b547da
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/pkg/WhiskPackageWithActions.java
@@ -0,0 +1,92 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.pkg;
+
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.CompactWhiskAction;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class WhiskPackageWithActions extends WhiskPackage {
+
+    private List<Map<String, Object>> parameters = new ArrayList<>();
+    private List<CompactWhiskAction> actions = new ArrayList<>();
+    private List<Object> feeds = new ArrayList<>();
+
+    public WhiskPackageWithActions() {
+    }
+
+    public WhiskPackageWithActions(String name, String namespace, boolean publish, long updated, String version,
+                                   List<Map<String, Object>> annotations,
+                                   Object binding,
+                                   List<Map<String, Object>> parameters,
+                                   List<CompactWhiskAction> actions,
+                                   List<Object> feeds) {
+        super(name, namespace, publish, updated, version, annotations, binding);
+        this.parameters = parameters;
+        this.actions = actions;
+        this.feeds = feeds;
+    }
+
+    public List<CompactWhiskAction> getActions() {
+        return actions;
+    }
+
+    public void setActions(List<CompactWhiskAction> actions) {
+        this.actions = actions;
+    }
+
+    public List<Map<String, Object>> getParameters() {
+        return parameters;
+    }
+
+    public void setParameters(List<Map<String, Object>> parameters) {
+        this.parameters = parameters;
+    }
+
+
+    public List<Object> getFeeds() {
+        return feeds;
+    }
+
+    public void setFeeds(List<Object> feeds) {
+        this.feeds = feeds;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        if (!super.equals(o)) return false;
+
+        WhiskPackageWithActions that = (WhiskPackageWithActions) o;
+
+        if (parameters != null ? !parameters.equals(that.parameters) : that.parameters != null) return false;
+        if (actions != null ? !actions.equals(that.actions) : that.actions != null) return false;
+        return feeds != null ? feeds.equals(that.feeds) : that.feeds == null;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = super.hashCode();
+        result = 31 * result + (parameters != null ? parameters.hashCode() : 0);
+        result = 31 * result + (actions != null ? actions.hashCode() : 0);
+        result = 31 * result + (feeds != null ? feeds.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/trigger/ExecutableWhiskTrigger.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/trigger/ExecutableWhiskTrigger.java
new file mode 100644
index 0000000..e9c9fae
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/trigger/ExecutableWhiskTrigger.java
@@ -0,0 +1,93 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.trigger;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+public class ExecutableWhiskTrigger extends WhiskTrigger {
+
+    private List<Map<String, Object>> parameters = new ArrayList<>();
+    private Map<String, SimplifiedWhiskRule> rules = new LinkedHashMap<>();
+
+    /**
+     * Limits on a specific trigger. None yet.
+     */
+    private Map<String, Object> limits;
+
+    public ExecutableWhiskTrigger() {
+    }
+
+    public ExecutableWhiskTrigger(String name, String namespace, String version, long updated, boolean publish,
+                                  List<Map<String, Object>> annotations,
+                                  List<Map<String, Object>> parameters,
+                                  Map<String, SimplifiedWhiskRule> rules,
+                                  Map<String, Object> limits) {
+        super(name, namespace, version, updated, publish, annotations);
+        this.parameters = parameters;
+        this.rules = rules;
+        this.limits = limits;
+    }
+
+    public List<Map<String, Object>> getParameters() {
+        return parameters;
+    }
+
+    public void setParameters(List<Map<String, Object>> parameters) {
+        this.parameters = parameters;
+    }
+
+    public Map<String, SimplifiedWhiskRule> getRules() {
+        return rules;
+    }
+
+    public void setRules(Map<String, SimplifiedWhiskRule> rules) {
+        this.rules = rules;
+    }
+
+    public Map<String, Object> getLimits() {
+        return limits;
+    }
+
+    public void setLimits(Map<String, Object> limits) {
+        this.limits = limits;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        if (!super.equals(o)) return false;
+
+        ExecutableWhiskTrigger that = (ExecutableWhiskTrigger) o;
+
+        if (parameters != null ? !parameters.equals(that.parameters) : that.parameters != null) return false;
+        if (rules != null ? !rules.equals(that.rules) : that.rules != null) return false;
+        return limits != null ? limits.equals(that.limits) : that.limits == null;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = super.hashCode();
+        result = 31 * result + (parameters != null ? parameters.hashCode() : 0);
+        result = 31 * result + (rules != null ? rules.hashCode() : 0);
+        result = 31 * result + (limits != null ? limits.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/trigger/SimplifiedEntityMetaData.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/trigger/SimplifiedEntityMetaData.java
new file mode 100644
index 0000000..6ce3ed5
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/trigger/SimplifiedEntityMetaData.java
@@ -0,0 +1,72 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.trigger;
+
+public class SimplifiedEntityMetaData {
+    private String path;
+    private String name;
+
+    public SimplifiedEntityMetaData() {
+    }
+
+    public SimplifiedEntityMetaData(String path, String name) {
+        this.path = path;
+        this.name = name;
+    }
+
+    public String getPath() {
+        return path;
+    }
+
+    public void setPath(String path) {
+        this.path = path;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getPkgActionName() {
+        String pkg = "";
+        if (path.contains("/")) {
+            pkg = path.split("/")[1] + "/";
+        }
+        return pkg + name;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        SimplifiedEntityMetaData that = (SimplifiedEntityMetaData) o;
+
+        if (path != null ? !path.equals(that.path) : that.path != null) return false;
+        return name != null ? name.equals(that.name) : that.name == null;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = path != null ? path.hashCode() : 0;
+        result = 31 * result + (name != null ? name.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/trigger/SimplifiedWhiskRule.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/trigger/SimplifiedWhiskRule.java
new file mode 100644
index 0000000..cb3a420
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/trigger/SimplifiedWhiskRule.java
@@ -0,0 +1,65 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.trigger;
+
+public class SimplifiedWhiskRule {
+
+    private SimplifiedEntityMetaData action;
+    private String status;
+
+    public SimplifiedWhiskRule() {
+    }
+
+    public SimplifiedWhiskRule(SimplifiedEntityMetaData action, String status) {
+        this.action = action;
+        this.status = status;
+    }
+
+    public SimplifiedEntityMetaData getAction() {
+        return action;
+    }
+
+    public void setAction(SimplifiedEntityMetaData action) {
+        this.action = action;
+    }
+
+    public String getStatus() {
+        return status;
+    }
+
+    public void setStatus(String status) {
+        this.status = status;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        SimplifiedWhiskRule that = (SimplifiedWhiskRule) o;
+
+        if (action != null ? !action.equals(that.action) : that.action != null) return false;
+        return status != null ? status.equals(that.status) : that.status == null;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = action != null ? action.hashCode() : 0;
+        result = 31 * result + (status != null ? status.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/trigger/WhiskRule.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/trigger/WhiskRule.java
new file mode 100644
index 0000000..5b57f94
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/trigger/WhiskRule.java
@@ -0,0 +1,71 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.trigger;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class WhiskRule extends WhiskRuleMetaData {
+
+    private String status;
+    private List<Map<String, Object>> annotations = new ArrayList<>();
+
+    public WhiskRule() {
+    }
+
+    public WhiskRule(String name, String namespace, String version, long updated, boolean publish,
+                     SimplifiedEntityMetaData action, SimplifiedEntityMetaData trigger) {
+        super(name, namespace, version, updated, publish, action, trigger);
+    }
+
+    public String getStatus() {
+        return status;
+    }
+
+    public void setStatus(String status) {
+        this.status = status;
+    }
+
+    public List<Map<String, Object>> getAnnotations() {
+        return annotations;
+    }
+
+    public void setAnnotations(List<Map<String, Object>> annotations) {
+        this.annotations = annotations;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        if (!super.equals(o)) return false;
+
+        WhiskRule whiskRule = (WhiskRule) o;
+
+        if (status != null ? !status.equals(whiskRule.status) : whiskRule.status != null) return false;
+        return annotations != null ? annotations.equals(whiskRule.annotations) : whiskRule.annotations == null;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = super.hashCode();
+        result = 31 * result + (status != null ? status.hashCode() : 0);
+        result = 31 * result + (annotations != null ? annotations.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/trigger/WhiskRuleMetaData.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/trigger/WhiskRuleMetaData.java
new file mode 100644
index 0000000..6c92016
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/trigger/WhiskRuleMetaData.java
@@ -0,0 +1,126 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.trigger;
+
+public class WhiskRuleMetaData {
+
+    private String name;
+    private String namespace;
+    private String version;
+    private long updated;
+    private boolean publish;
+    private SimplifiedEntityMetaData action;
+    private SimplifiedEntityMetaData trigger;
+
+    public WhiskRuleMetaData() {
+    }
+
+    public WhiskRuleMetaData(String name, String namespace, String version, long updated, boolean publish,
+                             SimplifiedEntityMetaData action, SimplifiedEntityMetaData trigger) {
+        this.name = name;
+        this.namespace = namespace;
+        this.version = version;
+        this.updated = updated;
+        this.publish = publish;
+        this.action = action;
+        this.trigger = trigger;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getNamespace() {
+        return namespace;
+    }
+
+    public void setNamespace(String namespace) {
+        this.namespace = namespace;
+    }
+
+    public String getVersion() {
+        return version;
+    }
+
+    public void setVersion(String version) {
+        this.version = version;
+    }
+
+    public long getUpdated() {
+        return updated;
+    }
+
+    public void setUpdated(long updated) {
+        this.updated = updated;
+    }
+
+    public boolean isPublish() {
+        return publish;
+    }
+
+    public void setPublish(boolean publish) {
+        this.publish = publish;
+    }
+
+    public SimplifiedEntityMetaData getAction() {
+        return action;
+    }
+
+    public void setAction(SimplifiedEntityMetaData action) {
+        this.action = action;
+    }
+
+    public SimplifiedEntityMetaData getTrigger() {
+        return trigger;
+    }
+
+    public void setTrigger(SimplifiedEntityMetaData trigger) {
+        this.trigger = trigger;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        WhiskRuleMetaData whiskRuleMetaData = (WhiskRuleMetaData) o;
+
+        if (updated != whiskRuleMetaData.updated) return false;
+        if (publish != whiskRuleMetaData.publish) return false;
+        if (name != null ? !name.equals(whiskRuleMetaData.name) : whiskRuleMetaData.name != null) return false;
+        if (namespace != null ? !namespace.equals(whiskRuleMetaData.namespace) : whiskRuleMetaData.namespace != null) return false;
+        if (version != null ? !version.equals(whiskRuleMetaData.version) : whiskRuleMetaData.version != null) return false;
+        if (action != null ? !action.equals(whiskRuleMetaData.action) : whiskRuleMetaData.action != null) return false;
+        return trigger != null ? trigger.equals(whiskRuleMetaData.trigger) : whiskRuleMetaData.trigger == null;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = name != null ? name.hashCode() : 0;
+        result = 31 * result + (namespace != null ? namespace.hashCode() : 0);
+        result = 31 * result + (version != null ? version.hashCode() : 0);
+        result = 31 * result + (int) (updated ^ (updated >>> 32));
+        result = 31 * result + (publish ? 1 : 0);
+        result = 31 * result + (action != null ? action.hashCode() : 0);
+        result = 31 * result + (trigger != null ? trigger.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/trigger/WhiskTrigger.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/trigger/WhiskTrigger.java
new file mode 100644
index 0000000..8063521
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/trigger/WhiskTrigger.java
@@ -0,0 +1,117 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.trigger;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public abstract class WhiskTrigger {
+
+    private String name;
+    private String namespace;
+    private String version;
+    private long updated;
+    private boolean publish;
+    private List<Map<String, Object>> annotations = new ArrayList<>();
+
+    public WhiskTrigger() {
+    }
+
+    public WhiskTrigger(String name, String namespace, String version, long updated, boolean publish, List<Map<String, Object>> annotations) {
+        this.name = name;
+        this.namespace = namespace;
+        this.version = version;
+        this.updated = updated;
+        this.publish = publish;
+        this.annotations = annotations;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getNamespace() {
+        return namespace;
+    }
+
+    public void setNamespace(String namespace) {
+        this.namespace = namespace;
+    }
+
+    public String getVersion() {
+        return version;
+    }
+
+    public void setVersion(String version) {
+        this.version = version;
+    }
+
+    public long getUpdated() {
+        return updated;
+    }
+
+    public void setUpdated(long updated) {
+        this.updated = updated;
+    }
+
+    public boolean isPublish() {
+        return publish;
+    }
+
+    public void setPublish(boolean publish) {
+        this.publish = publish;
+    }
+
+    public List<Map<String, Object>> getAnnotations() {
+        return annotations;
+    }
+
+    public void setAnnotations(List<Map<String, Object>> annotations) {
+        this.annotations = annotations;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        WhiskTrigger that = (WhiskTrigger) o;
+
+        if (updated != that.updated) return false;
+        if (publish != that.publish) return false;
+        if (name != null ? !name.equals(that.name) : that.name != null) return false;
+        if (namespace != null ? !namespace.equals(that.namespace) : that.namespace != null) return false;
+        if (version != null ? !version.equals(that.version) : that.version != null) return false;
+        return annotations != null ? annotations.equals(that.annotations) : that.annotations == null;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = name != null ? name.hashCode() : 0;
+        result = 31 * result + (namespace != null ? namespace.hashCode() : 0);
+        result = 31 * result + (version != null ? version.hashCode() : 0);
+        result = 31 * result + (int) (updated ^ (updated >>> 32));
+        result = 31 * result + (publish ? 1 : 0);
+        result = 31 * result + (annotations != null ? annotations.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/trigger/WhiskTriggerMetaData.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/trigger/WhiskTriggerMetaData.java
new file mode 100644
index 0000000..a5250b4
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/trigger/WhiskTriggerMetaData.java
@@ -0,0 +1,37 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.trigger;
+
+import com.navercorp.openwhisk.intellij.explorer.editor.model.ComboBoxEntityEntry;
+import com.navercorp.openwhisk.intellij.explorer.editor.model.ComboBoxEntityType;
+
+import java.util.List;
+import java.util.Map;
+
+public class WhiskTriggerMetaData extends WhiskTrigger {
+
+    public WhiskTriggerMetaData() {
+    }
+
+    public WhiskTriggerMetaData(String name, String namespace, String version, long updated, boolean publish, List<Map<String, Object>> annotations) {
+        super(name, namespace, version, updated, publish, annotations);
+    }
+
+    public ComboBoxEntityEntry toCombBoxEntityEntry() {
+        return new ComboBoxEntityEntry(getName(), ComboBoxEntityType.TRIGGER);
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/trigger/WhiskTriggerRoot.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/trigger/WhiskTriggerRoot.java
new file mode 100644
index 0000000..be28a78
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/trigger/WhiskTriggerRoot.java
@@ -0,0 +1,20 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.trigger;
+
+public class WhiskTriggerRoot {
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/wskdeploy/NullWskDeployBinary.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/wskdeploy/NullWskDeployBinary.java
new file mode 100644
index 0000000..32bd8e1
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/wskdeploy/NullWskDeployBinary.java
@@ -0,0 +1,21 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.wskdeploy;
+
+public class NullWskDeployBinary implements WskDeployFile {
+
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/wskdeploy/WskDeployBinary.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/wskdeploy/WskDeployBinary.java
new file mode 100644
index 0000000..86062d5
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/wskdeploy/WskDeployBinary.java
@@ -0,0 +1,43 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.wskdeploy;
+
+public class WskDeployBinary implements WskDeployFile {
+    private String fullPath;
+    private String name;
+
+    public WskDeployBinary(String fullPath, String name) {
+        this.fullPath = fullPath;
+        this.name = name;
+    }
+
+    public String getFullPath() {
+        return fullPath;
+    }
+
+    public void setFullPath(String fullPath) {
+        this.fullPath = fullPath;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/wskdeploy/WskDeployCmd.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/wskdeploy/WskDeployCmd.java
new file mode 100644
index 0000000..8c3db12
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/wskdeploy/WskDeployCmd.java
@@ -0,0 +1,31 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.wskdeploy;
+
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+
+public interface WskDeployCmd {
+
+    String[] toCmd(WhiskAuth auth);
+
+    String toCmdString();
+
+    String getCmdName();
+
+    WskDeployManifest getManifest();
+
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/wskdeploy/WskDeployCmdDeploy.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/wskdeploy/WskDeployCmdDeploy.java
new file mode 100644
index 0000000..daf0463
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/wskdeploy/WskDeployCmdDeploy.java
@@ -0,0 +1,74 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.wskdeploy;
+
+import com.navercorp.openwhisk.intellij.common.error.NotExistFileException;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+
+import java.util.Optional;
+
+public class WskDeployCmdDeploy implements WskDeployCmd {
+
+    private Optional<WskDeployBinary> wskDeployBinary;
+    private WskDeployManifest manifest;
+
+    private WskDeployCmdDeploy() {
+    }
+
+    public WskDeployCmdDeploy(Optional<WskDeployBinary> wskDeployBinary, WskDeployManifest manifest) {
+        this.wskDeployBinary = wskDeployBinary;
+        this.manifest = manifest;
+    }
+
+    @Override
+    public String[] toCmd(WhiskAuth auth) {
+        return wskDeployBinary
+                .map(bin -> new String[]{
+                        bin.getFullPath(),
+                        "--manifest", manifest.getFullPath(),
+                        "--auth", auth.getAuth(),
+                        "--apihost", auth.getApihost()
+                })
+                .orElseThrow(() -> new NotExistFileException("The wskdeploy binary file does not exist."));
+    }
+
+    @Override
+    public String toCmdString() {
+        String[] cmd = wskDeployBinary
+                .map(bin -> new String[]{
+                        bin.getFullPath(),
+                        "--manifest", manifest.getFullPath(),
+                })
+                .orElseThrow(() -> new NotExistFileException("The wskdeploy binary file does not exist."));
+        StringBuilder str = new StringBuilder();
+        for (String c : cmd) {
+            str.append(c + " ");
+        }
+        return str.toString();
+    }
+
+    @Override
+    public String getCmdName() {
+        return "deploy";
+    }
+
+    @Override
+    public WskDeployManifest getManifest() {
+        return manifest;
+    }
+
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/wskdeploy/WskDeployCmdResponse.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/wskdeploy/WskDeployCmdResponse.java
new file mode 100644
index 0000000..6ccdb3b
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/wskdeploy/WskDeployCmdResponse.java
@@ -0,0 +1,53 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.wskdeploy;
+
+public class WskDeployCmdResponse {
+    private int existCode;
+    private String successOutput;
+    private String errorOutput;
+
+    public WskDeployCmdResponse(int existCode, String successOutput, String errorOutput) {
+        this.existCode = existCode;
+        this.successOutput = successOutput;
+        this.errorOutput = errorOutput;
+    }
+
+    public int getExistCode() {
+        return existCode;
+    }
+
+    public void setExistCode(int existCode) {
+        this.existCode = existCode;
+    }
+
+    public String getSuccessOutput() {
+        return successOutput;
+    }
+
+    public void setSuccessOutput(String successOutput) {
+        this.successOutput = successOutput;
+    }
+
+    public String getErrorOutput() {
+        return errorOutput;
+    }
+
+    public void setErrorOutput(String errorOutput) {
+        this.errorOutput = errorOutput;
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/wskdeploy/WskDeployCmdUndeploy.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/wskdeploy/WskDeployCmdUndeploy.java
new file mode 100644
index 0000000..d7a2d2b
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/wskdeploy/WskDeployCmdUndeploy.java
@@ -0,0 +1,74 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.wskdeploy;
+
+import com.navercorp.openwhisk.intellij.common.error.NotExistFileException;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+
+import java.util.Optional;
+
+public class WskDeployCmdUndeploy implements WskDeployCmd {
+
+    private Optional<WskDeployBinary> wskDeployBinary;
+    private WskDeployManifest manifest;
+
+    private WskDeployCmdUndeploy() {
+    }
+
+    public WskDeployCmdUndeploy(Optional<WskDeployBinary> wskDeployBinary, WskDeployManifest manifest) {
+        this.wskDeployBinary = wskDeployBinary;
+        this.manifest = manifest;
+    }
+
+    @Override
+    public String[] toCmd(WhiskAuth auth) {
+        return wskDeployBinary
+                .map(bin -> new String[]{
+                        bin.getFullPath(), "undeploy",
+                        "--manifest", manifest.getFullPath(),
+                        "--auth", auth.getAuth(),
+                        "--apihost", auth.getApihost()
+                })
+                .orElseThrow(() -> new NotExistFileException("The wskdeploy binary file does not exist."));
+    }
+
+    @Override
+    public String toCmdString() throws NotExistFileException {
+        String[] cmd = wskDeployBinary
+                .map(bin -> new String[]{
+                        bin.getFullPath(), "undeploy",
+                        "--manifest", manifest.getFullPath(),
+                })
+                .orElseThrow(() -> new NotExistFileException("The wskdeploy binary file does not exist."));
+        StringBuilder str = new StringBuilder();
+        for (String c : cmd) {
+            str.append(c + " ");
+        }
+        return str.toString();
+    }
+
+    @Override
+    public String getCmdName() {
+        return "undeploy";
+    }
+
+    @Override
+    public WskDeployManifest getManifest() {
+        return manifest;
+    }
+
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/wskdeploy/WskDeployFile.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/wskdeploy/WskDeployFile.java
new file mode 100644
index 0000000..4b010c1
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/wskdeploy/WskDeployFile.java
@@ -0,0 +1,20 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.wskdeploy;
+
+public interface WskDeployFile {
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/wskdeploy/WskDeployManifest.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/wskdeploy/WskDeployManifest.java
new file mode 100644
index 0000000..8bc4b1f
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/model/wskdeploy/WskDeployManifest.java
@@ -0,0 +1,77 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.model.wskdeploy;
+
+public class WskDeployManifest {
+
+    private String path;
+    private String fullPath;
+    private String name;
+
+    public WskDeployManifest() {
+    }
+
+    public WskDeployManifest(String filePath, String fullFilePath, String fileName) {
+        this.path = filePath;
+        this.fullPath = fullFilePath;
+        this.name = fileName;
+    }
+
+    public String getPath() {
+        return path;
+    }
+
+    public void setPath(String path) {
+        this.path = path;
+    }
+
+    public String getFullPath() {
+        return fullPath;
+    }
+
+    public void setFullPath(String fullPath) {
+        this.fullPath = fullPath;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        WskDeployManifest manifest = (WskDeployManifest) o;
+
+        if (path != null ? !path.equals(manifest.path) : manifest.path != null) return false;
+        if (fullPath != null ? !fullPath.equals(manifest.fullPath) : manifest.fullPath != null) return false;
+        return name != null ? name.equals(manifest.name) : manifest.name == null;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = path != null ? path.hashCode() : 0;
+        result = 31 * result + (fullPath != null ? fullPath.hashCode() : 0);
+        result = 31 * result + (name != null ? name.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/service/WhiskActionService.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/service/WhiskActionService.java
new file mode 100644
index 0000000..1bdfda4
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/service/WhiskActionService.java
@@ -0,0 +1,128 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.service;
+
+import com.intellij.openapi.diagnostic.Logger;
+import com.navercorp.openwhisk.intellij.common.utils.JsonParserUtils;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.ExecutableWhiskAction;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.WhiskActionMetaData;
+import org.apache.http.HttpHeaders;
+import org.apache.http.client.fluent.Request;
+import org.apache.http.entity.ContentType;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+public class WhiskActionService {
+    private static final Logger LOG = Logger.getInstance(WhiskActionService.class);
+
+    private WhiskActionService() {
+
+    }
+
+    private static class LazyHolder {
+        private static final WhiskActionService INSTANCE = new WhiskActionService();
+    }
+
+    public static WhiskActionService getInstance() {
+        return LazyHolder.INSTANCE;
+    }
+
+    public List<WhiskActionMetaData> getWhiskActions(WhiskAuth whiskAuth) throws IOException {
+        String endpoint = whiskAuth.getApihost() + "/api/v1/namespaces/_/actions?limit=200&skip=0";
+        String authorization = whiskAuth.getBasicAuthHeader();
+
+        String result = Request.Get(endpoint)
+                .setHeader(HttpHeaders.AUTHORIZATION, authorization)
+                .execute()
+                .returnContent()
+                .asString(UTF_8);
+        return JsonParserUtils.parseWhiskActions(result);
+    }
+
+    public Optional<ExecutableWhiskAction> getWhiskAction(WhiskAuth whiskAuth,
+                                                          Optional<String> namespaceName,
+                                                          Optional<String> pkgName,
+                                                          String actionName) throws IOException {
+        String namespace = namespaceName.orElse("_");
+        String name = pkgName.map(p -> p + "/" + actionName).orElse(actionName);
+        String endpoint = whiskAuth.getApihost() + "/api/v1/namespaces/" + namespace + "/actions/" + name + "?code=true";
+        String authorization = whiskAuth.getBasicAuthHeader();
+
+        String result = Request.Get(endpoint)
+                .setHeader(HttpHeaders.AUTHORIZATION, authorization)
+                .execute()
+                .returnContent()
+                .asString(UTF_8);
+        return JsonParserUtils.parseWhiskAction(result);
+    }
+
+    public String invokeWhiskAction(WhiskAuth whiskAuth,
+                                    Optional<String> namespaceName,
+                                    Optional<String> pkgName,
+                                    String actionName,
+                                    String params) throws IOException {
+        String namespace = namespaceName.orElse("_");
+        String name = pkgName.map(p -> p + "/" + actionName).orElse(actionName);
+        String endpoint = whiskAuth.getApihost() + "/api/v1/namespaces/" + namespace + "/actions/" + name + "?blocking=true&result=true";
+        String authorization = whiskAuth.getBasicAuthHeader();
+        String result = Request.Post(endpoint)
+                .setHeader(HttpHeaders.AUTHORIZATION, authorization)
+                .bodyString(params, ContentType.APPLICATION_JSON)
+                .execute()
+                .returnContent()
+                .asString(UTF_8);
+        return JsonParserUtils.beautifyJson(result);
+    }
+
+    public Optional<ExecutableWhiskAction> updateWhiskAction(WhiskAuth whiskAuth,
+                                                             ExecutableWhiskAction updatedAction,
+                                                             Map<String, Object> payload) throws IOException {
+        String namespace = updatedAction.getNamespacePath();
+        String name = updatedAction.getWhiskPackage().map(p -> p + "/" + updatedAction.getName()).orElse(updatedAction.getName());
+        String endpoint = whiskAuth.getApihost() + "/api/v1/namespaces/" + namespace + "/actions/" + name + "?overwrite=true";
+        String authorization = whiskAuth.getBasicAuthHeader();
+        String body = JsonParserUtils.writeMapToJson(payload);
+        LOG.info("Action updated: " + body);
+        String result = Request.Put(endpoint)
+                .setHeader(HttpHeaders.AUTHORIZATION, authorization)
+                .bodyString(body, ContentType.APPLICATION_JSON)
+                .execute()
+                .returnContent()
+                .asString(UTF_8);
+        return JsonParserUtils.parseWhiskAction(result);
+    }
+
+    public Optional<ExecutableWhiskAction> deleteWhiskActions(WhiskAuth whiskAuth, Optional<String> pkgName, String actionName) throws IOException {
+        String name = pkgName.map(p -> p + "/" + actionName).orElse(actionName);
+        String endpoint = whiskAuth.getApihost() + "/api/v1/namespaces/_/actions/" + name;
+        String authorization = whiskAuth.getBasicAuthHeader();
+
+        String result = Request.Delete(endpoint)
+                .setHeader(HttpHeaders.AUTHORIZATION, authorization)
+                .execute()
+                .returnContent()
+                .asString(UTF_8);
+        return JsonParserUtils.parseWhiskAction(result);
+    }
+
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/service/WhiskActivationService.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/service/WhiskActivationService.java
new file mode 100644
index 0000000..c3b1ac5
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/service/WhiskActivationService.java
@@ -0,0 +1,70 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.service;
+
+import com.navercorp.openwhisk.intellij.common.utils.JsonParserUtils;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.activation.WhiskActivationMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.model.activation.WhiskActivationWithLogs;
+import org.apache.http.HttpHeaders;
+import org.apache.http.client.fluent.Request;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Optional;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+public class WhiskActivationService {
+
+    private WhiskActivationService() {
+    }
+
+    private static class LazyHolder {
+        private static final WhiskActivationService INSTANCE = new WhiskActivationService();
+    }
+
+    public static WhiskActivationService getInstance() {
+        return LazyHolder.INSTANCE;
+    }
+
+    public List<WhiskActivationMetaData> getWhiskActivations(WhiskAuth whiskAuth, Optional<String> name, int limit, int skip) throws IOException {
+        String endpoint = whiskAuth.getApihost()
+                + "/api/v1/namespaces/_/activations"
+                + "?limit=" + limit + "&skip=" + skip + "" + name.map(n -> "&name=" + n).orElse("");
+        String authorization = whiskAuth.getBasicAuthHeader();
+
+        String result = Request.Get(endpoint)
+                .setHeader(HttpHeaders.AUTHORIZATION, authorization)
+                .execute()
+                .returnContent()
+                .asString(UTF_8);
+        return JsonParserUtils.parseWhiskActivations(result);
+    }
+
+    public Optional<WhiskActivationWithLogs> getWhiskActivation(WhiskAuth whiskAuth, String activationId) throws IOException {
+        String endpoint = whiskAuth.getApihost() + "/api/v1/namespaces/_/activations/" + activationId;
+        String authorization = whiskAuth.getBasicAuthHeader();
+
+        String result = Request.Get(endpoint)
+                .setHeader(HttpHeaders.AUTHORIZATION, authorization)
+                .execute()
+                .returnContent()
+                .asString(UTF_8);
+        return JsonParserUtils.parseWhiskActivation(result);
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/service/WhiskNamespaceService.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/service/WhiskNamespaceService.java
new file mode 100644
index 0000000..9621aa3
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/service/WhiskNamespaceService.java
@@ -0,0 +1,65 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.service;
+
+import com.intellij.openapi.diagnostic.Logger;
+import com.navercorp.openwhisk.intellij.common.utils.JsonParserUtils;
+import org.apache.http.HttpHeaders;
+import org.apache.http.client.fluent.Request;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskNamespace;
+
+import java.io.IOException;
+import java.util.Optional;
+
+public class WhiskNamespaceService {
+    private static final Logger LOG = Logger.getInstance(WhiskNamespaceService.class);
+
+    private WhiskNamespaceService() {
+
+    }
+
+    private static class LazyHolder {
+        private static final WhiskNamespaceService INSTANCE = new WhiskNamespaceService();
+    }
+
+    public static WhiskNamespaceService getInstance() {
+        return LazyHolder.INSTANCE;
+    }
+
+    public Optional<WhiskNamespace> validateNamespace(WhiskAuth whiskAuth) {
+        String endpoint = whiskAuth.getApihost() + "/api/v1/namespaces";
+        String authorization = whiskAuth.getBasicAuthHeader();
+        try {
+            String result = Request.Get(endpoint)
+                    .setHeader(HttpHeaders.AUTHORIZATION, authorization)
+                    .execute()
+                    .returnContent()
+                    .asString();
+
+            String[] namespaces = JsonParserUtils.parseWhiskNamespace(result);
+            if (namespaces.length > 0) {
+                return Optional.of(new WhiskNamespace(whiskAuth.getAuth(), namespaces[0]));
+            } else {
+                return Optional.empty();
+            }
+        } catch (IOException e) {
+            LOG.warn("Invalid namespace", e);
+            return Optional.empty();
+        }
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/service/WhiskPackageService.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/service/WhiskPackageService.java
new file mode 100644
index 0000000..f6702b5
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/service/WhiskPackageService.java
@@ -0,0 +1,140 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.service;
+
+import com.intellij.openapi.diagnostic.Logger;
+import com.navercorp.openwhisk.intellij.common.utils.JsonParserUtils;
+import org.apache.http.HttpHeaders;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.client.fluent.Request;
+import org.apache.http.entity.ContentType;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.pkg.WhiskPackage;
+import com.navercorp.openwhisk.intellij.common.whisk.model.pkg.WhiskPackageWithActions;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+public class WhiskPackageService {
+    private static final Logger LOG = Logger.getInstance(WhiskPackageService.class);
+
+    private WhiskPackageService() {
+
+    }
+
+    private static class LazyHolder {
+        private static final WhiskPackageService INSTANCE = new WhiskPackageService();
+    }
+
+    public static WhiskPackageService getInstance() {
+        return WhiskPackageService.LazyHolder.INSTANCE;
+    }
+
+    public List<WhiskPackage> getWhiskPackages(WhiskAuth whiskAuth) throws IOException {
+        String endpoint = whiskAuth.getApihost() + "/api/v1/namespaces/_/packages?limit=200&skip=0";
+        String authorization = whiskAuth.getBasicAuthHeader();
+
+        String result = Request.Get(endpoint)
+                .setHeader(HttpHeaders.AUTHORIZATION, authorization)
+                .execute()
+                .returnContent()
+                .asString(UTF_8);
+        return JsonParserUtils.parseWhiskPackages(result);
+    }
+
+    public Optional<WhiskPackageWithActions> getWhiskPackage(WhiskAuth whiskAuth, String namespace, String name) throws IOException {
+        String endpoint = whiskAuth.getApihost() + "/api/v1/namespaces/" + namespace + "/packages/" + name;
+        String authorization = whiskAuth.getBasicAuthHeader();
+
+        String result = Request.Get(endpoint)
+                .setHeader(HttpHeaders.AUTHORIZATION, authorization)
+                .execute()
+                .returnContent()
+                .asString(UTF_8);
+        return JsonParserUtils.parseWhiskPackage(result);
+    }
+
+    public Optional<WhiskPackageWithActions> deleteWhiskPackage(WhiskAuth whiskAuth, String name) throws IOException {
+        String endpoint = whiskAuth.getApihost() + "/api/v1/namespaces/_/packages/" + name;
+        String authorization = whiskAuth.getBasicAuthHeader();
+        HttpResponse response = Request.Delete(endpoint)
+                .setHeader(HttpHeaders.AUTHORIZATION, authorization)
+                .execute()
+                .returnResponse();
+
+        if (response.getStatusLine().getStatusCode() == HttpStatus.SC_CONFLICT) {
+            return Optional.empty();
+        } else {
+            BufferedReader reader = new BufferedReader(new InputStreamReader(
+                    response.getEntity().getContent()));
+            String inputLine;
+            StringBuffer result = new StringBuffer();
+            while ((inputLine = reader.readLine()) != null) {
+                result.append(inputLine);
+            }
+            reader.close();
+            return JsonParserUtils.parseWhiskPackage(result.toString());
+        }
+    }
+
+    public Optional<WhiskPackageWithActions> updateWhiskPackage(WhiskAuth whiskAuth, String name, Map<String, Object> payload) throws IOException {
+        String endpoint = whiskAuth.getApihost() + "/api/v1/namespaces/_/packages/" + name + "?overwrite=true";
+        String authorization = whiskAuth.getBasicAuthHeader();
+        String body = JsonParserUtils.writeMapToJson(payload);
+        LOG.info("Package updated: " + body);
+        String result = Request.Put(endpoint)
+                .setHeader(HttpHeaders.AUTHORIZATION, authorization)
+                .bodyString(body, ContentType.APPLICATION_JSON)
+                .execute()
+                .returnContent()
+                .asString(UTF_8);
+        return JsonParserUtils.parseWhiskPackage(result);
+    }
+
+    public Optional<WhiskPackageWithActions> createWhiskPackage(WhiskAuth whiskAuth, String name, Map<String, Object> payload) throws IOException {
+        String endpoint = whiskAuth.getApihost() + "/api/v1/namespaces/_/packages/" + name + "?overwrite=false";
+        String authorization = whiskAuth.getBasicAuthHeader();
+        String body = JsonParserUtils.writeMapToJson(payload);
+        LOG.info("Package craeted: " + body);
+        HttpResponse response = Request.Put(endpoint)
+                .setHeader(HttpHeaders.AUTHORIZATION, authorization)
+                .bodyString(body, ContentType.APPLICATION_JSON)
+                .execute()
+                .returnResponse();
+
+        if (response.getStatusLine().getStatusCode() == HttpStatus.SC_CONFLICT) {
+            return Optional.empty();
+        } else {
+            BufferedReader reader = new BufferedReader(new InputStreamReader(
+                    response.getEntity().getContent()));
+            String inputLine;
+            StringBuffer result = new StringBuffer();
+            while ((inputLine = reader.readLine()) != null) {
+                result.append(inputLine);
+            }
+            reader.close();
+            return JsonParserUtils.parseWhiskPackage(result.toString());
+        }
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/service/WhiskRuleService.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/service/WhiskRuleService.java
new file mode 100644
index 0000000..bc93ae5
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/service/WhiskRuleService.java
@@ -0,0 +1,72 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.service;
+
+import com.intellij.openapi.diagnostic.Logger;
+import com.navercorp.openwhisk.intellij.common.utils.JsonParserUtils;
+import org.apache.http.HttpHeaders;
+import org.apache.http.client.fluent.Request;
+import org.apache.http.entity.ContentType;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.WhiskRule;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.Optional;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+public class WhiskRuleService {
+    private static final Logger LOG = Logger.getInstance(WhiskRuleService.class);
+
+    private WhiskRuleService() {
+    }
+
+    private static class LazyHolder {
+        private static final WhiskRuleService INSTANCE = new WhiskRuleService();
+    }
+
+    public static WhiskRuleService getInstance() {
+        return LazyHolder.INSTANCE;
+    }
+
+    public Optional<WhiskRule> updateWhiskRule(WhiskAuth whiskAuth, String ruleName, Map<String, Object> payload) throws IOException {
+        String endpoint = whiskAuth.getApihost() + "/api/v1/namespaces/_/rules/" + ruleName + "?overwrite=true";
+        String authorization = whiskAuth.getBasicAuthHeader();
+        String body = JsonParserUtils.writeMapToJson(payload);
+        LOG.info("Rule updated: " + body);
+        String result = Request.Put(endpoint)
+                .setHeader(HttpHeaders.AUTHORIZATION, authorization)
+                .bodyString(body, ContentType.APPLICATION_JSON)
+                .execute()
+                .returnContent()
+                .asString(UTF_8);
+        return JsonParserUtils.parseWhiskRule(result);
+    }
+
+    public Optional<WhiskRule> deleteWhiskRule(WhiskAuth whiskAuth, String ruleName) throws IOException {
+        String endpoint = whiskAuth.getApihost() + "/api/v1/namespaces/_/rules/" + ruleName;
+        String authorization = whiskAuth.getBasicAuthHeader();
+        String result = Request.Delete(endpoint)
+                .setHeader(HttpHeaders.AUTHORIZATION, authorization)
+                .execute()
+                .returnContent()
+                .asString(UTF_8);
+        return JsonParserUtils.parseWhiskRule(result);
+    }
+
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/service/WhiskTriggerService.java b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/service/WhiskTriggerService.java
new file mode 100644
index 0000000..4c3e095
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/common/whisk/service/WhiskTriggerService.java
@@ -0,0 +1,129 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.common.whisk.service;
+
+import com.intellij.openapi.diagnostic.Logger;
+import com.navercorp.openwhisk.intellij.common.utils.JsonParserUtils;
+import org.apache.http.HttpHeaders;
+import org.apache.http.client.fluent.Content;
+import org.apache.http.client.fluent.Request;
+import org.apache.http.entity.ContentType;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.ExecutableWhiskTrigger;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.WhiskTriggerMetaData;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+public class WhiskTriggerService {
+    private static final Logger LOG = Logger.getInstance(WhiskTriggerService.class);
+
+    private WhiskTriggerService() {
+    }
+
+    private static class LazyHolder {
+        private static final WhiskTriggerService INSTANCE = new WhiskTriggerService();
+    }
+
+    public static WhiskTriggerService getInstance() {
+        return LazyHolder.INSTANCE;
+    }
+
+    public List<WhiskTriggerMetaData> getWhiskTriggers(WhiskAuth whiskAuth) throws IOException {
+        String endpoint = whiskAuth.getApihost() + "/api/v1/namespaces/_/triggers?limit=50&skip=0";
+        String authorization = whiskAuth.getBasicAuthHeader();
+
+        String result = Request.Get(endpoint)
+                .setHeader(HttpHeaders.AUTHORIZATION, authorization)
+                .execute()
+                .returnContent()
+                .asString(UTF_8);
+        return JsonParserUtils.parseWhiskTriggers(result);
+    }
+
+    public Optional<ExecutableWhiskTrigger> getWhiskTrigger(WhiskAuth whiskAuth, String triggerName) throws IOException {
+        String endpoint = whiskAuth.getApihost() + "/api/v1/namespaces/_/triggers/" + triggerName;
+        String authorization = whiskAuth.getBasicAuthHeader();
+
+        String result = Request.Get(endpoint)
+                .setHeader(HttpHeaders.AUTHORIZATION, authorization)
+                .execute()
+                .returnContent()
+                .asString(UTF_8);
+        return JsonParserUtils.parseWhiskTrigger(result);
+    }
+
+    public Optional<String> fireWhiskTrigger(WhiskAuth whiskAuth, String triggerName, String params) throws IOException {
+        String endpoint = whiskAuth.getApihost() + "/api/v1/namespaces/_/triggers/" + triggerName;
+        String authorization = whiskAuth.getBasicAuthHeader();
+        Content content = Request.Post(endpoint)
+                .setHeader(HttpHeaders.AUTHORIZATION, authorization)
+                .bodyString(params, ContentType.APPLICATION_JSON)
+                .execute()
+                .returnContent();
+        if (content != null) {
+            return Optional.ofNullable(JsonParserUtils.beautifyJson(content.asString(UTF_8)));
+        } else {
+            return Optional.empty();
+        }
+    }
+
+    public Optional<ExecutableWhiskTrigger> deleteWhiskTrigger(WhiskAuth whiskAuth, String triggerName) throws IOException {
+        String endpoint = whiskAuth.getApihost() + "/api/v1/namespaces/_/triggers/" + triggerName;
+        String authorization = whiskAuth.getBasicAuthHeader();
+
+        String result = Request.Delete(endpoint)
+                .setHeader(HttpHeaders.AUTHORIZATION, authorization)
+                .execute()
+                .returnContent()
+                .asString(UTF_8);
+        return JsonParserUtils.parseWhiskTrigger(result);
+    }
+
+    public Optional<ExecutableWhiskTrigger> createWhiskTrigger(WhiskAuth whiskAuth, String triggerName, Map<String, Object> payload) throws IOException {
+        String endpoint = whiskAuth.getApihost() + "/api/v1/namespaces/_/triggers/" + triggerName + "?overwrite=false";
+        String authorization = whiskAuth.getBasicAuthHeader();
+        String body = JsonParserUtils.writeMapToJson(payload);
+        LOG.info("Trigger updated: " + body);
+        String result = Request.Put(endpoint)
+                .setHeader(HttpHeaders.AUTHORIZATION, authorization)
+                .bodyString(body, ContentType.APPLICATION_JSON)
+                .execute()
+                .returnContent()
+                .asString(UTF_8);
+        return JsonParserUtils.parseWhiskTrigger(result);
+    }
+
+    public Optional<ExecutableWhiskTrigger> updateWhiskTrigger(WhiskAuth whiskAuth, String triggerName, Map<String, Object> payload) throws IOException {
+        String endpoint = whiskAuth.getApihost() + "/api/v1/namespaces/_/triggers/" + triggerName + "?overwrite=true";
+        String authorization = whiskAuth.getBasicAuthHeader();
+        String body = JsonParserUtils.writeMapToJson(payload);
+        LOG.info("Trigger updated: " + body);
+        String result = Request.Put(endpoint)
+                .setHeader(HttpHeaders.AUTHORIZATION, authorization)
+                .bodyString(body, ContentType.APPLICATION_JSON)
+                .execute()
+                .returnContent()
+                .asString(UTF_8);
+        return JsonParserUtils.parseWhiskTrigger(result);
+    }
+
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ActionManagerDialog.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ActionManagerDialog.java
new file mode 100644
index 0000000..7a10e4b
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ActionManagerDialog.java
@@ -0,0 +1,67 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.action;
+
+import com.intellij.openapi.project.Project;
+import com.navercorp.openwhisk.intellij.common.DialogWrapperWithApply;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.ExecutableWhiskAction;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.WhiskActionMetaData;
+import com.navercorp.openwhisk.intellij.explorer.dialog.action.ui.ActionManagerDialogForm;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.util.List;
+
+public class ActionManagerDialog extends DialogWrapperWithApply {
+
+    private ActionManagerDialogForm actionManagerDialogForm;
+
+    public ActionManagerDialog(Project project, WhiskAuth auth, ExecutableWhiskAction action, List<WhiskActionMetaData> actions) {
+        super(project, true); // use current window as parent
+        actionManagerDialogForm = new ActionManagerDialogForm(project, auth, action, actions);
+        setTitle("Manage Action");
+        setResizable(false);
+        init();
+    }
+
+    @Nullable
+    @Override
+    protected JComponent createCenterPanel() {
+        if (actionManagerDialogForm != null) {
+            return this.actionManagerDialogForm.getContent();
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    protected void doApplyAction() {
+        if (actionManagerDialogForm != null) {
+            actionManagerDialogForm.updateAction();
+        }
+        super.doApplyAction();
+    }
+
+    @Override
+    protected void doOKAction() {
+        if (actionManagerDialogForm != null && !myApplyAction.isApplied()) {
+            actionManagerDialogForm.updateAction();
+        }
+        super.doOKAction();
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/DeleteActionDialog.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/DeleteActionDialog.java
new file mode 100644
index 0000000..bb510bb
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/DeleteActionDialog.java
@@ -0,0 +1,46 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.action;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.DialogWrapper;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.*;
+
+public class DeleteActionDialog extends DialogWrapper {
+
+    public DeleteActionDialog(Project project) {
+        super(true); // use current window as parent
+        setTitle("Delete Action");
+        setResizable(false);
+        init();
+    }
+
+    @Nullable
+    @Override
+    protected JComponent createCenterPanel() {
+        JPanel dialogPanel = new JPanel(new BorderLayout());
+
+        JLabel label = new JLabel("Are you sure you want to delete your action?");
+        label.setPreferredSize(new Dimension(100, 30));
+        dialogPanel.add(label, BorderLayout.CENTER);
+
+        return dialogPanel;
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/listener/DownActionListener.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/listener/DownActionListener.java
new file mode 100644
index 0000000..c3ca8ed
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/listener/DownActionListener.java
@@ -0,0 +1,21 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.action.listener;
+
+public interface DownActionListener {
+    void downAction(int index);
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/listener/RemoveActionListener.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/listener/RemoveActionListener.java
new file mode 100644
index 0000000..c211853
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/listener/RemoveActionListener.java
@@ -0,0 +1,21 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.action.listener;
+
+public interface RemoveActionListener {
+    void removeAction(int index);
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/listener/UpActionListener.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/listener/UpActionListener.java
new file mode 100644
index 0000000..32d88bc
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/listener/UpActionListener.java
@@ -0,0 +1,21 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.action.listener;
+
+public interface UpActionListener {
+    void upAction(int index);
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/ActionManagerDialogForm.form b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/ActionManagerDialogForm.form
new file mode 100644
index 0000000..9ecf0e0
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/ActionManagerDialogForm.form
@@ -0,0 +1,328 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.navercorp.openwhisk.intellij.explorer.dialog.action.ui.ActionManagerDialogForm">
+  <grid id="27dc6" binding="mainJPanel" layout-manager="GridLayoutManager" row-count="14" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+    <margin top="0" left="0" bottom="0" right="0"/>
+    <constraints>
+      <xy x="20" y="20" width="936" height="525"/>
+    </constraints>
+    <properties>
+      <preferredSize width="800" height="300"/>
+    </properties>
+    <border type="none"/>
+    <children>
+      <grid id="519e8" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="0">
+        <margin top="0" left="0" bottom="0" right="0"/>
+        <constraints>
+          <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false">
+            <preferred-size width="800" height="30"/>
+          </grid>
+        </constraints>
+        <properties/>
+        <border type="none"/>
+        <children>
+          <component id="27de0" class="javax.swing.JLabel">
+            <constraints>
+              <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
+                <preferred-size width="150" height="-1"/>
+              </grid>
+            </constraints>
+            <properties>
+              <text value="Runtime"/>
+            </properties>
+          </component>
+          <component id="e5ddb" class="javax.swing.JComboBox" binding="runtimeJComboBox">
+            <constraints>
+              <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="1" indent="0" use-parent-layout="false">
+                <preferred-size width="700" height="-1"/>
+              </grid>
+            </constraints>
+            <properties/>
+          </component>
+          <component id="4cbec" class="javax.swing.JSeparator">
+            <constraints>
+              <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+            </constraints>
+            <properties>
+              <orientation value="1"/>
+            </properties>
+          </component>
+        </children>
+      </grid>
+      <grid id="fd8b" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="0">
+        <margin top="0" left="0" bottom="0" right="0"/>
+        <constraints>
+          <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false">
+            <preferred-size width="800" height="30"/>
+          </grid>
+        </constraints>
+        <properties/>
+        <border type="none"/>
+        <children>
+          <grid id="10d33" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+            <margin top="0" left="0" bottom="0" right="0"/>
+            <constraints>
+              <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+            </constraints>
+            <properties/>
+            <border type="none"/>
+            <children>
+              <component id="47667" class="javax.swing.JLabel">
+                <constraints>
+                  <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
+                    <preferred-size width="150" height="-1"/>
+                  </grid>
+                </constraints>
+                <properties>
+                  <text value="Timeout (ms)"/>
+                </properties>
+              </component>
+              <component id="8ac53" class="javax.swing.JSpinner" binding="timeoutJSpinner">
+                <constraints>
+                  <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
+                    <preferred-size width="250" height="-1"/>
+                  </grid>
+                </constraints>
+                <properties/>
+              </component>
+              <component id="b5d2b" class="javax.swing.JSeparator">
+                <constraints>
+                  <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+                </constraints>
+                <properties>
+                  <orientation value="1"/>
+                </properties>
+              </component>
+            </children>
+          </grid>
+          <grid id="dd67a" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+            <margin top="0" left="0" bottom="0" right="0"/>
+            <constraints>
+              <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+            </constraints>
+            <properties/>
+            <border type="none"/>
+            <children>
+              <component id="866bd" class="javax.swing.JLabel">
+                <constraints>
+                  <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
+                    <preferred-size width="150" height="-1"/>
+                  </grid>
+                </constraints>
+                <properties>
+                  <text value="Memory (MB)"/>
+                </properties>
+              </component>
+              <component id="43f8a" class="javax.swing.JSpinner" binding="memoryJSpinner">
+                <constraints>
+                  <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
+                    <preferred-size width="250" height="-1"/>
+                  </grid>
+                </constraints>
+                <properties/>
+              </component>
+              <component id="6f7cf" class="javax.swing.JSeparator">
+                <constraints>
+                  <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+                </constraints>
+                <properties>
+                  <orientation value="1"/>
+                </properties>
+              </component>
+            </children>
+          </grid>
+          <component id="19f4a" class="javax.swing.JSeparator">
+            <constraints>
+              <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+            </constraints>
+            <properties>
+              <orientation value="1"/>
+            </properties>
+          </component>
+        </children>
+      </grid>
+      <grid id="2e6c5" layout-manager="GridLayoutManager" row-count="1" column-count="4" same-size-horizontally="false" same-size-vertically="true" hgap="-1" vgap="0">
+        <margin top="0" left="0" bottom="0" right="0"/>
+        <constraints>
+          <grid row="4" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="3" indent="1" use-parent-layout="false">
+            <minimum-size width="-1" height="30"/>
+            <preferred-size width="800" height="30"/>
+            <maximum-size width="-1" height="30"/>
+          </grid>
+        </constraints>
+        <properties/>
+        <border type="none"/>
+        <children>
+          <grid id="17e79" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+            <margin top="0" left="0" bottom="0" right="0"/>
+            <constraints>
+              <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+            </constraints>
+            <properties/>
+            <border type="none"/>
+            <children>
+              <component id="77c80" class="javax.swing.JCheckBox" binding="webactionJCheckBox">
+                <constraints>
+                  <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false">
+                    <preferred-size width="300" height="-1"/>
+                  </grid>
+                </constraints>
+                <properties>
+                  <text value="Web Action"/>
+                </properties>
+              </component>
+            </children>
+          </grid>
+          <grid id="30b99" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+            <margin top="0" left="0" bottom="0" right="0"/>
+            <constraints>
+              <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+            </constraints>
+            <properties/>
+            <border type="none"/>
+            <children>
+              <component id="61458" class="javax.swing.JCheckBox" binding="rawHttpJCheckBox">
+                <constraints>
+                  <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false">
+                    <preferred-size width="250" height="-1"/>
+                  </grid>
+                </constraints>
+                <properties>
+                  <text value="Raw HTTP"/>
+                </properties>
+              </component>
+            </children>
+          </grid>
+          <grid id="660f" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+            <margin top="0" left="0" bottom="0" right="0"/>
+            <constraints>
+              <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+            </constraints>
+            <properties/>
+            <border type="none"/>
+            <children>
+              <component id="d7b90" class="javax.swing.JCheckBox" binding="customOptionHeaderJCheckBox">
+                <constraints>
+                  <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false">
+                    <preferred-size width="250" height="-1"/>
+                  </grid>
+                </constraints>
+                <properties>
+                  <text value="Custom Options Header"/>
+                </properties>
+              </component>
+            </children>
+          </grid>
+          <grid id="96f9d" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+            <margin top="0" left="0" bottom="0" right="0"/>
+            <constraints>
+              <grid row="0" column="3" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+            </constraints>
+            <properties/>
+            <border type="none"/>
+            <children>
+              <component id="ed717" class="javax.swing.JCheckBox" binding="finalOptionJCheckBox">
+                <constraints>
+                  <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false">
+                    <preferred-size width="250" height="-1"/>
+                  </grid>
+                </constraints>
+                <properties>
+                  <text value="Protect Default Parameter"/>
+                </properties>
+              </component>
+            </children>
+          </grid>
+        </children>
+      </grid>
+      <grid id="4506f" binding="defaultParameterJPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
+        <constraints>
+          <grid row="12" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties/>
+        <border type="none"/>
+        <children/>
+      </grid>
+      <component id="bc149" class="javax.swing.JSeparator">
+        <constraints>
+          <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false">
+            <preferred-size width="-1" height="5"/>
+            <maximum-size width="-1" height="5"/>
+          </grid>
+        </constraints>
+        <properties/>
+      </component>
+      <component id="f54d9" class="javax.swing.JSeparator">
+        <constraints>
+          <grid row="3" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false">
+            <preferred-size width="-1" height="5"/>
+            <maximum-size width="-1" height="5"/>
+          </grid>
+        </constraints>
+        <properties/>
+      </component>
+      <component id="ee953" class="javax.swing.JSeparator">
+        <constraints>
+          <grid row="5" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false">
+            <preferred-size width="-1" height="5"/>
+            <maximum-size width="-1" height="5"/>
+          </grid>
+        </constraints>
+        <properties/>
+      </component>
+      <component id="1ae9" class="javax.swing.JSeparator" binding="defaultParameterJSeparator">
+        <constraints>
+          <grid row="13" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false">
+            <maximum-size width="-1" height="5"/>
+          </grid>
+        </constraints>
+        <properties/>
+      </component>
+      <grid id="82b18" binding="dockerImageJPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
+        <constraints>
+          <grid row="8" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties/>
+        <border type="none"/>
+        <children/>
+      </grid>
+      <component id="7ff6e" class="javax.swing.JSeparator" binding="dockerImageJSeparator">
+        <constraints>
+          <grid row="9" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false">
+            <maximum-size width="-1" height="5"/>
+          </grid>
+        </constraints>
+        <properties/>
+      </component>
+      <grid id="33dc2" binding="linkedActionsJPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
+        <constraints>
+          <grid row="6" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false">
+            <preferred-size width="-1" height="150"/>
+          </grid>
+        </constraints>
+        <properties/>
+        <border type="none"/>
+        <children/>
+      </grid>
+      <component id="c5701" class="javax.swing.JSeparator" binding="linkedActionsJSeparator">
+        <constraints>
+          <grid row="7" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties/>
+      </component>
+      <grid id="96812" binding="codeTypeJPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
+        <constraints>
+          <grid row="10" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties/>
+        <border type="none"/>
+        <children/>
+      </grid>
+      <component id="4bebf" class="javax.swing.JSeparator" binding="codeTypeJSeparator">
+        <constraints>
+          <grid row="11" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties/>
+      </component>
+    </children>
+  </grid>
+</form>
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/ActionManagerDialogForm.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/ActionManagerDialogForm.java
new file mode 100644
index 0000000..6f68c21
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/ActionManagerDialogForm.java
@@ -0,0 +1,413 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.action.ui;
+
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.intellij.uiDesigner.core.GridConstraints;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import com.navercorp.openwhisk.intellij.common.utils.EventUtils;
+import com.navercorp.openwhisk.intellij.common.utils.JsonParserUtils;
+import com.navercorp.openwhisk.intellij.common.utils.ParameterUtils;
+import com.navercorp.openwhisk.intellij.common.whisk.model.Limits;
+import com.navercorp.openwhisk.intellij.common.whisk.model.Runtime;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.ExecutableWhiskAction;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.WhiskActionMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.model.exec.CodeExec;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskActionService;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.listener.RefreshWhiskTreeListener;
+import com.navercorp.openwhisk.intellij.run.toolwindow.listener.RefreshActionOrTriggerListener;
+
+import javax.swing.*;
+import javax.swing.event.ListDataListener;
+import java.awt.*;
+import java.awt.event.ItemEvent;
+import java.io.IOException;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import static com.intellij.uiDesigner.core.GridConstraints.*;
+
+public class ActionManagerDialogForm {
+    private static final Logger LOG = Logger.getInstance(ActionManagerDialogForm.class);
+    private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+    private JPanel mainJPanel;
+    private JComboBox runtimeJComboBox;
+    private JCheckBox webactionJCheckBox;
+    private JCheckBox rawHttpJCheckBox;
+    private JCheckBox customOptionHeaderJCheckBox;
+    private JCheckBox finalOptionJCheckBox;
+    private JSpinner timeoutJSpinner;
+    private JSpinner memoryJSpinner;
+    private JPanel defaultParameterJPanel;
+    private JSeparator defaultParameterJSeparator;
+    private JPanel dockerImageJPanel;
+    private JSeparator dockerImageJSeparator;
+    private JPanel linkedActionsJPanel;
+    private JSeparator linkedActionsJSeparator;
+    private JSeparator codeTypeJSeparator;
+    private JPanel codeTypeJPanel;
+
+    private DefaultParameterForm defaultParameterForm;
+    private DockerImageForm dockerImageForm;
+    private CodeTypeForm codeTypeForm;
+    private LinkedActionsForm linkedActionsForm;
+
+    private Project project;
+    private WhiskAuth whiskAuth;
+    private ExecutableWhiskAction action;
+
+    private WhiskActionService whiskActionService = WhiskActionService.getInstance();
+
+    public ActionManagerDialogForm(Project project, WhiskAuth auth, ExecutableWhiskAction action, List<WhiskActionMetaData> actions) {
+        this.project = project;
+        this.whiskAuth = auth;
+        this.action = action;
+
+        // add linked actions panel
+        linkedActionsForm = new LinkedActionsForm(project, action.getNamespace().split("/")[0], actions, action.getExec().getComponents());
+        linkedActionsJPanel.add(linkedActionsForm.getContent(), BorderLayout.CENTER);
+
+        // add docker image panel
+        dockerImageForm = new DockerImageForm();
+        dockerImageJPanel.add(dockerImageForm.getContent(), BorderLayout.CENTER);
+
+        // add code type panel
+        codeTypeForm = new CodeTypeForm();
+        codeTypeJPanel.add(codeTypeForm.getContent(), BorderLayout.CENTER);
+
+        // add default parameter panel
+        defaultParameterForm = new DefaultParameterForm();
+        defaultParameterJPanel.add(defaultParameterForm.getContent(), BorderLayout.CENTER);
+
+        switch (action.getKind()) {
+            case "sequence":
+                // remove docker image panel
+                mainJPanel.remove(dockerImageJPanel);
+                mainJPanel.remove(dockerImageJSeparator);
+
+                // remove code type panel
+                mainJPanel.remove(codeTypeJPanel);
+                mainJPanel.remove(codeTypeJSeparator);
+
+                // remove default parameter panel
+                mainJPanel.remove(defaultParameterJPanel);
+                mainJPanel.remove(defaultParameterJSeparator);
+                break;
+            case "blackbox":
+                // remove linked actions panel
+                mainJPanel.remove(linkedActionsJPanel);
+                mainJPanel.remove(linkedActionsJSeparator);
+                break;
+            default: // normal
+                // remove linked actions panel
+                mainJPanel.remove(linkedActionsJPanel);
+                mainJPanel.remove(linkedActionsJSeparator);
+
+                // remove docker image panel
+                mainJPanel.remove(dockerImageJPanel);
+                mainJPanel.remove(dockerImageJSeparator);
+
+                // remove code type panel
+                mainJPanel.remove(codeTypeJPanel);
+                mainJPanel.remove(codeTypeJSeparator);
+        }
+
+        runtimeJComboBox.setModel(new ComboBoxModel<Runtime>() {
+            private Runtime selected;
+
+            @Override
+            public void setSelectedItem(Object anItem) {
+                selected = (Runtime) anItem;
+            }
+
+            @Override
+            public Runtime getSelectedItem() {
+                return this.selected;
+            }
+
+            @Override
+            public int getSize() {
+                return Runtime.values().length;
+            }
+
+            @Override
+            public Runtime getElementAt(int index) {
+                return Runtime.toRuntime(index);
+            }
+
+            @Override
+            public void addListDataListener(ListDataListener l) {
+                // nothing to do
+            }
+
+            @Override
+            public void removeListDataListener(ListDataListener l) {
+                // nothing to do
+            }
+        });
+
+        if (!webactionJCheckBox.isSelected()) {
+            // raw http
+            rawHttpJCheckBox.setEnabled(false);
+            // custom option header
+            customOptionHeaderJCheckBox.setEnabled(false);
+            // final option
+            finalOptionJCheckBox.setEnabled(false);
+        }
+
+        webactionJCheckBox.addChangeListener(e -> {
+            AbstractButton abstractButton = (AbstractButton) e.getSource();
+            ButtonModel buttonModel = abstractButton.getModel();
+
+            if (buttonModel.isSelected()) {
+                // raw http
+                rawHttpJCheckBox.setEnabled(true);
+                // custom option header
+                customOptionHeaderJCheckBox.setEnabled(true);
+                // final option
+                finalOptionJCheckBox.setEnabled(true);
+            } else {
+                // raw http
+                rawHttpJCheckBox.setSelected(false);
+                rawHttpJCheckBox.setEnabled(false);
+                // custom option header
+                customOptionHeaderJCheckBox.setSelected(false);
+                customOptionHeaderJCheckBox.setEnabled(false);
+                // final option
+                finalOptionJCheckBox.setSelected(false);
+                finalOptionJCheckBox.setEnabled(false);
+            }
+        });
+
+        /**
+         * Set Value
+         */
+        // runtime
+        runtimeJComboBox.setSelectedItem(Runtime.toRuntime(action.getKind()));
+        // timeout
+        timeoutJSpinner.setValue(action.getLimits().getTimeout());
+        // memory
+        memoryJSpinner.setValue(action.getLimits().getMemory());
+        // webaction
+        webactionJCheckBox.setSelected(action.isWebAction());
+        // raw http
+        rawHttpJCheckBox.setSelected(action.isRawHttp());
+        // custom option header
+        customOptionHeaderJCheckBox.setSelected(action.isCustomOptions());
+        // final option
+        finalOptionJCheckBox.setSelected(action.isFinalDefaultParameter());
+
+        try {
+            // default parameter
+            defaultParameterForm.setDefaultParameter(JsonParserUtils.writeParameterToJson(action.getParameters()));
+        } catch (IOException e) {
+            LOG.error("Failed to parse json: " + action.getFullyQualifiedName(), e);
+        }
+        // docker image url
+        dockerImageForm.setDockerImage(action.getExec().getImage());
+        // code type
+        action.getCodeType().ifPresent(codeType -> codeTypeForm.setCodeType(codeType));
+
+        /**
+         * Set runtime event
+         */
+        runtimeJComboBox.addItemListener(e -> {
+            if (e.getStateChange() != ItemEvent.SELECTED) {
+                return;
+            }
+
+            switch ((Runtime) e.getItem()) {
+                case SEQUENCE:
+                    removeAllPanel();
+
+                    mainJPanel.add(linkedActionsJPanel, new GridConstraints(7, 0, 1, 1,
+                            SIZEPOLICY_FIXED, SIZEPOLICY_CAN_GROW | SIZEPOLICY_CAN_SHRINK, FILL_NONE, ANCHOR_CENTER,
+                            new Dimension(-1, -1), new Dimension(-1, 150), new Dimension(-1, -1), 0));
+                    mainJPanel.add(linkedActionsJSeparator, new GridConstraints(8, 0, 1, 1,
+                            SIZEPOLICY_FIXED, SIZEPOLICY_CAN_GROW | SIZEPOLICY_CAN_SHRINK, FILL_NONE, ANCHOR_CENTER,
+                            new Dimension(-1, -1), new Dimension(-1, -1), new Dimension(-1, 5), 0));
+
+                    mainJPanel.updateUI();
+                    break;
+                case DOCKER:
+                    removeAllPanel();
+
+                    mainJPanel.add(dockerImageJPanel, new GridConstraints(7, 0, 1, 1,
+                            SIZEPOLICY_FIXED, SIZEPOLICY_CAN_GROW | SIZEPOLICY_CAN_SHRINK, FILL_NONE, ANCHOR_CENTER,
+                            new Dimension(-1, -1), new Dimension(-1, -1), new Dimension(-1, -1), 0));
+                    mainJPanel.add(dockerImageJSeparator, new GridConstraints(8, 0, 1, 1,
+                            SIZEPOLICY_FIXED, SIZEPOLICY_CAN_GROW | SIZEPOLICY_CAN_SHRINK, FILL_NONE, ANCHOR_CENTER,
+                            new Dimension(-1, -1), new Dimension(-1, -1), new Dimension(-1, 5), 0));
+
+                    mainJPanel.add(codeTypeJPanel, new GridConstraints(9, 0, 1, 1,
+                            SIZEPOLICY_FIXED, SIZEPOLICY_CAN_GROW | SIZEPOLICY_CAN_SHRINK, FILL_NONE, ANCHOR_CENTER,
+                            new Dimension(-1, -1), new Dimension(-1, -1), new Dimension(-1, -1), 0));
+                    mainJPanel.add(codeTypeJSeparator, new GridConstraints(10, 0, 1, 1,
+                            SIZEPOLICY_FIXED, SIZEPOLICY_CAN_GROW | SIZEPOLICY_CAN_SHRINK, FILL_NONE, ANCHOR_CENTER,
+                            new Dimension(-1, -1), new Dimension(-1, -1), new Dimension(-1, 5), 0));
+
+                    mainJPanel.add(defaultParameterJPanel, new GridConstraints(11, 0, 1, 1,
+                            SIZEPOLICY_FIXED, SIZEPOLICY_CAN_GROW | SIZEPOLICY_CAN_SHRINK, FILL_NONE, ANCHOR_CENTER,
+                            new Dimension(-1, -1), new Dimension(-1, -1), new Dimension(-1, -1), 0));
+                    mainJPanel.add(defaultParameterJSeparator, new GridConstraints(12, 0, 1, 1,
+                            SIZEPOLICY_FIXED, SIZEPOLICY_CAN_GROW | SIZEPOLICY_CAN_SHRINK, FILL_NONE, ANCHOR_CENTER,
+                            new Dimension(-1, -1), new Dimension(-1, -1), new Dimension(-1, 5), 0));
+
+                    mainJPanel.updateUI();
+                    break;
+                default: // normal
+                    removeAllPanel();
+
+                    mainJPanel.add(defaultParameterJPanel, new GridConstraints(7, 0, 1, 1,
+                            SIZEPOLICY_FIXED, SIZEPOLICY_CAN_GROW | SIZEPOLICY_CAN_SHRINK, FILL_NONE, ANCHOR_CENTER,
+                            new Dimension(-1, -1), new Dimension(-1, -1), new Dimension(-1, -1), 0));
+                    mainJPanel.add(defaultParameterJSeparator, new GridConstraints(8, 0, 1, 1,
+                            SIZEPOLICY_FIXED, SIZEPOLICY_CAN_GROW | SIZEPOLICY_CAN_SHRINK, FILL_NONE, ANCHOR_CENTER,
+                            new Dimension(-1, -1), new Dimension(-1, -1), new Dimension(-1, 5), 0));
+
+                    mainJPanel.updateUI();
+            }
+        });
+    }
+
+    private void removeAllPanel() {
+        // remove linked actions panel
+        mainJPanel.remove(linkedActionsJPanel);
+        mainJPanel.remove(linkedActionsJSeparator);
+
+        // remove docker image panel
+        mainJPanel.remove(dockerImageJPanel);
+        mainJPanel.remove(dockerImageJSeparator);
+
+        // remove code type panel
+        mainJPanel.remove(codeTypeJPanel);
+        mainJPanel.remove(codeTypeJSeparator);
+
+        // remove default parameter panel
+        mainJPanel.remove(defaultParameterJPanel);
+        mainJPanel.remove(defaultParameterJSeparator);
+    }
+
+    public void updateAction() {
+        try {
+            // parameters
+            Optional<String> params = getParameter();
+            if (!params.isPresent()) {
+                NOTIFIER.notify(project, "The json format of the parameter is incorrect.", NotificationType.ERROR);
+                return;
+            }
+            List<Map<String, Object>> validParams = ParameterUtils.mapToListMap(JsonParserUtils.parseMap(params.get()));
+            // timeout
+            int timeout = (Integer) timeoutJSpinner.getValue();
+            // memory
+            int memory = (Integer) memoryJSpinner.getValue();
+            Limits limits = action.getLimits();
+            limits.setTimeout(timeout);
+            limits.setMemory(memory);
+            // runtime
+            Runtime runtime = (Runtime) runtimeJComboBox.getSelectedItem();
+            CodeExec exec = createExec(action, runtime);
+            // web action
+            boolean web = webactionJCheckBox.isSelected();
+            boolean rawHttp = rawHttpJCheckBox.isSelected();
+            boolean customOption = customOptionHeaderJCheckBox.isSelected();
+            boolean finalDefaultParameter = finalOptionJCheckBox.isSelected();
+            // code type
+            Optional<String> codeType = getCodeType(runtime);
+            // annotations
+            List<Map<String, Object>> annotations = annotationToCollection(action, web, rawHttp, customOption, finalDefaultParameter, codeType);
+
+            // payload
+            Map<String, Object> payload = new LinkedHashMap<>();
+            payload.put("namespace", action.getNamespacePath());
+            payload.put("name", action.getWhiskPackage().map(pkg -> pkg + "/" + action.getName()).orElse(action.getName()));
+            payload.put("parameters", validParams);
+            payload.put("annotations", annotations);
+            payload.put("limits", limits);
+            payload.put("exec", exec);
+            whiskActionService.updateWhiskAction(whiskAuth, action, payload).ifPresent(updated -> {
+                NOTIFIER.notify(project, "Action update succeeded: " + updated.getFullyQualifiedName(), NotificationType.INFORMATION);
+                EventUtils.publish(project, RefreshActionOrTriggerListener.TOPIC, RefreshActionOrTriggerListener::fetchActionMetadata);
+                EventUtils.publish(project, RefreshWhiskTreeListener.TOPIC, RefreshWhiskTreeListener::refreshWhiskTree);
+            });
+        } catch (IOException e) {
+            String msg = "Failed to update action: " + action.getFullyQualifiedName();
+            LOG.error(msg, e);
+            NOTIFIER.notify(project, msg, NotificationType.ERROR);
+        }
+    }
+
+    private Optional<String> getCodeType(Runtime runtime) {
+        switch (runtime) {
+            case DOCKER:
+                Runtime codeType = codeTypeForm.getSelectedCodeType();
+                return Optional.ofNullable(codeType.toString());
+            default:
+                return Optional.empty();
+        }
+    }
+
+    private Optional<String> getParameter() {
+        if (defaultParameterForm == null) {
+            return Optional.of("{}");
+        }
+        return ParameterUtils.validateParams(defaultParameterForm.getDefaultParameter());
+    }
+
+    private CodeExec createExec(ExecutableWhiskAction executableWhiskAction, Runtime runtime) {
+        CodeExec codeExec = executableWhiskAction.getExec();
+        codeExec.setKind(runtime.toString());
+
+        switch (runtime) {
+            case SEQUENCE:
+                codeExec.setComponents(linkedActionsForm.getComponents());
+                break;
+            case DOCKER:
+                codeExec.setImage(dockerImageForm.getDockerImage());
+                break;
+            default: // normal
+        }
+
+        return codeExec;
+    }
+
+    private List<Map<String, Object>> annotationToCollection(ExecutableWhiskAction executableWhiskAction,
+                                                             boolean web,
+                                                             boolean rawHttp,
+                                                             boolean customOption,
+                                                             boolean finalDefaultParameter,
+                                                             Optional<String> codeType) {
+        Map<String, Object> annotations = ParameterUtils.listMapToMap(executableWhiskAction.getAnnotations());
+        annotations.put("web-export", web);
+        annotations.put("raw-http", rawHttp);
+        annotations.put("web-custom-options", customOption);
+        annotations.put("final", finalDefaultParameter);
+        codeType.map(ct -> annotations.put("code-type", ct));
+        return ParameterUtils.mapToListMap(annotations);
+    }
+
+    public JPanel getContent() {
+        return mainJPanel;
+    }
+
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/CodeTypeForm.form b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/CodeTypeForm.form
new file mode 100644
index 0000000..e4c6cab
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/CodeTypeForm.form
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.navercorp.openwhisk.intellij.explorer.dialog.action.ui.CodeTypeForm">
+  <grid id="27dc6" binding="mainJPanel" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+    <margin top="0" left="0" bottom="0" right="0"/>
+    <constraints>
+      <xy x="20" y="20" width="811" height="400"/>
+    </constraints>
+    <properties/>
+    <border type="none"/>
+    <children>
+      <component id="16beb" class="javax.swing.JLabel">
+        <constraints>
+          <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
+            <preferred-size width="150" height="-1"/>
+          </grid>
+        </constraints>
+        <properties>
+          <text value="Code Type"/>
+        </properties>
+      </component>
+      <component id="2ec8b" class="javax.swing.JComboBox" binding="codeTypeJComboBox">
+        <constraints>
+          <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="1" indent="0" use-parent-layout="false">
+            <preferred-size width="700" height="-1"/>
+          </grid>
+        </constraints>
+        <properties/>
+      </component>
+      <component id="af638" class="javax.swing.JSeparator">
+        <constraints>
+          <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties>
+          <orientation value="1"/>
+        </properties>
+      </component>
+    </children>
+  </grid>
+</form>
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/CodeTypeForm.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/CodeTypeForm.java
new file mode 100644
index 0000000..3314167
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/CodeTypeForm.java
@@ -0,0 +1,85 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.action.ui;
+
+import com.navercorp.openwhisk.intellij.common.whisk.model.Runtime;
+
+import javax.swing.*;
+import javax.swing.event.ListDataListener;
+
+public class CodeTypeForm {
+    private JPanel mainJPanel;
+    private JComboBox codeTypeJComboBox;
+
+    public CodeTypeForm() {
+
+        codeTypeJComboBox.setModel(new ComboBoxModel<Runtime>() {
+            private Runtime selected;
+
+            @Override
+            public void setSelectedItem(Object anItem) {
+                selected = (Runtime) anItem;
+            }
+
+            @Override
+            public Object getSelectedItem() {
+                return this.selected;
+            }
+
+            @Override
+            public int getSize() {
+                // remove blackbox, sequence
+                return Runtime.values().length - 2;
+            }
+
+            @Override
+            public Runtime getElementAt(int index) {
+                return Runtime.toCodeType(index);
+            }
+
+            @Override
+            public void addListDataListener(ListDataListener l) {
+                // nothing to do
+            }
+
+            @Override
+            public void removeListDataListener(ListDataListener l) {
+                // nothing to do
+            }
+        });
+    }
+
+    public void setCodeType(String codeType) {
+        Runtime runtime = Runtime.toRuntime(codeType);
+        switch (runtime) {
+            case SEQUENCE:
+            case DOCKER:
+                break;
+            default: // normal
+                codeTypeJComboBox.setSelectedItem(runtime);
+        }
+    }
+
+    public Runtime getSelectedCodeType() {
+        return (Runtime) codeTypeJComboBox.getSelectedItem();
+    }
+
+    public JPanel getContent() {
+        return mainJPanel;
+    }
+
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/DefaultParameterForm.form b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/DefaultParameterForm.form
new file mode 100644
index 0000000..4c08f09
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/DefaultParameterForm.form
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.navercorp.openwhisk.intellij.explorer.dialog.action.ui.DefaultParameterForm">
+  <grid id="27dc6" binding="mainJPanel" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+    <margin top="0" left="0" bottom="0" right="0"/>
+    <constraints>
+      <xy x="20" y="20" width="500" height="400"/>
+    </constraints>
+    <properties/>
+    <border type="none"/>
+    <children>
+      <component id="dc471" class="javax.swing.JLabel">
+        <constraints>
+          <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
+            <preferred-size width="150" height="-1"/>
+          </grid>
+        </constraints>
+        <properties>
+          <text value="Default Parameter"/>
+        </properties>
+      </component>
+      <scrollpane id="c704a">
+        <constraints>
+          <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="7" hsize-policy="7" anchor="0" fill="3" indent="0" use-parent-layout="false">
+            <preferred-size width="700" height="150"/>
+          </grid>
+        </constraints>
+        <properties/>
+        <border type="none"/>
+        <children>
+          <component id="e3336" class="javax.swing.JTextArea" binding="defaultParameterJTextArea">
+            <constraints/>
+            <properties/>
+          </component>
+        </children>
+      </scrollpane>
+      <component id="146a8" class="javax.swing.JSeparator">
+        <constraints>
+          <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties>
+          <orientation value="1"/>
+        </properties>
+      </component>
+    </children>
+  </grid>
+</form>
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/DefaultParameterForm.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/DefaultParameterForm.java
new file mode 100644
index 0000000..bcdac1e
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/DefaultParameterForm.java
@@ -0,0 +1,40 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.action.ui;
+
+import javax.swing.*;
+
+public class DefaultParameterForm {
+    private JPanel mainJPanel;
+    private JTextArea defaultParameterJTextArea;
+
+    public DefaultParameterForm() {
+
+    }
+
+    public void setDefaultParameter(String parameter) {
+        defaultParameterJTextArea.setText(parameter);
+    }
+
+    public String getDefaultParameter() {
+        return defaultParameterJTextArea.getText();
+    }
+
+    public JPanel getContent() {
+        return mainJPanel;
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/DockerImageForm.form b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/DockerImageForm.form
new file mode 100644
index 0000000..45720b1
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/DockerImageForm.form
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.navercorp.openwhisk.intellij.explorer.dialog.action.ui.DockerImageForm">
+  <grid id="27dc6" binding="mainJPanel" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+    <margin top="0" left="0" bottom="0" right="0"/>
+    <constraints>
+      <xy x="20" y="20" width="500" height="400"/>
+    </constraints>
+    <properties/>
+    <border type="none"/>
+    <children>
+      <component id="724b3" class="javax.swing.JLabel">
+        <constraints>
+          <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
+            <preferred-size width="150" height="-1"/>
+          </grid>
+        </constraints>
+        <properties>
+          <text value="Image"/>
+        </properties>
+      </component>
+      <component id="8e07" class="javax.swing.JTextField" binding="dockerImageJTextField">
+        <constraints>
+          <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
+            <preferred-size width="700" height="-1"/>
+          </grid>
+        </constraints>
+        <properties/>
+      </component>
+      <component id="c87" class="javax.swing.JSeparator">
+        <constraints>
+          <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties>
+          <orientation value="1"/>
+        </properties>
+      </component>
+    </children>
+  </grid>
+</form>
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/DockerImageForm.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/DockerImageForm.java
new file mode 100644
index 0000000..c07ecab
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/DockerImageForm.java
@@ -0,0 +1,40 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.action.ui;
+
+import javax.swing.*;
+
+public class DockerImageForm {
+    private JTextField dockerImageJTextField;
+    private JPanel mainJPanel;
+
+    public DockerImageForm() {
+
+    }
+
+    public void setDockerImage(String dockerImage) {
+        dockerImageJTextField.setText(dockerImage);
+    }
+
+    public String getDockerImage() {
+        return dockerImageJTextField.getText();
+    }
+
+    public JPanel getContent() {
+        return mainJPanel;
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/LinkedActionsEntryForm.form b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/LinkedActionsEntryForm.form
new file mode 100644
index 0000000..3050ceb
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/LinkedActionsEntryForm.form
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.navercorp.openwhisk.intellij.explorer.dialog.action.ui.LinkedActionsEntryForm">
+  <grid id="27dc6" binding="mainJPanel" layout-manager="GridLayoutManager" row-count="1" column-count="5" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+    <margin top="0" left="0" bottom="0" right="0"/>
+    <constraints>
+      <xy x="20" y="20" width="500" height="400"/>
+    </constraints>
+    <properties/>
+    <border type="none"/>
+    <children>
+      <component id="ac3fe" class="javax.swing.JLabel" binding="indexJLabel">
+        <constraints>
+          <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
+            <preferred-size width="50" height="-1"/>
+          </grid>
+        </constraints>
+        <properties>
+          <text value=""/>
+        </properties>
+      </component>
+      <component id="5ae4" class="javax.swing.JTextField" binding="actionJTextField">
+        <constraints>
+          <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
+            <preferred-size width="360" height="-1"/>
+          </grid>
+        </constraints>
+        <properties/>
+      </component>
+      <component id="89105" class="javax.swing.JButton" binding="upJButton">
+        <constraints>
+          <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false">
+            <preferred-size width="30" height="-1"/>
+          </grid>
+        </constraints>
+        <properties>
+          <text value=""/>
+        </properties>
+      </component>
+      <component id="afc8f" class="javax.swing.JButton" binding="downJButton">
+        <constraints>
+          <grid row="0" column="3" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false">
+            <preferred-size width="30" height="-1"/>
+          </grid>
+        </constraints>
+        <properties>
+          <text value=""/>
+        </properties>
+      </component>
+      <component id="fa2ee" class="javax.swing.JButton" binding="removeJButton">
+        <constraints>
+          <grid row="0" column="4" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false">
+            <preferred-size width="30" height="-1"/>
+          </grid>
+        </constraints>
+        <properties>
+          <text value=""/>
+        </properties>
+      </component>
+    </children>
+  </grid>
+</form>
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/LinkedActionsEntryForm.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/LinkedActionsEntryForm.java
new file mode 100644
index 0000000..9bb5739
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/LinkedActionsEntryForm.java
@@ -0,0 +1,98 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.action.ui;
+
+import com.intellij.openapi.diagnostic.Logger;
+import com.navercorp.openwhisk.intellij.explorer.dialog.action.listener.DownActionListener;
+import com.navercorp.openwhisk.intellij.explorer.dialog.action.listener.RemoveActionListener;
+import com.navercorp.openwhisk.intellij.explorer.dialog.action.listener.UpActionListener;
+
+import javax.swing.*;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+
+import static com.intellij.icons.AllIcons.General.Remove;
+import static com.navercorp.openwhisk.intellij.common.Icons.ARROW_DOWN;
+import static com.navercorp.openwhisk.intellij.common.Icons.ARROW_UP;
+
+public class LinkedActionsEntryForm {
+    private static final Logger LOG = Logger.getInstance(LinkedActionsEntryForm.class);
+
+    private JPanel mainJPanel;
+    private JTextField actionJTextField;
+    private JButton upJButton;
+    private JButton downJButton;
+    private JButton removeJButton;
+    private JLabel indexJLabel;
+
+    private String namespace;
+
+    public LinkedActionsEntryForm(int index,
+                                  String namespace,
+                                  String actionName,
+                                  UpActionListener upActionListener,
+                                  DownActionListener downActionListener,
+                                  RemoveActionListener removeActionListener) {
+        this.namespace = namespace;
+
+        indexJLabel.setText(String.valueOf(index));
+        indexJLabel.setHorizontalAlignment(SwingConstants.CENTER);
+        setActionName(actionName);
+
+        upJButton.setIcon(ARROW_UP);
+        upJButton.addMouseListener(new MouseAdapter() {
+            @Override
+            public void mouseClicked(MouseEvent e) {
+                upActionListener.upAction(Integer.parseInt(indexJLabel.getText()));
+            }
+        });
+
+        downJButton.setIcon(ARROW_DOWN);
+        downJButton.addMouseListener(new MouseAdapter() {
+            @Override
+            public void mouseClicked(MouseEvent e) {
+                downActionListener.downAction(Integer.parseInt(indexJLabel.getText()));
+            }
+        });
+
+        removeJButton.setIcon(Remove);
+        removeJButton.addMouseListener(new MouseAdapter() {
+            @Override
+            public void mouseClicked(MouseEvent e) {
+                removeActionListener.removeAction(Integer.parseInt(indexJLabel.getText()));
+            }
+        });
+    }
+
+    private void setActionName(String actionName) {
+        String name = actionName.replaceAll("/" + namespace + "/", "");
+        actionJTextField.setText(name);
+    }
+
+    public String getActionName() {
+        return "/" + namespace + "/" + actionJTextField.getText();
+    }
+
+    public void setIndex(int index) {
+        indexJLabel.setText(String.valueOf(index));
+    }
+
+    public JPanel getContent() {
+        return mainJPanel;
+    }
+
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/LinkedActionsForm.form b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/LinkedActionsForm.form
new file mode 100644
index 0000000..078e2f7
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/LinkedActionsForm.form
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.navercorp.openwhisk.intellij.explorer.dialog.action.ui.LinkedActionsForm">
+  <grid id="27dc6" binding="mainJPanel" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+    <margin top="0" left="0" bottom="0" right="0"/>
+    <constraints>
+      <xy x="20" y="20" width="500" height="400"/>
+    </constraints>
+    <properties/>
+    <border type="none"/>
+    <children>
+      <component id="960cb" class="javax.swing.JLabel">
+        <constraints>
+          <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
+            <preferred-size width="150" height="-1"/>
+          </grid>
+        </constraints>
+        <properties>
+          <text value="Linked Actions"/>
+        </properties>
+      </component>
+      <scrollpane id="70411">
+        <constraints>
+          <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="7" hsize-policy="7" anchor="0" fill="3" indent="0" use-parent-layout="false">
+            <preferred-size width="700" height="-1"/>
+          </grid>
+        </constraints>
+        <properties/>
+        <border type="none"/>
+        <children>
+          <grid id="db8f" layout-manager="GridLayoutManager" row-count="2" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+            <margin top="0" left="0" bottom="0" right="0"/>
+            <constraints/>
+            <properties/>
+            <border type="none"/>
+            <children>
+              <component id="5365b" class="javax.swing.JLabel">
+                <constraints>
+                  <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
+                    <preferred-size width="50" height="-1"/>
+                  </grid>
+                </constraints>
+                <properties>
+                  <text value="Action"/>
+                </properties>
+              </component>
+              <component id="7c1b7" class="javax.swing.JComboBox" binding="actionsJComboBox">
+                <constraints>
+                  <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="1" indent="0" use-parent-layout="false">
+                    <preferred-size width="520" height="-1"/>
+                  </grid>
+                </constraints>
+                <properties/>
+              </component>
+              <component id="56534" class="javax.swing.JButton" binding="addJButton">
+                <constraints>
+                  <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false">
+                    <preferred-size width="30" height="-1"/>
+                  </grid>
+                </constraints>
+                <properties>
+                  <text value=""/>
+                </properties>
+              </component>
+              <grid id="83040" binding="linkedActionsJPanel" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+                <margin top="0" left="0" bottom="0" right="0"/>
+                <constraints>
+                  <grid row="1" column="0" row-span="1" col-span="3" vsize-policy="3" hsize-policy="3" anchor="1" fill="1" indent="0" use-parent-layout="false"/>
+                </constraints>
+                <properties/>
+                <border type="none"/>
+                <children/>
+              </grid>
+            </children>
+          </grid>
+        </children>
+      </scrollpane>
+      <component id="278f2" class="javax.swing.JSeparator">
+        <constraints>
+          <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties>
+          <orientation value="1"/>
+        </properties>
+      </component>
+    </children>
+  </grid>
+</form>
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/LinkedActionsForm.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/LinkedActionsForm.java
new file mode 100644
index 0000000..4d6fc35
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/action/ui/LinkedActionsForm.java
@@ -0,0 +1,180 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.action.ui;
+
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.WhiskActionMetaData;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+
+import javax.swing.*;
+import javax.swing.event.ListDataListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static com.intellij.icons.AllIcons.General.Add;
+
+public class LinkedActionsForm {
+    private static final Logger LOG = Logger.getInstance(LinkedActionsForm.class);
+    private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+    private JPanel mainJPanel;
+    private JComboBox actionsJComboBox;
+    private JButton addJButton;
+    private JPanel linkedActionsJPanel;
+
+    private Project project;
+    private List<LinkedActionsEntryForm> cachedActions = new ArrayList<>();
+
+    public LinkedActionsForm(Project project, String namespace, List<WhiskActionMetaData> actions, List<String> components) {
+        this.project = project;
+
+        for (int i = 0; i < components.size(); i++) {
+            addLinkedAction(i, namespace, components.get(i));
+        }
+
+        actionsJComboBox.setModel(new ComboBoxModel() {
+            private WhiskActionMetaData selected;
+
+            @Override
+            public void setSelectedItem(Object anItem) {
+                selected = (WhiskActionMetaData) anItem;
+            }
+
+            @Override
+            public Object getSelectedItem() {
+                return selected;
+            }
+
+            @Override
+            public int getSize() {
+                return actions.size();
+            }
+
+            @Override
+            public Object getElementAt(int index) {
+                return actions.get(index);
+            }
+
+            @Override
+            public void addListDataListener(ListDataListener l) {
+                // nothing to do
+            }
+
+            @Override
+            public void removeListDataListener(ListDataListener l) {
+                // nothing to do
+            }
+        });
+
+        addJButton.setIcon(Add);
+        addJButton.addMouseListener(new MouseAdapter() {
+            @Override
+            public void mouseClicked(MouseEvent e) {
+                if (actionsJComboBox.getSelectedItem() == null) {
+                    return;
+                }
+
+                WhiskActionMetaData action = (WhiskActionMetaData) actionsJComboBox.getSelectedItem();
+                String actionName = action.getWhiskPackage().map(pkg -> pkg + "/" + action.getName()).orElse(action.getName());
+                addLinkedAction(cachedActions.size(), namespace, actionName);
+            }
+        });
+    }
+
+    public List<String> getComponents() {
+        return cachedActions.stream().map(LinkedActionsEntryForm::getActionName).collect(Collectors.toList());
+    }
+
+    public JPanel getContent() {
+        return mainJPanel;
+    }
+
+    /**
+     * Helper functions.
+     */
+    private void addLinkedAction(int index, String namespace, String actionName) {
+        LinkedActionsEntryForm entry = new LinkedActionsEntryForm(index, namespace, actionName,
+                this::upLinkedAction,
+                this::downLinkedAction,
+                this::removeLinkedAction);
+        linkedActionsJPanel.setLayout(new BoxLayout(linkedActionsJPanel, BoxLayout.Y_AXIS));
+        linkedActionsJPanel.add(entry.getContent());
+        cachedActions.add(entry);
+        linkedActionsJPanel.updateUI();
+    }
+
+    private void upLinkedAction(int index) {
+        if (index <= 0) {
+            return;
+        }
+
+        for (int i = index - 1; i < cachedActions.size(); i++) {
+            linkedActionsJPanel.remove(cachedActions.get(i).getContent());
+        }
+
+        Collections.swap(cachedActions, index - 1, index);
+
+        for (int i = index - 1; i < cachedActions.size(); i++) {
+            LinkedActionsEntryForm l = cachedActions.get(i);
+            l.setIndex(i);
+            linkedActionsJPanel.add(l.getContent());
+        }
+
+        linkedActionsJPanel.updateUI();
+    }
+
+    private void downLinkedAction(int index) {
+        if (index >= cachedActions.size() - 1) {
+            return;
+        }
+
+        for (int i = index; i < cachedActions.size(); i++) {
+            linkedActionsJPanel.remove(cachedActions.get(i).getContent());
+        }
+
+        Collections.swap(cachedActions, index, index + 1);
+
+        for (int i = index; i < cachedActions.size(); i++) {
+            LinkedActionsEntryForm l = cachedActions.get(i);
+            l.setIndex(i);
+            linkedActionsJPanel.add(l.getContent());
+        }
+
+        linkedActionsJPanel.updateUI();
+    }
+
+    private void removeLinkedAction(int index) {
+        if (cachedActions.size() <= 1) {
+            NOTIFIER.notify(project, "Sequence actions require at least one action.", NotificationType.WARNING);
+            return;
+        }
+
+        linkedActionsJPanel.remove(cachedActions.get(index).getContent());
+        cachedActions.remove(index);
+        for (int i = index; i < cachedActions.size(); i++) {
+            cachedActions.get(i).setIndex(i);
+        }
+
+        linkedActionsJPanel.updateUI();
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/endpoint/AddEndpointDialog.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/endpoint/AddEndpointDialog.java
new file mode 100644
index 0000000..f3bbf3d
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/endpoint/AddEndpointDialog.java
@@ -0,0 +1,63 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.endpoint;
+
+import com.intellij.openapi.project.Project;
+import com.navercorp.openwhisk.intellij.common.DialogWrapperWithApply;
+import com.navercorp.openwhisk.intellij.explorer.dialog.endpoint.ui.AddEndpointDialogForm;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+public class AddEndpointDialog extends DialogWrapperWithApply {
+
+    private AddEndpointDialogForm addEndpointDialogForm;
+
+    public AddEndpointDialog(Project project) {
+        super(project, true); // use current window as parent
+        addEndpointDialogForm = new AddEndpointDialogForm(project);
+        setTitle("Add Endpoint");
+        setResizable(false);
+        init();
+    }
+
+    @Nullable
+    @Override
+    protected JComponent createCenterPanel() {
+        if (addEndpointDialogForm != null) {
+            return addEndpointDialogForm.getContent();
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    protected void doApplyAction() {
+        if (addEndpointDialogForm != null) {
+            addEndpointDialogForm.addEndpoint();
+        }
+        super.doApplyAction();
+    }
+
+    @Override
+    protected void doOKAction() {
+        if (addEndpointDialogForm != null && !myApplyAction.isApplied()) {
+            addEndpointDialogForm.addEndpoint();
+        }
+        super.doOKAction();
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/endpoint/DeleteEndpointDialog.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/endpoint/DeleteEndpointDialog.java
new file mode 100644
index 0000000..f0bd398
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/endpoint/DeleteEndpointDialog.java
@@ -0,0 +1,50 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.endpoint;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskEndpoint;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.*;
+
+public class DeleteEndpointDialog extends DialogWrapper {
+
+    private WhiskEndpoint whiskEndpoint;
+
+    public DeleteEndpointDialog(Project project, WhiskEndpoint whiskEndpoint) {
+        super(true); // use current window as parent
+        setTitle("Delete Endpoint");
+        setResizable(false);
+        this.whiskEndpoint = whiskEndpoint;
+        init();
+    }
+
+    @Nullable
+    @Override
+    protected JComponent createCenterPanel() {
+        JPanel dialogPanel = new JPanel(new BorderLayout());
+
+        JLabel label = new JLabel("Are you sure you want to delete " + whiskEndpoint.getAlias() + " endpoint?");
+        label.setPreferredSize(new Dimension(100, 30));
+        dialogPanel.add(label, BorderLayout.CENTER);
+
+        return dialogPanel;
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/endpoint/EditEndpointDialog.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/endpoint/EditEndpointDialog.java
new file mode 100644
index 0000000..78b8a87
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/endpoint/EditEndpointDialog.java
@@ -0,0 +1,64 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.endpoint;
+
+import com.intellij.openapi.project.Project;
+import com.navercorp.openwhisk.intellij.common.DialogWrapperWithApply;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskEndpoint;
+import com.navercorp.openwhisk.intellij.explorer.dialog.endpoint.ui.EditEndpointDialogForm;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+public class EditEndpointDialog extends DialogWrapperWithApply {
+
+    private EditEndpointDialogForm editEndpointDialogForm;
+
+    public EditEndpointDialog(Project project, WhiskEndpoint whiskEndpoint) {
+        super(project, true); // use current window as parent
+        editEndpointDialogForm = new EditEndpointDialogForm(project, whiskEndpoint);
+        setTitle("Edit Endpoint");
+        setResizable(false);
+        init();
+    }
+
+    @Nullable
+    @Override
+    protected JComponent createCenterPanel() {
+        if (editEndpointDialogForm != null) {
+            return this.editEndpointDialogForm.getContent();
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    protected void doApplyAction() {
+        if (editEndpointDialogForm != null) {
+            editEndpointDialogForm.updateEndpoint();
+        }
+        super.doApplyAction();
+    }
+
+    @Override
+    protected void doOKAction() {
+        if (editEndpointDialogForm != null && !myApplyAction.isApplied()) {
+            editEndpointDialogForm.updateEndpoint();
+        }
+        super.doOKAction();
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/endpoint/ui/AddEndpointDialogForm.form b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/endpoint/ui/AddEndpointDialogForm.form
new file mode 100644
index 0000000..3392fc4
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/endpoint/ui/AddEndpointDialogForm.form
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.navercorp.openwhisk.intellij.explorer.dialog.endpoint.ui.AddEndpointDialogForm">
+  <grid id="27dc6" binding="mainJPanel" layout-manager="GridLayoutManager" row-count="4" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+    <margin top="0" left="0" bottom="0" right="0"/>
+    <constraints>
+      <xy x="20" y="20" width="500" height="400"/>
+    </constraints>
+    <properties/>
+    <border type="none"/>
+    <children>
+      <grid id="401ea" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+        <margin top="0" left="0" bottom="0" right="0"/>
+        <constraints>
+          <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties/>
+        <border type="none"/>
+        <children>
+          <component id="6e866" class="javax.swing.JLabel">
+            <constraints>
+              <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
+                <preferred-size width="100" height="-1"/>
+              </grid>
+            </constraints>
+            <properties>
+              <text value="Alias"/>
+            </properties>
+          </component>
+          <component id="330f9" class="javax.swing.JTextField" binding="aliasJTextField">
+            <constraints>
+              <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
+                <preferred-size width="250" height="-1"/>
+              </grid>
+            </constraints>
+            <properties/>
+          </component>
+          <component id="366c6" class="javax.swing.JSeparator">
+            <constraints>
+              <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+            </constraints>
+            <properties>
+              <orientation value="1"/>
+            </properties>
+          </component>
+        </children>
+      </grid>
+      <grid id="11db1" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+        <margin top="0" left="0" bottom="0" right="0"/>
+        <constraints>
+          <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties/>
+        <border type="none"/>
+        <children>
+          <component id="34574" class="javax.swing.JLabel">
+            <constraints>
+              <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
+                <preferred-size width="100" height="-1"/>
+              </grid>
+            </constraints>
+            <properties>
+              <text value="Apihost"/>
+            </properties>
+          </component>
+          <component id="5bf65" class="javax.swing.JTextField" binding="apihostJTextField">
+            <constraints>
+              <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
+                <preferred-size width="250" height="-1"/>
+              </grid>
+            </constraints>
+            <properties/>
+          </component>
+          <component id="f57a8" class="javax.swing.JSeparator">
+            <constraints>
+              <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+            </constraints>
+            <properties>
+              <orientation value="1"/>
+            </properties>
+          </component>
+        </children>
+      </grid>
+      <component id="8df6d" class="javax.swing.JSeparator">
+        <constraints>
+          <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties/>
+      </component>
+      <component id="7794b" class="javax.swing.JSeparator">
+        <constraints>
+          <grid row="3" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties/>
+      </component>
+    </children>
+  </grid>
+</form>
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/endpoint/ui/AddEndpointDialogForm.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/endpoint/ui/AddEndpointDialogForm.java
new file mode 100644
index 0000000..669b5b2
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/endpoint/ui/AddEndpointDialogForm.java
@@ -0,0 +1,102 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.endpoint.ui;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.navercorp.openwhisk.intellij.common.service.WhiskService;
+import com.navercorp.openwhisk.intellij.common.utils.EventUtils;
+import com.navercorp.openwhisk.intellij.common.utils.JsonParserUtils;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskEndpoint;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.listener.RefreshWhiskTreeListener;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+
+import javax.swing.*;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+public class AddEndpointDialogForm {
+    private static final Logger LOG = Logger.getInstance(AddEndpointDialogForm.class);
+    private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+    private JPanel mainJPanel;
+    private JTextField aliasJTextField;
+    private JTextField apihostJTextField;
+
+    private WhiskService whiskService;
+    private List<WhiskEndpoint> endpoints;
+
+    private Project project;
+
+    public AddEndpointDialogForm(Project project) {
+        this.project = project;
+
+        /**
+         * Load endpoints
+         */
+        this.whiskService = ServiceManager.getService(project, WhiskService.class);
+        try {
+            endpoints = new ArrayList<>(JsonParserUtils.parseWhiskEndpoints(whiskService.getEndpoints())); // make mutable
+        } catch (IOException e) {
+            LOG.error("Endpoint parsing failed", e);
+        }
+
+    }
+
+    public void addEndpoint() {
+        final String alias = aliasJTextField.getText().trim();
+        final String apihost = apihostJTextField.getText().trim(); // TODO validate api host
+
+        if (existAlias(endpoints, alias)) {
+            NOTIFIER.notify(project, "Failed to added endpoint: " + alias + " already exists.", NotificationType.ERROR);
+        } else {
+            endpoints.add(new WhiskEndpoint(alias, apihost, new ArrayList<>()));
+            saveEndpoints(endpoints);
+
+            // Update action tree
+            EventUtils.publish(project, RefreshWhiskTreeListener.TOPIC, RefreshWhiskTreeListener::refreshWhiskTree);
+            NOTIFIER.notify(project, alias + " has been added successfully.", NotificationType.INFORMATION);
+        }
+    }
+
+    private boolean existAlias(List<WhiskEndpoint> whiskEndpoints, String alias) {
+        for (WhiskEndpoint ep : whiskEndpoints) {
+            if (ep.getAlias().equals(alias)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private void saveEndpoints(List<WhiskEndpoint> newEndpoints) {
+        try {
+            String eps = JsonParserUtils.writeEndpointsToJson(newEndpoints);
+            whiskService.setEndpoints(eps);
+            whiskService.loadState(whiskService);
+        } catch (JsonProcessingException e) {
+            LOG.error("Endpoint parsing failed", e);
+        }
+    }
+
+    public JPanel getContent() {
+        return mainJPanel;
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/endpoint/ui/EditEndpointDialogForm.form b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/endpoint/ui/EditEndpointDialogForm.form
new file mode 100644
index 0000000..82f636a
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/endpoint/ui/EditEndpointDialogForm.form
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.navercorp.openwhisk.intellij.explorer.dialog.endpoint.ui.EditEndpointDialogForm">
+  <grid id="27dc6" binding="mainJPanel" layout-manager="GridLayoutManager" row-count="4" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+    <margin top="0" left="0" bottom="0" right="0"/>
+    <constraints>
+      <xy x="20" y="20" width="500" height="400"/>
+    </constraints>
+    <properties/>
+    <border type="none"/>
+    <children>
+      <grid id="3b01a" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+        <margin top="0" left="0" bottom="0" right="0"/>
+        <constraints>
+          <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties/>
+        <border type="none"/>
+        <children>
+          <component id="4ed82" class="javax.swing.JLabel">
+            <constraints>
+              <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
+                <preferred-size width="100" height="-1"/>
+              </grid>
+            </constraints>
+            <properties>
+              <text value="Alias"/>
+            </properties>
+          </component>
+          <component id="e975e" class="javax.swing.JTextField" binding="aliasJTextField">
+            <constraints>
+              <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
+                <preferred-size width="250" height="-1"/>
+              </grid>
+            </constraints>
+            <properties/>
+          </component>
+          <component id="3d5e6" class="javax.swing.JSeparator">
+            <constraints>
+              <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+            </constraints>
+            <properties>
+              <orientation value="1"/>
+            </properties>
+          </component>
+        </children>
+      </grid>
+      <grid id="a4647" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+        <margin top="0" left="0" bottom="0" right="0"/>
+        <constraints>
+          <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties/>
+        <border type="none"/>
+        <children>
+          <component id="dc17c" class="javax.swing.JLabel">
+            <constraints>
+              <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
+                <preferred-size width="100" height="-1"/>
+              </grid>
+            </constraints>
+            <properties>
+              <text value="Apihost"/>
+            </properties>
+          </component>
+          <component id="f76a8" class="javax.swing.JTextField" binding="apihostJTextField">
+            <constraints>
+              <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
+                <preferred-size width="250" height="-1"/>
+              </grid>
+            </constraints>
+            <properties/>
+          </component>
+          <component id="eb8b4" class="javax.swing.JSeparator">
+            <constraints>
+              <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+            </constraints>
+            <properties>
+              <orientation value="1"/>
+            </properties>
+          </component>
+        </children>
+      </grid>
+      <component id="320bb" class="javax.swing.JSeparator">
+        <constraints>
+          <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties/>
+      </component>
+      <component id="78128" class="javax.swing.JSeparator">
+        <constraints>
+          <grid row="3" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties/>
+      </component>
+    </children>
+  </grid>
+</form>
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/endpoint/ui/EditEndpointDialogForm.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/endpoint/ui/EditEndpointDialogForm.java
new file mode 100644
index 0000000..8714a4d
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/endpoint/ui/EditEndpointDialogForm.java
@@ -0,0 +1,115 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.endpoint.ui;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.navercorp.openwhisk.intellij.common.service.WhiskService;
+import com.navercorp.openwhisk.intellij.common.utils.EventUtils;
+import com.navercorp.openwhisk.intellij.common.utils.JsonParserUtils;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.listener.RefreshWhiskTreeListener;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskEndpoint;
+
+import javax.swing.*;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+public class EditEndpointDialogForm {
+    private static final Logger LOG = Logger.getInstance(EditEndpointDialogForm.class);
+    private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+    private JPanel mainJPanel;
+    private JTextField aliasJTextField;
+    private JTextField apihostJTextField;
+
+    private WhiskService whiskService;
+    private List<WhiskEndpoint> endpoints;
+
+    private Project project;
+    private WhiskEndpoint whiskEndpoint;
+
+    public EditEndpointDialogForm(Project project, WhiskEndpoint whiskEndpoint) {
+        this.project = project;
+        this.whiskEndpoint = whiskEndpoint;
+
+        /**
+         * Load endpoints
+         */
+        this.whiskService = ServiceManager.getService(project, WhiskService.class);
+        try {
+            endpoints = new ArrayList<>(JsonParserUtils.parseWhiskEndpoints(whiskService.getEndpoints())); // make mutable
+        } catch (IOException e) {
+            LOG.error("Endpoint parsing failed", e);
+        }
+
+        aliasJTextField.setText(whiskEndpoint.getAlias());
+        apihostJTextField.setText(whiskEndpoint.getApihost());
+    }
+
+    public void updateEndpoint() {
+        final String alias = aliasJTextField.getText().trim();
+        final String apihost = apihostJTextField.getText().trim(); // TODO validate api host
+
+        if (existAlias(endpoints, whiskEndpoint.getAlias(), alias)) {
+            NOTIFIER.notify(project, "Failed to update endpoint: " + alias + " already exists.", NotificationType.ERROR);
+        } else {
+            List<WhiskEndpoint> newEndpoints = updateWhiskEndpoint(endpoints,
+                    whiskEndpoint.getAlias(),
+                    new WhiskEndpoint(alias, apihost, whiskEndpoint.getNamespaces()));
+            saveEndpoints(newEndpoints);
+
+            // Update action tree
+            EventUtils.publish(project, RefreshWhiskTreeListener.TOPIC, RefreshWhiskTreeListener::refreshWhiskTree);
+            NOTIFIER.notify(project, alias + " has been added successfully.", NotificationType.INFORMATION);
+        }
+    }
+
+    private List<WhiskEndpoint> updateWhiskEndpoint(List<WhiskEndpoint> eps, String key, WhiskEndpoint newEndpoint) {
+        for (int i = 0; i < eps.size(); i++) {
+            if (eps.get(i).getAlias().equals(key)) {
+                eps.set(i, newEndpoint);
+            }
+        }
+        return eps;
+    }
+
+    private boolean existAlias(List<WhiskEndpoint> whiskEndpoints, String originAlias, String alias) {
+        return whiskEndpoints.stream()
+                .filter(ep -> !ep.getAlias().equals(originAlias)) // exclude current endpoint
+                .anyMatch(ep -> ep.getAlias().equals(alias));     // find duplicated alias
+    }
+
+    private void saveEndpoints(List<WhiskEndpoint> newEndpoints) {
+        try {
+            String eps = JsonParserUtils.writeEndpointsToJson(newEndpoints);
+            whiskService.setEndpoints(eps);
+            whiskService.loadState(whiskService);
+        } catch (JsonProcessingException e) {
+            LOG.error("Endpoint parsing failed", e);
+        }
+    }
+
+
+    public JPanel getContent() {
+        return mainJPanel;
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/namespace/AddNamespaceDialog.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/namespace/AddNamespaceDialog.java
new file mode 100644
index 0000000..bbab99f
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/namespace/AddNamespaceDialog.java
@@ -0,0 +1,64 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.namespace;
+
+import com.intellij.openapi.project.Project;
+import com.navercorp.openwhisk.intellij.common.DialogWrapperWithApply;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskEndpoint;
+import com.navercorp.openwhisk.intellij.explorer.dialog.namespace.ui.AddNamespaceDialogForm;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+public class AddNamespaceDialog extends DialogWrapperWithApply {
+
+    private AddNamespaceDialogForm addNamespaceDialogForm;
+
+    public AddNamespaceDialog(Project project, WhiskEndpoint whiskEndpoint) {
+        super(project, true); // use current window as parent
+        addNamespaceDialogForm = new AddNamespaceDialogForm(project, whiskEndpoint);
+        setTitle("Add Namespace");
+        setResizable(false);
+        init();
+    }
+
+    @Nullable
+    @Override
+    protected JComponent createCenterPanel() {
+        if (addNamespaceDialogForm != null) {
+            return addNamespaceDialogForm.getContent();
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    protected void doApplyAction() {
+        if (addNamespaceDialogForm != null) {
+            addNamespaceDialogForm.addNamespace();
+        }
+        super.doApplyAction();
+    }
+
+    @Override
+    protected void doOKAction() {
+        if (addNamespaceDialogForm != null && !myApplyAction.isApplied()) {
+            addNamespaceDialogForm.addNamespace();
+        }
+        super.doOKAction();
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/namespace/DeleteNamespaceDialog.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/namespace/DeleteNamespaceDialog.java
new file mode 100644
index 0000000..b12b080
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/namespace/DeleteNamespaceDialog.java
@@ -0,0 +1,50 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.namespace;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskNamespace;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.*;
+
+public class DeleteNamespaceDialog extends DialogWrapper {
+
+    private WhiskNamespace whiskNamespace;
+
+    public DeleteNamespaceDialog(Project project, WhiskNamespace whiskNamespace) {
+        super(true); // use current window as parent
+        setTitle("Delete Namespace");
+        setResizable(false);
+        this.whiskNamespace = whiskNamespace;
+        init();
+    }
+
+    @Nullable
+    @Override
+    protected JComponent createCenterPanel() {
+        JPanel dialogPanel = new JPanel(new BorderLayout());
+
+        JLabel label = new JLabel("Are you sure you want to delete " + whiskNamespace.getPath() + " namespace?");
+        label.setPreferredSize(new Dimension(100, 30));
+        dialogPanel.add(label, BorderLayout.CENTER);
+
+        return dialogPanel;
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/namespace/EditNamespaceDialog.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/namespace/EditNamespaceDialog.java
new file mode 100644
index 0000000..59a49e3
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/namespace/EditNamespaceDialog.java
@@ -0,0 +1,65 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.namespace;
+
+import com.intellij.openapi.project.Project;
+import com.navercorp.openwhisk.intellij.common.DialogWrapperWithApply;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskNamespace;
+import com.navercorp.openwhisk.intellij.explorer.dialog.namespace.ui.EditNamespaceDialogForm;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+public class EditNamespaceDialog extends DialogWrapperWithApply {
+
+    private EditNamespaceDialogForm editNamespaceDialogForm;
+
+    public EditNamespaceDialog(Project project, WhiskAuth whiskAuth, WhiskNamespace whiskNamespace) {
+        super(project, true); // use current window as parent
+        editNamespaceDialogForm = new EditNamespaceDialogForm(project, whiskAuth, whiskNamespace);
+        setTitle("Edit Namespace");
+        setResizable(false);
+        init();
+    }
+
+    @Nullable
+    @Override
+    protected JComponent createCenterPanel() {
+        if (editNamespaceDialogForm != null) {
+            return this.editNamespaceDialogForm.getContent();
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    protected void doApplyAction() {
+        if (editNamespaceDialogForm != null) {
+            editNamespaceDialogForm.updateNamespace();
+        }
+        super.doApplyAction();
+    }
+
+    @Override
+    protected void doOKAction() {
+        if (editNamespaceDialogForm != null && !myApplyAction.isApplied()) {
+            editNamespaceDialogForm.updateNamespace();
+        }
+        super.doOKAction();
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/namespace/ui/AddNamespaceDialogForm.form b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/namespace/ui/AddNamespaceDialogForm.form
new file mode 100644
index 0000000..d9cd4bd
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/namespace/ui/AddNamespaceDialogForm.form
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.navercorp.openwhisk.intellij.explorer.dialog.namespace.ui.AddNamespaceDialogForm">
+  <grid id="27dc6" binding="mainJPanel" layout-manager="GridLayoutManager" row-count="2" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+    <margin top="0" left="0" bottom="0" right="0"/>
+    <constraints>
+      <xy x="20" y="20" width="569" height="400"/>
+    </constraints>
+    <properties/>
+    <border type="none"/>
+    <children>
+      <grid id="48a1a" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+        <margin top="0" left="0" bottom="0" right="0"/>
+        <constraints>
+          <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties/>
+        <border type="none"/>
+        <children>
+          <component id="3d4" class="javax.swing.JLabel">
+            <constraints>
+              <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+            </constraints>
+            <properties>
+              <text value="Auth Key"/>
+            </properties>
+          </component>
+          <component id="2385f" class="javax.swing.JPasswordField" binding="authKeyJPasswordField">
+            <constraints>
+              <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
+                <preferred-size width="700" height="-1"/>
+              </grid>
+            </constraints>
+            <properties/>
+          </component>
+          <component id="1a120" class="javax.swing.JCheckBox" binding="showAuthKeyJCheckBox">
+            <constraints>
+              <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false">
+                <preferred-size width="100" height="-1"/>
+              </grid>
+            </constraints>
+            <properties>
+              <text value="Show Key"/>
+            </properties>
+          </component>
+        </children>
+      </grid>
+      <grid id="4ab28" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+        <margin top="0" left="0" bottom="0" right="0"/>
+        <constraints>
+          <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties/>
+        <border type="none"/>
+        <children>
+          <grid id="1094b" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+            <margin top="0" left="0" bottom="0" right="0"/>
+            <constraints>
+              <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="8" fill="2" indent="0" use-parent-layout="false"/>
+            </constraints>
+            <properties/>
+            <border type="none"/>
+            <children>
+              <component id="6c796" class="javax.swing.JButton" binding="verifyAuthKeyJButton">
+                <constraints>
+                  <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false">
+                    <preferred-size width="150" height="-1"/>
+                    <maximum-size width="150" height="-1"/>
+                  </grid>
+                </constraints>
+                <properties>
+                  <text value="Verify AuthKey"/>
+                </properties>
+              </component>
+              <component id="1be9" class="javax.swing.JLabel" binding="verifyResultJLabel">
+                <constraints>
+                  <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+                </constraints>
+                <properties>
+                  <text value=""/>
+                </properties>
+              </component>
+            </children>
+          </grid>
+          <grid id="d2d81" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+            <margin top="0" left="0" bottom="0" right="0"/>
+            <constraints>
+              <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+            </constraints>
+            <properties/>
+            <border type="none"/>
+            <children/>
+          </grid>
+        </children>
+      </grid>
+    </children>
+  </grid>
+</form>
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/namespace/ui/AddNamespaceDialogForm.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/namespace/ui/AddNamespaceDialogForm.java
new file mode 100644
index 0000000..1f2cbc5
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/namespace/ui/AddNamespaceDialogForm.java
@@ -0,0 +1,145 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.namespace.ui;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.intellij.ui.JBColor;
+import com.navercorp.openwhisk.intellij.common.service.WhiskService;
+import com.navercorp.openwhisk.intellij.common.utils.EventUtils;
+import com.navercorp.openwhisk.intellij.common.utils.JsonParserUtils;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskNamespaceService;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.listener.RefreshWhiskTreeListener;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskEndpoint;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskNamespace;
+
+import javax.swing.*;
+import java.awt.event.ItemEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+public class AddNamespaceDialogForm {
+    private static final Logger LOG = Logger.getInstance(AddNamespaceDialogForm.class);
+    private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+    private JPanel mainJPanel;
+    private JPasswordField authKeyJPasswordField;
+    private JButton verifyAuthKeyJButton;
+    private JLabel verifyResultJLabel;
+    private JCheckBox showAuthKeyJCheckBox;
+
+    private WhiskNamespaceService whiskNamespaceService = WhiskNamespaceService.getInstance();
+    private WhiskService whiskService;
+    private List<WhiskEndpoint> endpoints;
+
+    private Project project;
+    private WhiskEndpoint whiskEndpoint;
+
+    public AddNamespaceDialogForm(Project project, WhiskEndpoint whiskEndpoint) {
+        this.project = project;
+        this.whiskEndpoint = whiskEndpoint;
+
+        /**
+         * Load endpoints
+         */
+        this.whiskService = ServiceManager.getService(project, WhiskService.class);
+        try {
+            endpoints = new ArrayList<>(JsonParserUtils.parseWhiskEndpoints(whiskService.getEndpoints())); // make mutable
+        } catch (IOException e) {
+            LOG.error("Endpoint parsing failed", e);
+        }
+
+        verifyAuthKeyJButton.addMouseListener(new MouseAdapter() {
+            @Override
+            public void mouseClicked(MouseEvent e) {
+                final String auth = new String(authKeyJPasswordField.getPassword());
+                final String apihost = whiskEndpoint.getApihost();
+                final WhiskAuth newWhiskAuth = new WhiskAuth(auth, apihost);
+                Optional<WhiskNamespace> result = whiskNamespaceService.validateNamespace(newWhiskAuth);
+                if (result.isPresent()) {
+                    verifyResultJLabel.setText("Success");
+                    verifyResultJLabel.setForeground(JBColor.GREEN);
+                } else {
+                    verifyResultJLabel.setText("Invalid authkey");
+                    verifyResultJLabel.setForeground(JBColor.RED);
+                }
+            }
+        });
+
+        showAuthKeyJCheckBox.addItemListener(e -> {
+            if (e.getStateChange() == ItemEvent.SELECTED) {
+                authKeyJPasswordField.setEchoChar((char) 0);
+            } else {
+                authKeyJPasswordField.setEchoChar('*');
+            }
+        });
+    }
+
+    public void addNamespace() {
+        final String auth = new String(authKeyJPasswordField.getPassword());
+        final String apihost = whiskEndpoint.getApihost();
+        final WhiskAuth newWhiskAuth = new WhiskAuth(auth, apihost);
+        Optional<WhiskNamespace> result = whiskNamespaceService.validateNamespace(newWhiskAuth);
+        if (result.isPresent()) {
+            WhiskNamespace whiskNamespace = result.get();
+            List<WhiskEndpoint> newEndpoints = addNamespace(whiskEndpoint, whiskNamespace);
+            saveEndpoints(newEndpoints);
+
+            // Update action tree
+            EventUtils.publish(project, RefreshWhiskTreeListener.TOPIC, RefreshWhiskTreeListener::refreshWhiskTree);
+            NOTIFIER.notify(project, whiskNamespace.getPath() + " has been added successfully.", NotificationType.INFORMATION);
+        } else {
+            NOTIFIER.notify(project, "Invalid authkey", NotificationType.ERROR);
+        }
+    }
+
+    private List<WhiskEndpoint> addNamespace(WhiskEndpoint endpoint, WhiskNamespace whiskNamespace) {
+        for (int i = 0; i < endpoints.size(); i++) {
+            WhiskEndpoint ep = endpoints.get(i);
+            if (ep.getAlias().equals(endpoint.getAlias()) && ep.getApihost().equals(endpoint.getApihost())) {
+                List<WhiskNamespace> namespaces = ep.getNamespaces();
+                namespaces.add(whiskNamespace);
+                ep.setNamespaces(namespaces);
+            }
+            endpoints.set(i, ep);
+        }
+        return endpoints;
+    }
+
+    private void saveEndpoints(List<WhiskEndpoint> newEndpoints) {
+        try {
+            String eps = JsonParserUtils.writeEndpointsToJson(newEndpoints);
+            whiskService.setEndpoints(eps);
+            whiskService.loadState(whiskService);
+        } catch (JsonProcessingException e) {
+            LOG.error("Endpoint parsing failed", e);
+        }
+    }
+
+    public JPanel getContent() {
+        return mainJPanel;
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/namespace/ui/EditNamespaceDialogForm.form b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/namespace/ui/EditNamespaceDialogForm.form
new file mode 100644
index 0000000..94d394c
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/namespace/ui/EditNamespaceDialogForm.form
@@ -0,0 +1,175 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.navercorp.openwhisk.intellij.explorer.dialog.namespace.ui.EditNamespaceDialogForm">
+  <grid id="27dc6" binding="mainJPanel" layout-manager="GridLayoutManager" row-count="5" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+    <margin top="0" left="0" bottom="0" right="0"/>
+    <constraints>
+      <xy x="20" y="20" width="942" height="400"/>
+    </constraints>
+    <properties/>
+    <border type="none"/>
+    <children>
+      <grid id="ac30b" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+        <margin top="0" left="0" bottom="0" right="0"/>
+        <constraints>
+          <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="8" fill="2" indent="0" use-parent-layout="false">
+            <preferred-size width="500" height="-1"/>
+          </grid>
+        </constraints>
+        <properties/>
+        <border type="none"/>
+        <children>
+          <component id="ac8d2" class="javax.swing.JLabel">
+            <constraints>
+              <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
+                <preferred-size width="100" height="-1"/>
+              </grid>
+            </constraints>
+            <properties>
+              <text value="Name"/>
+            </properties>
+          </component>
+          <component id="4bc0d" class="javax.swing.JTextField" binding="nameJTextField">
+            <constraints>
+              <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false">
+                <preferred-size width="700" height="-1"/>
+              </grid>
+            </constraints>
+            <properties>
+              <editable value="false"/>
+            </properties>
+          </component>
+          <component id="c2d6a" class="javax.swing.JSeparator">
+            <constraints>
+              <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false">
+                <maximum-size width="5" height="-1"/>
+              </grid>
+            </constraints>
+            <properties>
+              <orientation value="1"/>
+            </properties>
+          </component>
+        </children>
+      </grid>
+      <grid id="259ba" layout-manager="GridLayoutManager" row-count="1" column-count="4" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+        <margin top="0" left="0" bottom="0" right="0"/>
+        <constraints>
+          <grid row="2" column="0" row-span="1" col-span="2" vsize-policy="3" hsize-policy="3" anchor="8" fill="2" indent="0" use-parent-layout="false">
+            <preferred-size width="500" height="-1"/>
+          </grid>
+        </constraints>
+        <properties/>
+        <border type="none"/>
+        <children>
+          <component id="a750" class="javax.swing.JLabel">
+            <constraints>
+              <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
+                <preferred-size width="100" height="-1"/>
+              </grid>
+            </constraints>
+            <properties>
+              <text value="Auth Key"/>
+            </properties>
+          </component>
+          <component id="cc357" class="javax.swing.JPasswordField" binding="authKeyJPasswordField">
+            <constraints>
+              <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false">
+                <preferred-size width="700" height="-1"/>
+              </grid>
+            </constraints>
+            <properties>
+              <echoChar value="*"/>
+            </properties>
+          </component>
+          <component id="ee04d" class="javax.swing.JCheckBox" binding="showAuthKeyJCheckbox">
+            <constraints>
+              <grid row="0" column="3" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false">
+                <preferred-size width="100" height="-1"/>
+              </grid>
+            </constraints>
+            <properties>
+              <text value="Show Key"/>
+            </properties>
+          </component>
+          <component id="8c925" class="javax.swing.JSeparator">
+            <constraints>
+              <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false">
+                <maximum-size width="5" height="-1"/>
+              </grid>
+            </constraints>
+            <properties>
+              <orientation value="1"/>
+            </properties>
+          </component>
+        </children>
+      </grid>
+      <grid id="44877" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="true" same-size-vertically="false" hgap="-1" vgap="-1">
+        <margin top="0" left="0" bottom="0" right="0"/>
+        <constraints>
+          <grid row="4" column="0" row-span="1" col-span="2" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false">
+            <preferred-size width="500" height="-1"/>
+          </grid>
+        </constraints>
+        <properties/>
+        <border type="none"/>
+        <children>
+          <grid id="db084" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+            <margin top="0" left="0" bottom="0" right="0"/>
+            <constraints>
+              <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="8" fill="2" indent="0" use-parent-layout="false"/>
+            </constraints>
+            <properties/>
+            <border type="none"/>
+            <children>
+              <component id="ae99f" class="javax.swing.JButton" binding="verifyAuthKeyJButton">
+                <constraints>
+                  <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false">
+                    <preferred-size width="150" height="-1"/>
+                    <maximum-size width="150" height="-1"/>
+                  </grid>
+                </constraints>
+                <properties>
+                  <text value="Verify AuthKey"/>
+                </properties>
+              </component>
+              <component id="a7218" class="javax.swing.JLabel" binding="verifyResultJLabel">
+                <constraints>
+                  <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+                </constraints>
+                <properties>
+                  <horizontalAlignment value="10"/>
+                  <text value=""/>
+                </properties>
+              </component>
+            </children>
+          </grid>
+          <grid id="8e824" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+            <margin top="0" left="0" bottom="0" right="0"/>
+            <constraints>
+              <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+            </constraints>
+            <properties/>
+            <border type="none"/>
+            <children/>
+          </grid>
+        </children>
+      </grid>
+      <component id="6a07a" class="javax.swing.JSeparator">
+        <constraints>
+          <grid row="1" column="0" row-span="1" col-span="2" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties/>
+      </component>
+      <component id="ce2ed" class="javax.swing.JSeparator">
+        <constraints>
+          <grid row="3" column="0" row-span="1" col-span="2" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties/>
+      </component>
+      <hspacer id="2c3b2">
+        <constraints>
+          <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+        </constraints>
+      </hspacer>
+    </children>
+  </grid>
+</form>
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/namespace/ui/EditNamespaceDialogForm.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/namespace/ui/EditNamespaceDialogForm.java
new file mode 100644
index 0000000..9a363c4
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/namespace/ui/EditNamespaceDialogForm.java
@@ -0,0 +1,156 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.namespace.ui;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.intellij.ui.JBColor;
+import com.navercorp.openwhisk.intellij.common.service.WhiskService;
+import com.navercorp.openwhisk.intellij.common.utils.EventUtils;
+import com.navercorp.openwhisk.intellij.common.utils.JsonParserUtils;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskNamespaceService;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskEndpoint;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskNamespace;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.listener.RefreshWhiskTreeListener;
+
+import javax.swing.*;
+import java.awt.event.ItemEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+public class EditNamespaceDialogForm {
+    private static final Logger LOG = Logger.getInstance(EditNamespaceDialogForm.class);
+    private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+    private JPanel mainJPanel;
+
+    private JTextField nameJTextField;
+    private JPasswordField authKeyJPasswordField;
+
+    private JButton verifyAuthKeyJButton;
+    private JLabel verifyResultJLabel;
+    private JCheckBox showAuthKeyJCheckbox;
+
+    private WhiskNamespaceService whiskNamespaceService = WhiskNamespaceService.getInstance();
+    private WhiskService whiskService;
+    private List<WhiskEndpoint> endpoints;
+
+    private Project project;
+    private WhiskAuth whiskAuth;
+    private WhiskNamespace whiskNamespace;
+
+    public EditNamespaceDialogForm(Project project, WhiskAuth whiskAuth, WhiskNamespace whiskNamespace) {
+        this.project = project;
+        this.whiskAuth = whiskAuth;
+        this.whiskNamespace = whiskNamespace;
+
+        /**
+         * Load endpoints
+         */
+        this.whiskService = ServiceManager.getService(project, WhiskService.class);
+        try {
+            endpoints = new ArrayList<>(JsonParserUtils.parseWhiskEndpoints(whiskService.getEndpoints())); // make mutable
+        } catch (IOException e) {
+            LOG.error("Endpoint parsing failed", e);
+        }
+
+        nameJTextField.setText(whiskNamespace.getPath());
+        authKeyJPasswordField.setText(whiskNamespace.getAuth());
+
+        verifyAuthKeyJButton.addMouseListener(new MouseAdapter() {
+            @Override
+            public void mouseClicked(MouseEvent e) {
+                final String auth = new String(authKeyJPasswordField.getPassword());
+                final String apihost = whiskAuth.getApihost();
+                final WhiskAuth newWhiskAuth = new WhiskAuth(auth, apihost);
+                Optional<WhiskNamespace> result = whiskNamespaceService.validateNamespace(newWhiskAuth);
+                if (result.isPresent()) {
+                    verifyResultJLabel.setText("Success");
+                    verifyResultJLabel.setForeground(JBColor.GREEN);
+                } else {
+                    verifyResultJLabel.setText("Invalid authkey");
+                    verifyResultJLabel.setForeground(JBColor.RED);
+                }
+            }
+        });
+
+        showAuthKeyJCheckbox.addItemListener(e -> {
+            if (e.getStateChange() == ItemEvent.SELECTED) {
+                authKeyJPasswordField.setEchoChar((char) 0);
+            } else {
+                authKeyJPasswordField.setEchoChar('*');
+            }
+        });
+    }
+
+    public void updateNamespace() {
+        final String auth = new String(authKeyJPasswordField.getPassword());
+        final String apihost = whiskAuth.getApihost();
+        final WhiskAuth newWhiskAuth = new WhiskAuth(auth, apihost);
+        Optional<WhiskNamespace> result = whiskNamespaceService.validateNamespace(newWhiskAuth);
+        if (result.isPresent()) {
+            WhiskNamespace newNamespace = result.get();
+            List<WhiskEndpoint> newEndpoints = updateNamespace(whiskNamespace, newNamespace);
+            saveEndpoints(newEndpoints);
+            nameJTextField.setText(newNamespace.getPath());
+
+            // Update action tree
+            EventUtils.publish(project, RefreshWhiskTreeListener.TOPIC, RefreshWhiskTreeListener::refreshWhiskTree);
+            NOTIFIER.notify(project, whiskNamespace.getPath() + " modification was successful.", NotificationType.INFORMATION);
+        } else {
+            NOTIFIER.notify(project, "Invalid authkey", NotificationType.ERROR);
+        }
+    }
+
+    private List<WhiskEndpoint> updateNamespace(WhiskNamespace originNamespace, WhiskNamespace newNamespace) {
+        for (int i = 0; i < endpoints.size(); i++) {
+            WhiskEndpoint endpoint = endpoints.get(i);
+            List<WhiskNamespace> namespaces = endpoint.getNamespaces();
+            for (int j = 0; j < namespaces.size(); j++) {
+                if (namespaces.get(j).getPath().equals(originNamespace.getPath())) {
+                    namespaces.set(j, newNamespace);
+                }
+            }
+            endpoint.setNamespaces(namespaces);
+            endpoints.set(i, endpoint);
+        }
+        return endpoints;
+    }
+
+    private void saveEndpoints(List<WhiskEndpoint> newEndpoints) {
+        try {
+            String eps = JsonParserUtils.writeEndpointsToJson(newEndpoints);
+            whiskService.setEndpoints(eps);
+            whiskService.loadState(whiskService);
+        } catch (JsonProcessingException e) {
+            LOG.error("Endpoint parsing failed", e);
+        }
+    }
+
+    public JPanel getContent() {
+        return mainJPanel;
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/pkg/DeletePackageDialog.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/pkg/DeletePackageDialog.java
new file mode 100644
index 0000000..e2784ed
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/pkg/DeletePackageDialog.java
@@ -0,0 +1,66 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.pkg;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.CompactWhiskAction;
+import com.navercorp.openwhisk.intellij.common.whisk.model.pkg.WhiskPackageWithActions;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.*;
+
+public class DeletePackageDialog extends DialogWrapper {
+
+    private WhiskPackageWithActions whiskPackage;
+
+    public DeletePackageDialog(Project project, WhiskPackageWithActions whiskPackage) {
+        super(true); // use current window as parent
+        setTitle("Delete Package");
+        setResizable(false);
+        this.whiskPackage = whiskPackage;
+        init();
+    }
+
+    @Nullable
+    @Override
+    protected JComponent createCenterPanel() {
+        JPanel dialogPanel = new JPanel(new BorderLayout());
+
+        StringBuilder builder = new StringBuilder();
+        builder.append("<html>");
+        builder.append("Are you sure you want to delete " + whiskPackage.getName() + " package?");
+        if (!whiskPackage.getBinding().isPresent()) {
+            if (!whiskPackage.getActions().isEmpty()) {
+                builder.append("<br/><br/>");
+                builder.append("The following actions will be deleted together<br/>");
+            }
+            for (CompactWhiskAction action : whiskPackage.getActions()) {
+                builder.append("- " + action.getName() + "<br/>");
+            }
+            builder.append("<br/><br/>");
+        }
+        builder.append("</html>");
+
+        JLabel label = new JLabel(builder.toString());
+        label.setPreferredSize(new Dimension(400, 100));
+        dialogPanel.add(label, BorderLayout.CENTER);
+
+        return dialogPanel;
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/pkg/PackageCreationManagerDialog.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/pkg/PackageCreationManagerDialog.java
new file mode 100644
index 0000000..5de1397
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/pkg/PackageCreationManagerDialog.java
@@ -0,0 +1,64 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.pkg;
+
+import com.intellij.openapi.project.Project;
+import com.navercorp.openwhisk.intellij.common.DialogWrapperWithApply;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.explorer.dialog.pkg.ui.PackageCreationManagerDialogForm;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+public class PackageCreationManagerDialog extends DialogWrapperWithApply {
+
+    private PackageCreationManagerDialogForm packageCreationManagerDialogForm;
+
+    public PackageCreationManagerDialog(Project project, WhiskAuth auth) {
+        super(project, true); // use current window as parent
+        packageCreationManagerDialogForm = new PackageCreationManagerDialogForm(project, auth);
+        setTitle("Create Package");
+        setResizable(false);
+        init();
+    }
+
+    @Nullable
+    @Override
+    protected JComponent createCenterPanel() {
+        if (packageCreationManagerDialogForm != null) {
+            return packageCreationManagerDialogForm.getContent();
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    protected void doApplyAction() {
+        if (packageCreationManagerDialogForm != null) {
+            packageCreationManagerDialogForm.createPackage();
+        }
+        super.doApplyAction();
+    }
+
+    @Override
+    protected void doOKAction() {
+        if (packageCreationManagerDialogForm != null && !myApplyAction.isApplied()) {
+            packageCreationManagerDialogForm.createPackage();
+        }
+        super.doOKAction();
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/pkg/PackageManagerDialog.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/pkg/PackageManagerDialog.java
new file mode 100644
index 0000000..78a366a
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/pkg/PackageManagerDialog.java
@@ -0,0 +1,65 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.pkg;
+
+import com.intellij.openapi.project.Project;
+import com.navercorp.openwhisk.intellij.common.DialogWrapperWithApply;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.pkg.WhiskPackageWithActions;
+import com.navercorp.openwhisk.intellij.explorer.dialog.pkg.ui.PackageManagerDialogForm;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+public class PackageManagerDialog extends DialogWrapperWithApply {
+
+    private PackageManagerDialogForm packageManagerDialogForm;
+
+    public PackageManagerDialog(Project project, WhiskAuth auth, WhiskPackageWithActions whiskPackage) {
+        super(project, true); // use current window as parent
+        packageManagerDialogForm = new PackageManagerDialogForm(project, auth, whiskPackage);
+        setTitle("Manage Package");
+        setResizable(false);
+        init();
+    }
+
+    @Nullable
+    @Override
+    protected JComponent createCenterPanel() {
+        if (packageManagerDialogForm != null) {
+            return this.packageManagerDialogForm.getContent();
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    protected void doApplyAction() {
+        if (packageManagerDialogForm != null) {
+            packageManagerDialogForm.updatePackage();
+        }
+        super.doApplyAction();
+    }
+
+    @Override
+    protected void doOKAction() {
+        if (packageManagerDialogForm != null && !myApplyAction.isApplied()) {
+            packageManagerDialogForm.updatePackage();
+        }
+        super.doOKAction();
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/pkg/ui/PackageCreationManagerDialogForm.form b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/pkg/ui/PackageCreationManagerDialogForm.form
new file mode 100644
index 0000000..6e8a746
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/pkg/ui/PackageCreationManagerDialogForm.form
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.navercorp.openwhisk.intellij.explorer.dialog.pkg.ui.PackageCreationManagerDialogForm">
+  <grid id="27dc6" binding="mainJPanel" layout-manager="GridLayoutManager" row-count="2" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+    <margin top="0" left="0" bottom="0" right="0"/>
+    <constraints>
+      <xy x="20" y="20" width="500" height="400"/>
+    </constraints>
+    <properties/>
+    <border type="none"/>
+    <children>
+      <grid id="2d156" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+        <margin top="0" left="0" bottom="0" right="0"/>
+        <constraints>
+          <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties/>
+        <border type="none"/>
+        <children>
+          <component id="e6557" class="javax.swing.JLabel">
+            <constraints>
+              <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
+                <preferred-size width="100" height="-1"/>
+              </grid>
+            </constraints>
+            <properties>
+              <text value="Name"/>
+            </properties>
+          </component>
+          <component id="f6602" class="javax.swing.JTextField" binding="nameJTextField">
+            <constraints>
+              <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
+                <preferred-size width="150" height="-1"/>
+              </grid>
+            </constraints>
+            <properties/>
+          </component>
+          <component id="62535" class="javax.swing.JSeparator">
+            <constraints>
+              <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false">
+                <maximum-size width="5" height="-1"/>
+              </grid>
+            </constraints>
+            <properties>
+              <orientation value="1"/>
+            </properties>
+          </component>
+        </children>
+      </grid>
+      <component id="2f681" class="javax.swing.JSeparator">
+        <constraints>
+          <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false">
+            <maximum-size width="-1" height="5"/>
+          </grid>
+        </constraints>
+        <properties/>
+      </component>
+    </children>
+  </grid>
+</form>
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/pkg/ui/PackageCreationManagerDialogForm.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/pkg/ui/PackageCreationManagerDialogForm.java
new file mode 100644
index 0000000..9a10c24
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/pkg/ui/PackageCreationManagerDialogForm.java
@@ -0,0 +1,75 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.pkg.ui;
+
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.pkg.WhiskPackageWithActions;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskPackageService;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+
+import javax.swing.*;
+import java.io.IOException;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Optional;
+
+public class PackageCreationManagerDialogForm {
+    private static final Logger LOG = Logger.getInstance(PackageCreationManagerDialogForm.class);
+    private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+    private JTextField nameJTextField;
+    private JPanel mainJPanel;
+
+    private WhiskPackageService whiskPackageService = WhiskPackageService.getInstance();
+
+    private Project project;
+    private WhiskAuth whiskAuth;
+
+    public PackageCreationManagerDialogForm(Project project, WhiskAuth whiskAuth) {
+        this.project = project;
+        this.whiskAuth = whiskAuth;
+    }
+
+    public void createPackage() {
+        try {
+            String name = nameJTextField.getText().trim();
+
+            Map<String, Object> payload = new LinkedHashMap<>();
+            payload.put("name", name);
+            payload.put("namespace", "_");
+
+            Optional<WhiskPackageWithActions> created = whiskPackageService.createWhiskPackage(whiskAuth, name, payload);
+            if (created.isPresent()) {
+                NOTIFIER.notify(project, name + " created", NotificationType.INFORMATION);
+            } else {
+                NOTIFIER.notify(project, name + " already exist", NotificationType.ERROR);
+            }
+        } catch (IOException e) {
+            String msg = "Failed to create package: ";
+            LOG.error(msg, e);
+            NOTIFIER.notify(project, msg, NotificationType.ERROR);
+        }
+    }
+
+    public JPanel getContent() {
+        return mainJPanel;
+    }
+
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/pkg/ui/PackageManagerDialogForm.form b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/pkg/ui/PackageManagerDialogForm.form
new file mode 100644
index 0000000..b7729e8
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/pkg/ui/PackageManagerDialogForm.form
@@ -0,0 +1,287 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.navercorp.openwhisk.intellij.explorer.dialog.pkg.ui.PackageManagerDialogForm">
+  <grid id="27dc6" binding="mainJPanel" layout-manager="GridLayoutManager" row-count="6" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+    <margin top="0" left="0" bottom="0" right="0"/>
+    <constraints>
+      <xy x="20" y="20" width="1060" height="400"/>
+    </constraints>
+    <properties/>
+    <border type="none"/>
+    <children>
+      <grid id="e083a" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+        <margin top="0" left="0" bottom="0" right="0"/>
+        <constraints>
+          <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false">
+            <preferred-size width="800" height="30"/>
+          </grid>
+        </constraints>
+        <properties/>
+        <border type="none"/>
+        <children>
+          <grid id="67410" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+            <margin top="0" left="0" bottom="0" right="0"/>
+            <constraints>
+              <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+            </constraints>
+            <properties/>
+            <border type="none"/>
+            <children>
+              <component id="635bb" class="javax.swing.JLabel">
+                <constraints>
+                  <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
+                    <preferred-size width="150" height="-1"/>
+                    <maximum-size width="150" height="-1"/>
+                  </grid>
+                </constraints>
+                <properties>
+                  <text value="Name"/>
+                </properties>
+              </component>
+              <component id="a98f5" class="javax.swing.JLabel" binding="nameJLabel">
+                <constraints>
+                  <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
+                    <preferred-size width="250" height="-1"/>
+                  </grid>
+                </constraints>
+                <properties>
+                  <text value=""/>
+                </properties>
+              </component>
+              <component id="faa4" class="javax.swing.JSeparator">
+                <constraints>
+                  <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false">
+                    <maximum-size width="5" height="-1"/>
+                  </grid>
+                </constraints>
+                <properties>
+                  <orientation value="1"/>
+                </properties>
+              </component>
+            </children>
+          </grid>
+          <grid id="e942a" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+            <margin top="0" left="0" bottom="0" right="0"/>
+            <constraints>
+              <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+            </constraints>
+            <properties/>
+            <border type="none"/>
+            <children>
+              <component id="818fc" class="javax.swing.JLabel">
+                <constraints>
+                  <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
+                    <preferred-size width="150" height="-1"/>
+                    <maximum-size width="150" height="-1"/>
+                  </grid>
+                </constraints>
+                <properties>
+                  <text value="Namespace"/>
+                </properties>
+              </component>
+              <component id="4c848" class="javax.swing.JLabel" binding="namespaceJPanel">
+                <constraints>
+                  <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
+                    <preferred-size width="250" height="-1"/>
+                  </grid>
+                </constraints>
+                <properties>
+                  <text value=""/>
+                </properties>
+              </component>
+              <component id="e60d9" class="javax.swing.JSeparator">
+                <constraints>
+                  <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false">
+                    <maximum-size width="5" height="-1"/>
+                  </grid>
+                </constraints>
+                <properties>
+                  <orientation value="1"/>
+                </properties>
+              </component>
+            </children>
+          </grid>
+          <component id="73d3d" class="javax.swing.JSeparator">
+            <constraints>
+              <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false">
+                <maximum-size width="5" height="-1"/>
+              </grid>
+            </constraints>
+            <properties>
+              <orientation value="1"/>
+            </properties>
+          </component>
+        </children>
+      </grid>
+      <grid id="8ee3" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+        <margin top="0" left="0" bottom="0" right="0"/>
+        <constraints>
+          <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false">
+            <preferred-size width="800" height="30"/>
+          </grid>
+        </constraints>
+        <properties/>
+        <border type="none"/>
+        <children>
+          <grid id="d02f0" binding="sharedJPanel" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+            <margin top="0" left="0" bottom="0" right="0"/>
+            <constraints>
+              <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+            </constraints>
+            <properties/>
+            <border type="none"/>
+            <children>
+              <component id="58b28" class="javax.swing.JLabel">
+                <constraints>
+                  <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
+                    <preferred-size width="150" height="-1"/>
+                    <maximum-size width="150" height="-1"/>
+                  </grid>
+                </constraints>
+                <properties>
+                  <text value="Shared"/>
+                </properties>
+              </component>
+              <component id="c5ea3" class="javax.swing.JCheckBox" binding="shareJCheckBox">
+                <constraints>
+                  <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false">
+                    <preferred-size width="250" height="-1"/>
+                  </grid>
+                </constraints>
+                <properties>
+                  <text value=""/>
+                </properties>
+              </component>
+              <component id="c38a4" class="javax.swing.JSeparator">
+                <constraints>
+                  <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false">
+                    <maximum-size width="5" height="-1"/>
+                  </grid>
+                </constraints>
+                <properties>
+                  <orientation value="1"/>
+                </properties>
+              </component>
+            </children>
+          </grid>
+          <grid id="d76d" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+            <margin top="0" left="0" bottom="0" right="0"/>
+            <constraints>
+              <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+            </constraints>
+            <properties/>
+            <border type="none"/>
+            <children>
+              <component id="a3d13" class="javax.swing.JLabel">
+                <constraints>
+                  <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
+                    <preferred-size width="150" height="-1"/>
+                    <maximum-size width="150" height="-1"/>
+                  </grid>
+                </constraints>
+                <properties>
+                  <text value="Version"/>
+                </properties>
+              </component>
+              <component id="c1dcf" class="javax.swing.JLabel" binding="versionJLabel">
+                <constraints>
+                  <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
+                    <preferred-size width="250" height="-1"/>
+                  </grid>
+                </constraints>
+                <properties>
+                  <text value=""/>
+                </properties>
+              </component>
+              <component id="662c8" class="javax.swing.JSeparator">
+                <constraints>
+                  <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false">
+                    <maximum-size width="5" height="-1"/>
+                  </grid>
+                </constraints>
+                <properties>
+                  <orientation value="1"/>
+                </properties>
+              </component>
+            </children>
+          </grid>
+          <component id="dd7ea" class="javax.swing.JSeparator">
+            <constraints>
+              <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false">
+                <maximum-size width="5" height="-1"/>
+              </grid>
+            </constraints>
+            <properties>
+              <orientation value="1"/>
+            </properties>
+          </component>
+        </children>
+      </grid>
+      <grid id="71261" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+        <margin top="0" left="0" bottom="0" right="0"/>
+        <constraints>
+          <grid row="4" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false">
+            <preferred-size width="800" height="-1"/>
+          </grid>
+        </constraints>
+        <properties/>
+        <border type="none"/>
+        <children>
+          <component id="d4c6f" class="javax.swing.JLabel">
+            <constraints>
+              <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
+                <preferred-size width="150" height="-1"/>
+                <maximum-size width="150" height="-1"/>
+              </grid>
+            </constraints>
+            <properties>
+              <text value="Default Parameter"/>
+            </properties>
+          </component>
+          <component id="f162d" class="javax.swing.JSeparator">
+            <constraints>
+              <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false">
+                <maximum-size width="5" height="-1"/>
+              </grid>
+            </constraints>
+            <properties>
+              <orientation value="1"/>
+            </properties>
+          </component>
+          <scrollpane id="20925">
+            <constraints>
+              <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="7" hsize-policy="7" anchor="0" fill="3" indent="0" use-parent-layout="false">
+                <preferred-size width="700" height="150"/>
+              </grid>
+            </constraints>
+            <properties/>
+            <border type="none"/>
+            <children>
+              <component id="36ec3" class="javax.swing.JTextArea" binding="defaultParameterTextArea">
+                <constraints/>
+                <properties/>
+              </component>
+            </children>
+          </scrollpane>
+        </children>
+      </grid>
+      <component id="d0bd5" class="javax.swing.JSeparator">
+        <constraints>
+          <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties/>
+      </component>
+      <component id="e5d4b" class="javax.swing.JSeparator">
+        <constraints>
+          <grid row="3" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties/>
+      </component>
+      <component id="811bd" class="javax.swing.JSeparator">
+        <constraints>
+          <grid row="5" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties/>
+      </component>
+    </children>
+  </grid>
+</form>
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/pkg/ui/PackageManagerDialogForm.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/pkg/ui/PackageManagerDialogForm.java
new file mode 100644
index 0000000..51937c3
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/pkg/ui/PackageManagerDialogForm.java
@@ -0,0 +1,131 @@
+/**
+ * Copyright 2020-present NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.openwhisk.intellij.explorer.dialog.pkg.ui;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.intellij.uiDesigner.core.GridConstraints;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import com.navercorp.openwhisk.intellij.common.utils.JsonParserUtils;
+import com.navercorp.openwhisk.intellij.common.utils.ParameterUtils;
+import com.navercorp.openwhisk.intellij.common.whisk.model.Binding;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.pkg.WhiskPackageWithActions;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskPackageService;
+import org.apache.commons.lang.StringUtils;
+
+import javax.swing.*;
+import java.awt.*;
+import java.io.IOException;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import static com.intellij.uiDesigner.core.GridConstraints.*;
+
+public class PackageManagerDialogForm {
+    private static final Logger LOG = Logger.getInstance(PackageManagerDialogForm.class);
+    private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+    private JPanel mainJPanel;
+    private JCheckBox shareJCheckBox;
+    private JLabel nameJLabel;
+    private JLabel namespaceJPanel;
+    private JLabel versionJLabel;
+    private JTextArea defaultParameterTextArea;
+    private JPanel sharedJPanel;
+
+    private WhiskPackageService whiskPackageService = WhiskPackageService.getInstance();
+
+    private Project project;
+    private WhiskAuth whiskAuth;
+    private WhiskPackageWithActions whiskPackage;
+
+    public PackageManagerDialogForm(Project project, WhiskAuth whiskAuth, WhiskPackageWithActions whiskPackage) {
+        this.project = project;
+        this.whiskAuth = whiskAuth;
+        this.whiskPackage = whiskPackage;
+
+        nameJLabel.setText(whiskPackage.getName());
+        namespaceJPanel.setText(whiskPackage.getNamespace());
+        versionJLabel.setText(whiskPackage.getVersion());
+
+        Optional<Binding> binding = whiskPackage.getBinding();
+        if (binding.isPresent()) {
+            sharedJPanel.remove(shareJCheckBox);
+            Binding b = binding.get();
+            JLabel label = new JLabel("from " + b.getNamespace() + "/" + b.getName());
+            label.setPreferredSize(new Dimension(250, -1));
+            sharedJPanel.add(label, new GridConstraints(0, 2, 1, 1,
+                    SIZEPOLICY_CAN_GROW | SIZEPOLICY_CAN_SHRINK,
+                    SIZEPOLICY_CAN_GROW | SIZEPOLICY_CAN_SHRINK, FILL_NONE, ANCHOR_CENTER,
+                    new Dimension(-1, -1), new Dimension(250, -1), new Dimension(-1, -1), 0));
+        } else {
+            shareJCheckBox.setSelected(whiskPackage.isPublish());
+        }
+
+        try {
+            defaultParameterTextArea.setText(JsonParserUtils.writeParameterToJson(whiskPackage.getParameters()));
+        } catch (JsonProcessingException e) {
+            LOG.error("Failed to parse json: " + whiskPackage.getName(), e);
+        }
+    }
+
+    public void updatePackage() {
+        try {
+            /**
+             * Update default parameters
+             */
+            // parameters
+            List<Map<String, Object>> params = parametersToCollection(whiskPackage, defaultParameterTextArea.getText());
+            // payload
+            Map<String, Object> payload = new LinkedHashMap<>();
+            payload.put("name", whiskPackage.getName());
+            payload.put("namespace", whiskPackage.getNamespace());
+            payload.put("parameters", params);
+            if (shareJCheckBox.isValid()) {
+                payload.put("publish", shareJCheckBox.isSelected());
+            }
+
+            whiskPackageService.updateWhiskPackage(whiskAuth, whiskPackage.getName(), payload)
+                    .ifPresent(updated -> NOTIFIER.notify(project, updated.getName() + " updated", NotificationType.INFORMATION));
+        } catch (IOException e) {
+            String msg = "Failed to update package: " + whiskPackage.getName();
+            LOG.error(msg, e);
+            NOTIFIER.notify(project, msg, NotificationType.ERROR);
+        }
+
+    }
+
+    private boolean validateParams(String params) throws IOException {
+        return JsonParserUtils.isValidJson(params);
+    }
+
+    private List<Map<String, Object>> parametersToCollection(WhiskPackageWithActions pkg, String param) throws IOException {
+        if (StringUtils.isEmpty(param) || !validateParams(param)) {
+            return pkg.getParameters();
+        }
+        return ParameterUtils.mapToListMap(JsonParserUtils.parseMap(param));
+    }
+
+    public JPanel getContent() {
... 9478 lines suppressed ...