Initial import of openwhisk-intellij-plugin donation from NAVER (#5)

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
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.jar
Binary files 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() {
+        return mainJPanel;
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/DeleteTriggerDialog.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/DeleteTriggerDialog.java
new file mode 100644
index 0000000..e9f06a2
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/DeleteTriggerDialog.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.trigger;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.WhiskTriggerMetaData;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.*;
+
+public class DeleteTriggerDialog extends DialogWrapper {
+
+    private WhiskTriggerMetaData whiskTriggerMetaData;
+
+    public DeleteTriggerDialog(Project project, WhiskTriggerMetaData whiskTriggerMetaData) {
+        super(true); // use current window as parent
+        setTitle("Delete Trigger");
+        setResizable(false);
+        this.whiskTriggerMetaData = whiskTriggerMetaData;
+        init();
+    }
+
+    @Nullable
+    @Override
+    protected JComponent createCenterPanel() {
+        JPanel dialogPanel = new JPanel(new BorderLayout());
+
+        JLabel label = new JLabel("Are you sure you want to delete " + whiskTriggerMetaData.getName() + " trigger?");
+        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/trigger/TriggerCreationManagerDialog.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/TriggerCreationManagerDialog.java
new file mode 100644
index 0000000..6454823
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/TriggerCreationManagerDialog.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.trigger;
+
+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.trigger.ui.TriggerCreationManagerDialogForm;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+public class TriggerCreationManagerDialog extends DialogWrapperWithApply {
+
+    private TriggerCreationManagerDialogForm triggerCreationManagerDialogForm;
+
+    public TriggerCreationManagerDialog(Project project, WhiskAuth auth) {
+        super(project, true); // use current window as parent
+        triggerCreationManagerDialogForm = new TriggerCreationManagerDialogForm(project, auth);
+        setTitle("Create Trigger");
+        setResizable(false);
+        init();
+    }
+
+    @Nullable
+    @Override
+    protected JComponent createCenterPanel() {
+        if (triggerCreationManagerDialogForm != null) {
+            return triggerCreationManagerDialogForm.getContent();
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    protected void doApplyAction() {
+        if (triggerCreationManagerDialogForm != null) {
+            triggerCreationManagerDialogForm.createTrigger();
+        }
+        super.doApplyAction();
+    }
+
+    @Override
+    protected void doOKAction() {
+        if (triggerCreationManagerDialogForm != null && !myApplyAction.isApplied()) {
+            triggerCreationManagerDialogForm.createTrigger();
+        }
+        super.doOKAction();
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/TriggerManagerDialog.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/TriggerManagerDialog.java
new file mode 100644
index 0000000..98e0fcf
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/TriggerManagerDialog.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.trigger;
+
+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.WhiskActionMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.ExecutableWhiskTrigger;
+import com.navercorp.openwhisk.intellij.explorer.dialog.trigger.ui.TriggerManagerDialogForm;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.util.List;
+
+public class TriggerManagerDialog extends DialogWrapperWithApply {
+
+    private TriggerManagerDialogForm triggerManagerDialogForm;
+
+    public TriggerManagerDialog(Project project, WhiskAuth auth, ExecutableWhiskTrigger trigger, List<WhiskActionMetaData> actions) {
+        super(project, true); // use current window as parent
+        triggerManagerDialogForm = new TriggerManagerDialogForm(project, auth, trigger, actions);
+        setTitle("Manage Trigger");
+        setResizable(false);
+        init();
+    }
+
+    @Nullable
+    @Override
+    protected JComponent createCenterPanel() {
+        if (triggerManagerDialogForm != null) {
+            return this.triggerManagerDialogForm.getContent();
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    protected void doApplyAction() {
+        if (triggerManagerDialogForm != null) {
+            triggerManagerDialogForm.updateTrigger();
+        }
+        super.doApplyAction();
+    }
+
+    @Override
+    protected void doOKAction() {
+        if (triggerManagerDialogForm != null && !myApplyAction.isApplied()) {
+            triggerManagerDialogForm.updateTrigger();
+        }
+        super.doOKAction();
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/listener/RemoveLinkedRuleListener.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/listener/RemoveLinkedRuleListener.java
new file mode 100644
index 0000000..1c98bc4
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/listener/RemoveLinkedRuleListener.java
@@ -0,0 +1,22 @@
+/**
+ * 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.trigger.listener;
+
+public interface RemoveLinkedRuleListener {
+
+    void removeLinkedRule(String ruleName);
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/ui/LinkedActionsForm.form b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/ui/LinkedActionsForm.form
new file mode 100644
index 0000000..ca70047
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/ui/LinkedActionsForm.form
@@ -0,0 +1,53 @@
+<?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.trigger.ui.LinkedActionsForm">
+  <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="74847" 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="Action"/>
+        </properties>
+      </component>
+      <component id="613fe" 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="300" height="-1"/>
+          </grid>
+        </constraints>
+        <properties/>
+      </component>
+      <component id="e8a35" class="javax.swing.JLabel">
+        <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"/>
+        </constraints>
+        <properties>
+          <text value="Rule"/>
+        </properties>
+      </component>
+      <component id="ebfc1" class="javax.swing.JTextField" binding="ruleJTextField">
+        <constraints>
+          <grid row="0" column="3" 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="300" height="-1"/>
+          </grid>
+        </constraints>
+        <properties/>
+      </component>
+      <component id="5e36b" class="javax.swing.JButton" binding="deleteJButton">
+        <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"/>
+        </constraints>
+        <properties>
+          <text value=""/>
+        </properties>
+      </component>
+    </children>
+  </grid>
+</form>
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/ui/LinkedActionsForm.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/ui/LinkedActionsForm.java
new file mode 100644
index 0000000..47a3ae3
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/ui/LinkedActionsForm.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.explorer.dialog.trigger.ui;
+
+import com.intellij.openapi.project.Project;
+import com.navercorp.openwhisk.intellij.explorer.dialog.trigger.listener.RemoveLinkedRuleListener;
+
+import javax.swing.*;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+
+import static com.intellij.icons.AllIcons.General.Remove;
+
+public class LinkedActionsForm {
+    private JTextField actionJTextField;
+    private JTextField ruleJTextField;
+    private JButton deleteJButton;
+    private JPanel mainJPanel;
+
+    public LinkedActionsForm(Project project, String actionName, String ruleName, RemoveLinkedRuleListener remover) {
+        actionJTextField.setText(actionName);
+        actionJTextField.setEnabled(false);
+        ruleJTextField.setText(ruleName);
+        ruleJTextField.setEnabled(false);
+
+        deleteJButton.setIcon(Remove);
+        deleteJButton.addMouseListener(new MouseAdapter() {
+            @Override
+            public void mouseClicked(MouseEvent e) {
+                String ruleName = ruleJTextField.getText();
+                remover.removeLinkedRule(ruleName);
+            }
+        });
+    }
+
+
+    public String getAction() {
+        return actionJTextField.getText();
+    }
+
+    public String getRule() {
+        return ruleJTextField.getText();
+    }
+
+    public JPanel getContent() {
+        return mainJPanel;
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/ui/TriggerCreationManagerDialogForm.form b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/ui/TriggerCreationManagerDialogForm.form
new file mode 100644
index 0000000..25f5275
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/ui/TriggerCreationManagerDialogForm.form
@@ -0,0 +1,104 @@
+<?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.trigger.ui.TriggerCreationManagerDialogForm">
+  <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="648" height="400"/>
+    </constraints>
+    <properties/>
+    <border type="none"/>
+    <children>
+      <grid id="44d24" binding="triggerNameJPanel" 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="939a0" 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="Name"/>
+            </properties>
+          </component>
+          <component id="fa786" class="javax.swing.JTextField" binding="triggerNameJTextField">
+            <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="450" height="-1"/>
+              </grid>
+            </constraints>
+            <properties/>
+          </component>
+          <component id="6825b" 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="a087a" binding="triggerDefaultParameterJPanl" 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="cc040" 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>
+          <component id="13369" 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>
+          <scrollpane id="1795a">
+            <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="450" height="150"/>
+              </grid>
+            </constraints>
+            <properties/>
+            <border type="none"/>
+            <children>
+              <component id="ef664" class="javax.swing.JTextArea" binding="triggerDefaultParameterJTextArea">
+                <constraints/>
+                <properties/>
+              </component>
+            </children>
+          </scrollpane>
+        </children>
+      </grid>
+      <component id="c5a25" 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="5189c" 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>
+    </children>
+  </grid>
+</form>
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/ui/TriggerCreationManagerDialogForm.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/ui/TriggerCreationManagerDialogForm.java
new file mode 100644
index 0000000..ea016f5
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/ui/TriggerCreationManagerDialogForm.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.explorer.dialog.trigger.ui;
+
+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.utils.ParameterUtils;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskEndpoint;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskTriggerService;
+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 javax.swing.*;
+import java.io.IOException;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+public class TriggerCreationManagerDialogForm {
+    private static final Logger LOG = Logger.getInstance(TriggerCreationManagerDialogForm.class);
+    private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+    private JPanel mainJPanel;
+    private JTextField triggerNameJTextField;
+    private JPanel triggerNameJPanel;
+    private JPanel triggerDefaultParameterJPanl;
+    private JTextArea triggerDefaultParameterJTextArea;
+
+    private Project project;
+    private WhiskAuth auth;
+    private WhiskTriggerService whiskTriggerService;
+
+    public TriggerCreationManagerDialogForm(Project project, WhiskAuth auth) {
+        this.project = project;
+        this.auth = auth;
+        this.whiskTriggerService = WhiskTriggerService.getInstance();
+    }
+
+    public void createTrigger() {
+        try {
+            // name
+            String triggerName = triggerNameJTextField.getText().trim();
+            // parameters
+            Optional<String> params = ParameterUtils.validateParams(triggerDefaultParameterJTextArea.getText());
+            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()));
+            // payload
+            Map<String, Object> payload = new LinkedHashMap<>();
+            payload.put("name", triggerName);
+            payload.put("parameters", validParams);
+            whiskTriggerService.createWhiskTrigger(auth, triggerName, payload);
+
+            refreshWhiskTree();
+            NOTIFIER.notify(project, triggerName + " created", NotificationType.INFORMATION);
+        } catch (IOException e) {
+            String msg = "Failed to create trigger: " + triggerNameJTextField.getText().trim();
+            LOG.error(msg, e);
+            NOTIFIER.notify(project, msg, NotificationType.ERROR);
+        }
+    }
+
+    /**
+     * Helper functions.
+     */
+    private void refreshWhiskTree() {
+        EventUtils.publish(project, RefreshWhiskTreeListener.TOPIC, RefreshWhiskTreeListener::refreshWhiskTree);
+        WhiskService service = ServiceManager.getService(project, WhiskService.class);
+        try {
+            List<WhiskEndpoint> endpoints = JsonParserUtils.parseWhiskEndpoints(service.getEndpoints());
+            if (!endpoints.isEmpty()) {
+                EventUtils.publish(project, RefreshWhiskTreeListener.TOPIC, RefreshWhiskTreeListener::refreshWhiskTree);
+            } else {
+                final String msg = "There are no endpoints saved.";
+                LOG.info(msg);
+                NOTIFIER.notify(project, msg, NotificationType.INFORMATION);
+            }
+        } catch (IOException e) {
+            final String msg = "Entities cannot be loaded.";
+            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/trigger/ui/TriggerManagerDialogForm.form b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/ui/TriggerManagerDialogForm.form
new file mode 100644
index 0000000..4220be2
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/ui/TriggerManagerDialogForm.form
@@ -0,0 +1,215 @@
+<?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.trigger.ui.TriggerManagerDialogForm">
+  <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="1018" height="677"/>
+    </constraints>
+    <properties/>
+    <border type="none"/>
+    <children>
+      <grid id="c6dc7" binding="tirggerNameJPanel" 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="f997a" 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="Name"/>
+            </properties>
+          </component>
+          <component id="eeae8" 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>
+              <enabled value="true"/>
+              <orientation value="1"/>
+            </properties>
+          </component>
+          <component id="2eac4" class="javax.swing.JLabel" binding="triggerNameJLabel">
+            <constraints>
+              <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false">
+                <preferred-size width="800" height="-1"/>
+              </grid>
+            </constraints>
+            <properties>
+              <horizontalAlignment value="2"/>
+              <text value="Label"/>
+            </properties>
+          </component>
+        </children>
+      </grid>
+      <grid id="194f" binding="triggerActionsJPanel" 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"/>
+        </constraints>
+        <properties/>
+        <border type="none"/>
+        <children>
+          <component id="d68b0" 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="70daf" binding="linkedActionsJScrollPane">
+            <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="800" height="150"/>
+              </grid>
+            </constraints>
+            <properties>
+              <horizontalScrollBarPolicy value="31"/>
+              <verticalScrollBarPolicy value="20"/>
+            </properties>
+            <border type="none"/>
+            <children>
+              <grid id="3b8c2" layout-manager="GridLayoutManager" row-count="2" 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/>
+                <properties/>
+                <border type="none"/>
+                <children>
+                  <component id="957bf" 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="Action"/>
+                    </properties>
+                  </component>
+                  <component id="cd93d" class="javax.swing.JComboBox" binding="selectActionJComboBox">
+                    <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="305" height="-1"/>
+                      </grid>
+                    </constraints>
+                    <properties/>
+                  </component>
+                  <component id="f4ad6" class="javax.swing.JLabel">
+                    <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"/>
+                    </constraints>
+                    <properties>
+                      <text value="Rule"/>
+                    </properties>
+                  </component>
+                  <component id="676" class="javax.swing.JTextField" binding="ruleNameJTextField">
+                    <constraints>
+                      <grid row="0" column="3" 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="300" height="-1"/>
+                      </grid>
+                    </constraints>
+                    <properties/>
+                  </component>
+                  <component id="aa829" class="javax.swing.JButton" binding="addJButton">
+                    <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"/>
+                    </constraints>
+                    <properties>
+                      <text value=""/>
+                    </properties>
+                  </component>
+                  <grid id="3651a" 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="5" 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="994a7" 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="f96ea" binding="triggerDefaultParameterJPanel" 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="7cf0" 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>
+          <component id="e4ee8" 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>
+          <scrollpane id="a4ee0">
+            <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="800" height="150"/>
+              </grid>
+            </constraints>
+            <properties/>
+            <border type="none">
+              <color color="-4473925"/>
+            </border>
+            <children>
+              <component id="cfc60" class="javax.swing.JTextArea" binding="defaultParameterJTextArea">
+                <constraints/>
+                <properties/>
+              </component>
+            </children>
+          </scrollpane>
+        </children>
+      </grid>
+      <component id="6c728" 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="cc882" 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="9736e" 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/trigger/ui/TriggerManagerDialogForm.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/ui/TriggerManagerDialogForm.java
new file mode 100644
index 0000000..34e9793
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/dialog/trigger/ui/TriggerManagerDialogForm.java
@@ -0,0 +1,247 @@
+/**
+ * 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.trigger.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.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.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.WhiskActionMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.ExecutableWhiskTrigger;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.SimplifiedWhiskRule;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskRuleService;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskTriggerService;
+import com.navercorp.openwhisk.intellij.run.toolwindow.listener.RefreshActionOrTriggerListener;
+import org.apache.commons.lang.RandomStringUtils;
+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.io.IOException;
+import java.util.*;
+
+import static com.intellij.icons.AllIcons.General.Add;
+
+public class TriggerManagerDialogForm {
+    private static final Logger LOG = Logger.getInstance(TriggerManagerDialogForm.class);
+    private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+    private static final String DEFAULT_RULE_TEXTAREA_MSG = "If not entered, it is automatically generated.";
+
+    private JPanel mainJPanel;
+    private JPanel tirggerNameJPanel;
+    private JPanel triggerActionsJPanel;
+    private JLabel triggerNameJLabel;
+
+    private JComboBox selectActionJComboBox;
+    private JTextField ruleNameJTextField;
+    private JButton addJButton;
+    private JScrollPane linkedActionsJScrollPane;
+    private JPanel linkedActionsJPanel;
+    private JPanel triggerDefaultParameterJPanel;
+    private JTextArea defaultParameterJTextArea;
+
+    private Project project;
+    private Map<String, LinkedActionsForm> cachedRules = new HashMap<>();
+    private Map<String, LinkedActionsForm> removedRules = new HashMap<>();
+    private ExecutableWhiskTrigger cachedTrigger;
+    private WhiskAuth auth;
+    private WhiskTriggerService whiskTriggerService;
+    private WhiskRuleService whiskRuleService;
+
+    public TriggerManagerDialogForm(Project project, WhiskAuth auth, ExecutableWhiskTrigger trigger, List<WhiskActionMetaData> actions) {
+        this.project = project;
+        this.auth = auth;
+        this.cachedTrigger = trigger;
+        this.whiskTriggerService = WhiskTriggerService.getInstance();
+        this.whiskRuleService = WhiskRuleService.getInstance();
+
+        triggerNameJLabel.setText(trigger.getName());
+
+        try {
+            defaultParameterJTextArea.setText(JsonParserUtils.writeParameterToJson(trigger.getParameters()));
+        } catch (JsonProcessingException e) {
+            LOG.error("Failed to parse json: " + trigger.getName(), e);
+        }
+
+        selectActionJComboBox.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
+            }
+        });
+
+        ruleNameJTextField.setText(DEFAULT_RULE_TEXTAREA_MSG);
+
+        String namespace = trigger.getNamespace();
+        for (Map.Entry<String, SimplifiedWhiskRule> set : trigger.getRules().entrySet()) {
+            String ruleName = set.getKey().replace(namespace + "/", "");
+            String actionName = set.getValue().getAction().getPkgActionName();
+            addLinkedAction(actionName, ruleName);
+        }
+
+        addJButton.setIcon(Add);
+        addJButton.addMouseListener(new MouseAdapter() {
+            @Override
+            public void mouseClicked(MouseEvent e) {
+                if (selectActionJComboBox.getSelectedItem() == null) {
+                    return;
+                }
+
+                String ruleName = getRuleName(ruleNameJTextField.getText());
+                if (existRule(ruleName)) {
+                    NOTIFIER.notify(project, "The " + ruleName + " already exist", NotificationType.WARNING);
+                    return;
+                }
+
+                WhiskActionMetaData action = (WhiskActionMetaData) selectActionJComboBox.getSelectedItem();
+                String actionName = action.getWhiskPackage().map(pkg -> pkg + "/" + action.getName()).orElse(action.getName());
+                addLinkedAction(actionName, ruleName);
+            }
+        });
+    }
+
+    public void updateTrigger() {
+        try {
+            /**
+             * Update default parameters
+             */
+            Optional<String> params = ParameterUtils.validateParams(defaultParameterJTextArea.getText());
+            if (!params.isPresent()) {
+                NOTIFIER.notify(project, "The json format of the parameter is incorrect.", NotificationType.ERROR);
+                return;
+            }
+            // parameters
+            List<Map<String, Object>> validParams = ParameterUtils.mapToListMap(JsonParserUtils.parseMap(params.get()));
+            // payload
+            Map<String, Object> payload = new LinkedHashMap<>();
+            payload.put("name", cachedTrigger.getName());
+            payload.put("parameters", validParams);
+            whiskTriggerService.updateWhiskTrigger(auth, cachedTrigger.getName(), payload);
+
+            /**
+             * Update rules
+             */
+            for (LinkedActionsForm linkedActionsForm : cachedRules.values()) {
+                String ruleName = linkedActionsForm.getRule();
+                String actionName = linkedActionsForm.getAction();
+                // payload
+                Map<String, Object> rulePayload = new LinkedHashMap<>();
+                rulePayload.put("name", ruleName);
+                rulePayload.put("trigger", "/_/" + cachedTrigger.getName());
+                rulePayload.put("action", "/_/" + actionName);
+                rulePayload.put("status", "");
+                whiskRuleService.updateWhiskRule(auth, ruleName, rulePayload).ifPresent(rule ->
+                        LOG.info("Updated rule: " + rule.getName()));
+            }
+
+            /**
+             * Remove rules
+             */
+            for (LinkedActionsForm linkedActionsForm : removedRules.values()) {
+                whiskRuleService.deleteWhiskRule(auth, linkedActionsForm.getRule()).ifPresent(rule ->
+                        LOG.info("Deleted rule: " + rule.getName()));
+            }
+
+            NOTIFIER.notify(project, cachedTrigger.getName() + " updated", NotificationType.INFORMATION);
+            EventUtils.publish(project, RefreshActionOrTriggerListener.TOPIC, RefreshActionOrTriggerListener::fetchActionMetadata);
+        } catch (IOException e) {
+            String msg = "Failed to update trigger: " + cachedTrigger.getName();
+            LOG.error(msg, e);
+            NOTIFIER.notify(project, msg, NotificationType.ERROR);
+        }
+    }
+
+    /**
+     * Helper functions.
+     */
+    private void addLinkedAction(String actionName, String ruleName) {
+        LinkedActionsForm linkedActionsForm = new LinkedActionsForm(project, actionName, ruleName, this::removeLinkedAction);
+        linkedActionsJPanel.setLayout(new BoxLayout(linkedActionsJPanel, BoxLayout.Y_AXIS));
+        linkedActionsJPanel.add(linkedActionsForm.getContent());
+        cachedRules.put(ruleName, linkedActionsForm);
+        linkedActionsJPanel.updateUI();
+    }
+
+    private void removeLinkedAction(String ruleName) {
+        LinkedActionsForm form = cachedRules.get(ruleName);
+        removedRules.put(ruleName, form);
+        cachedRules.remove(ruleName);
+        linkedActionsJPanel.remove(form.getContent());
+        linkedActionsJPanel.updateUI();
+    }
+
+    private boolean existRule(String ruleName) {
+        for (String r : cachedRules.keySet()) {
+            if (r.equals(ruleName.trim())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private String getRuleName(String userInput) {
+        if (DEFAULT_RULE_TEXTAREA_MSG.trim().equals(userInput.trim())) {
+            // auto generate
+            return generateName();
+        } else {
+            // use user input
+            return userInput.trim();
+        }
+    }
+
+    private String generateName() {
+        return RandomStringUtils.randomAlphabetic(30);
+    }
+
+    public JPanel getContent() {
+        return mainJPanel;
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/editor/ActivationViewEditor.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/editor/ActivationViewEditor.java
new file mode 100644
index 0000000..7aec225
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/editor/ActivationViewEditor.java
@@ -0,0 +1,191 @@
+/**
+ * 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.editor;
+
+import com.intellij.codeHighlighting.BackgroundEditorHighlighter;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.fileEditor.FileEditor;
+import com.intellij.openapi.fileEditor.FileEditorLocation;
+import com.intellij.openapi.fileEditor.FileEditorState;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Key;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import com.navercorp.openwhisk.intellij.common.utils.WhiskUtils;
+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.common.whisk.model.action.WhiskActionMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.model.activation.WhiskActivationMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.WhiskTriggerMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskActivationService;
+import com.navercorp.openwhisk.intellij.explorer.editor.model.ComboBoxEntityEntry;
+import com.navercorp.openwhisk.intellij.explorer.editor.ui.ActivationViewEditorForm;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.beans.PropertyChangeListener;
+import java.io.IOException;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+public class ActivationViewEditor implements FileEditor {
+    private static final Logger LOG = Logger.getInstance(ActivationViewEditor.class);
+    private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+    private Project project;
+    private ActivationViewEditorForm activationViewEditorForm;
+    private WhiskActivationService whiskActivationService = WhiskActivationService.getInstance();
+
+    public ActivationViewEditor(Project project,
+                                List<WhiskEndpoint> endpoints,
+                                Optional<WhiskAuth> whiskAuth,
+                                Optional<WhiskActionMetaData> action,
+                                Optional<WhiskTriggerMetaData> trigger) {
+        activationViewEditorForm = new ActivationViewEditorForm(project, endpoints);
+        activationViewEditorForm.cacheWhiskAuth(whiskAuth);
+
+        /**
+         * Initialize ComboBox values
+         */
+        List<WhiskNamespace> namespaces = endpoints.stream().flatMap(ep -> ep.getNamespaces().stream()).collect(Collectors.toList());
+        activationViewEditorForm.initializeNamespaceJComboBox(namespaces);
+
+        whiskAuth.ifPresent(auth ->
+                getEntity(action, trigger).ifPresent(entity -> {
+
+                    /**
+                     * Load Activations
+                     *
+                     * Note: Activations of binding package action cannot be loaded.
+                     * TODO load activations of binding action after the follow upstream pr is merged: https://github.com/apache/openwhisk/pull/4919
+                     */
+                    try {
+                        List<WhiskActivationMetaData> activations =
+                                whiskActivationService.getWhiskActivations(auth, entity.toEntityName(), 100, 0); // TODO pagination
+                        activationViewEditorForm.initializeActivationTable(activations);
+                    } catch (IOException e) {
+                        LOG.error(e);
+                    }
+
+                    /**
+                     * Set value to namespace combobox
+                     */
+                    WhiskUtils.findWhiskNamespace(endpoints, auth.getAuth())
+                            .ifPresent(namespace -> activationViewEditorForm.setNamespaceJComboBox(namespace));
+
+                    /**
+                     * Set value to action or trigger combobox
+                     */
+                    activationViewEditorForm.setActionOrTriggerJComboBox(entity);
+                }));
+    }
+
+    private Optional<ComboBoxEntityEntry> getEntity(Optional<WhiskActionMetaData> action, Optional<WhiskTriggerMetaData> trigger) {
+        if (action.isPresent() && trigger.isPresent()) {
+            return Optional.empty();
+        } else if (action.isPresent()) {
+            return action.map(WhiskActionMetaData::toCombBoxEntityEntry);
+        } else if (trigger.isPresent()) {
+            return trigger.map(WhiskTriggerMetaData::toCombBoxEntityEntry);
+        } else {
+            return Optional.of(ComboBoxEntityEntry.NONE_COMBO_BOX_ENTITY_ENTRY);
+        }
+    }
+
+    @NotNull
+    @Override
+    public JComponent getComponent() {
+        return activationViewEditorForm.getContent();
+    }
+
+    @Nullable
+    @Override
+    public JComponent getPreferredFocusedComponent() {
+        return null;
+    }
+
+    @NotNull
+    @Override
+    public String getName() {
+        return "activation editor";
+    }
+
+    @Override
+    public void setState(@NotNull FileEditorState state) {
+
+    }
+
+    @Override
+    public boolean isModified() {
+        return false;
+    }
+
+    @Override
+    public boolean isValid() {
+        return true;
+    }
+
+    @Override
+    public void addPropertyChangeListener(@NotNull PropertyChangeListener listener) {
+
+    }
+
+    @Override
+    public void removePropertyChangeListener(@NotNull PropertyChangeListener listener) {
+
+    }
+
+    @Nullable
+    @Override
+    public FileEditorLocation getCurrentLocation() {
+        return null;
+    }
+
+    @Override
+    public void dispose() {
+
+    }
+
+    @Nullable
+    @Override
+    public <T> T getUserData(@NotNull Key<T> key) {
+        return null;
+    }
+
+    @Override
+    public <T> void putUserData(@NotNull Key<T> key, @Nullable T value) {
+
+    }
+
+    @Override
+    public void selectNotify() {
+
+    }
+
+    @Override
+    public void deselectNotify() {
+
+    }
+
+    @Nullable
+    @Override
+    public BackgroundEditorHighlighter getBackgroundHighlighter() {
+        return null; // Return {@code null} if no background highlighting activity necessary for this file editor.
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/editor/ActivationViewEditorProvider.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/editor/ActivationViewEditorProvider.java
new file mode 100644
index 0000000..f1d3643
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/editor/ActivationViewEditorProvider.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.explorer.editor;
+
+import com.intellij.openapi.fileEditor.FileEditor;
+import com.intellij.openapi.fileEditor.FileEditorPolicy;
+import com.intellij.openapi.fileEditor.FileEditorProvider;
+import com.intellij.openapi.project.DumbAware;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.NotNull;
+
+public class ActivationViewEditorProvider implements FileEditorProvider, DumbAware {
+    @Override
+    public boolean accept(@NotNull Project project, @NotNull VirtualFile file) {
+        if (file instanceof ActivationViewVirtualFile) {
+            return true;
+        }
+        return false;
+    }
+
+    @NotNull
+    @Override
+    public FileEditor createEditor(@NotNull Project project, @NotNull VirtualFile file) {
+        ActivationViewVirtualFile activation = (ActivationViewVirtualFile) file;
+        return new ActivationViewEditor(project, activation.getEndpoints(), activation.getWhiskAuth(), activation.getAction(), activation.getTrigger());
+    }
+
+    @NotNull
+    @Override
+    public String getEditorTypeId() {
+        return "activation-editor";
+    }
+
+    @NotNull
+    @Override
+    public FileEditorPolicy getPolicy() {
+        return FileEditorPolicy.HIDE_DEFAULT_EDITOR;
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/editor/ActivationViewVirtualFile.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/editor/ActivationViewVirtualFile.java
new file mode 100644
index 0000000..484fdb3
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/editor/ActivationViewVirtualFile.java
@@ -0,0 +1,147 @@
+/**
+ * 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.editor;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.VirtualFileSystem;
+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.action.WhiskActionMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.WhiskTriggerMetaData;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.List;
+import java.util.Optional;
+
+public class ActivationViewVirtualFile extends VirtualFile {
+
+    private List<WhiskEndpoint> endpoints;
+    private Optional<WhiskAuth> whiskAuth;
+    private Optional<WhiskActionMetaData> action;
+    private Optional<WhiskTriggerMetaData> trigger;
+
+    private ActivationViewVirtualFileSystem fileSystem;
+
+    public ActivationViewVirtualFile(@NotNull Project project,
+                                     List<WhiskEndpoint> endpoints,
+                                     Optional<WhiskAuth> whiskAuth,
+                                     Optional<WhiskActionMetaData> action,
+                                     Optional<WhiskTriggerMetaData> trigger) {
+        this.endpoints = endpoints;
+        this.whiskAuth = whiskAuth;
+        this.action = action;
+        this.trigger = trigger;
+        this.fileSystem = ActivationViewVirtualFileSystem.getInstance();
+    }
+
+    public List<WhiskEndpoint> getEndpoints() {
+        return endpoints;
+    }
+
+    public Optional<WhiskAuth> getWhiskAuth() {
+        return whiskAuth;
+    }
+
+    public Optional<WhiskActionMetaData> getAction() {
+        return action;
+    }
+
+    public Optional<WhiskTriggerMetaData> getTrigger() {
+        return trigger;
+    }
+
+    @NotNull
+    @Override
+    public String getName() {
+        return "Activation View";
+    }
+
+    @NotNull
+    @Override
+    public VirtualFileSystem getFileSystem() {
+        return fileSystem;
+    }
+
+    @NotNull
+    @Override
+    public String getPath() {
+        return "";
+    }
+
+    @Override
+    public boolean isWritable() {
+        return false;
+    }
+
+    @Override
+    public boolean isDirectory() {
+        return false;
+    }
+
+    @Override
+    public boolean isValid() {
+        return true;
+    }
+
+    @Override
+    public VirtualFile getParent() {
+        return null;
+    }
+
+    @Override
+    public VirtualFile[] getChildren() {
+        return new VirtualFile[0];
+    }
+
+    @NotNull
+    @Override
+    public OutputStream getOutputStream(Object requestor, long newModificationStamp, long newTimeStamp) throws IOException {
+        return new ByteArrayOutputStream();
+    }
+
+    @NotNull
+    @Override
+    public byte[] contentsToByteArray() throws IOException {
+        return null;
+    }
+
+    @Override
+    public long getTimeStamp() {
+        return 0;
+    }
+
+    @Override
+    public long getLength() {
+        return 0;
+    }
+
+    @Override
+    public void refresh(boolean asynchronous, boolean recursive, @Nullable Runnable postRunnable) {
+
+    }
+
+    @Override
+    public InputStream getInputStream() throws IOException {
+        return null;
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/editor/ActivationViewVirtualFileSystem.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/editor/ActivationViewVirtualFileSystem.java
new file mode 100644
index 0000000..640750f
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/editor/ActivationViewVirtualFileSystem.java
@@ -0,0 +1,114 @@
+/**
+ * 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.editor;
+
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.VirtualFileListener;
+import com.intellij.openapi.vfs.VirtualFileManager;
+import com.intellij.openapi.vfs.VirtualFileSystem;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.IOException;
+
+public class ActivationViewVirtualFileSystem extends VirtualFileSystem {
+
+    private static final String PROTOCOL = "activation-view";
+
+    private static final IOException READONLY_FILE_SYSTEM = new IOException("Operation not support");
+
+    public ActivationViewVirtualFileSystem() {
+    }
+
+    public static ActivationViewVirtualFileSystem getInstance() {
+        return (ActivationViewVirtualFileSystem) VirtualFileManager.getInstance().getFileSystem(PROTOCOL);
+    }
+
+    @NotNull
+    @Override
+    public String getProtocol() {
+        return PROTOCOL;
+    }
+
+    @Nullable
+    @Override
+    public VirtualFile findFileByPath(@NotNull String path) {
+        return null;
+    }
+
+    @Override
+    public void refresh(boolean asynchronous) {
+
+    }
+
+    @Nullable
+    @Override
+    public VirtualFile refreshAndFindFileByPath(@NotNull String path) {
+        return null;
+    }
+
+    @Override
+    public void addVirtualFileListener(@NotNull VirtualFileListener listener) {
+
+    }
+
+    @Override
+    public void removeVirtualFileListener(@NotNull VirtualFileListener listener) {
+
+    }
+
+    @Override
+    protected void deleteFile(Object requestor, @NotNull VirtualFile vFile) throws IOException {
+
+    }
+
+    @Override
+    protected void moveFile(Object requestor, @NotNull VirtualFile vFile, @NotNull VirtualFile newParent) throws IOException {
+
+    }
+
+    @Override
+    protected void renameFile(Object requestor, @NotNull VirtualFile vFile, @NotNull String newName) throws IOException {
+
+    }
+
+    @NotNull
+    @Override
+    protected VirtualFile createChildFile(Object requestor, @NotNull VirtualFile vDir, @NotNull String fileName) throws IOException {
+        throw READONLY_FILE_SYSTEM;
+    }
+
+    @NotNull
+    @Override
+    protected VirtualFile createChildDirectory(Object requestor, @NotNull VirtualFile vDir, @NotNull String dirName) throws IOException {
+        throw READONLY_FILE_SYSTEM;
+    }
+
+    @NotNull
+    @Override
+    protected VirtualFile copyFile(Object requestor,
+                                   @NotNull VirtualFile virtualFile,
+                                   @NotNull VirtualFile newParent,
+                                   @NotNull String copyName) throws IOException {
+        throw READONLY_FILE_SYSTEM;
+    }
+
+    @Override
+    public boolean isReadOnly() {
+        return false;
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/editor/model/ComboBoxEntityEntry.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/editor/model/ComboBoxEntityEntry.java
new file mode 100644
index 0000000..ff8e4f1
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/editor/model/ComboBoxEntityEntry.java
@@ -0,0 +1,80 @@
+/**
+ * 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.editor.model;
+
+import java.util.Objects;
+import java.util.Optional;
+
+public class ComboBoxEntityEntry {
+    public static final ComboBoxEntityEntry NONE_COMBO_BOX_ENTITY_ENTRY = new ComboBoxEntityEntry("None (Please select an entity)", ComboBoxEntityType.NONE);
+
+    private String name;
+    private ComboBoxEntityType type;
+
+    public ComboBoxEntityEntry() {
+    }
+
+    public ComboBoxEntityEntry(String name, ComboBoxEntityType type) {
+        this.name = name;
+        this.type = type;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public ComboBoxEntityType getType() {
+        return type;
+    }
+
+    public void setType(ComboBoxEntityType type) {
+        this.type = type;
+    }
+
+    @Override
+    public String toString() {
+        return name;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        ComboBoxEntityEntry that = (ComboBoxEntityEntry) o;
+        return Objects.equals(name, that.name) && type == that.type;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(name, type);
+    }
+
+    public Optional<String> toEntityName() {
+        switch (type) {
+            case ACTION:
+            case TRIGGER:
+                return Optional.of(name);
+            case NONE:
+            default:
+                return Optional.empty();
+        }
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/editor/model/ComboBoxEntityType.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/editor/model/ComboBoxEntityType.java
new file mode 100644
index 0000000..a31e7ff
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/editor/model/ComboBoxEntityType.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.editor.model;
+
+public enum ComboBoxEntityType {
+    NONE, ACTION, TRIGGER
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/editor/ui/ActivationViewEditorForm.form b/src/main/java/com/navercorp/openwhisk/intellij/explorer/editor/ui/ActivationViewEditorForm.form
new file mode 100644
index 0000000..c234f3c
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/editor/ui/ActivationViewEditorForm.form
@@ -0,0 +1,85 @@
+<?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.editor.ui.ActivationViewEditorForm">
+  <grid id="27dc6" binding="mainJPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
+    <constraints>
+      <xy x="20" y="20" width="2220" height="400"/>
+    </constraints>
+    <properties/>
+    <border type="none"/>
+    <children>
+      <grid id="5711a" 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 border-constraint="North"/>
+        <properties/>
+        <border type="none"/>
+        <children>
+          <grid id="96fee" 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="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="14afe" class="javax.swing.JComboBox" binding="namespaceJComboBox">
+                <constraints>
+                  <grid row="0" column="0" 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="300" height="-1"/>
+                  </grid>
+                </constraints>
+                <properties/>
+              </component>
+              <component id="c419f" class="javax.swing.JComboBox" binding="actionOrTriggerJComboBox">
+                <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="300" height="-1"/>
+                  </grid>
+                </constraints>
+                <properties/>
+              </component>
+              <component id="1fb64" 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>
+              <component id="a14c9" class="javax.swing.JButton" binding="searchJButton">
+                <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"/>
+                </constraints>
+                <properties>
+                  <text value="Search"/>
+                </properties>
+              </component>
+            </children>
+          </grid>
+        </children>
+      </grid>
+      <grid id="87efb" 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 border-constraint="Center"/>
+        <properties/>
+        <border type="none"/>
+        <children>
+          <scrollpane id="3ee83">
+            <constraints>
+              <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="7" hsize-policy="7" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+            </constraints>
+            <properties/>
+            <border type="none"/>
+            <children>
+              <component id="a8016" class="javax.swing.JTable" binding="activationsJTable">
+                <constraints/>
+                <properties/>
+              </component>
+            </children>
+          </scrollpane>
+        </children>
+      </grid>
+    </children>
+  </grid>
+</form>
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/editor/ui/ActivationViewEditorForm.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/editor/ui/ActivationViewEditorForm.java
new file mode 100644
index 0000000..a908ec7
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/editor/ui/ActivationViewEditorForm.java
@@ -0,0 +1,276 @@
+/**
+ * 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.editor.ui;
+
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.fileEditor.FileEditorManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.navercorp.openwhisk.intellij.common.utils.FileUtils;
+import com.navercorp.openwhisk.intellij.common.utils.WhiskUtils;
+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.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.trigger.WhiskTriggerMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskActivationService;
+import com.navercorp.openwhisk.intellij.explorer.editor.model.ComboBoxEntityEntry;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+
+import javax.swing.*;
+import javax.swing.event.ListDataListener;
+import javax.swing.table.DefaultTableModel;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.io.IOException;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.util.*;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public class ActivationViewEditorForm {
+    private static final Logger LOG = Logger.getInstance(ActivationViewEditorForm.class);
+    private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+    private DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss", Locale.KOREA);
+    private static final int ACTIVATION_ID_COLUMN = 1;
+
+    private JPanel mainJPanel;
+    private JPanel actionsToobarJPanel;
+    private JTable activationsJTable;
+    private JComboBox namespaceJComboBox;
+    private JComboBox actionOrTriggerJComboBox;
+    private JButton searchJButton;
+
+    private WhiskActivationService whiskActivationService = WhiskActivationService.getInstance();
+    private FileEditorManager fileEditorManager;
+
+    private DefaultTableModel activationsTableModel;
+
+    private Optional<WhiskAuth> currentAuth = Optional.empty();
+
+    public ActivationViewEditorForm(Project project, List<WhiskEndpoint> endpoints) {
+        this.fileEditorManager = FileEditorManager.getInstance(project);
+
+        activationsTableModel = new DefaultTableModel(initActivationColumnModel(), 0) {
+            @Override
+            public boolean isCellEditable(int row, int column) {
+                // Activation ID(1) & Entity(6) are editable to copy value
+                return column == 1 || column == 6;
+            }
+        };
+
+        activationsJTable.setModel(activationsTableModel);
+        activationsJTable.setDragEnabled(true);
+        activationsJTable.setAutoCreateRowSorter(true);
+
+        activationsJTable.addMouseListener(new MouseAdapter() {
+            @Override
+            public void mouseClicked(MouseEvent e) {
+                if (e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() > 1 && currentAuth.isPresent()) {
+                    int row = activationsJTable.rowAtPoint(e.getPoint());
+                    String activationId = (String) activationsTableModel.getValueAt(row, ACTIVATION_ID_COLUMN);
+                    try {
+                        whiskActivationService.getWhiskActivation(currentAuth.get(), activationId)
+                                .ifPresent(activationWithLogs -> openActivationEditor(project, activationWithLogs));
+                    } catch (IOException ex) {
+                        LOG.error("Failed to fetch activations: " + activationId, ex);
+                    }
+                }
+            }
+        });
+
+        searchJButton.addMouseListener(new MouseAdapter() {
+            @Override
+            public void mouseClicked(MouseEvent e) {
+                WhiskNamespace whiskNamespace = (WhiskNamespace) namespaceJComboBox.getSelectedItem();
+                ComboBoxEntityEntry entity = (ComboBoxEntityEntry) actionOrTriggerJComboBox.getSelectedItem();
+                WhiskUtils.findWhiskAuth(endpoints, whiskNamespace).ifPresent(auth ->
+                        initializeActivationTable(loadActivations(auth, Optional.ofNullable(entity))));
+            }
+        });
+    }
+
+    public void initializeNamespaceJComboBox(List<WhiskNamespace> namespaces) {
+        namespaceJComboBox.setModel(new ComboBoxModel<WhiskNamespace>() {
+            private WhiskNamespace namespace;
+
+            @Override
+            public void setSelectedItem(Object anItem) {
+                namespace = (WhiskNamespace) anItem;
+            }
+
+            @Override
+            public WhiskNamespace getSelectedItem() {
+                return this.namespace;
+            }
+
+            @Override
+            public int getSize() {
+                return namespaces.size();
+            }
+
+            @Override
+            public WhiskNamespace getElementAt(int index) {
+                return namespaces.get(index);
+            }
+
+            @Override
+            public void addListDataListener(ListDataListener l) {
+                // nothing to do
+            }
+
+            @Override
+            public void removeListDataListener(ListDataListener l) {
+                // nothing to do
+            }
+        });
+
+        namespaceJComboBox.addActionListener(e -> {
+            WhiskNamespace namespace = (WhiskNamespace) namespaceJComboBox.getSelectedItem();
+            initializeActionAndTriggerJComboBox(namespace.getActions(), namespace.getTriggers());
+        });
+    }
+
+    public void initializeActionAndTriggerJComboBox(List<WhiskActionMetaData> actions, List<WhiskTriggerMetaData> triggers) {
+        /**
+         * It shows the list of none, action, trigger in one JComboBox at once. The order is as follows: None ~ Actions ~ triggers
+         *
+         * TODO Add binding action
+         */
+        List<ComboBoxEntityEntry> entries = Stream.of(
+                Stream.of(ComboBoxEntityEntry.NONE_COMBO_BOX_ENTITY_ENTRY),
+                actions.stream().map(WhiskActionMetaData::toCombBoxEntityEntry),
+                triggers.stream().map(WhiskTriggerMetaData::toCombBoxEntityEntry))
+                .flatMap(a -> a)
+                .collect(Collectors.toList());
+
+        actionOrTriggerJComboBox.setModel(new ComboBoxModel<ComboBoxEntityEntry>() {
+            private ComboBoxEntityEntry selected;
+
+            @Override
+            public void setSelectedItem(Object anItem) {
+                this.selected = (ComboBoxEntityEntry) anItem;
+            }
+
+            @Override
+            public ComboBoxEntityEntry getSelectedItem() {
+                return selected;
+            }
+
+            @Override
+            public int getSize() {
+                return entries.size();
+            }
+
+            @Override
+            public ComboBoxEntityEntry getElementAt(int index) {
+                return entries.get(index);
+            }
+
+            @Override
+            public void addListDataListener(ListDataListener l) {
+                // nothing to do
+            }
+
+            @Override
+            public void removeListDataListener(ListDataListener l) {
+                // nothing to do
+            }
+        });
+    }
+
+    public void cacheWhiskAuth(Optional<WhiskAuth> whiskAuth) {
+        this.currentAuth = whiskAuth;
+    }
+
+    public void initializeActivationTable(List<WhiskActivationMetaData> activations) {
+        clearActivationTable();
+        for (WhiskActivationMetaData activation : activations) {
+            activationsTableModel.addRow(createRow(activation));
+        }
+    }
+
+    public void setNamespaceJComboBox(WhiskNamespace namespace) {
+        namespaceJComboBox.setSelectedItem(namespace);
+        initializeActionAndTriggerJComboBox(namespace.getActions(), namespace.getTriggers());
+    }
+
+    public void setActionOrTriggerJComboBox(ComboBoxEntityEntry entity) {
+        actionOrTriggerJComboBox.setSelectedItem(entity);
+    }
+
+    /**
+     * Helper functions.
+     */
+    private List<WhiskActivationMetaData> loadActivations(WhiskAuth auth, Optional<ComboBoxEntityEntry> entity) {
+        try {
+            return whiskActivationService.getWhiskActivations(auth, entity.flatMap(ComboBoxEntityEntry::toEntityName), 100, 0); // TODO pagination
+        } catch (IOException e) {
+            LOG.error(e);
+            return new ArrayList<>();
+        }
+    }
+
+    private void openActivationEditor(Project project, WhiskActivationWithLogs activationWithLogs) {
+        try {
+            final String tmpFilePath = project.getBasePath() + "/.idea/openwhisk";
+            VirtualFile activationFile = FileUtils.writeActivationToFile(tmpFilePath, activationWithLogs);
+            fileEditorManager.openFile(activationFile, true);
+        } catch (IOException e) {
+            LOG.error("Failed to open file: " + activationWithLogs.getName(), e);
+        }
+    }
+
+    private void clearActivationTable() {
+        activationsTableModel.setRowCount(0);
+    }
+
+    private Vector<String> initActivationColumnModel() {
+        Vector<String> columnModel = new Vector<>();
+        columnModel.add("Datetime");
+        columnModel.add("Activation ID");
+        columnModel.add("Kind");
+        columnModel.add("Start");
+        columnModel.add("Duration");
+        columnModel.add("Status");
+        columnModel.add("Entity");
+        return columnModel;
+    }
+
+    private Vector<String> createRow(WhiskActivationMetaData activation) {
+        LocalDateTime ldt = LocalDateTime.ofInstant(Instant.ofEpochMilli(activation.getStart()), ZoneId.systemDefault());
+        Vector<String> rowModel = new Vector<>();
+        rowModel.add(dateFormat.format(ldt));           // Datetime
+        rowModel.add(activation.getActivationId());     // Activation Id
+        rowModel.add(activation.getKind());             // Kind
+        rowModel.add(activation.getStartType());        // Start
+        rowModel.add(activation.getDuration() + "ms");  // Duration
+        rowModel.add(activation.getStatus());           // Status
+        rowModel.add(activation.getNamespace() + "/" + activation.getName() + ":" + activation.getVersion()); // Entity
+        return rowModel;
+    }
+
+    public JPanel getContent() {
+        return mainJPanel;
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/WhiskExplorerWindowFactory.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/WhiskExplorerWindowFactory.java
new file mode 100644
index 0000000..055ca9e
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/WhiskExplorerWindowFactory.java
@@ -0,0 +1,153 @@
+/**
+ * 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.toolwindow;
+
+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.openapi.wm.ToolWindow;
+import com.intellij.openapi.wm.ToolWindowFactory;
+import com.intellij.ui.content.Content;
+import com.intellij.ui.content.ContentFactory;
+import com.navercorp.openwhisk.intellij.common.service.WhiskService;
+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.WhiskEndpoint;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskNamespace;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskNamespaceService;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.ui.WhiskExplorerWindowForm;
+import org.apache.commons.lang.StringUtils;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.*;
+import java.util.stream.Collectors;
+
+public class WhiskExplorerWindowFactory implements ToolWindowFactory {
+    private static final Logger LOG = Logger.getInstance(WhiskExplorerWindowFactory.class);
+    private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+    @Override
+    public void createToolWindowContent(@NotNull Project project, @NotNull ToolWindow toolWindow) {
+        try {
+            // Load endpoint from ~/.wskprops
+            WhiskService service = ServiceManager.getService(project, WhiskService.class);
+
+            String filterEndpoints = filterEndpoints(service.getEndpoints());
+            LOG.info(filterEndpoints);
+            List<WhiskEndpoint> endpoints = new ArrayList<>(JsonParserUtils.parseWhiskEndpoints(filterEndpoints));
+            WhiskNamespaceService whiskNamespaceService = WhiskNamespaceService.getInstance();
+
+            readWskProps().ifPresent(auth -> {
+                Optional<WhiskNamespace> validNamespace = whiskNamespaceService.validateNamespace(auth);
+                if (validNamespace.isPresent()) {
+                    WhiskNamespace ns = validNamespace.get();
+                    Optional<WhiskEndpoint> existEndpoint = hasWhiskEndpoint(endpoints, auth);
+                    if (existEndpoint.isPresent()) {
+                        Optional<WhiskNamespace> existNamespace = hasWhiskNamespace(existEndpoint.get().getNamespaces(), auth);
+                        if (!existNamespace.isPresent()) {
+                            int epIndex = endpoints.indexOf(existEndpoint.get());
+                            WhiskEndpoint ep = endpoints.get(epIndex);
+                            ep.addNamespaces(ns);
+                            endpoints.set(epIndex, ep);
+                        }
+                    } else {
+                        endpoints.add(new WhiskEndpoint("endpoint(" + endpoints.size() + ")", auth.getApihost(), Arrays.asList(ns)));
+                    }
+                } else {
+                    final String msg = "Cannot read the " + System.getProperty("user.home") + "/.wskprops file Please check the file.";
+                    NOTIFIER.notify(project, msg, NotificationType.WARNING);
+                }
+            });
+            service.setEndpoints(JsonParserUtils.writeEndpointsToJson(endpoints));
+            service.loadState(service);
+        } catch (IOException e) {
+            final String msg = "Endpoints cannot be loaded.";
+            LOG.error(msg, e);
+            NOTIFIER.notify(project, msg, NotificationType.ERROR);
+        }
+
+        // Tree View
+        WhiskExplorerWindowForm whiskExplorerWindowForm = new WhiskExplorerWindowForm(project, toolWindow);
+        ContentFactory contentFactory = ContentFactory.SERVICE.getInstance();
+        Content content = contentFactory.createContent(whiskExplorerWindowForm.getContent(), null, false);
+        toolWindow.getContentManager().addContent(content);
+    }
+
+    private String filterEndpoints(String endpoints) {
+        if (StringUtils.isEmpty(endpoints)) {
+            return "[]";
+        }
+
+        try {
+            List<Map<String, Object>> eps = JsonParserUtils.parseListMap(endpoints)
+                    .stream()
+                    .filter(ep -> {
+                        String apihost = (String) ep.get("apihost");
+                        return StringUtils.isNotEmpty(apihost);
+                    }).collect(Collectors.toList());
+            return JsonParserUtils.writeListMapToJson(eps);
+        } catch (IOException e) {
+            LOG.error("Failed to parse json endpoints to map: ", endpoints);
+            return "[]";
+        }
+    }
+
+    private Optional<WhiskEndpoint> hasWhiskEndpoint(List<WhiskEndpoint> endpoints, WhiskAuth auth) {
+        for (WhiskEndpoint ep : endpoints) {
+            if (ep.getApihost().equals(auth.getApihost())) {
+                return Optional.of(ep);
+            }
+        }
+        return Optional.empty();
+    }
+
+    private Optional<WhiskNamespace> hasWhiskNamespace(List<WhiskNamespace> namespaces, WhiskAuth auth) {
+        for (WhiskNamespace ns : namespaces) {
+            if (ns.getAuth().equals(auth.getAuth())) {
+                return Optional.of(ns);
+            }
+        }
+        return Optional.empty();
+    }
+
+    private Optional<WhiskAuth> readWskProps() {
+        Path path = Paths.get(System.getProperty("user.home") + "/.wskprops");
+        Charset cs = StandardCharsets.UTF_8;
+        try {
+            Map<String, String> wskprops = new HashMap<>();
+            for (String prop : Files.readAllLines(path, cs)) {
+                String[] p = prop.split("=");
+                wskprops.put(p[0], p[1]);
+            }
+            return Optional.of(new WhiskAuth(wskprops.get("AUTH"), wskprops.get("APIHOST")));
+        } catch (IOException e) {
+            LOG.error(e);
+            return Optional.empty();
+        } catch (Exception e) {
+            LOG.error(e);
+            return Optional.empty();
+        }
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/AddEndpointAction.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/AddEndpointAction.java
new file mode 100644
index 0000000..9e55d82
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/AddEndpointAction.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.explorer.toolwindow.action;
+
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.diagnostic.Logger;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import com.navercorp.openwhisk.intellij.explorer.dialog.endpoint.AddEndpointDialog;
+import org.jetbrains.annotations.NotNull;
+
+import static com.intellij.icons.AllIcons.General.Add;
+
+public class AddEndpointAction extends AnAction {
+    private static final Logger LOG = Logger.getInstance(AddEndpointAction.class);
+    private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+    public AddEndpointAction() {
+        super("Add Endpoint", "Add Endpoint", Add);
+    }
+
+    @Override
+    public void actionPerformed(@NotNull AnActionEvent e) {
+        AddEndpointDialog dialog = new AddEndpointDialog(e.getProject());
+        if (dialog.showAndGet()) {
+            LOG.info("AddEndpointDialogForm is closed");
+        }
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/AddNamespaceAction.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/AddNamespaceAction.java
new file mode 100644
index 0000000..65e6c5b
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/AddNamespaceAction.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.explorer.toolwindow.action;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.diagnostic.Logger;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskEndpoint;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import com.navercorp.openwhisk.intellij.explorer.dialog.namespace.AddNamespaceDialog;
+import org.jetbrains.annotations.NotNull;
+
+public class AddNamespaceAction extends AnAction {
+    private static final Logger LOG = Logger.getInstance(AddNamespaceAction.class);
+    private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+    private WhiskEndpoint whiskEndpoint;
+
+    public AddNamespaceAction(WhiskEndpoint whiskEndpoint) {
+        super("Add Namespace", "Add Namespace", AllIcons.General.Add);
+        this.whiskEndpoint = whiskEndpoint;
+    }
+
+    @Override
+    public void actionPerformed(@NotNull AnActionEvent e) {
+        if (whiskEndpoint != null) {
+            AddNamespaceDialog dialog = new AddNamespaceDialog(e.getProject(), whiskEndpoint);
+            if (dialog.showAndGet()) {
+                LOG.info("AddNamespaceDialog is closed");
+            }
+        }
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/CreatePackageAction.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/CreatePackageAction.java
new file mode 100644
index 0000000..1478aa5
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/CreatePackageAction.java
@@ -0,0 +1,49 @@
+/**
+ * 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.toolwindow.action;
+
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.diagnostic.Logger;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import com.navercorp.openwhisk.intellij.explorer.dialog.pkg.PackageCreationManagerDialog;
+import org.jetbrains.annotations.NotNull;
+
+import static com.intellij.icons.AllIcons.General.Add;
+
+public class CreatePackageAction extends AnAction {
+    private static final Logger LOG = Logger.getInstance(CreatePackageAction.class);
+    private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+    private WhiskAuth whiskAuth;
+
+    public CreatePackageAction(WhiskAuth whiskAuth) {
+        super("Create Package", "Create package", Add);
+        this.whiskAuth = whiskAuth;
+    }
+
+    @Override
+    public void actionPerformed(@NotNull AnActionEvent e) {
+        if (whiskAuth != null) {
+            PackageCreationManagerDialog dialog = new PackageCreationManagerDialog(e.getProject(), whiskAuth);
+            if (dialog.showAndGet()) {
+                LOG.info("TriggerManagerDialog is closed");
+            }
+        }
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/CreateTriggerAction.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/CreateTriggerAction.java
new file mode 100644
index 0000000..b65b4d5
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/CreateTriggerAction.java
@@ -0,0 +1,49 @@
+/**
+ * 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.toolwindow.action;
+
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.diagnostic.Logger;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import com.navercorp.openwhisk.intellij.explorer.dialog.trigger.TriggerCreationManagerDialog;
+import org.jetbrains.annotations.NotNull;
+
+import static com.intellij.icons.AllIcons.General.Add;
+
+public class CreateTriggerAction extends AnAction {
+    private static final Logger LOG = Logger.getInstance(CreateTriggerAction.class);
+    private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+    private WhiskAuth whiskAuth;
+
+    public CreateTriggerAction(WhiskAuth whiskAuth) {
+        super("Create Trigger", "Create trigger", Add);
+        this.whiskAuth = whiskAuth;
+    }
+
+    @Override
+    public void actionPerformed(@NotNull AnActionEvent e) {
+        if (whiskAuth != null) {
+            TriggerCreationManagerDialog dialog = new TriggerCreationManagerDialog(e.getProject(), whiskAuth);
+            if (dialog.showAndGet()) {
+                LOG.info("TriggerManagerDialog is closed");
+            }
+        }
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/DeleteActionAction.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/DeleteActionAction.java
new file mode 100644
index 0000000..ca560f7
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/DeleteActionAction.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.explorer.toolwindow.action;
+
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.actionSystem.ActionManager;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.diagnostic.Logger;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.WhiskActionMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskActionService;
+import com.navercorp.openwhisk.intellij.explorer.dialog.action.DeleteActionDialog;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.IOException;
+
+import static com.intellij.icons.AllIcons.Actions.GC;
+
+public class DeleteActionAction extends AnAction {
+    private static final Logger LOG = Logger.getInstance(DeleteActionAction.class);
+    private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+    private WhiskActionMetaData whiskActionMetaData;
+    private WhiskAuth whiskAuth;
+    private WhiskActionService whiskActionService;
+
+    public DeleteActionAction(WhiskAuth whiskAuth, WhiskActionMetaData whiskActionMetaData) {
+        super("Delete", "Delete action", GC);
+        this.whiskActionMetaData = whiskActionMetaData;
+        this.whiskAuth = whiskAuth;
+        this.whiskActionService = WhiskActionService.getInstance();
+    }
+
+    @Override
+    public void actionPerformed(@NotNull AnActionEvent e) {
+        if (whiskAuth != null && whiskActionMetaData != null) {
+            if ((new DeleteActionDialog(e.getProject())).showAndGet()) {
+                try {
+                    whiskActionService.deleteWhiskActions(whiskAuth, whiskActionMetaData.getWhiskPackage(), whiskActionMetaData.getName())
+                            .ifPresent(deletedAction -> {
+                                String msg = deletedAction.getName() + " action is deleted";
+                                LOG.info(msg);
+                                NOTIFIER.notify(e.getProject(), msg, NotificationType.INFORMATION);
+
+                                ActionManager.getInstance().getAction("WhiskExplorer.Actions.Controls.Refresh").actionPerformed(e);
+                            });
+                } catch (IOException ex) {
+                    final String msg = "The action cannot be deleted: " + whiskActionMetaData.getName();
+                    LOG.error(msg, ex);
+                    NOTIFIER.notify(e.getProject(), msg, NotificationType.ERROR);
+                }
+            }
+        }
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/DeleteEndpointAction.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/DeleteEndpointAction.java
new file mode 100644
index 0000000..0cb0f61
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/DeleteEndpointAction.java
@@ -0,0 +1,97 @@
+/**
+ * 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.toolwindow.action;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+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 com.navercorp.openwhisk.intellij.explorer.dialog.endpoint.DeleteEndpointDialog;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static com.intellij.icons.AllIcons.General.Remove;
+
+public class DeleteEndpointAction extends AnAction {
+    private static final Logger LOG = Logger.getInstance(DeleteEndpointAction.class);
+    private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+    private WhiskEndpoint whiskEndpoint;
+
+    public DeleteEndpointAction(WhiskEndpoint whiskEndpoint) {
+        super("Delete Endpoint", "Delete Endpoint", Remove);
+        this.whiskEndpoint = whiskEndpoint;
+    }
+
+    @Override
+    public void actionPerformed(@NotNull AnActionEvent e) {
+        if (whiskEndpoint != null) {
+            final Project project = e.getProject();
+            DeleteEndpointDialog dialog = new DeleteEndpointDialog(project, whiskEndpoint);
+            if (dialog.showAndGet()) {
+
+                /**
+                 * Load endpoints
+                 */
+                WhiskService whiskService = ServiceManager.getService(project, WhiskService.class);
+                List<WhiskEndpoint> endpoints = new ArrayList<>();
+                try {
+                    endpoints = new ArrayList<>(JsonParserUtils.parseWhiskEndpoints(whiskService.getEndpoints())); // make mutable
+                } catch (IOException ex) {
+                    LOG.error("Endpoint parsing failed", ex);
+                }
+
+                List<WhiskEndpoint> updatedEndpoints = removeEndpoint(endpoints, whiskEndpoint);
+                saveEndpoints(whiskService, updatedEndpoints);
+
+                // Update action tree
+                EventUtils.publish(project, RefreshWhiskTreeListener.TOPIC, RefreshWhiskTreeListener::refreshWhiskTree);
+                NOTIFIER.notify(project, whiskEndpoint.getAlias() + " deleted successfully.", NotificationType.INFORMATION);
+            }
+        }
+    }
+
+
+    private List<WhiskEndpoint> removeEndpoint(List<WhiskEndpoint> endpoints, WhiskEndpoint endpoint) {
+        return endpoints.stream()
+                .filter(ep -> !ep.getAlias().equals(endpoint.getAlias())) // remove endpoint
+                .collect(Collectors.toList());
+    }
+
+    private void saveEndpoints(WhiskService whiskService, List<WhiskEndpoint> newEndpoints) {
+        try {
+            String eps = JsonParserUtils.writeEndpointsToJson(newEndpoints);
+            whiskService.setEndpoints(eps);
+            whiskService.loadState(whiskService);
+        } catch (JsonProcessingException e) {
+            LOG.error("Endpoint parsing failed", e);
+        }
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/DeleteNamespaceAction.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/DeleteNamespaceAction.java
new file mode 100644
index 0000000..6be6fc9
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/DeleteNamespaceAction.java
@@ -0,0 +1,104 @@
+/**
+ * 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.toolwindow.action;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+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.common.whisk.model.WhiskNamespace;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.listener.RefreshWhiskTreeListener;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import com.navercorp.openwhisk.intellij.explorer.dialog.namespace.DeleteNamespaceDialog;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static com.intellij.icons.AllIcons.General.Remove;
+
+public class DeleteNamespaceAction extends AnAction {
+    private static final Logger LOG = Logger.getInstance(DeleteNamespaceAction.class);
+    private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+    private WhiskNamespace whiskNamespace;
+
+    public DeleteNamespaceAction(WhiskNamespace whiskNamespace) {
+        super("Delete Namespace", "Delete Namespace", Remove);
+        this.whiskNamespace = whiskNamespace;
+    }
+
+    @Override
+    public void actionPerformed(@NotNull AnActionEvent e) {
+        if (whiskNamespace != null) {
+            final Project project = e.getProject();
+            DeleteNamespaceDialog dialog = new DeleteNamespaceDialog(project, whiskNamespace);
+            if (dialog.showAndGet()) {
+
+                /**
+                 * Load endpoints
+                 */
+                WhiskService whiskService = ServiceManager.getService(project, WhiskService.class);
+                List<WhiskEndpoint> endpoints = new ArrayList<>();
+                try {
+                    endpoints = new ArrayList<>(JsonParserUtils.parseWhiskEndpoints(whiskService.getEndpoints())); // make mutable
+                } catch (IOException ex) {
+                    LOG.error("Endpoint parsing failed", ex);
+                }
+
+                List<WhiskEndpoint> updatedEndpoints = removeNamespace(endpoints, whiskNamespace);
+                saveEndpoints(whiskService, updatedEndpoints);
+
+                // Update action tree
+                EventUtils.publish(project, RefreshWhiskTreeListener.TOPIC, RefreshWhiskTreeListener::refreshWhiskTree);
+                NOTIFIER.notify(project, whiskNamespace.getPath() + " deleted successfully.", NotificationType.INFORMATION);
+            }
+        }
+    }
+
+
+    private List<WhiskEndpoint> removeNamespace(List<WhiskEndpoint> endpoints, WhiskNamespace namespace) {
+        return endpoints.stream()
+                .peek(ep -> {
+                    List<WhiskNamespace> namespaces = ep.getNamespaces()
+                            .stream()
+                            .filter(ns -> !ns.getPath().equals(namespace.getPath()) && !ns.getAuth().equals(namespace.getAuth()))  // remove namespace
+                            .collect(Collectors.toList());
+                    ep.setNamespaces(namespaces);
+                })
+                .collect(Collectors.toList());
+    }
+
+    private void saveEndpoints(WhiskService whiskService, List<WhiskEndpoint> newEndpoints) {
+        try {
+            String eps = JsonParserUtils.writeEndpointsToJson(newEndpoints);
+            whiskService.setEndpoints(eps);
+            whiskService.loadState(whiskService);
+        } catch (JsonProcessingException e) {
+            LOG.error("Endpoint parsing failed", e);
+        }
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/DeletePackageAction.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/DeletePackageAction.java
new file mode 100644
index 0000000..00c29ec
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/DeletePackageAction.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.explorer.toolwindow.action;
+
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.actionSystem.ActionManager;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.diagnostic.Logger;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.CompactWhiskAction;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.ExecutableWhiskAction;
+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.service.WhiskActionService;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskPackageService;
+import com.navercorp.openwhisk.intellij.explorer.dialog.pkg.DeletePackageDialog;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+import static com.intellij.icons.AllIcons.Actions.GC;
+
+public class DeletePackageAction extends AnAction {
+    private static final Logger LOG = Logger.getInstance(DeletePackageAction.class);
+    private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+    private WhiskAuth whiskAuth;
+    private WhiskPackage whiskPackage;
+
+    private WhiskPackageService whiskPackageService = WhiskPackageService.getInstance();
+    private WhiskActionService whiskActionService = WhiskActionService.getInstance();
+
+    public DeletePackageAction(WhiskAuth whiskAuth, WhiskPackage whiskPackage) {
+        super("Delete", "Delete package", GC);
+        this.whiskPackage = whiskPackage;
+        this.whiskAuth = whiskAuth;
+    }
+
+    @Override
+    public void actionPerformed(@NotNull AnActionEvent e) {
+        if (whiskAuth != null && whiskPackage != null) {
+            try {
+                whiskPackageService.getWhiskPackage(whiskAuth, whiskPackage.getNamespace(), whiskPackage.getName()).ifPresent(whiskPackageWithActions -> {
+                    if ((new DeletePackageDialog(e.getProject(), whiskPackageWithActions)).showAndGet()) {
+                        deletePackage(e, whiskPackageWithActions);
+                    }
+                });
+            } catch (IOException ex) {
+                final String msg = "The package cannot be deleted: " + whiskPackage.getName();
+                LOG.error(msg, ex);
+                NOTIFIER.notify(e.getProject(), msg, NotificationType.ERROR);
+            }
+        }
+    }
+
+    private void deletePackage(AnActionEvent e, WhiskPackageWithActions whiskPackageWithActions) {
+        try {
+            /**
+             * Delete actions
+             */
+            List<ExecutableWhiskAction> deletedActions = new ArrayList<>();
+            if (!whiskPackageWithActions.getBinding().isPresent()) {
+                deletedActions.addAll(deleteActions(e, whiskPackageWithActions));
+            }
+
+            /**
+             * Delete package
+             */
+            Optional<WhiskPackageWithActions> deletedPackage = whiskPackageService.deleteWhiskPackage(whiskAuth, whiskPackage.getName());
+            if (deletedPackage.isPresent()) {
+                StringBuilder builder = new StringBuilder();
+                builder.append("<html>");
+                builder.append("The following entities were deleted<br/>");
+                builder.append("- " + deletedPackage.get().getName() + "<br/>");
+                for (ExecutableWhiskAction deleted : deletedActions) {
+                    builder.append("- " + deletedPackage.get().getName() + "/" + deleted.getName() + "<br/>");
+                }
+                builder.append("</html>");
+                String msg = builder.toString();
+                LOG.info(msg);
+                NOTIFIER.notify(e.getProject(), msg, NotificationType.INFORMATION);
+
+                ActionManager.getInstance().getAction("WhiskExplorer.Actions.Controls.Refresh").actionPerformed(e);
+            } else {
+                String msg = whiskPackageWithActions.getName() + " package can't be deleted";
+                LOG.info(msg);
+                NOTIFIER.notify(e.getProject(), msg, NotificationType.ERROR);
+            }
+        } catch (IOException ex) {
+            String msg = whiskPackageWithActions.getName() + " package can't be deleted";
+            LOG.error(msg, ex);
+            NOTIFIER.notify(e.getProject(), msg, NotificationType.ERROR);
+        }
+    }
+
+    private List<ExecutableWhiskAction> deleteActions(AnActionEvent e, WhiskPackageWithActions whiskPackageWithActions) {
+        List<ExecutableWhiskAction> deletedActions = new ArrayList<>();
+        try {
+            for (CompactWhiskAction action : whiskPackageWithActions.getActions()) {
+                whiskActionService.deleteWhiskActions(whiskAuth, Optional.of(whiskPackageWithActions.getName()), action.getName())
+                        .ifPresent(deletedActions::add);
+            }
+        } catch (IOException ex) {
+            String msg = whiskPackageWithActions.getName() + " action can't be deleted";
+            LOG.error(msg, ex);
+            NOTIFIER.notify(e.getProject(), msg, NotificationType.ERROR);
+        }
+        return deletedActions;
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/DeleteTriggerAction.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/DeleteTriggerAction.java
new file mode 100644
index 0000000..4867b19
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/DeleteTriggerAction.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.explorer.toolwindow.action;
+
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.actionSystem.ActionManager;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.diagnostic.Logger;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.WhiskTriggerMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskTriggerService;
+import com.navercorp.openwhisk.intellij.explorer.dialog.trigger.DeleteTriggerDialog;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.IOException;
+
+import static com.intellij.icons.AllIcons.Actions.GC;
+
+public class DeleteTriggerAction extends AnAction {
+    private static final Logger LOG = Logger.getInstance(DeleteTriggerAction.class);
+    private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+    private WhiskTriggerMetaData whiskTriggerMetaData;
+    private WhiskAuth whiskAuth;
+
+    private WhiskTriggerService whiskTriggerService;
+
+    public DeleteTriggerAction(WhiskAuth whiskAuth, WhiskTriggerMetaData whiskTriggerMetaData) {
+        super("Delete", "Delete trigger", GC);
+        this.whiskTriggerMetaData = whiskTriggerMetaData;
+        this.whiskAuth = whiskAuth;
+        this.whiskTriggerService = WhiskTriggerService.getInstance();
+    }
+
+    @Override
+    public void actionPerformed(@NotNull AnActionEvent e) {
+        if (whiskAuth != null && whiskTriggerMetaData != null) {
+            if ((new DeleteTriggerDialog(e.getProject(), whiskTriggerMetaData)).showAndGet()) {
+                try {
+                    whiskTriggerService.deleteWhiskTrigger(whiskAuth, whiskTriggerMetaData.getName()).ifPresent(deletedTrigger -> {
+                        String msg = deletedTrigger.getName() + " trigger is deleted";
+                        LOG.info(msg);
+                        NOTIFIER.notify(e.getProject(), msg, NotificationType.INFORMATION);
+                    });
+                    ActionManager.getInstance().getAction("WhiskExplorer.Actions.Controls.Refresh").actionPerformed(e);
+                } catch (IOException ex) {
+                    final String msg = "The trigger cannot be deleted: " + whiskTriggerMetaData.getName();
+                    LOG.error(msg, ex);
+                    NOTIFIER.notify(e.getProject(), msg, NotificationType.ERROR);
+                }
+            }
+        }
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/EditActionAction.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/EditActionAction.java
new file mode 100644
index 0000000..aa5be37
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/EditActionAction.java
@@ -0,0 +1,73 @@
+/**
+ * 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.toolwindow.action;
+
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.diagnostic.Logger;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.WhiskActionMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskActionService;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import com.navercorp.openwhisk.intellij.explorer.dialog.action.ActionManagerDialog;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Optional;
+
+import static com.intellij.icons.AllIcons.Actions.Edit;
+
+public class EditActionAction extends AnAction {
+    private static final Logger LOG = Logger.getInstance(EditActionAction.class);
+    private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+    private WhiskAuth whiskAuth;
+    private WhiskActionMetaData whiskActionMetaData;
+
+    private WhiskActionService whiskActionService = WhiskActionService.getInstance();
+
+    public EditActionAction(WhiskAuth whiskAuth, WhiskActionMetaData whiskActionMetaData) {
+        super("Edit", "Edit Action", Edit);
+        this.whiskAuth = whiskAuth;
+        this.whiskActionMetaData = whiskActionMetaData;
+    }
+
+    @Override
+    public void actionPerformed(@NotNull AnActionEvent e) {
+        if (whiskAuth != null && whiskActionMetaData != null) {
+            try {
+                List<WhiskActionMetaData> actions = whiskActionService.getWhiskActions(whiskAuth);
+                whiskActionService.getWhiskAction(whiskAuth,
+                        Optional.of(whiskActionMetaData.getNamespacePath()),
+                        whiskActionMetaData.getWhiskPackage(),
+                        whiskActionMetaData.getName())
+                        .ifPresent(action -> {
+                            ActionManagerDialog dialog = new ActionManagerDialog(e.getProject(), whiskAuth, action, actions);
+                            if (dialog.showAndGet()) {
+                                LOG.info("ActionManagerDialog is closed");
+                            }
+                        });
+            } catch (IOException ex) {
+                final String msg = "The action cannot be loaded: " + whiskActionMetaData.getFullyQualifiedName();
+                LOG.error(msg, ex);
+                NOTIFIER.notify(e.getProject(), msg, NotificationType.ERROR);
+            }
+        }
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/EditEndpointAction.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/EditEndpointAction.java
new file mode 100644
index 0000000..064eddd
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/EditEndpointAction.java
@@ -0,0 +1,44 @@
+/**
+ * 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.toolwindow.action;
+
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.diagnostic.Logger;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskEndpoint;
+import com.navercorp.openwhisk.intellij.explorer.dialog.endpoint.EditEndpointDialog;
+
+import static com.intellij.icons.AllIcons.Actions.Edit;
+
+public class EditEndpointAction extends AnAction {
+    private static final Logger LOG = Logger.getInstance(EditEndpointAction.class);
+
+    private WhiskEndpoint whiskEndpoint;
+
+    public EditEndpointAction(WhiskEndpoint whiskEndpoint) {
+        super("Edit Endpoint", "Edit Endpoint", Edit);
+        this.whiskEndpoint = whiskEndpoint;
+    }
+
+    @Override
+    public void actionPerformed(AnActionEvent event) {
+        EditEndpointDialog dialog = new EditEndpointDialog(event.getProject(), whiskEndpoint);
+        if (dialog.showAndGet()) {
+            LOG.info("closed");
+        }
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/EditNamespaceAction.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/EditNamespaceAction.java
new file mode 100644
index 0000000..7708f46
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/EditNamespaceAction.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.explorer.toolwindow.action;
+
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.diagnostic.Logger;
+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.EditNamespaceDialog;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import org.jetbrains.annotations.NotNull;
+
+import static com.intellij.icons.AllIcons.Actions.Edit;
+
+public class EditNamespaceAction extends AnAction {
+    private static final Logger LOG = Logger.getInstance(EditNamespaceAction.class);
+    private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+    private WhiskAuth whiskAuth;
+    private WhiskNamespace whiskNamespace;
+
+    public EditNamespaceAction(WhiskAuth whiskAuth, WhiskNamespace whiskNamespace) {
+        super("Edit Namespace", "Edit Namespace", Edit);
+        this.whiskAuth = whiskAuth;
+        this.whiskNamespace = whiskNamespace;
+    }
+
+    @Override
+    public void actionPerformed(@NotNull AnActionEvent e) {
+        if (whiskAuth != null) {
+            EditNamespaceDialog dialog = new EditNamespaceDialog(e.getProject(), whiskAuth, whiskNamespace);
+            if (dialog.showAndGet()) {
+                LOG.info("EditNamespaceDialog is closed");
+            }
+        }
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/EditPackageAction.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/EditPackageAction.java
new file mode 100644
index 0000000..b0b7838
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/EditPackageAction.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.toolwindow.action;
+
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.diagnostic.Logger;
+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.service.WhiskPackageService;
+import com.navercorp.openwhisk.intellij.explorer.dialog.pkg.PackageManagerDialog;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.IOException;
+
+import static com.intellij.icons.AllIcons.Actions.Edit;
+
+public class EditPackageAction extends AnAction {
+    private static final Logger LOG = Logger.getInstance(EditPackageAction.class);
+    private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+    private WhiskAuth whiskAuth;
+    private WhiskPackage whiskPackage;
+
+    private WhiskPackageService whiskPackageService = WhiskPackageService.getInstance();
+
+    public EditPackageAction(WhiskAuth whiskAuth, WhiskPackage whiskPackage) {
+        super("Edit", "Edit Package", Edit);
+        this.whiskAuth = whiskAuth;
+        this.whiskPackage = whiskPackage;
+    }
+
+    @Override
+    public void actionPerformed(@NotNull AnActionEvent e) {
+        if (whiskAuth != null) {
+            try {
+                whiskPackageService.getWhiskPackage(whiskAuth, whiskPackage.getNamespace(), whiskPackage.getName())
+                        .ifPresent(whiskPackageWithActions -> {
+                            PackageManagerDialog dialog = new PackageManagerDialog(e.getProject(), whiskAuth, whiskPackageWithActions);
+                            if (dialog.showAndGet()) {
+                                LOG.info("PackageManagerDialog is closed");
+                            }
+                        });
+            } catch (IOException ex) {
+                final String msg = "The package cannot be loaded: " + whiskPackage.getName();
+                LOG.error(msg, ex);
+                NOTIFIER.notify(e.getProject(), msg, NotificationType.ERROR);
+            }
+        }
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/EditTriggerAction.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/EditTriggerAction.java
new file mode 100644
index 0000000..c9f9d83
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/EditTriggerAction.java
@@ -0,0 +1,82 @@
+/**
+ * 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.toolwindow.action;
+
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+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.action.WhiskActionMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.ExecutableWhiskTrigger;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.WhiskTriggerMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskActionService;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskTriggerService;
+import com.navercorp.openwhisk.intellij.explorer.dialog.trigger.TriggerManagerDialog;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.IOException;
+import java.util.List;
+
+import static com.intellij.icons.AllIcons.Actions.Edit;
+
+public class EditTriggerAction extends AnAction {
+    private static final Logger LOG = Logger.getInstance(EditTriggerAction.class);
+    private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+    private WhiskAuth whiskAuth;
+    private WhiskTriggerMetaData whiskTriggerMetaData;
+
+    private WhiskTriggerService whiskTriggerService = WhiskTriggerService.getInstance();
+    private WhiskActionService whiskActionService = WhiskActionService.getInstance();
+
+    public EditTriggerAction(WhiskAuth whiskAuth, WhiskTriggerMetaData whiskTriggerMetaData) {
+        super("Edit", "Edit trigger", Edit);
+        this.whiskAuth = whiskAuth;
+        this.whiskTriggerMetaData = whiskTriggerMetaData;
+    }
+
+    @Override
+    public void actionPerformed(@NotNull AnActionEvent e) {
+        if (whiskAuth != null && whiskTriggerMetaData != null) {
+            try {
+                whiskTriggerService.getWhiskTrigger(whiskAuth, whiskTriggerMetaData.getName())
+                        .ifPresent(executableWhiskTrigger -> openEditTriggerDialog(e.getProject(), executableWhiskTrigger));
+            } catch (IOException ex) {
+                final String msg = "The trigger cannot be loaded: " + whiskTriggerMetaData.getName();
+                LOG.error(msg, ex);
+                NOTIFIER.notify(e.getProject(), msg, NotificationType.ERROR);
+            }
+        }
+    }
+
+    private void openEditTriggerDialog(Project project, ExecutableWhiskTrigger executableWhiskTrigger) {
+        try {
+            List<WhiskActionMetaData> actions = whiskActionService.getWhiskActions(whiskAuth);
+            TriggerManagerDialog dialog = new TriggerManagerDialog(project, whiskAuth, executableWhiskTrigger, actions);
+            if (dialog.showAndGet()) {
+                LOG.info("TriggerManagerDialog is closed");
+            }
+        } catch (IOException ex) {
+            final String msg = "The trigger cannot be loaded: " + whiskTriggerMetaData.getName();
+            LOG.error(msg, ex);
+            NOTIFIER.notify(project, msg, NotificationType.ERROR);
+        }
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/OpenActivationViewAction.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/OpenActivationViewAction.java
new file mode 100644
index 0000000..cea1e45
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/OpenActivationViewAction.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.toolwindow.action;
+
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.fileEditor.FileEditorManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+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.action.WhiskActionMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.WhiskTriggerMetaData;
+import com.navercorp.openwhisk.intellij.explorer.editor.ActivationViewVirtualFile;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+import java.util.Optional;
+
+import static com.intellij.icons.AllIcons.Nodes.Module;
+
+public class OpenActivationViewAction extends AnAction {
+    private static final Logger LOG = Logger.getInstance(OpenActivationViewAction.class);
+    private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+    private List<WhiskEndpoint> endpoints;
+
+    private Optional<WhiskAuth> whiskAuth;
+    private Optional<WhiskActionMetaData> action;
+    private Optional<WhiskTriggerMetaData> trigger;
+
+    public OpenActivationViewAction(List<WhiskEndpoint> endpoints,
+                                    Optional<WhiskAuth> whiskAuth,
+                                    Optional<WhiskActionMetaData> action,
+                                    Optional<WhiskTriggerMetaData> trigger) {
+        super("Activation", "Open activation view", Module);
+        this.endpoints = endpoints;
+        this.whiskAuth = whiskAuth;
+        this.action = action;
+        this.trigger = trigger;
+    }
+
+    @Override
+    public void actionPerformed(@NotNull AnActionEvent e) {
+        final Project project = e.getProject();
+        FileEditorManager fileEditorManager = FileEditorManager.getInstance(project);
+        VirtualFile virtualFile = new ActivationViewVirtualFile(project, endpoints, whiskAuth, action, trigger);
+        fileEditorManager.openFile(virtualFile, true);
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/OpenAndFireTriggerAction.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/OpenAndFireTriggerAction.java
new file mode 100644
index 0000000..adaad0b
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/OpenAndFireTriggerAction.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.toolwindow.action;
+
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.diagnostic.Logger;
+import com.navercorp.openwhisk.intellij.common.utils.EventUtils;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.WhiskTriggerMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskTriggerService;
+import com.navercorp.openwhisk.intellij.run.toolwindow.listener.OpenAndFireTriggerControlActionListener;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.IOException;
+
+import static com.intellij.icons.AllIcons.Actions.Execute;
+
+public class OpenAndFireTriggerAction extends AnAction {
+    private static final Logger LOG = Logger.getInstance(OpenAndFireTriggerAction.class);
+    private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+    private WhiskTriggerMetaData whiskTriggerMetaData;
+    private WhiskAuth whiskAuth;
+
+    private WhiskTriggerService whiskTriggerService;
+
+    public OpenAndFireTriggerAction(WhiskAuth whiskAuth, WhiskTriggerMetaData whiskTriggerMetaData) {
+        super("Fire", "Fire trigger", Execute);
+        this.whiskTriggerMetaData = whiskTriggerMetaData;
+        this.whiskAuth = whiskAuth;
+        this.whiskTriggerService = WhiskTriggerService.getInstance();
+    }
+
+    @Override
+    public void actionPerformed(@NotNull AnActionEvent e) {
+        if (whiskAuth != null && whiskTriggerMetaData != null) {
+            try {
+                whiskTriggerService.getWhiskTrigger(whiskAuth, whiskTriggerMetaData.getName()).ifPresent(executableWhiskTrigger ->
+                        EventUtils.publish(e.getProject(),
+                                OpenAndFireTriggerControlActionListener.TOPIC,
+                                (l) -> l.openAndFireTriggerControlWindow(whiskAuth, executableWhiskTrigger)));
+            } catch (IOException ex) {
+                final String msg = "The trigger cannot be loaded: " + whiskTriggerMetaData.getName();
+                LOG.error(msg, ex);
+                NOTIFIER.notify(e.getProject(), msg, NotificationType.ERROR);
+            }
+        }
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/OpenAndRunActionAction.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/OpenAndRunActionAction.java
new file mode 100644
index 0000000..c204f86
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/OpenAndRunActionAction.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.explorer.toolwindow.action;
+
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.diagnostic.Logger;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import com.navercorp.openwhisk.intellij.common.utils.EventUtils;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.WhiskActionMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskActionService;
+import com.navercorp.openwhisk.intellij.run.toolwindow.listener.OpenAndRunActionControlActionListener;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.IOException;
+import java.util.Optional;
+
+import static com.intellij.icons.AllIcons.Actions.Execute;
+
+public class OpenAndRunActionAction extends AnAction {
+    private static final Logger LOG = Logger.getInstance(OpenAndRunActionAction.class);
+    private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+    private WhiskActionService whiskActionService = WhiskActionService.getInstance();
+
+    private WhiskActionMetaData whiskActionMetaData;
+    private WhiskAuth whiskAuth;
+
+    public OpenAndRunActionAction(WhiskAuth whiskAuth, WhiskActionMetaData whiskActionMetaData) {
+        super("Run", "Run action", Execute);
+        this.whiskActionMetaData = whiskActionMetaData;
+        this.whiskAuth = whiskAuth;
+    }
+
+    @Override
+    public void actionPerformed(@NotNull AnActionEvent e) {
+        if (whiskAuth != null && whiskActionMetaData != null) {
+            try {
+                whiskActionService.getWhiskAction(whiskAuth,
+                        Optional.ofNullable(whiskActionMetaData.getNamespacePath()),
+                        whiskActionMetaData.getWhiskPackage(),
+                        whiskActionMetaData.getName())
+                        .ifPresent(executableWhiskAction ->
+                                EventUtils.publish(e.getProject(),
+                                        OpenAndRunActionControlActionListener.TOPIC,
+                                        (l) -> l.openAndRunActionControlWindow(whiskAuth, executableWhiskAction)));
+            } catch (IOException ex) {
+                final String msg = "The action cannot be loaded: " + whiskActionMetaData.getFullyQualifiedName();
+                LOG.error(msg, ex);
+                NOTIFIER.notify(e.getProject(), msg, NotificationType.ERROR);
+            }
+
+        }
+
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/RefreshTreeAction.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/RefreshTreeAction.java
new file mode 100644
index 0000000..b5d7814
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/action/RefreshTreeAction.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.toolwindow.action;
+
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.diagnostic.Logger;
+import com.navercorp.openwhisk.intellij.common.utils.EventUtils;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.listener.RefreshWhiskTreeListener;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+
+import static com.intellij.icons.AllIcons.Actions.Refresh;
+
+public class RefreshTreeAction extends AnAction {
+    private static final Logger LOG = Logger.getInstance(RefreshTreeAction.class);
+    private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+    public RefreshTreeAction() {
+        super(Refresh);
+    }
+
+    @Override
+    public void actionPerformed(AnActionEvent event) {
+        EventUtils.publish(event.getProject(), RefreshWhiskTreeListener.TOPIC, RefreshWhiskTreeListener::refreshWhiskTree);
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/actiongroup/WhiskActionGroup.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/actiongroup/WhiskActionGroup.java
new file mode 100644
index 0000000..e7adc8a
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/actiongroup/WhiskActionGroup.java
@@ -0,0 +1,39 @@
+/**
+ * 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.toolwindow.actiongroup;
+
+import com.intellij.openapi.actionSystem.DefaultActionGroup;
+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.action.WhiskActionMetaData;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.action.DeleteActionAction;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.action.EditActionAction;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.action.OpenActivationViewAction;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.action.OpenAndRunActionAction;
+
+import java.util.List;
+import java.util.Optional;
+
+public class WhiskActionGroup extends DefaultActionGroup {
+
+    public WhiskActionGroup(WhiskAuth whiskAuth, WhiskActionMetaData whiskActionMetaData, List<WhiskEndpoint> endpoints) {
+        add(new OpenAndRunActionAction(whiskAuth, whiskActionMetaData));
+        add(new OpenActivationViewAction(endpoints, Optional.of(whiskAuth), Optional.of(whiskActionMetaData), Optional.empty()));
+        add(new EditActionAction(whiskAuth, whiskActionMetaData));
+        add(new DeleteActionAction(whiskAuth, whiskActionMetaData));
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/actiongroup/WhiskEndpointGroup.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/actiongroup/WhiskEndpointGroup.java
new file mode 100644
index 0000000..637cd9a
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/actiongroup/WhiskEndpointGroup.java
@@ -0,0 +1,32 @@
+/**
+ * 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.toolwindow.actiongroup;
+
+import com.intellij.openapi.actionSystem.DefaultActionGroup;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskEndpoint;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.action.AddNamespaceAction;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.action.DeleteEndpointAction;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.action.EditEndpointAction;
+
+public class WhiskEndpointGroup extends DefaultActionGroup {
+
+    public WhiskEndpointGroup(WhiskEndpoint whiskEndpoint) {
+        add(new EditEndpointAction(whiskEndpoint));
+        add(new DeleteEndpointAction(whiskEndpoint));
+        add(new AddNamespaceAction(whiskEndpoint));
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/actiongroup/WhiskNamespaceGroup.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/actiongroup/WhiskNamespaceGroup.java
new file mode 100644
index 0000000..4c6ff7c
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/actiongroup/WhiskNamespaceGroup.java
@@ -0,0 +1,39 @@
+/**
+ * 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.toolwindow.actiongroup;
+
+import com.intellij.openapi.actionSystem.DefaultActionGroup;
+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.action.CreatePackageAction;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.action.DeleteNamespaceAction;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.action.EditNamespaceAction;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.action.OpenActivationViewAction;
+
+import java.util.List;
+import java.util.Optional;
+
+public class WhiskNamespaceGroup extends DefaultActionGroup {
+
+    public WhiskNamespaceGroup(WhiskAuth whiskAuth, WhiskNamespace namespace, List<WhiskEndpoint> endpoints) {
+        add(new EditNamespaceAction(whiskAuth, namespace));
+        add(new DeleteNamespaceAction(namespace));
+        add(new CreatePackageAction(whiskAuth));
+        add(new OpenActivationViewAction(endpoints, Optional.of(whiskAuth), Optional.empty(), Optional.empty()));
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/actiongroup/WhiskPackageGroup.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/actiongroup/WhiskPackageGroup.java
new file mode 100644
index 0000000..3a700fc
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/actiongroup/WhiskPackageGroup.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.explorer.toolwindow.actiongroup;
+
+import com.intellij.openapi.actionSystem.DefaultActionGroup;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.pkg.WhiskPackage;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.action.DeletePackageAction;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.action.EditPackageAction;
+
+public class WhiskPackageGroup extends DefaultActionGroup {
+
+    public WhiskPackageGroup(WhiskAuth whiskAuth, WhiskPackage whiskPackage) {
+        add(new EditPackageAction(whiskAuth, whiskPackage));
+        add(new DeletePackageAction(whiskAuth, whiskPackage));
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/actiongroup/WhiskTriggerGroup.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/actiongroup/WhiskTriggerGroup.java
new file mode 100644
index 0000000..90ea4ad
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/actiongroup/WhiskTriggerGroup.java
@@ -0,0 +1,39 @@
+/**
+ * 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.toolwindow.actiongroup;
+
+import com.intellij.openapi.actionSystem.DefaultActionGroup;
+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.trigger.WhiskTriggerMetaData;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.action.DeleteTriggerAction;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.action.EditTriggerAction;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.action.OpenActivationViewAction;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.action.OpenAndFireTriggerAction;
+
+import java.util.List;
+import java.util.Optional;
+
+public class WhiskTriggerGroup extends DefaultActionGroup {
+
+    public WhiskTriggerGroup(WhiskAuth whiskAuth, WhiskTriggerMetaData whiskTriggerMetaData, List<WhiskEndpoint> endpoints) {
+        add(new OpenAndFireTriggerAction(whiskAuth, whiskTriggerMetaData));
+        add(new OpenActivationViewAction(endpoints, Optional.of(whiskAuth), Optional.empty(), Optional.of(whiskTriggerMetaData)));
+        add(new EditTriggerAction(whiskAuth, whiskTriggerMetaData));
+        add(new DeleteTriggerAction(whiskAuth, whiskTriggerMetaData));
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/actiongroup/WhiskTriggerRootGroup.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/actiongroup/WhiskTriggerRootGroup.java
new file mode 100644
index 0000000..f825760
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/actiongroup/WhiskTriggerRootGroup.java
@@ -0,0 +1,28 @@
+/**
+ * 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.toolwindow.actiongroup;
+
+import com.intellij.openapi.actionSystem.DefaultActionGroup;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.action.CreateTriggerAction;
+
+public class WhiskTriggerRootGroup extends DefaultActionGroup {
+
+    public WhiskTriggerRootGroup(WhiskAuth whiskAuth) {
+        add(new CreateTriggerAction(whiskAuth));
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/listener/OpenActionControlActionListener.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/listener/OpenActionControlActionListener.java
new file mode 100644
index 0000000..13971b7
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/listener/OpenActionControlActionListener.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.explorer.toolwindow.listener;
+
+import com.intellij.util.messages.Topic;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.ExecutableWhiskAction;
+
+import java.util.EventListener;
+
+public interface OpenActionControlActionListener extends EventListener {
+
+    Topic<OpenActionControlActionListener> TOPIC = Topic.create("Open Action Control", OpenActionControlActionListener.class);
+
+    void openActionControlWindow(WhiskAuth whiskAuth, ExecutableWhiskAction executableWhiskAction);
+
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/listener/OpenTriggerControlActionListener.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/listener/OpenTriggerControlActionListener.java
new file mode 100644
index 0000000..0eb73e3
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/listener/OpenTriggerControlActionListener.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.explorer.toolwindow.listener;
+
+import com.intellij.util.messages.Topic;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.ExecutableWhiskTrigger;
+
+import java.util.EventListener;
+
+public interface OpenTriggerControlActionListener extends EventListener {
+
+    Topic<OpenTriggerControlActionListener> TOPIC = Topic.create("Open Trigger Control", OpenTriggerControlActionListener.class);
+
+    void openTriggerControlWindow(WhiskAuth whiskAuth, ExecutableWhiskTrigger executableWhiskTrigger);
+
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/listener/RefreshWhiskTreeListener.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/listener/RefreshWhiskTreeListener.java
new file mode 100644
index 0000000..1a015fc
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/listener/RefreshWhiskTreeListener.java
@@ -0,0 +1,27 @@
+/**
+ * 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.toolwindow.listener;
+
+import com.intellij.util.messages.Topic;
+
+import java.util.EventListener;
+
+public interface RefreshWhiskTreeListener extends EventListener {
+    Topic<RefreshWhiskTreeListener> TOPIC = Topic.create("Refresh Whisk Tree", RefreshWhiskTreeListener.class);
+
+    void refreshWhiskTree();
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/tree/WhiskTree.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/tree/WhiskTree.java
new file mode 100644
index 0000000..5cd3eb9
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/tree/WhiskTree.java
@@ -0,0 +1,178 @@
+/**
+ * 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.toolwindow.tree;
+
+import com.intellij.notification.NotificationType;
+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 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 com.navercorp.openwhisk.intellij.common.whisk.model.trigger.WhiskTriggerRoot;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskPackageService;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+
+import javax.swing.event.TreeModelListener;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.DefaultTreeModel;
+import javax.swing.tree.TreeModel;
+import javax.swing.tree.TreePath;
+import java.io.IOException;
+import java.util.List;
+import java.util.Optional;
+
+public class WhiskTree implements TreeModel {
+    private static final Logger LOG = Logger.getInstance(WhiskTree.class);
+    private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+    private DefaultTreeModel innerModel;
+    private DefaultMutableTreeNode root;
+    private WhiskPackageService whiskPackageService;
+
+    public WhiskTree(List<WhiskEndpoint> endpoints, WhiskPackageService whiskPackageService) {
+        this.whiskPackageService = whiskPackageService;
+        this.setTree(endpoints);
+    }
+
+    public void setTree(List<WhiskEndpoint> endpoints) {
+        this.root = new DefaultMutableTreeNode("root");
+
+        // endpoints
+        for (WhiskEndpoint e : endpoints) {
+            DefaultMutableTreeNode endPointNode = new DefaultMutableTreeNode(e);
+
+            // namespaces
+            for (WhiskNamespace ns : e.getNamespaces()) {
+                DefaultMutableTreeNode namespaceNode = new DefaultMutableTreeNode(ns);
+                endPointNode.add(namespaceNode);
+
+                //triggers
+                DefaultMutableTreeNode triggerRootNode = new DefaultMutableTreeNode(new WhiskTriggerRoot());
+                namespaceNode.add(triggerRootNode);
+                List<WhiskTriggerMetaData> triggers = ns.getTriggers();
+                for (WhiskTriggerMetaData t : triggers) {
+                    DefaultMutableTreeNode triggerNode = new DefaultMutableTreeNode(t);
+                    triggerRootNode.add(triggerNode);
+                }
+
+                // packages
+                List<WhiskPackage> packages = ns.getPackages();
+                for (WhiskPackage p : packages) {
+                    DefaultMutableTreeNode pkgNode = new DefaultMutableTreeNode(p);
+                    namespaceNode.add(pkgNode);
+
+                    // Add origin action of binding package to tree
+                    p.getBinding().ifPresent(binding -> {
+                        try {
+                            whiskPackageService.getWhiskPackage(new WhiskAuth(ns.getAuth(), e.getApihost()), binding.getNamespace(), binding.getName())
+                                    .ifPresent(packageWithActions ->
+                                            packageWithActions.getActions()
+                                                    .forEach(action -> {
+                                                        DefaultMutableTreeNode actionNode = new DefaultMutableTreeNode(action);
+                                                        pkgNode.add(actionNode);
+                                                    }));
+                        } catch (IOException ex) {
+                            final String msg = binding.getNamespace() + "/" + binding.getName() + " package information cannot be retrieved.";
+                            LOG.error(msg, ex);
+                            NOTIFIER.notify(msg, NotificationType.ERROR);
+                            ex.printStackTrace();
+                        }
+                    });
+                }
+
+                // actions
+                List<WhiskActionMetaData> actions = ns.getActions();
+                for (WhiskActionMetaData a : actions) {
+                    DefaultMutableTreeNode actionNode = new DefaultMutableTreeNode(a);
+                    if (a.getWhiskPackage().isPresent()) {
+                        String pkgPath = a.getWhiskPackage().get();
+                        Optional<DefaultMutableTreeNode> pkgNode = findPackageNode(namespaceNode, pkgPath);
+                        if (pkgNode.isPresent()) {
+                            pkgNode.get().add(actionNode);
+                        } else {
+                            namespaceNode.add(actionNode);
+                        }
+                    } else {
+                        namespaceNode.add(actionNode);
+                    }
+                }
+            }
+            this.root.add(endPointNode);
+        }
+
+        this.innerModel = new DefaultTreeModel(root);
+    }
+
+    // Search 1 depth
+    private Optional<DefaultMutableTreeNode> findPackageNode(DefaultMutableTreeNode ns, String pkgPath) {
+        for (int i = 0; i < ns.getChildCount(); i++) {
+            DefaultMutableTreeNode children = (DefaultMutableTreeNode) ns.getChildAt(i);
+            if (children.getUserObject() instanceof WhiskPackage) {
+                WhiskPackage existPkg = (WhiskPackage) children.getUserObject();
+                if (pkgPath.equals(existPkg.getName())) {
+                    return Optional.of(children);
+                }
+            }
+        }
+        return Optional.empty();
+    }
+
+    @Override
+    public Object getRoot() {
+        return this.innerModel.getRoot();
+    }
+
+    @Override
+    public Object getChild(Object parent, int index) {
+        return this.innerModel.getChild(parent, index);
+    }
+
+    @Override
+    public int getChildCount(Object parent) {
+        return this.innerModel.getChildCount(parent);
+    }
+
+    @Override
+    public boolean isLeaf(Object node) {
+        return this.innerModel.isLeaf(node);
+    }
+
+    @Override
+    public void valueForPathChanged(TreePath path, Object newValue) {
+        this.innerModel.valueForPathChanged(path, newValue);
+
+    }
+
+    @Override
+    public int getIndexOfChild(Object parent, Object child) {
+        return this.innerModel.getIndexOfChild(parent, child);
+    }
+
+    @Override
+    public void addTreeModelListener(TreeModelListener l) {
+        this.innerModel.addTreeModelListener(l);
+
+    }
+
+    @Override
+    public void removeTreeModelListener(TreeModelListener l) {
+        this.innerModel.removeTreeModelListener(l);
+
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/tree/WhiskTreeCellRenderer.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/tree/WhiskTreeCellRenderer.java
new file mode 100644
index 0000000..f910a9f
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/tree/WhiskTreeCellRenderer.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.explorer.toolwindow.tree;
+
+import com.intellij.ui.ColoredTreeCellRenderer;
+import com.navercorp.openwhisk.intellij.common.Icons;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskEndpoint;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskNamespace;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.CompactWhiskAction;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.WhiskAction;
+import com.navercorp.openwhisk.intellij.common.whisk.model.pkg.WhiskPackage;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.WhiskTriggerMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.WhiskTriggerRoot;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import javax.swing.tree.DefaultMutableTreeNode;
+
+import static com.intellij.icons.AllIcons.Modules.SourceRoot;
+import static com.intellij.icons.AllIcons.Nodes.Module;
+import static com.intellij.icons.AllIcons.Webreferences.Server;
+
+public class WhiskTreeCellRenderer extends ColoredTreeCellRenderer {
+    @Override
+    public void customizeCellRenderer(@NotNull JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
+        DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) value;
+        Object userObject = treeNode.getUserObject();
+        if (userObject instanceof WhiskEndpoint) {
+            WhiskEndpoint whiskEndpoint = (WhiskEndpoint) userObject;
+            setIcon(Server);
+            append(whiskEndpoint.getAlias() + " (" + whiskEndpoint.getApihost() + ")");
+        } else if (userObject instanceof WhiskNamespace) {
+            WhiskNamespace whiskNamespace = (WhiskNamespace) userObject;
+            setIcon(SourceRoot);
+            append(whiskNamespace.getPath());
+        } else if (userObject instanceof WhiskPackage) {
+            WhiskPackage whiskPackage = (WhiskPackage) userObject;
+            setIcon(Module);
+            String boundMark = whiskPackage.getBinding().map(b -> " (from " + b.getNamespace() + "/" + b.getName() + ")").orElse("");
+            append(whiskPackage.getName() + boundMark);
+        } else if (userObject instanceof WhiskAction) {
+            WhiskAction whiskAction = (WhiskAction) userObject;
+            setIcon(whiskAction.getKindIcon());
+            append(whiskAction.getName() + whiskAction.getKindExtension());
+        } else if (userObject instanceof CompactWhiskAction) {
+            CompactWhiskAction compactWhiskAction = (CompactWhiskAction) userObject;
+            setIcon(compactWhiskAction.getKindIcon());
+            append(compactWhiskAction.getName() + compactWhiskAction.getKindExtension());
+        } else if (userObject instanceof WhiskTriggerRoot) {
+            append("Triggers");
+            setIcon(Icons.ENTITY_TRIGGER_ROOT);
+        } else if (userObject instanceof WhiskTriggerMetaData) {
+            WhiskTriggerMetaData whiskTriggerMetaData = (WhiskTriggerMetaData) userObject;
+            setIcon(Icons.ENTITY_TRIGGER);
+            append(whiskTriggerMetaData.getName());
+        } else {
+            append(userObject.toString());
+        }
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/ui/WhiskExplorerWindowForm.form b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/ui/WhiskExplorerWindowForm.form
new file mode 100644
index 0000000..28ee2c3
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/ui/WhiskExplorerWindowForm.form
@@ -0,0 +1,29 @@
+<?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.toolwindow.ui.WhiskExplorerWindowForm">
+  <grid id="27dc6" binding="mainJPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
+    <constraints>
+      <xy x="20" y="20" width="500" height="400"/>
+    </constraints>
+    <properties/>
+    <border type="none"/>
+    <children>
+      <grid id="9f9d2" binding="actionsJPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
+        <constraints border-constraint="North"/>
+        <properties/>
+        <border type="none"/>
+        <children/>
+      </grid>
+      <scrollpane id="4961f" binding="contentsJPanel">
+        <constraints border-constraint="Center"/>
+        <properties/>
+        <border type="none"/>
+        <children>
+          <component id="7b9b9" class="javax.swing.JTree" binding="whiskJTree">
+            <constraints/>
+            <properties/>
+          </component>
+        </children>
+      </scrollpane>
+    </children>
+  </grid>
+</form>
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/ui/WhiskExplorerWindowForm.java b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/ui/WhiskExplorerWindowForm.java
new file mode 100644
index 0000000..0ecf27e
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/explorer/toolwindow/ui/WhiskExplorerWindowForm.java
@@ -0,0 +1,462 @@
+/**
+ * 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.toolwindow.ui;
+
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.actionSystem.ActionGroup;
+import com.intellij.openapi.actionSystem.ActionManager;
+import com.intellij.openapi.actionSystem.ActionPopupMenu;
+import com.intellij.openapi.actionSystem.ActionToolbar;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.fileEditor.FileEditorManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.wm.ToolWindow;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import com.navercorp.openwhisk.intellij.common.service.WhiskService;
+import com.navercorp.openwhisk.intellij.common.utils.EventUtils;
+import com.navercorp.openwhisk.intellij.common.utils.FileUtils;
+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.WhiskEndpoint;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskNamespace;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.CompactWhiskAction;
+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.pkg.WhiskPackage;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.WhiskTriggerMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.WhiskTriggerRoot;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskActionService;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskPackageService;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskTriggerService;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.actiongroup.*;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.listener.OpenActionControlActionListener;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.listener.OpenTriggerControlActionListener;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.listener.RefreshWhiskTreeListener;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.tree.WhiskTree;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.tree.WhiskTreeCellRenderer;
+import org.apache.commons.lang.StringUtils;
+
+import javax.swing.*;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.TreeNode;
+import javax.swing.tree.TreePath;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Optional;
+
+public class WhiskExplorerWindowForm {
+    private static final Logger LOG = Logger.getInstance(WhiskExplorerWindowForm.class);
+    private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+    private JPanel mainJPanel;
+    private JPanel actionsJPanel;
+    private JScrollPane contentsJPanel;
+    private JTree whiskJTree;
+
+    private FileEditorManager fileEditorManager;
+    private Project project;
+
+    private List<WhiskEndpoint> endpoints = new ArrayList<>();
+
+    public WhiskExplorerWindowForm(Project project, ToolWindow toolWindow) {
+        this.project = project;
+        this.fileEditorManager = FileEditorManager.getInstance(project);
+
+        String actionGroupName = "WhiskExplorer.Actions.Controls";
+        ActionManager actionManager = ActionManager.getInstance();
+        ActionGroup actionGroup = (ActionGroup) actionManager.getAction(actionGroupName);
+        ActionToolbar actionToolbar = actionManager.createActionToolbar("", actionGroup, true);
+
+        actionToolbar.setTargetComponent(actionsJPanel);
+        actionsJPanel.add(actionToolbar.getComponent());
+
+        WhiskService service = ServiceManager.getService(project, WhiskService.class);
+        WhiskActionService whiskActionService = WhiskActionService.getInstance();
+        WhiskPackageService whiskPackageService = WhiskPackageService.getInstance();
+        WhiskTriggerService whiskTriggerService = WhiskTriggerService.getInstance();
+
+        setWhiskTree(service, whiskActionService, whiskPackageService, whiskTriggerService);
+    }
+
+    private void setWhiskTree(WhiskService service,
+                              WhiskActionService whiskActionService,
+                              WhiskPackageService whiskPackageService,
+                              WhiskTriggerService whiskTriggerService) {
+
+        if (StringUtils.isNotEmpty(service.getEndpoints())) {
+            try {
+                endpoints = getEntities(whiskPackageService,
+                        whiskActionService,
+                        whiskTriggerService,
+                        JsonParserUtils.parseWhiskEndpoints(service.getEndpoints()));
+            } catch (IOException e) {
+                final String msg = "Failed to parsing endpoints: " + service.getEndpoints();
+                LOG.error(msg, e);
+                NOTIFIER.notify(project, msg, NotificationType.ERROR);
+            }
+        }
+
+        whiskJTree.setModel(new WhiskTree(endpoints, whiskPackageService));
+        whiskJTree.setRootVisible(false);
+        whiskJTree.setCellRenderer(new WhiskTreeCellRenderer());
+        expandToNamespace(whiskJTree);
+        whiskJTree.addMouseListener(new MouseAdapter() {
+            @Override
+            public void mouseClicked(MouseEvent e) {
+                if (e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() > 1) {
+                    if (whiskJTree == null) {
+                        return;
+                    }
+
+                    DefaultMutableTreeNode node = (DefaultMutableTreeNode) whiskJTree.getLastSelectedPathComponent();
+                    if (node == null) {
+                        return;
+                    }
+
+                    Object userObject = node.getUserObject();
+                    if (userObject instanceof WhiskEndpoint) {
+                        WhiskEndpoint whiskEndpoint = (WhiskEndpoint) userObject;
+                        LOG.info(whiskEndpoint.getAlias());
+                    } else if (userObject instanceof WhiskNamespace) {
+                        WhiskNamespace whiskNamespace = (WhiskNamespace) userObject;
+                        LOG.info(whiskNamespace.getPath());
+                    } else if (userObject instanceof WhiskPackage) {
+                        WhiskPackage whiskPackage = (WhiskPackage) userObject;
+                        LOG.info(whiskPackage.getName());
+                    } else if (userObject instanceof WhiskActionMetaData) {
+                        WhiskActionMetaData whiskAction = (WhiskActionMetaData) userObject;
+                        LOG.info(whiskAction.getName());
+
+                        DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) node.getParent();
+                        if (parentNode.getUserObject() instanceof WhiskPackage) {
+                            // TODO refactor getAuthFromTreeNode method
+                            WhiskPackage pkg = (WhiskPackage) parentNode.getUserObject();
+
+                            DefaultMutableTreeNode namespaceNode = (DefaultMutableTreeNode) parentNode.getParent();
+                            WhiskNamespace namespace = (WhiskNamespace) namespaceNode.getUserObject();
+
+                            DefaultMutableTreeNode endpointNode = (DefaultMutableTreeNode) namespaceNode.getParent();
+                            WhiskEndpoint endpoint = (WhiskEndpoint) endpointNode.getUserObject();
+
+                            WhiskAuth auth = new WhiskAuth(namespace.getAuth(), endpoint.getApihost());
+
+                            try {
+                                whiskActionService.getWhiskAction(auth,
+                                        Optional.ofNullable(namespace.getPath()),
+                                        Optional.ofNullable(pkg.getName()), whiskAction.getName())
+                                        .ifPresent(executableWhiskAction -> openEditorAndWhiskRunWindow(auth, executableWhiskAction));
+                            } catch (IOException e1) {
+                                final String msg1 = "The action cannot be opened.";
+                                LOG.error(msg1, e1);
+                                NOTIFIER.notify(project, msg1, NotificationType.ERROR);
+                            }
+                        } else if (parentNode.getUserObject() instanceof WhiskNamespace) {
+                            WhiskNamespace namespace = (WhiskNamespace) parentNode.getUserObject();
+
+                            DefaultMutableTreeNode endpointNode = (DefaultMutableTreeNode) parentNode.getParent();
+                            WhiskEndpoint endpoint = (WhiskEndpoint) endpointNode.getUserObject();
+
+                            WhiskAuth auth = new WhiskAuth(namespace.getAuth(), endpoint.getApihost());
+
+                            try {
+                                whiskActionService.getWhiskAction(auth, Optional.ofNullable(namespace.getPath()), Optional.empty(), whiskAction.getName())
+                                        .ifPresent(executableWhiskAction -> openEditorAndWhiskRunWindow(auth, executableWhiskAction));
+                            } catch (IOException e1) {
+                                final String msg1 = "The action cannot be opened.";
+                                LOG.error(msg1, e1);
+                                NOTIFIER.notify(project, msg1, NotificationType.ERROR);
+                            }
+
+                        } else {
+                            LOG.error("[WhiskActionMetaData] The action cannot be loaded: " + whiskAction.getFullyQualifiedName());
+                        }
+                    } else if (userObject instanceof CompactWhiskAction) {
+                        CompactWhiskAction action = (CompactWhiskAction) userObject;
+                        LOG.info(action.getName());
+
+                        DefaultMutableTreeNode boundPkgNode = (DefaultMutableTreeNode) node.getParent();
+                        if (boundPkgNode.getUserObject() instanceof WhiskPackage) {
+                            WhiskPackage boundPkg = (WhiskPackage) boundPkgNode.getUserObject();
+
+                            DefaultMutableTreeNode namespaceNode = (DefaultMutableTreeNode) boundPkgNode.getParent();
+                            WhiskNamespace namespace = (WhiskNamespace) namespaceNode.getUserObject();
+
+                            DefaultMutableTreeNode endpointNode = (DefaultMutableTreeNode) namespaceNode.getParent();
+                            WhiskEndpoint endpoint = (WhiskEndpoint) endpointNode.getUserObject();
+
+                            WhiskAuth auth = new WhiskAuth(namespace.getAuth(), endpoint.getApihost());
+
+                            boundPkg.getBinding().ifPresent(p -> {
+                                try {
+                                    //When clicked, the code is fetched from the remote server.
+                                    whiskActionService.getWhiskAction(auth,
+                                            Optional.ofNullable(p.getNamespace()),
+                                            Optional.ofNullable(p.getName()), action.getName())
+                                            .ifPresent(executableWhiskAction -> openEditorAndWhiskRunWindow(auth, executableWhiskAction));
+                                } catch (IOException e1) {
+                                    final String msg1 = "The action cannot be opened.";
+                                    LOG.error(msg1, e1);
+                                    NOTIFIER.notify(project, msg1, NotificationType.ERROR);
+                                }
+
+                            });
+                        } else {
+                            LOG.error("[CompactWhiskAction] The action cannot be loaded: " + action.getName());
+                        }
+                    } else if (userObject instanceof WhiskTriggerRoot) {
+                        // Nothing to do
+                        LOG.debug("[WhiskTriggerRoot] is selected");
+                    } else if (userObject instanceof WhiskTriggerMetaData) {
+                        DefaultMutableTreeNode namespaceNode = (DefaultMutableTreeNode) node.getParent().getParent();
+
+                        WhiskNamespace namespace = (WhiskNamespace) namespaceNode.getUserObject();
+
+                        DefaultMutableTreeNode endpointNode = (DefaultMutableTreeNode) namespaceNode.getParent();
+                        WhiskEndpoint endpoint = (WhiskEndpoint) endpointNode.getUserObject();
+
+                        WhiskAuth auth = new WhiskAuth(namespace.getAuth(), endpoint.getApihost());
+
+                        WhiskTriggerMetaData whiskTriggerMetaData = (WhiskTriggerMetaData) userObject;
+                        LOG.info(whiskTriggerMetaData.getName());
+                        try {
+                            whiskTriggerService.getWhiskTrigger(auth, whiskTriggerMetaData.getName()).ifPresent(executableWhiskTrigger ->
+                                    EventUtils.publish(project,
+                                            OpenTriggerControlActionListener.TOPIC,
+                                            (l) -> l.openTriggerControlWindow(auth, executableWhiskTrigger)));
+                        } catch (IOException ex) {
+                            final String msg = "The trigger cannot be loaded: " + whiskTriggerMetaData.getName();
+                            LOG.error(msg, ex);
+                            NOTIFIER.notify(project, msg, NotificationType.ERROR);
+                        }
+                    } else {
+                        LOG.error("The unknown object of tree: " + userObject.toString());
+                    }
+                }
+            }
+
+            @Override
+            public void mouseReleased(MouseEvent e) {
+                if (e.getButton() == MouseEvent.BUTTON3) {
+                    if (whiskJTree == null) {
+                        return;
+                    }
+
+                    TreePath path = whiskJTree.getPathForLocation(e.getX(), e.getY());
+                    if (path == null) {
+                        return;
+                    }
+
+                    whiskJTree.setSelectionPath(path);
+                    DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
+                    if (node.getUserObject() instanceof WhiskEndpoint) {
+                        WhiskEndpoint whiskEndpoint = (WhiskEndpoint) node.getUserObject();
+                        ActionGroup actionGroup = new WhiskEndpointGroup(whiskEndpoint);
+                        ActionPopupMenu actionPopupMenu = ActionManager.getInstance().createActionPopupMenu("", actionGroup);
+                        actionPopupMenu.getComponent().show(whiskJTree, e.getX(), e.getY());
+                    } else if (node.getUserObject() instanceof WhiskNamespace) {
+                        getAuthFromTreeNode(node).ifPresent(auth -> {
+                            WhiskNamespace whiskNamespace = (WhiskNamespace) node.getUserObject();
+                            ActionGroup actionGroup = new WhiskNamespaceGroup(auth, whiskNamespace, endpoints);
+                            ActionPopupMenu actionPopupMenu = ActionManager.getInstance().createActionPopupMenu("", actionGroup);
+                            actionPopupMenu.getComponent().show(whiskJTree, e.getX(), e.getY());
+                        });
+                    } else if (node.getUserObject() instanceof WhiskActionMetaData) {
+                        WhiskActionMetaData whiskActionMetaData = (WhiskActionMetaData) node.getUserObject();
+                        getAuthFromTreeNode(node).ifPresent(auth -> {
+                            ActionGroup actionGroup = new WhiskActionGroup(auth, whiskActionMetaData, endpoints);
+                            ActionPopupMenu actionPopupMenu = ActionManager.getInstance().createActionPopupMenu("", actionGroup);
+                            actionPopupMenu.getComponent().show(whiskJTree, e.getX(), e.getY());
+                        });
+                    } else if (node.getUserObject() instanceof CompactWhiskAction) {
+                        CompactWhiskAction compactWhiskAction = (CompactWhiskAction) node.getUserObject();
+                        WhiskPackage pkg = (WhiskPackage) ((DefaultMutableTreeNode) node.getParent()).getUserObject();
+                        WhiskNamespace ns = (WhiskNamespace) ((DefaultMutableTreeNode) node.getParent().getParent()).getUserObject();
+
+                        getAuthFromTreeNode(node).ifPresent(auth -> {
+                            try {
+                                whiskActionService.getWhiskAction(auth, Optional.of(ns.getPath()), Optional.of(pkg.getName()), compactWhiskAction.getName())
+                                        .ifPresent(action -> {
+                                            ActionGroup actionGroup = new WhiskActionGroup(auth, toBindingWhiskActionMetaData(pkg, action), endpoints);
+                                            ActionPopupMenu actionPopupMenu = ActionManager.getInstance().createActionPopupMenu("", actionGroup);
+                                            actionPopupMenu.getComponent().show(whiskJTree, e.getX(), e.getY());
+                                        });
+                            } catch (IOException ex) {
+                                final String msg = "The action cannot be loaded.";
+                                LOG.error(msg, ex);
+                                NOTIFIER.notify(project, msg, NotificationType.ERROR);
+                            }
+                        });
+                    } else if (node.getUserObject() instanceof WhiskTriggerMetaData) {
+                        WhiskTriggerMetaData whiskTriggerMetaData = (WhiskTriggerMetaData) node.getUserObject();
+                        getAuthFromTreeNode(node).ifPresent(auth -> {
+                            ActionGroup actionGroup = new WhiskTriggerGroup(auth, whiskTriggerMetaData, endpoints);
+                            ActionPopupMenu actionPopupMenu = ActionManager.getInstance().createActionPopupMenu("", actionGroup);
+                            actionPopupMenu.getComponent().show(whiskJTree, e.getX(), e.getY());
+                        });
+                    } else if (node.getUserObject() instanceof WhiskTriggerRoot) {
+                        getAuthFromTreeNode(node).ifPresent(auth -> {
+                            ActionGroup actionGroup = new WhiskTriggerRootGroup(auth);
+                            ActionPopupMenu actionPopupMenu = ActionManager.getInstance().createActionPopupMenu("", actionGroup);
+                            actionPopupMenu.getComponent().show(whiskJTree, e.getX(), e.getY());
+                        });
+                    } else if (node.getUserObject() instanceof WhiskPackage) {
+                        getAuthFromTreeNode(node).ifPresent(auth -> {
+                            WhiskPackage whiskPackage = (WhiskPackage) node.getUserObject();
+                            ActionGroup actionGroup = new WhiskPackageGroup(auth, whiskPackage);
+                            ActionPopupMenu actionPopupMenu = ActionManager.getInstance().createActionPopupMenu("", actionGroup);
+                            actionPopupMenu.getComponent().show(whiskJTree, e.getX(), e.getY());
+                        });
+                    }
+                }
+            }
+        });
+
+        EventUtils.subscribe(project, project, RefreshWhiskTreeListener.TOPIC, () -> {
+            if (whiskJTree != null) {
+                try {
+                    List<WhiskEndpoint> whiskEndpoints = JsonParserUtils.parseWhiskEndpoints(service.getEndpoints());
+                    endpoints = getEntities(whiskPackageService, whiskActionService, whiskTriggerService, whiskEndpoints);
+                    whiskJTree.setModel(new WhiskTree(endpoints, whiskPackageService));
+                    expandToNamespace(whiskJTree);
+
+                    if (whiskEndpoints.isEmpty()) {
+                        final String msg = "There are no endpoints saved.";
+                        LOG.info(msg);
+                        NOTIFIER.notify(project, msg, NotificationType.INFORMATION);
+                    }
+                } catch (IOException e) {
+                    final String msg = "Failed to parsing endpoints: " + service.getEndpoints();
+                    LOG.error(msg, e);
+                    NOTIFIER.notify(project, msg, NotificationType.ERROR);
+                }
+            }
+        });
+    }
+
+    private WhiskActionMetaData toBindingWhiskActionMetaData(WhiskPackage whiskPackage, ExecutableWhiskAction action) {
+        return new WhiskActionMetaData(action.getName(),
+                whiskPackage.getNamespace() + "/" + whiskPackage.getName(),
+                action.getVersion(),
+                action.getUpdated(),
+                action.isPublish(),
+                action.getAnnotations(),
+                action.getLimits(),
+                action.getExec().toExecMetaData());
+    }
+
+
+    private void expandToNamespace(JTree tree) {
+        final Enumeration<?> topLevelNodes = ((TreeNode) tree.getModel().getRoot()).children();
+        final int level = 2;  // The level is the distance from the root to this node.
+        expandNode(tree, topLevelNodes, level);
+    }
+
+    private void expandNode(JTree tree, Enumeration<?> nodes, int level) {
+        while (nodes.hasMoreElements()) {
+            DefaultMutableTreeNode node = (DefaultMutableTreeNode) nodes.nextElement();
+            tree.expandPath(new TreePath(node.getPath()));
+            if (node.getLevel() < level) {
+                expandNode(tree, node.children(), level);
+            }
+        }
+    }
+
+    private void openEditorAndWhiskRunWindow(WhiskAuth auth, ExecutableWhiskAction executableWhiskAction) {
+        try {
+            final String tmpFilePath = project.getBasePath() + "/.idea/openwhisk";
+
+            if (!executableWhiskAction.isSequenceAction()) {
+                /**
+                 * Open Editor
+                 */
+                VirtualFile actionFile = FileUtils.writeActionToFile(tmpFilePath, executableWhiskAction);
+                fileEditorManager.openFile(actionFile, true);
+            }
+
+            /**
+             * Open Whisk Run Window
+             */
+            EventUtils.publish(project, OpenActionControlActionListener.TOPIC, (l) -> l.openActionControlWindow(auth, executableWhiskAction));
+        } catch (IOException e1) {
+            final String msg1 = "The action cannot be opened.";
+            LOG.error(msg1, e1);
+            NOTIFIER.notify(project, msg1, NotificationType.ERROR);
+        }
+
+    }
+
+    private Optional<WhiskAuth> getAuthFromTreeNode(DefaultMutableTreeNode node) {
+        try {
+            DefaultMutableTreeNode parent = (DefaultMutableTreeNode) node.getParent();
+            if (node.getUserObject() instanceof WhiskNamespace
+                    && parent.getUserObject() instanceof WhiskEndpoint) {
+                WhiskNamespace namespace = (WhiskNamespace) node.getUserObject();
+                WhiskEndpoint endpoint = (WhiskEndpoint) parent.getUserObject();
+                return Optional.of(new WhiskAuth(namespace.getAuth(), endpoint.getApihost()));
+            } else if (node.getUserObject() instanceof WhiskActionMetaData
+                    || node.getUserObject() instanceof CompactWhiskAction
+                    || node.getUserObject() instanceof WhiskTriggerMetaData
+                    || node.getUserObject() instanceof WhiskTriggerRoot
+                    || node.getUserObject() instanceof WhiskPackage
+            ) {
+                return getAuthFromTreeNode(parent);
+            }
+        } catch (Exception e) {
+            LOG.error(e);
+            return Optional.empty();
+        }
+        return Optional.empty();
+    }
+
+    private List<WhiskEndpoint> getEntities(WhiskPackageService whiskPackageService, WhiskActionService
+            whiskActionService, WhiskTriggerService whiskTriggerService, List<WhiskEndpoint> whiskEndpoints) {
+        List<WhiskEndpoint> newWhiskEndpoints = new ArrayList<>();
+        for (WhiskEndpoint ep : whiskEndpoints) {
+            List<WhiskNamespace> newNamespaces = new ArrayList<>();
+            for (WhiskNamespace ns : ep.getNamespaces()) {
+                try {
+                    WhiskAuth auth = new WhiskAuth(ns.getAuth(), ep.getApihost());
+                    // get pkg, action
+                    ns.setPackages(whiskPackageService.getWhiskPackages(auth));
+                    ns.setActions(whiskActionService.getWhiskActions(auth));
+                    ns.setTriggers(whiskTriggerService.getWhiskTriggers(auth));
+
+                    newNamespaces.add(ns);
+                } catch (IOException e) {
+                    final String msg = ns.getPath() + " entities cannot be loaded.";
+                    LOG.error(msg, e);
+                    NOTIFIER.notify(project, msg, NotificationType.ERROR);
+                }
+            }
+            ep.setNamespaces(newNamespaces);
+            newWhiskEndpoints.add(ep);
+        }
+        return newWhiskEndpoints;
+    }
+
+    public JPanel getContent() {
+        return mainJPanel;
+    }
+
+}
+
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/WhiskRunWindowFactory.java b/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/WhiskRunWindowFactory.java
new file mode 100644
index 0000000..8dad293
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/WhiskRunWindowFactory.java
@@ -0,0 +1,173 @@
+/**
+ * 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.run.toolwindow;
+
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.wm.ToolWindow;
+import com.intellij.openapi.wm.ToolWindowFactory;
+import com.intellij.openapi.wm.ToolWindowManager;
+import com.intellij.ui.content.Content;
+import com.intellij.ui.content.ContentFactory;
+import com.navercorp.openwhisk.intellij.common.utils.EventUtils;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskActionService;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskActivationService;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskTriggerService;
+import com.navercorp.openwhisk.intellij.run.toolwindow.listener.*;
+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.action.ExecutableWhiskAction;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.WhiskActionMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.ExecutableWhiskTrigger;
+import com.navercorp.openwhisk.intellij.explorer.dialog.action.ActionManagerDialog;
+import com.navercorp.openwhisk.intellij.explorer.dialog.trigger.TriggerManagerDialog;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.listener.OpenActionControlActionListener;
+import com.navercorp.openwhisk.intellij.explorer.toolwindow.listener.OpenTriggerControlActionListener;
+import com.navercorp.openwhisk.intellij.run.toolwindow.ui.WhiskRunWindowForm;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.IOException;
+import java.util.List;
+
+import static com.intellij.icons.AllIcons.Debugger.Console;
+import static com.navercorp.openwhisk.intellij.run.toolwindow.ui.WhiskRunWindowForm.ENTITY_ACTION;
+import static com.navercorp.openwhisk.intellij.run.toolwindow.ui.WhiskRunWindowForm.ENTITY_TRIGGER;
+
+public class WhiskRunWindowFactory implements ToolWindowFactory {
+    private static final Logger LOG = Logger.getInstance(WhiskRunWindowFactory.class);
+    private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+    private static final String ID = "Whisk Run";
+
+    private WhiskActionService whiskActionService = WhiskActionService.getInstance();
+    private WhiskActivationService whiskActivationService = WhiskActivationService.getInstance();
+    private WhiskTriggerService whiskTriggerService = WhiskTriggerService.getInstance();
+
+    private WhiskRunWindowForm whiskRunWindowForm;
+
+    @Override
+    public void createToolWindowContent(@NotNull Project project, @NotNull ToolWindow toolWindow) {
+        this.whiskRunWindowForm = new WhiskRunWindowForm(project, toolWindow);
+        ContentFactory contentFactory = ContentFactory.SERVICE.getInstance();
+        Content content = contentFactory.createContent(whiskRunWindowForm.getContent(), null, false);
+        toolWindow.getContentManager().addContent(content);
+
+        toolWindow.setIcon(Console);
+        ToolWindowManager toolWindowManager = ToolWindowManager.getInstance(project);
+        toolWindowManager.getToolWindow(ID).hide(() -> {
+        });
+
+        /**
+         * Receive Action events
+         */
+        EventUtils.subscribe(project, project, OpenActionControlActionListener.TOPIC,
+                (auth, executableWhiskAction) ->
+                        toolWindowManager.getToolWindow(ID).show(() ->
+                                initializeWhiskTabWithAction(auth, executableWhiskAction, false)));
+
+        EventUtils.subscribe(project, project, OpenAndRunActionControlActionListener.TOPIC,
+                (auth, executableWhiskAction) ->
+                        toolWindowManager.getToolWindow(ID).show(() ->
+                                initializeWhiskTabWithAction(auth, executableWhiskAction, true)));
+
+        /**
+         * Receive Trigger events
+         */
+        EventUtils.subscribe(project, project, OpenTriggerControlActionListener.TOPIC, (auth, executableWhiskTrigger) ->
+                toolWindowManager.getToolWindow(ID).show(() ->
+                        initializeWhiskTabWithTrigger(auth, executableWhiskTrigger, false)));
+
+        EventUtils.subscribe(project, project, OpenAndFireTriggerControlActionListener.TOPIC, (auth, executableWhiskTrigger) ->
+                toolWindowManager.getToolWindow(ID).show(() ->
+                        initializeWhiskTabWithTrigger(auth, executableWhiskTrigger, true)));
+
+        /**
+         * Receive Action Toolbar events
+         */
+        EventUtils.subscribe(project, project, RunActionListener.TOPIC, () ->
+                whiskRunWindowForm.getCachedAuth().ifPresent(auth -> {
+                    String entity = whiskRunWindowForm.getCurrentEntity();
+                    if (entity.equals(ENTITY_ACTION)) {
+                        whiskRunWindowForm.getCachedAction().ifPresent(action ->
+                                whiskRunWindowForm.runAction(whiskActionService, auth, action));
+                    } else if (entity.equals(ENTITY_TRIGGER)) {
+                        whiskRunWindowForm.getCachedTrigger().ifPresent(trigger ->
+                                whiskRunWindowForm.fireTrigger(whiskTriggerService, whiskActivationService, auth, trigger));
+                    }
+                }));
+
+        EventUtils.subscribe(project, project, RefreshActionOrTriggerListener.TOPIC, () ->
+                whiskRunWindowForm.getCachedAuth().ifPresent(auth -> {
+                    String entity = whiskRunWindowForm.getCurrentEntity();
+                    if (entity.equals(ENTITY_ACTION)) {
+                        whiskRunWindowForm.getCachedAction().ifPresent(action ->
+                                whiskRunWindowForm.refreshActionMetaData(whiskActionService, auth, action));
+                    } else if (entity.equals(ENTITY_TRIGGER)) {
+                        whiskRunWindowForm.getCachedTrigger().ifPresent(trigger ->
+                                whiskRunWindowForm.refreshTrigger(auth, whiskTriggerService, trigger));
+                    }
+                }));
+
+        EventUtils.subscribe(project, project, OpenActionManagerListener.TOPIC, () ->
+                whiskRunWindowForm.getCachedAuth().ifPresent(auth -> {
+                    try {
+                        List<WhiskActionMetaData> actions = whiskActionService.getWhiskActions(auth);
+                        String entity = whiskRunWindowForm.getCurrentEntity();
+                        if (entity.equals(ENTITY_ACTION)) {
+                            whiskRunWindowForm.getCachedAction().ifPresent(action -> {
+                                ActionManagerDialog dialog = new ActionManagerDialog(project, auth, action, actions);
+                                if (dialog.showAndGet()) {
+                                    LOG.info("ActionManagerDialog is closed");
+                                }
+                            });
+                        } else if (entity.equals(ENTITY_TRIGGER)) {
+                            whiskRunWindowForm.getCachedTrigger().ifPresent(trigger -> {
+                                TriggerManagerDialog dialog = new TriggerManagerDialog(project, auth, trigger, actions);
+                                if (dialog.showAndGet()) {
+                                    LOG.info("TriggerManagerDialog is closed");
+                                }
+                            });
+                        }
+                    } catch (IOException e) {
+                        final String msg = "The action cannot be executed.";
+                        LOG.error(msg, e);
+                        NOTIFIER.notify(project, msg, NotificationType.ERROR);
+                    }
+                }));
+    }
+
+    private void initializeWhiskTabWithAction(WhiskAuth auth, ExecutableWhiskAction executableWhiskAction, boolean isRunAction) {
+        whiskRunWindowForm.cacheAction(executableWhiskAction);
+        whiskRunWindowForm.cacheAuth(auth);
+        whiskRunWindowForm.setTitle(executableWhiskAction.getFullyQualifiedName());
+        whiskRunWindowForm.initializeActionTab(executableWhiskAction);
+        if (isRunAction) {
+            whiskRunWindowForm.runAction(whiskActionService, auth, executableWhiskAction);
+        }
+    }
+
+    private void initializeWhiskTabWithTrigger(WhiskAuth auth, ExecutableWhiskTrigger executableWhiskTrigger, boolean isFireTrigger) {
+        whiskRunWindowForm.cacheAuth(auth);
+        whiskRunWindowForm.cacheTrigger(executableWhiskTrigger);
+        whiskRunWindowForm.setTitle(executableWhiskTrigger.getName());
+        whiskRunWindowForm.initializeTriggerTab(executableWhiskTrigger);
+        if (isFireTrigger) {
+            whiskRunWindowForm.fireTrigger(whiskTriggerService, whiskActivationService, auth, executableWhiskTrigger);
+        }
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/action/OpenActionManagerDialogAction.java b/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/action/OpenActionManagerDialogAction.java
new file mode 100644
index 0000000..5fdce38
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/action/OpenActionManagerDialogAction.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.run.toolwindow.action;
+
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.navercorp.openwhisk.intellij.common.utils.EventUtils;
+import com.navercorp.openwhisk.intellij.run.toolwindow.listener.OpenActionManagerListener;
+import org.jetbrains.annotations.NotNull;
+
+import static com.intellij.icons.AllIcons.Actions.Edit;
+
+public class OpenActionManagerDialogAction extends AnAction {
+
+    public OpenActionManagerDialogAction() {
+        super(Edit);
+    }
+
+    @Override
+    public void actionPerformed(@NotNull AnActionEvent e) {
+        EventUtils.publish(e.getProject(), OpenActionManagerListener.TOPIC, OpenActionManagerListener::openActionManagerDialog);
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/action/RefreshMetaDataAction.java b/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/action/RefreshMetaDataAction.java
new file mode 100644
index 0000000..9bf026c
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/action/RefreshMetaDataAction.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.run.toolwindow.action;
+
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.navercorp.openwhisk.intellij.common.utils.EventUtils;
+import com.navercorp.openwhisk.intellij.run.toolwindow.listener.RefreshActionOrTriggerListener;
+import org.jetbrains.annotations.NotNull;
+
+import static com.intellij.icons.AllIcons.Actions.Refresh;
+
+public class RefreshMetaDataAction extends AnAction {
+
+    public RefreshMetaDataAction() {
+        super(Refresh);
+    }
+
+    @Override
+    public void actionPerformed(@NotNull AnActionEvent e) {
+        EventUtils.publish(e.getProject(), RefreshActionOrTriggerListener.TOPIC, RefreshActionOrTriggerListener::fetchActionMetadata);
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/action/RunActionAction.java b/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/action/RunActionAction.java
new file mode 100644
index 0000000..45469a5
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/action/RunActionAction.java
@@ -0,0 +1,36 @@
+/**
+ * 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.run.toolwindow.action;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.navercorp.openwhisk.intellij.common.utils.EventUtils;
+import com.navercorp.openwhisk.intellij.run.toolwindow.listener.RunActionListener;
+import org.jetbrains.annotations.NotNull;
+
+public class RunActionAction extends AnAction {
+
+    public RunActionAction() {
+        super(AllIcons.Actions.Execute);
+    }
+
+    @Override
+    public void actionPerformed(@NotNull AnActionEvent e) {
+        EventUtils.publish(e.getProject(), RunActionListener.TOPIC, RunActionListener::runAction);
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/listener/OpenActionManagerListener.java b/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/listener/OpenActionManagerListener.java
new file mode 100644
index 0000000..460a68f
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/listener/OpenActionManagerListener.java
@@ -0,0 +1,29 @@
+/**
+ * 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.run.toolwindow.listener;
+
+import com.intellij.util.messages.Topic;
+
+import java.util.EventListener;
+
+public interface OpenActionManagerListener extends EventListener {
+
+    Topic<OpenActionManagerListener> TOPIC = Topic.create("Open Action Manager Dialog", OpenActionManagerListener.class);
+
+    void openActionManagerDialog();
+
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/listener/OpenAndFireTriggerControlActionListener.java b/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/listener/OpenAndFireTriggerControlActionListener.java
new file mode 100644
index 0000000..b7265c2
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/listener/OpenAndFireTriggerControlActionListener.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.run.toolwindow.listener;
+
+import com.intellij.util.messages.Topic;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.ExecutableWhiskTrigger;
+
+import java.util.EventListener;
+
+public interface OpenAndFireTriggerControlActionListener extends EventListener {
+
+    Topic<OpenAndFireTriggerControlActionListener> TOPIC = Topic.create("Open and Fire Trigger Control", OpenAndFireTriggerControlActionListener.class);
+
+    void openAndFireTriggerControlWindow(WhiskAuth whiskAuth, ExecutableWhiskTrigger executableWhiskTrigger);
+
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/listener/OpenAndRunActionControlActionListener.java b/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/listener/OpenAndRunActionControlActionListener.java
new file mode 100644
index 0000000..32e4e07
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/listener/OpenAndRunActionControlActionListener.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.run.toolwindow.listener;
+
+import com.intellij.util.messages.Topic;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.ExecutableWhiskAction;
+
+import java.util.EventListener;
+
+public interface OpenAndRunActionControlActionListener extends EventListener {
+
+    Topic<OpenAndRunActionControlActionListener> TOPIC = Topic.create("Open and Run Action Control", OpenAndRunActionControlActionListener.class);
+
+    void openAndRunActionControlWindow(WhiskAuth whiskAuth, ExecutableWhiskAction executableWhiskAction);
+
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/listener/RefreshActionOrTriggerListener.java b/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/listener/RefreshActionOrTriggerListener.java
new file mode 100644
index 0000000..8274caa
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/listener/RefreshActionOrTriggerListener.java
@@ -0,0 +1,29 @@
+/**
+ * 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.run.toolwindow.listener;
+
+import com.intellij.util.messages.Topic;
+
+import java.util.EventListener;
+
+public interface RefreshActionOrTriggerListener extends EventListener {
+
+    Topic<RefreshActionOrTriggerListener> TOPIC = Topic.create("Refresh action or trigger", RefreshActionOrTriggerListener.class);
+
+    void fetchActionMetadata();
+
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/listener/RunActionListener.java b/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/listener/RunActionListener.java
new file mode 100644
index 0000000..92bca3f
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/listener/RunActionListener.java
@@ -0,0 +1,28 @@
+/**
+ * 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.run.toolwindow.listener;
+
+import com.intellij.util.messages.Topic;
+
+import java.util.EventListener;
+
+public interface RunActionListener extends EventListener {
+
+    Topic<RunActionListener> TOPIC = Topic.create("Run Action", RunActionListener.class);
+
+    void runAction();
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/ui/WhiskRunWindowForm.form b/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/ui/WhiskRunWindowForm.form
new file mode 100644
index 0000000..20a2e29
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/ui/WhiskRunWindowForm.form
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.navercorp.openwhisk.intellij.run.toolwindow.ui.WhiskRunWindowForm">
+  <grid id="27dc6" binding="mainJPanel" 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>
+      <xy x="20" y="20" width="4810" height="400"/>
+    </constraints>
+    <properties/>
+    <border type="none"/>
+    <children>
+      <splitpane id="d27a" binding="executionJSplitPane">
+        <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="200" height="200"/>
+          </grid>
+        </constraints>
+        <properties>
+          <continuousLayout value="true"/>
+          <dividerLocation value="600"/>
+          <dividerSize value="10"/>
+          <oneTouchExpandable value="false"/>
+        </properties>
+        <border type="none"/>
+        <children>
+          <grid id="94e72" binding="parameterJPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
+            <constraints>
+              <splitpane position="left"/>
+            </constraints>
+            <properties/>
+            <border type="none"/>
+            <children>
+              <grid id="e30f0" binding="executionActionsJPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
+                <constraints border-constraint="North"/>
+                <properties/>
+                <border type="none"/>
+                <children>
+                  <component id="e2a93" class="javax.swing.JSeparator">
+                    <constraints border-constraint="South"/>
+                    <properties/>
+                  </component>
+                </children>
+              </grid>
+              <scrollpane id="da4f6">
+                <constraints border-constraint="Center"/>
+                <properties/>
+                <border type="none"/>
+                <children>
+                  <component id="75f7b" class="javax.swing.JTextArea" binding="paramJTextArea">
+                    <constraints/>
+                    <properties/>
+                  </component>
+                </children>
+              </scrollpane>
+            </children>
+          </grid>
+          <scrollpane id="e04bb" binding="resultJScrollPane">
+            <constraints>
+              <splitpane position="right"/>
+            </constraints>
+            <properties/>
+            <border type="none"/>
+            <children>
+              <component id="a1c48" class="javax.swing.JTextArea" binding="resultJTextArea">
+                <constraints/>
+                <properties/>
+              </component>
+            </children>
+          </scrollpane>
+        </children>
+      </splitpane>
+    </children>
+  </grid>
+</form>
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/ui/WhiskRunWindowForm.java b/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/ui/WhiskRunWindowForm.java
new file mode 100644
index 0000000..38e330f
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/run/toolwindow/ui/WhiskRunWindowForm.java
@@ -0,0 +1,281 @@
+/**
+ * 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.run.toolwindow.ui;
+
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.actionSystem.ActionGroup;
+import com.intellij.openapi.actionSystem.ActionManager;
+import com.intellij.openapi.actionSystem.ActionToolbar;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.editor.colors.EditorColorsManager;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.progress.ProgressManager;
+import com.intellij.openapi.progress.Task;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.wm.ToolWindow;
+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.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.ExecutableWhiskAction;
+import com.navercorp.openwhisk.intellij.common.whisk.model.activation.WhiskActivationWithLogs;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.ExecutableWhiskTrigger;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskActionService;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskActivationService;
+import com.navercorp.openwhisk.intellij.common.whisk.service.WhiskTriggerService;
+import org.apache.commons.lang.StringUtils;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.awt.*;
+import java.io.IOException;
+import java.util.Optional;
+
+public class WhiskRunWindowForm {
+    private static final Logger LOG = Logger.getInstance(WhiskRunWindowForm.class);
+    private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+    public static final String ENTITY_ACTION = "Action";
+    public static final String ENTITY_TRIGGER = "Trigger";
+
+    /**
+     * Main Panel.
+     */
+    private JPanel mainJPanel;
+
+    private JSplitPane executionJSplitPane;
+    // Parameter
+    private JPanel parameterJPanel;
+    private JPanel executionActionsJPanel;
+    private JTextArea paramJTextArea;
+    private JTextArea resultJTextArea;
+    private JScrollPane resultJScrollPane;
+
+    /**
+     * Cache data.
+     */
+    private ToolWindow toolWindow;
+    private Project project;
+    private Optional<ExecutableWhiskAction> currentAction = Optional.empty();
+    private Optional<ExecutableWhiskTrigger> currentTrigger = Optional.empty();
+    private Optional<WhiskAuth> currentAuth = Optional.empty();
+    private String currentEntity = "";
+
+    public WhiskRunWindowForm(Project project, ToolWindow toolWindow) {
+        this.toolWindow = toolWindow;
+        this.project = project;
+
+        final Color defaultBackgroundColor = EditorColorsManager.getInstance().getSchemeForCurrentUITheme().getDefaultBackground();
+
+        /**
+         * Action Tab
+         */
+        ActionManager actionManager = ActionManager.getInstance();
+        ActionGroup actionActionGroup = (ActionGroup) actionManager.getAction("WhiskRunWindow.Actions.Controls");
+        ActionToolbar actionActionToolbar = actionManager.createActionToolbar("", actionActionGroup, true);
+        actionActionToolbar.setTargetComponent(executionActionsJPanel);
+        executionActionsJPanel.add(actionActionToolbar.getComponent());
+
+        resultJTextArea.setText("Please run action");
+        resultJTextArea.setBackground(defaultBackgroundColor);
+        executionJSplitPane.setBackground(defaultBackgroundColor);
+    }
+
+    public JPanel getContent() {
+        return mainJPanel;
+    }
+
+    public void updateResult(String result) {
+        resultJTextArea.setText(result);
+    }
+
+    public void cacheAction(ExecutableWhiskAction action) {
+        this.currentAction = Optional.ofNullable(action);
+    }
+
+    public void cacheTrigger(ExecutableWhiskTrigger trigger) {
+        this.currentTrigger = Optional.ofNullable(trigger);
+    }
+
+    public void cacheAuth(WhiskAuth auth) {
+        this.currentAuth = Optional.ofNullable(auth);
+    }
+
+    public Optional<ExecutableWhiskAction> getCachedAction() {
+        return this.currentAction;
+    }
+
+    public Optional<ExecutableWhiskTrigger> getCachedTrigger() {
+        return this.currentTrigger;
+    }
+
+    public Optional<WhiskAuth> getCachedAuth() {
+        return this.currentAuth;
+    }
+
+    public void setTitle(String title) {
+        toolWindow.setTitle(title);
+    }
+
+    public String getCurrentEntity() {
+        return currentEntity;
+    }
+
+    /**
+     * Action Tab.
+     */
+    public void initializeActionTab(ExecutableWhiskAction action) {
+        try {
+            paramJTextArea.setText(JsonParserUtils.writeParameterToJson(action.getParameters()));
+            resultJTextArea.setText("Please run action");
+            currentEntity = ENTITY_ACTION;
+        } catch (IOException e) {
+            LOG.error("Failed to parse json: " + action.getFullyQualifiedName(), e);
+        }
+    }
+
+    public void runAction(WhiskActionService whiskActionService, WhiskAuth auth, ExecutableWhiskAction action) {
+        ProgressManager.getInstance().run(new Task.Backgroundable(project, "Run action") {
+            @Override
+            public void run(@NotNull ProgressIndicator indicator) {
+                try {
+                    if (StringUtils.isEmpty(paramJTextArea.getText())) {
+                        paramJTextArea.setText("{}");
+                    }
+
+                    Optional<String> params = ParameterUtils.validateParams(paramJTextArea.getText());
+                    if (params.isPresent()) {
+                        String result = whiskActionService.invokeWhiskAction(auth,
+                                Optional.ofNullable(action.getNamespacePath()),
+                                action.getWhiskPackage(),
+                                action.getName(),
+                                params.get());
+                        updateResult(result);
+                    } else {
+                        NOTIFIER.notify(project, "The json format of the parameter is incorrect.", NotificationType.ERROR);
+                    }
+                } catch (IOException e) {
+                    LOG.error("Failed to invoke action: " + action.getFullyQualifiedName(), e);
+                }
+
+            }
+        });
+    }
+
+    public void refreshActionMetaData(WhiskActionService whiskActionService, WhiskAuth auth, ExecutableWhiskAction action) {
+        try {
+            whiskActionService.getWhiskAction(auth, Optional.ofNullable(action.getNamespacePath()), action.getWhiskPackage(), action.getName())
+                    .ifPresent(executableWhiskAction -> {
+                        cacheAction(executableWhiskAction);
+                        initializeActionTab(executableWhiskAction);
+                        NOTIFIER.notify(project, "Refreshed action metadata: " + executableWhiskAction.getFullyQualifiedName(), NotificationType.INFORMATION);
+                    });
+        } catch (IOException e) {
+            LOG.error("Failed to fetch action: " + action.getFullyQualifiedName(), e);
+        }
+    }
+
+    /**
+     * Trigger Tab.
+     */
+    public void initializeTriggerTab(ExecutableWhiskTrigger trigger) {
+        try {
+            paramJTextArea.setText(JsonParserUtils.writeParameterToJson(trigger.getParameters()));
+            resultJTextArea.setText("Please fire trigger");
+            currentEntity = ENTITY_TRIGGER;
+        } catch (IOException e) {
+            LOG.error("Failed to parse json: " + trigger.getName(), e);
+        }
+    }
+
+    public void fireTrigger(WhiskTriggerService whiskTriggerService,
+                            WhiskActivationService whiskActivationService,
+                            WhiskAuth auth,
+                            ExecutableWhiskTrigger trigger) {
+        ProgressManager.getInstance().run(new Task.Backgroundable(project, "Fire trigger") {
+            @Override
+            public void run(@NotNull ProgressIndicator indicator) {
+                try {
+                    if (StringUtils.isEmpty(paramJTextArea.getText())) {
+                        paramJTextArea.setText("{}");
+                    }
+
+                    Optional<String> params = ParameterUtils.validateParams(paramJTextArea.getText());
+                    if (params.isPresent()) {
+                        Optional<String> result = whiskTriggerService.fireWhiskTrigger(auth, trigger.getName(), params.get());
+                        if (result.isPresent()) {
+                            String activationId = (String) JsonParserUtils.parseMap(result.get()).get("activationId");
+                            getActivation(whiskActivationService, auth, activationId, 10)
+                                    .ifPresent(response -> updateResult(response));
+                        } else {
+                            updateResult("Trigger was fired (no return value)");
+                        }
+
+                    } else {
+                        NOTIFIER.notify(project, "The json format of the parameter is incorrect.", NotificationType.ERROR);
+                    }
+                } catch (IOException e) {
+                    LOG.error("Failed to fire trigger: " + trigger.getName(), e);
+                }
+            }
+        });
+    }
+
+    private Optional<String> getActivation(WhiskActivationService whiskActivationService, WhiskAuth auth, String activationId, int retryCount) {
+        if (retryCount == 0) {
+            LOG.error("Failed to get activation " + activationId);
+            return Optional.empty();
+        }
+        try {
+            return whiskActivationService.getWhiskActivation(auth, activationId).flatMap(this::getResponse);
+        } catch (IOException e) {
+            LOG.info("Get activation " + activationId + ", retry count: " + retryCount);
+            waitThread(500);
+            return getActivation(whiskActivationService, auth, activationId, retryCount - 1);
+        }
+    }
+
+    private Optional<String> getResponse(WhiskActivationWithLogs activation) {
+        try {
+            return Optional.ofNullable(JsonParserUtils.beautifyJson(JsonParserUtils.writeMapToJson(activation.getResponse())));
+        } catch (IOException e) {
+            LOG.error("Failed to parse response", e);
+            return Optional.empty();
+        }
+    }
+
+    private void waitThread(long ms) {
+        try {
+            Thread.sleep(ms);
+        } catch (InterruptedException ex) {
+            // ignore
+        }
+    }
+
+    public void refreshTrigger(WhiskAuth auth, WhiskTriggerService whiskTriggerService, ExecutableWhiskTrigger old) {
+        try {
+            whiskTriggerService.getWhiskTrigger(auth, old.getName()).ifPresent(newTrigger -> {
+                cacheTrigger(newTrigger);
+                initializeTriggerTab(newTrigger);
+            });
+        } catch (IOException e) {
+            final String msg = "The action cannot be executed.";
+            LOG.error(msg, e);
+            NOTIFIER.notify(project, msg, NotificationType.ERROR);
+        }
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/dialog/WskDeployCmdDialog.java b/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/dialog/WskDeployCmdDialog.java
new file mode 100644
index 0000000..4e6c909
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/dialog/WskDeployCmdDialog.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.wskdeploy.dialog;
+
+import com.intellij.openapi.project.Project;
+import com.navercorp.openwhisk.intellij.common.DialogWrapperWithApply;
+import com.navercorp.openwhisk.intellij.common.whisk.model.wskdeploy.WskDeployCmd;
+import com.navercorp.openwhisk.intellij.wskdeploy.dialog.ui.WskDeployCmdDialogForm;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+public class WskDeployCmdDialog extends DialogWrapperWithApply {
+
+
+    private WskDeployCmdDialogForm wskDeployCmdDialogForm;
+    private WskDeployCmd cmd;
+
+    public WskDeployCmdDialog(Project project, WskDeployCmd cmd) {
+        super(project, true); // use current window as parent
+        setTitle("Run WskDeploy Command");
+        setResizable(false);
+        this.cmd = cmd;
+        wskDeployCmdDialogForm = new WskDeployCmdDialogForm(project, cmd);
+        init();
+    }
+
+    @Nullable
+    @Override
+    protected JComponent createCenterPanel() {
+        if (wskDeployCmdDialogForm != null) {
+            return this.wskDeployCmdDialogForm.getContent();
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    protected void doApplyAction() {
+        if (wskDeployCmdDialogForm != null) {
+            wskDeployCmdDialogForm.runWskDeploy();
+        }
+        super.doApplyAction();
+    }
+
+    @Override
+    protected void doOKAction() {
+        if (wskDeployCmdDialogForm != null && !myApplyAction.isApplied()) {
+            wskDeployCmdDialogForm.runWskDeploy();
+        }
+        super.doOKAction();
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/dialog/WskDeployTempleteDialog.java b/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/dialog/WskDeployTempleteDialog.java
new file mode 100644
index 0000000..3b7e05f
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/dialog/WskDeployTempleteDialog.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.wskdeploy.dialog;
+
+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 WskDeployTempleteDialog extends DialogWrapper {
+
+    private Project project;
+    private String[] metaFiles;
+    private String[] srcFiles;
+
+    public WskDeployTempleteDialog(Project project, String[] metaFiles, String[] srcFiles) {
+        super(true); // use current window as parent
+        setTitle("Create WskDeploy Template");
+        setResizable(false);
+        this.project = project;
+        this.metaFiles = metaFiles;
+        this.srcFiles = srcFiles;
+        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 create wskdeploy project in your workspace?<br/>");
+        builder.append("The following file will be created:<br/>");
+        for (String path : metaFiles) {
+            builder.append("- " + path + "<br/>");
+        }
+
+        for (String path : srcFiles) {
+            builder.append("- src/" + path + "<br/>");
+        }
+        builder.append("</html>");
+        JLabel label = new JLabel(builder.toString());
+        label.setPreferredSize(new Dimension(500, 100));
+        dialogPanel.add(label, BorderLayout.CENTER);
+
+        return dialogPanel;
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/dialog/ui/WskDeployCmdDialogForm.form b/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/dialog/ui/WskDeployCmdDialogForm.form
new file mode 100644
index 0000000..575c4df
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/dialog/ui/WskDeployCmdDialogForm.form
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.navercorp.openwhisk.intellij.wskdeploy.dialog.ui.WskDeployCmdDialogForm">
+  <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="c2540" 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>
+          <grid id="399fc" 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="56c6e" class="javax.swing.JLabel" binding="selectMsgJabel">
+                <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="Label"/>
+                </properties>
+              </component>
+            </children>
+          </grid>
+        </children>
+      </grid>
+      <scrollpane id="f14e7">
+        <constraints>
+          <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="7" hsize-policy="7" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties/>
+        <border type="none"/>
+        <children>
+          <component id="de57e" class="javax.swing.JList" binding="namespaceJList">
+            <constraints/>
+            <properties/>
+          </component>
+        </children>
+      </scrollpane>
+    </children>
+  </grid>
+</form>
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/dialog/ui/WskDeployCmdDialogForm.java b/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/dialog/ui/WskDeployCmdDialogForm.java
new file mode 100644
index 0000000..a9c3a05
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/dialog/ui/WskDeployCmdDialogForm.java
@@ -0,0 +1,100 @@
+/**
+ * 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.wskdeploy.dialog.ui;
+
+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.error.NotExistFileException;
+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.WhiskAuth;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskAuthWithName;
+import com.navercorp.openwhisk.intellij.common.whisk.model.WhiskEndpoint;
+import com.navercorp.openwhisk.intellij.common.whisk.model.wskdeploy.WskDeployCmd;
+import com.navercorp.openwhisk.intellij.common.whisk.model.wskdeploy.WskDeployCmdResponse;
+
+import javax.swing.*;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import static com.navercorp.openwhisk.intellij.common.utils.CommandUtils.runCommand;
+
+public class WskDeployCmdDialogForm {
+    private static final Logger LOG = Logger.getInstance(WskDeployCmdDialogForm.class);
+    private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+    private JPanel mainJPanel;
+    private JList namespaceJList;
+    private JLabel selectMsgJabel;
+
+    private Project project;
+    private WskDeployCmd cmd;
+
+    public WskDeployCmdDialogForm(Project project, WskDeployCmd cmd) {
+        this.project = project;
+        this.cmd = cmd;
+
+        selectMsgJabel.setText("Please select the namespace you want to " + cmd.getCmdName() + ".");
+
+        try {
+            WhiskService whiskService = ServiceManager.getService(project, WhiskService.class);
+            List<WhiskEndpoint> endpoints = new ArrayList<>(JsonParserUtils.parseWhiskEndpoints(whiskService.getEndpoints())); // make mutable
+
+            namespaceJList.setListData(endpoints.stream().flatMap(ep ->
+                    ep.getNamespaces().stream().map(ns ->
+                            new WhiskAuthWithName(new WhiskAuth(ns.getAuth(), ep.getApihost()), ns.getPath()))).toArray());
+
+        } catch (IOException e) {
+            LOG.error("Endpoint parsing failed", e);
+        }
+    }
+
+    public void runWskDeploy() {
+        try {
+            WhiskAuthWithName auth = (WhiskAuthWithName) namespaceJList.getSelectedValue();
+
+            LOG.info(cmd.toCmdString());
+            WskDeployCmdResponse response = runCommand(cmd.toCmd(auth.getAuth())); // TODO running background thread
+            LOG.info(response.getSuccessOutput());
+            String errMsg = response.getErrorOutput();
+            if (!errMsg.isEmpty()) {
+                LOG.error(errMsg);
+            }
+
+            if (response.getExistCode() == 0) {
+                NOTIFIER.notify(project, response.getSuccessOutput(), NotificationType.INFORMATION);
+                EventUtils.publish(project, RefreshWhiskTreeListener.TOPIC, RefreshWhiskTreeListener::refreshWhiskTree);
+            } else {
+                NOTIFIER.notify(project, response.getErrorOutput(), NotificationType.ERROR);
+            }
+
+        } catch (NotExistFileException | IOException | InterruptedException ex) {
+            LOG.error(ex);
+            NOTIFIER.notify(project, "Failed to running command(" + cmd.getCmdName() + ") for " + ex.getMessage(), NotificationType.ERROR);
+        }
+    }
+
+    public JPanel getContent() {
+        return mainJPanel;
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/WskDeployWindowFactory.java b/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/WskDeployWindowFactory.java
new file mode 100644
index 0000000..8a461d2
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/WskDeployWindowFactory.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.wskdeploy.toolwindow;
+
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.wm.ToolWindow;
+import com.intellij.openapi.wm.ToolWindowFactory;
+import com.intellij.ui.content.Content;
+import com.intellij.ui.content.ContentFactory;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import com.navercorp.openwhisk.intellij.wskdeploy.toolwindow.ui.WskDeployWindowForm;
+import org.jetbrains.annotations.NotNull;
+
+public class WskDeployWindowFactory implements ToolWindowFactory {
+    private static final Logger LOG = Logger.getInstance(WskDeployWindowFactory.class);
+    private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+    @Override
+    public void createToolWindowContent(@NotNull Project project, @NotNull ToolWindow toolWindow) {
+        WskDeployWindowForm wskDeployWindowForm = new WskDeployWindowForm(project, toolWindow);
+        ContentFactory contentFactory = ContentFactory.SERVICE.getInstance();
+        Content content = contentFactory.createContent(wskDeployWindowForm.getContent(), null, false);
+        toolWindow.getContentManager().addContent(content);
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/action/ChooseWskDeployBinAction.java b/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/action/ChooseWskDeployBinAction.java
new file mode 100644
index 0000000..efeccf2
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/action/ChooseWskDeployBinAction.java
@@ -0,0 +1,59 @@
+/**
+ * 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.wskdeploy.toolwindow.action;
+
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.fileChooser.FileChooser;
+import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import com.navercorp.openwhisk.intellij.common.utils.EventUtils;
+import com.navercorp.openwhisk.intellij.common.utils.ValidationUtils;
+import com.navercorp.openwhisk.intellij.wskdeploy.toolwindow.listener.ChooseWskDeployBinaryListener;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Optional;
+
+import static com.navercorp.openwhisk.intellij.common.Icons.OPEN_DISK_HOVER;
+
+public class ChooseWskDeployBinAction extends AnAction {
+    private static final Logger LOG = Logger.getInstance(ChooseWskDeployBinAction.class);
+    private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+    ChooseWskDeployBinAction() {
+        super(OPEN_DISK_HOVER);
+    }
+
+    @Override
+    public void actionPerformed(@NotNull AnActionEvent e) {
+        VirtualFile file = FileChooser.chooseFile(FileChooserDescriptorFactory.createSingleFileDescriptor(), e.getProject(), null);
+        if (file == null) {
+            return;
+        }
+
+        Optional<VirtualFile> validWskDeploy = ValidationUtils.validateWskDeploy(Optional.of(file));
+        if (validWskDeploy.isPresent()) {
+            NOTIFIER.notify(e.getProject(), file.getName() + " file has been registered with wskdeploy.", NotificationType.INFORMATION);
+            EventUtils.publish(e.getProject(), ChooseWskDeployBinaryListener.TOPIC, l -> l.chooseWskDeployBinary(validWskDeploy.get()));
+        } else {
+            NOTIFIER.notify(e.getProject(), file.getName() + " file cannot be used with wskdeploy.", NotificationType.ERROR);
+        }
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/action/CreateManifestTemplateAction.java b/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/action/CreateManifestTemplateAction.java
new file mode 100644
index 0000000..b2381d9
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/action/CreateManifestTemplateAction.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.wskdeploy.toolwindow.action;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VfsUtilCore;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.search.FilenameIndex;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.util.ResourceUtil;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import com.navercorp.openwhisk.intellij.common.utils.EventUtils;
+import com.navercorp.openwhisk.intellij.wskdeploy.dialog.WskDeployTempleteDialog;
+import com.navercorp.openwhisk.intellij.wskdeploy.toolwindow.listener.RefreshWskDeployManifestListener;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+public class CreateManifestTemplateAction extends AnAction {
+    private static final Logger LOG = Logger.getInstance(CreateManifestTemplateAction.class);
+    private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+    private static final String[] META_FILES = new String[]{"manifest.yaml", "HOW-TO-DEPLOY.md"};
+    private static final String[] SOURCE_FILES = new String[]{"index.js", "index.test.js", "package.json"};
+
+    CreateManifestTemplateAction() {
+        super(AllIcons.Actions.Menu_paste);
+    }
+
+    @Override
+    public void actionPerformed(@NotNull AnActionEvent e) {
+        final Project project = e.getProject();
+        final String basePath = project.getBasePath();
+
+        if ((new WskDeployTempleteDialog(project, META_FILES, SOURCE_FILES)).showAndGet()) {
+            ApplicationManager.getApplication().runWriteAction(() -> {
+                for (String filePath : META_FILES) {
+                    copyFile(project, "/template", filePath, basePath);
+                }
+
+                for (String filePath : SOURCE_FILES) {
+                    copyFile(project, "/template/src", filePath, basePath + "/src");
+                }
+
+                // refresh manifest file tree
+                EventUtils.publish(project, RefreshWskDeployManifestListener.TOPIC, RefreshWskDeployManifestListener::refreshWskDeployManifest);
+            });
+        }
+    }
+
+    private void copyFile(Project project, String resourcePath, String fileName, String toDir) {
+        try {
+            boolean exist = Arrays.stream(FilenameIndex.getFilesByName(project, fileName, GlobalSearchScope.allScope(project)))
+                    .anyMatch(file -> file.getVirtualFile().getPath().startsWith(toDir));
+
+            if (!exist) {
+                final VirtualFile file = VfsUtil.findFileByURL(ResourceUtil.getResource(getClass(), resourcePath, fileName));
+                final VirtualFile parent = VfsUtil.createDirectories(toDir);
+                VfsUtilCore.copyFile(this, file, parent);
+                NOTIFIER.notify(project, fileName + " file is created", NotificationType.INFORMATION);
+            } else {
+                NOTIFIER.notify(project, fileName + " file already exist", NotificationType.WARNING);
+            }
+        } catch (IOException e) {
+            LOG.error(e);
+        }
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/action/RefreshWskDeployAction.java b/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/action/RefreshWskDeployAction.java
new file mode 100644
index 0000000..ec6586d
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/action/RefreshWskDeployAction.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.wskdeploy.toolwindow.action;
+
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.diagnostic.Logger;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import com.navercorp.openwhisk.intellij.common.utils.EventUtils;
+import com.navercorp.openwhisk.intellij.wskdeploy.toolwindow.listener.RefreshWskDeployManifestListener;
+
+import static com.intellij.icons.AllIcons.Actions.Refresh;
+
+public class RefreshWskDeployAction extends AnAction {
+    private static final Logger LOG = Logger.getInstance(RefreshWskDeployAction.class);
+    private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+    public RefreshWskDeployAction() {
+        super(Refresh);
+    }
+
+    @Override
+    public void actionPerformed(AnActionEvent event) {
+        EventUtils.publish(event.getProject(), RefreshWskDeployManifestListener.TOPIC, RefreshWskDeployManifestListener::refreshWskDeployManifest);
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/listener/ChooseWskDeployBinaryListener.java b/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/listener/ChooseWskDeployBinaryListener.java
new file mode 100644
index 0000000..73fbdb7
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/listener/ChooseWskDeployBinaryListener.java
@@ -0,0 +1,28 @@
+/**
+ * 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.wskdeploy.toolwindow.listener;
+
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.messages.Topic;
+
+import java.util.EventListener;
+
+public interface ChooseWskDeployBinaryListener extends EventListener {
+    Topic<ChooseWskDeployBinaryListener> TOPIC = Topic.create("Choose wskdeploy binary", ChooseWskDeployBinaryListener.class);
+
+    void chooseWskDeployBinary(VirtualFile wskDeployBin);
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/listener/RefreshWskDeployManifestListener.java b/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/listener/RefreshWskDeployManifestListener.java
new file mode 100644
index 0000000..64a00d5
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/listener/RefreshWskDeployManifestListener.java
@@ -0,0 +1,27 @@
+/**
+ * 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.wskdeploy.toolwindow.listener;
+
+import com.intellij.util.messages.Topic;
+
+import java.util.EventListener;
+
+public interface RefreshWskDeployManifestListener extends EventListener {
+    Topic<RefreshWskDeployManifestListener> TOPIC = Topic.create("Refresh wskdeploy manifest", RefreshWskDeployManifestListener.class);
+
+    void refreshWskDeployManifest();
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/tree/WskDeployTreeCellRenderer.java b/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/tree/WskDeployTreeCellRenderer.java
new file mode 100644
index 0000000..6398693
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/tree/WskDeployTreeCellRenderer.java
@@ -0,0 +1,59 @@
+/**
+ * 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.wskdeploy.toolwindow.tree;
+
+import com.intellij.ui.ColoredTreeCellRenderer;
+import com.navercorp.openwhisk.intellij.common.whisk.model.wskdeploy.*;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import javax.swing.tree.DefaultMutableTreeNode;
+
+import static com.intellij.icons.AllIcons.Actions.Execute;
+import static com.intellij.icons.AllIcons.Actions.Rollback;
+import static com.navercorp.openwhisk.intellij.common.Icons.RUN_ANYTHING;
+import static com.navercorp.openwhisk.intellij.common.Icons.YAML;
+
+public class WskDeployTreeCellRenderer extends ColoredTreeCellRenderer {
+    @Override
+    public void customizeCellRenderer(@NotNull JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
+        DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) value;
+        Object userObject = treeNode.getUserObject();
+        if (userObject instanceof WskDeployManifest) {
+            WskDeployManifest manifest = (WskDeployManifest) userObject;
+            setIcon(YAML);
+            append(manifest.getPath());
+        } else if (userObject instanceof WskDeployCmdDeploy) {
+            WskDeployCmdDeploy wskDeployCmdDeploy = (WskDeployCmdDeploy) userObject;
+            setIcon(Execute);
+            append(wskDeployCmdDeploy.getCmdName());
+        } else if (userObject instanceof WskDeployCmdUndeploy) {
+            WskDeployCmdUndeploy wskDeployCmdUndeploy = (WskDeployCmdUndeploy) userObject;
+            setIcon(Rollback);
+            append(wskDeployCmdUndeploy.getCmdName());
+        } else if (userObject instanceof WskDeployBinary) {
+            WskDeployBinary wskDeployBinary = (WskDeployBinary) userObject;
+            setIcon(RUN_ANYTHING);
+            append("wskdeploy (" + wskDeployBinary.getFullPath() + ")");
+        } else if (userObject instanceof NullWskDeployBinary) {
+            setIcon(RUN_ANYTHING);
+            append("There is no registered wskdeploy binary. Please find the file and register.");
+        } else {
+            append(userObject.toString());
+        }
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/tree/WskDeployTreeModel.java b/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/tree/WskDeployTreeModel.java
new file mode 100644
index 0000000..aa5889e
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/tree/WskDeployTreeModel.java
@@ -0,0 +1,106 @@
+/**
+ * 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.wskdeploy.toolwindow.tree;
+
+import com.intellij.openapi.diagnostic.Logger;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import com.navercorp.openwhisk.intellij.common.whisk.model.wskdeploy.*;
+
+import javax.swing.event.TreeModelListener;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.DefaultTreeModel;
+import javax.swing.tree.TreeModel;
+import javax.swing.tree.TreePath;
+import java.util.List;
+import java.util.Optional;
+
+public class WskDeployTreeModel implements TreeModel {
+    private static final Logger LOG = Logger.getInstance(WskDeployTreeModel.class);
+    private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+    private DefaultTreeModel innerModel;
+    private DefaultMutableTreeNode root;
+
+    public WskDeployTreeModel(WskDeployFile wskDeployFile, List<WskDeployManifest> manifests) {
+        setTree(wskDeployFile, manifests);
+    }
+
+    public void setTree(WskDeployFile wskDeployFile, List<WskDeployManifest> manifests) {
+        this.root = new DefaultMutableTreeNode(wskDeployFile);
+
+        for (WskDeployManifest manifest : manifests) {
+            DefaultMutableTreeNode manifestNode = new DefaultMutableTreeNode(manifest);
+            root.add(manifestNode);
+
+            Optional<WskDeployBinary> wskDeployBinary = toWskDeployBinary(wskDeployFile);
+            manifestNode.add(new DefaultMutableTreeNode(new WskDeployCmdDeploy(wskDeployBinary, manifest)));
+            manifestNode.add(new DefaultMutableTreeNode(new WskDeployCmdUndeploy(wskDeployBinary, manifest)));
+        }
+
+        this.innerModel = new DefaultTreeModel(this.root);
+    }
+
+    private Optional<WskDeployBinary> toWskDeployBinary(WskDeployFile wskDeployFile) {
+        if (wskDeployFile instanceof WskDeployBinary) {
+            return Optional.of((WskDeployBinary) wskDeployFile);
+        }
+        return Optional.empty();
+    }
+
+    @Override
+    public Object getRoot() {
+        return this.innerModel.getRoot();
+    }
+
+    @Override
+    public Object getChild(Object parent, int index) {
+        return this.innerModel.getChild(parent, index);
+    }
+
+    @Override
+    public int getChildCount(Object parent) {
+        return this.innerModel.getChildCount(parent);
+    }
+
+    @Override
+    public boolean isLeaf(Object node) {
+        return this.innerModel.isLeaf(node);
+    }
+
+    @Override
+    public void valueForPathChanged(TreePath path, Object newValue) {
+        this.innerModel.valueForPathChanged(path, newValue);
+
+    }
+
+    @Override
+    public int getIndexOfChild(Object parent, Object child) {
+        return this.innerModel.getIndexOfChild(parent, child);
+    }
+
+    @Override
+    public void addTreeModelListener(TreeModelListener l) {
+        this.innerModel.addTreeModelListener(l);
+
+    }
+
+    @Override
+    public void removeTreeModelListener(TreeModelListener l) {
+        this.innerModel.removeTreeModelListener(l);
+
+    }
+}
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/ui/WskDeployWindowForm.form b/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/ui/WskDeployWindowForm.form
new file mode 100644
index 0000000..156f6e1
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/ui/WskDeployWindowForm.form
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.navercorp.openwhisk.intellij.wskdeploy.toolwindow.ui.WskDeployWindowForm">
+  <grid id="27dc6" binding="mainJPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
+    <constraints>
+      <xy x="20" y="20" width="500" height="400"/>
+    </constraints>
+    <properties/>
+    <border type="none"/>
+    <children>
+      <grid id="1986e" binding="actionsJPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
+        <constraints border-constraint="North"/>
+        <properties/>
+        <border type="none"/>
+        <children/>
+      </grid>
+      <scrollpane id="dd1" binding="contentsJPanel">
+        <constraints border-constraint="Center"/>
+        <properties/>
+        <border type="none"/>
+        <children>
+          <component id="e6669" class="javax.swing.JTree" binding="wskdeployJTree">
+            <constraints/>
+            <properties/>
+          </component>
+        </children>
+      </scrollpane>
+      <grid id="4b4b2" binding="downloadGuideJPanel" 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 border-constraint="South"/>
+        <properties/>
+        <border type="none"/>
+        <children>
+          <component id="fd9f3" class="javax.swing.JLabel" binding="urlJLabel">
+            <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="Label"/>
+            </properties>
+          </component>
+        </children>
+      </grid>
+    </children>
+  </grid>
+</form>
diff --git a/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/ui/WskDeployWindowForm.java b/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/ui/WskDeployWindowForm.java
new file mode 100644
index 0000000..1b0996d
--- /dev/null
+++ b/src/main/java/com/navercorp/openwhisk/intellij/wskdeploy/toolwindow/ui/WskDeployWindowForm.java
@@ -0,0 +1,213 @@
+/**
+ * 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.wskdeploy.toolwindow.ui;
+
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.actionSystem.ActionGroup;
+import com.intellij.openapi.actionSystem.ActionManager;
+import com.intellij.openapi.actionSystem.ActionToolbar;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.wm.ToolWindow;
+import com.intellij.psi.search.FilenameIndex;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.navercorp.openwhisk.intellij.common.notification.SimpleNotifier;
+import com.navercorp.openwhisk.intellij.common.service.WskDeployService;
+import com.navercorp.openwhisk.intellij.common.utils.EventUtils;
+import com.navercorp.openwhisk.intellij.common.utils.ValidationUtils;
+import com.navercorp.openwhisk.intellij.common.whisk.model.wskdeploy.*;
+import com.navercorp.openwhisk.intellij.wskdeploy.dialog.WskDeployCmdDialog;
+import com.navercorp.openwhisk.intellij.wskdeploy.toolwindow.listener.ChooseWskDeployBinaryListener;
+import com.navercorp.openwhisk.intellij.wskdeploy.toolwindow.listener.RefreshWskDeployManifestListener;
+import com.navercorp.openwhisk.intellij.wskdeploy.toolwindow.tree.WskDeployTreeCellRenderer;
+import com.navercorp.openwhisk.intellij.wskdeploy.toolwindow.tree.WskDeployTreeModel;
+
+import javax.swing.*;
+import javax.swing.tree.DefaultMutableTreeNode;
+import java.awt.*;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public class WskDeployWindowForm {
+    private static final Logger LOG = Logger.getInstance(WskDeployWindowForm.class);
+    private static final SimpleNotifier NOTIFIER = SimpleNotifier.getInstance();
+
+    private static final String WSKDEPLOY_URL = "https://github.com/apache/openwhisk-wskdeploy/releases";
+
+    private JPanel mainJPanel;
+    private JPanel actionsJPanel;
+    private JScrollPane contentsJPanel;
+    private JTree wskdeployJTree;
+    private JPanel downloadGuideJPanel;
+    private JLabel urlJLabel;
+
+    private WskDeployService wskDeployService;
+
+    public WskDeployWindowForm(Project project, ToolWindow toolWindow) {
+        this.wskDeployService = ServiceManager.getService(project, WskDeployService.class);
+
+        // Toolbar
+        String actionGroupName = "WskDeployWindow.Actions.Controls";
+        ActionManager actionManager = ActionManager.getInstance();
+        ActionGroup actionGroup = (ActionGroup) actionManager.getAction(actionGroupName);
+        ActionToolbar actionToolbar = actionManager.createActionToolbar("", actionGroup, true);
+        actionToolbar.setTargetComponent(actionsJPanel);
+        actionsJPanel.add(actionToolbar.getComponent());
+
+        // Download url for wskdeploy
+        urlJLabel.setText("<html> WskDeploy download : <a href=\\\"\\\">" + WSKDEPLOY_URL + "</a></html>");
+        urlJLabel.setCursor(new Cursor(Cursor.HAND_CURSOR));
+        urlJLabel.addMouseListener(new MouseAdapter() {
+            @Override
+            public void mouseClicked(MouseEvent e) {
+                try {
+                    Desktop.getDesktop().browse(new URI(WSKDEPLOY_URL));
+                } catch (IOException | URISyntaxException ex) {
+                    String msg = "Cannot open " + WSKDEPLOY_URL;
+                    LOG.error(msg, ex);
+                    NOTIFIER.notify(project, msg, NotificationType.ERROR);
+                }
+            }
+        });
+
+        setWskDeployTree(project, loadWskDeployManifest(project));
+    }
+
+    private void setWskDeployTree(Project project, List<WskDeployManifest> manifests) {
+        boolean isWindows = System.getProperty("os.name").toLowerCase().startsWith("windows"); // TODO make a global value
+        WskDeployFile wskDeployFile = loadRegisteredWskDeploy()  // 1. Registered wskdeploy
+                .orElse(loadWskDeployFileFromLocal(isWindows));  // 2. Local wskdeploy
+
+        wskdeployJTree.setModel(new WskDeployTreeModel(wskDeployFile, manifests));
+        wskdeployJTree.setCellRenderer(new WskDeployTreeCellRenderer());
+        expandAllNode(wskdeployJTree);
+        wskdeployJTree.addMouseListener(new MouseAdapter() {
+            @Override
+            public void mouseClicked(MouseEvent e) {
+                if (e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() > 1) {
+                    DefaultMutableTreeNode node = (DefaultMutableTreeNode) wskdeployJTree.getLastSelectedPathComponent();
+                    Object userObject = node.getUserObject();
+                    if (userObject instanceof WskDeployCmdDeploy) {
+                        WskDeployCmdDeploy cmd = (WskDeployCmdDeploy) userObject;
+                        if (new WskDeployCmdDialog(project, cmd).showAndGet()) {
+                            LOG.info("WskDeployCmdDialog(deploy) closed");
+                        }
+                    } else if (userObject instanceof WskDeployCmdUndeploy) {
+                        WskDeployCmdUndeploy cmd = (WskDeployCmdUndeploy) userObject;
+                        if (new WskDeployCmdDialog(project, cmd).showAndGet()) {
+                            LOG.info("WskDeployCmdDialog(undeploy) closed");
+                        }
+                    }
+                }
+            }
+        });
+
+        EventUtils.subscribe(project, project, RefreshWskDeployManifestListener.TOPIC, () -> {
+            WskDeployFile reloadedWskDeployFile = loadRegisteredWskDeploy()  // 1. Registered wskdeploy
+                    .orElse(loadWskDeployFileFromLocal(isWindows));          // 2. Local wskdeploy
+
+            wskdeployJTree.setModel(new WskDeployTreeModel(reloadedWskDeployFile, loadWskDeployManifest(project)));
+            expandAllNode(wskdeployJTree);
+        });
+
+        EventUtils.subscribe(project, project, ChooseWskDeployBinaryListener.TOPIC, (chosenWskDeployFile) -> {
+            WskDeployBinary wskDeployBinary = new WskDeployBinary(chosenWskDeployFile.getPath(), chosenWskDeployFile.getName());
+            wskDeployService.setWskdeployName(wskDeployBinary.getName());
+            wskDeployService.setWskdeployPath(wskDeployBinary.getFullPath());
+            wskDeployService.loadState(wskDeployService);
+
+            wskdeployJTree.setModel(new WskDeployTreeModel(wskDeployBinary, loadWskDeployManifest(project)));
+            expandAllNode(wskdeployJTree);
+        });
+    }
+
+
+    private Optional<WskDeployFile> loadRegisteredWskDeploy() {
+        if (wskDeployService.getWskdeployPath() != null && wskDeployService.getWskdeployName() != null) {
+            VirtualFile file = LocalFileSystem.getInstance().findFileByPath(wskDeployService.getWskdeployPath());
+            return ValidationUtils.validateWskDeploy(Optional.ofNullable(file))
+                    .map(validWskDeploy -> new WskDeployBinary(validWskDeploy.getPath(), validWskDeploy.getName()));
+        } else {
+            return Optional.empty();
+        }
+    }
+
+    private WskDeployFile loadWskDeployFileFromLocal(boolean isWindow) {
+        String[] dirs;
+        if (isWindow) {
+            dirs = new String[]{};
+        } else {
+            dirs = new String[]{"/usr/local/bin", "/usr/bin"};
+        }
+
+        for (String dir : dirs) {
+            File[] files = findFiles(dir, "wskdeploy");
+            if (files != null && files.length > 0) {
+                VirtualFile file = LocalFileSystem.getInstance().findFileByIoFile(files[0]);
+                Optional<WskDeployBinary> wskDeployBinary = ValidationUtils.validateWskDeploy(Optional.ofNullable(file))
+                        .map(validWskDeploy -> new WskDeployBinary(validWskDeploy.getPath(), validWskDeploy.getName()));
+                if (wskDeployBinary.isPresent()) {
+                    return wskDeployBinary.get();
+                }
+            }
+        }
+        return new NullWskDeployBinary();
+    }
+
+    private File[] findFiles(String parentDir, String fileName) {
+        File dir = new File(parentDir);
+        return dir.listFiles((dir1, name) -> name.startsWith(fileName));
+    }
+
+    private void expandAllNode(JTree tree) {
+        for (int i = 0; i < tree.getRowCount(); i++) {
+            tree.expandRow(i);
+        }
+    }
+
+    private List<WskDeployManifest> loadWskDeployManifest(Project project) {
+        List<WskDeployManifest> manifests = new ArrayList<>();
+        Collection<VirtualFile> yamlFiles = FilenameIndex.getAllFilesByExt(project, "yaml", GlobalSearchScope.projectScope(project));
+        Collection<VirtualFile> ymlFiles = FilenameIndex.getAllFilesByExt(project, "yml", GlobalSearchScope.projectScope(project));
+        List<VirtualFile> files = Stream.of(yamlFiles, ymlFiles).flatMap(Collection::stream).collect(Collectors.toList());
+        for (VirtualFile f : files) {
+            String fullPath = f.getPath();
+            String path = fullPath.replaceAll(project.getBasePath() + "/", "");
+            String name = f.getName();
+            manifests.add(new WskDeployManifest(path, fullPath, name));
+        }
+        return manifests;
+    }
+
+    public JPanel getContent() {
+        return mainJPanel;
+    }
+
+}
diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml
new file mode 100644
index 0000000..a7e22eb
--- /dev/null
+++ b/src/main/resources/META-INF/plugin.xml
@@ -0,0 +1,121 @@
+<idea-plugin>
+    <id>com.navercorp.openwhisk.intellij</id>
+    <name>DevTools for Apache Openwhisk</name>
+    <vendor email="dl_lambda_op@navercorp.com" url="https://github.com/naver">NAVER</vendor>
+    <version>1.1.4</version>
+    <idea-version since-build="181.*" until-build="203.*"/>
+
+    <description><![CDATA[
+<p>
+    <strong>OpenWhisk-intellij-support</strong> is an open source Intellij Plugin for <a href="https://github.com/apache/openwhisk">Apache OpenWhisk</a>. It assists users to develop/deploy/manage OpenWhisk functions in Intellij.
+</p>
+
+<h2>Feature</h2>
+
+<h3>OpenWhisk Explorer</h3>
+<ul>
+    <li>Explore all entities in your endpoints/namespaces.
+        <ul>
+            <li>The <code>.wskprops</code> file is automatically registered.</li>
+            <li>You can add the API host manually.</li>
+            <li>You can add namespace manually by API auth key.</li>
+        </ul>
+    </li>
+    <li>Show the action code with syntax highlighting.</li>
+    <li>[Soon] Edit the action code on the remote server.</li>
+    <li>Invoke the action remotely and get the activation result.</li>
+    <li>Show a list of actions related to the sequence action.</li>
+    <li>Show information about the trigger and related rules.</li>
+    <li>Show activations related to the action (Same as <code>wsk activation list ${action}</code>).</li>
+    <li>Show detailed information of the activation (Same <code>as wsk activation get ${activation_id}</code>).</li>
+    <li>Update parameters of the action, package, and trigger.</li>
+</ul>
+
+<h3>Manifest View</h3>
+<ul>
+    <li>List up manifest YAML files in the workspace.</li>
+    <li>Deploy/Undeploy OpenWhisk packages with manifest (via wskdeploy).
+        <ul>
+            <li>Deploy with the deployment file.</li>
+            <li>Deploy with multiple credentials.</li>
+        </ul>
+    </li>
+</ul>
+    ]]></description>
+
+    <change-notes><![CDATA[
+<ul>
+    <li>Support IDEA 203.* version: https://github.com/naver/openwhisk-intellij-plugin/pull/31</li>
+</ul>
+    ]]></change-notes>
+
+    <depends>com.intellij.modules.lang</depends>
+
+    <extensions defaultExtensionNs="com.intellij">
+        <toolWindow id="Whisk Explorer" icon="/objectBrowser/flattenPackages.svg" anchor="right"
+                    factoryClass="com.navercorp.openwhisk.intellij.explorer.toolwindow.WhiskExplorerWindowFactory"/>
+        <toolWindow id="Whisk Run" icon="/icons/ssh.svg" anchor="bottom"
+                    factoryClass="com.navercorp.openwhisk.intellij.run.toolwindow.WhiskRunWindowFactory"/>
+        <toolWindow id="WskDeploy" icon="/actions/upload.svg" anchor="left" secondary="true"
+                    factoryClass="com.navercorp.openwhisk.intellij.wskdeploy.toolwindow.WskDeployWindowFactory"/>
+        <projectService serviceImplementation="com.navercorp.openwhisk.intellij.common.service.WhiskService"/>
+        <projectService serviceImplementation="com.navercorp.openwhisk.intellij.common.service.WskDeployService"/>
+        <virtualFileSystem
+                implementationClass="com.navercorp.openwhisk.intellij.explorer.editor.ActivationViewVirtualFileSystem"
+                key="activation-view"/>
+        <fileEditorProvider
+                implementation="com.navercorp.openwhisk.intellij.explorer.editor.ActivationViewEditorProvider"/>
+    </extensions>
+
+    <actions>
+        <group id="WhiskExplorer.Actions.Controls" text="Explorer whisk tree">
+            <action id="WhiskExplorer.Actions.Controls.AddEndpoint"
+                    class="com.navercorp.openwhisk.intellij.explorer.toolwindow.action.AddEndpointAction"
+                    text="Add Endpoint"
+                    description="Add Endpoint">
+            </action>
+            <separator/>
+            <action id="WhiskExplorer.Actions.Controls.Refresh"
+                    class="com.navercorp.openwhisk.intellij.explorer.toolwindow.action.RefreshTreeAction"
+                    text="Refresh"
+                    description="Reload whisk data from remote">
+            </action>
+        </group>
+        <group id="WhiskRunWindow.Actions.Controls">
+            <action id="WhiskRunWindow.Actions.Controls.RunAction"
+                    class="com.navercorp.openwhisk.intellij.run.toolwindow.action.RunActionAction"
+                    text="Run"
+                    description="Run action">
+            </action>
+            <action id="WhiskRunWindow.Actions.Controls.Refresh"
+                    class="com.navercorp.openwhisk.intellij.run.toolwindow.action.RefreshMetaDataAction"
+                    text="Refresh"
+                    description="Refresh">
+            </action>
+            <action id="WhiskRunWindow.Actions.Controls.Edit"
+                    class="com.navercorp.openwhisk.intellij.run.toolwindow.action.OpenActionManagerDialogAction"
+                    text="Edit"
+                    description="Edit action metadata">
+            </action>
+        </group>
+        <group id="WskDeployWindow.Actions.Controls" text="Manage wskdeploy">
+            <action id="WskDeployWindow.Actions.Controls.Refresh"
+                    class="com.navercorp.openwhisk.intellij.wskdeploy.toolwindow.action.RefreshWskDeployAction"
+                    text="WskDeploy"
+                    description="Refresh wskdeploy">
+            </action>
+            <action id="WskDeployWindow.Actions.Controls.CreateTemplate"
+                    class="com.navercorp.openwhisk.intellij.wskdeploy.toolwindow.action.CreateManifestTemplateAction"
+                    text="Create Manifest Template"
+                    description="Create Manifest Template">
+            </action>
+
+            <action id="WskDeployWindow.Actions.Controls.ChooseWskDeployBin"
+                    class="com.navercorp.openwhisk.intellij.wskdeploy.toolwindow.action.ChooseWskDeployBinAction"
+                    text="Find WskDeploy binary"
+                    description="Find WskDeploy binary">
+            </action>
+        </group>
+    </actions>
+
+</idea-plugin>
\ No newline at end of file
diff --git a/src/main/resources/icons/arrowDown.svg b/src/main/resources/icons/arrowDown.svg
new file mode 100644
index 0000000..a83b43f
--- /dev/null
+++ b/src/main/resources/icons/arrowDown.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><rect id="frame" width="16" height="16" fill="none"/><path fill="#6E6E6E" fill-rule="evenodd" d="M8 11l4.5-5h-9z"/></svg>
\ No newline at end of file
diff --git a/src/main/resources/icons/arrowUp.svg b/src/main/resources/icons/arrowUp.svg
new file mode 100644
index 0000000..3d6b0cc
--- /dev/null
+++ b/src/main/resources/icons/arrowUp.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><rect id="frame" width="16" height="16" fill="none"/><path fill="#6E6E6E" fill-rule="evenodd" d="M8 5l4.5 5h-9z"/></svg>
\ No newline at end of file
diff --git a/src/main/resources/icons/event.svg b/src/main/resources/icons/event.svg
new file mode 100644
index 0000000..fe9ea4b
--- /dev/null
+++ b/src/main/resources/icons/event.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><rect id="frame" width="16" height="16" fill="none"/><path fill="#F4AF3D" fill-rule="evenodd" d="M7.998 9.62l-6.286.003L6.765.996h4.93L7.99 5.927h7.404l-9.861 9.856z"/></svg>
\ No newline at end of file
diff --git a/src/main/resources/icons/eventGroup.svg b/src/main/resources/icons/eventGroup.svg
new file mode 100644
index 0000000..2bd7dc3
--- /dev/null
+++ b/src/main/resources/icons/eventGroup.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><rect id="frame" width="16" height="16" fill="none"/><g fill="none" fill-rule="evenodd"><path fill="#9AA7B0" fill-opacity=".8" d="M15 6h-5l-4.1 7H1V2.051C1 2.023 1.023 2 1.051 2H4.98a2.8 2.8 0 0 1 1.717.711L7.985 4H15v2z"/><path fill="#F4AF3D" d="M11.409 12.21l-3.8.002L10.664 7h2.979l-2.24 2.978h4.475l-5.96 5.956z"/></g></svg>
\ No newline at end of file
diff --git a/src/main/resources/icons/language/docker/DockerCompose.svg b/src/main/resources/icons/language/docker/DockerCompose.svg
new file mode 100644
index 0000000..5118aef
--- /dev/null
+++ b/src/main/resources/icons/language/docker/DockerCompose.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><rect id="frame" width="16" height="16" fill="none"/><g fill="none" fill-rule="evenodd"><g transform="translate(.4 5.6)"><path fill="#34A2F2" d="M14.85 2.439c-.039-.031-.4-.304-1.162-.304-.202 0-.403.018-.601.052-.148-1.012-.984-1.505-1.021-1.527L11.86.542l-.134.195a2.744 2.744 0 0 0-.364.85c-.137.578-.054 1.12.24 1.583-.355.198-.923.246-1.038.25H.6c-.05 1.381.087 2.44.412 3.176C1.5 7.7 2.042 8.409 2.945 8.84c.903.43 1.459.576 2.753.576a9.368 9.368 0 0 0 1.743-.158 7.286 7.286 0 0 0 2.274-.826 6.253 6.253 0 0 0 1.552-1.27c.745-.844 1.19-2.384 1.52-3.218h.13c.817 0 1.318-.327 1.595-.6a1.74 1.74 0 0 0 .42-.623l.059-.171-.141-.111z"/><circle cx="4.75" cy="5.65" r="1.25" fill="#FFF"/><circle cx="4.75" cy="5.65" r="1" fill="#34A2F2" fill-rule="nonzero"/></g><path fill="#34A2F2" fill-rule="nonzero" d="M3 5h3v3H3z"/><path fill="#34A2F2" fill-rule="nonzero" d="M7 5h3v3H7z"/><path fill="#34A2F2" fill-rule="nonzero" d="M5 1h3v3H5z"/></g></svg>
\ No newline at end of file
diff --git a/src/main/resources/icons/language/go/go.svg b/src/main/resources/icons/language/go/go.svg
new file mode 100644
index 0000000..41738b2
--- /dev/null
+++ b/src/main/resources/icons/language/go/go.svg
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 24.1.3, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
+<style type="text/css">
+	.st0{fill:none;}
+	.st1{fill-rule:evenodd;clip-rule:evenodd;fill:#2DE8C4;fill-opacity:0.7;}
+	.st2{fill-rule:evenodd;clip-rule:evenodd;fill:#9AA7B0;fill-opacity:0.8;}
+	.st3{fill-rule:evenodd;clip-rule:evenodd;fill:#A5D844;fill-opacity:0.7;}
+	.st4{fill-rule:evenodd;clip-rule:evenodd;fill:#E84242;fill-opacity:0.7;}
+	.st5{fill-rule:evenodd;clip-rule:evenodd;fill:#752EE5;fill-opacity:0.7;}
+	.st6{fill-rule:evenodd;clip-rule:evenodd;fill:#492A00;fill-opacity:0.7;}
+	.st7{fill-rule:evenodd;clip-rule:evenodd;fill:#ED6E06;fill-opacity:0.7;}
+	.st8{fill-rule:evenodd;clip-rule:evenodd;fill:#007535;fill-opacity:0.7;}
+	.st9{opacity:0.6;}
+</style>
+<rect id="frame_2_" x="0" class="st0" width="16" height="16"/>
+<path class="st4" d="M1,16h15V9H1V16z"/>
+<path class="st2" d="M7,1L3,5h4V1z"/>
+<path class="st2" d="M8,1v5H3v2h10V1H8z"/>
+<g class="st9">
+	<path d="M1.8,12.6v-0.2c0-1.9,1.1-2.5,2.4-2.5c0.6,0,1.1,0.1,1.4,0.3v0.8c-0.3-0.1-0.7-0.3-1.3-0.3c-1.1,0-1.4,0.7-1.4,1.6v0.2
+		c0,1,0.2,1.7,1.3,1.7c0.3,0,0.5-0.1,0.7-0.1v-1H4v-0.7h1.8v2.3c-0.4,0.1-1,0.3-1.6,0.3C2.7,15,1.8,14.4,1.8,12.6z"/>
+	<path d="M6.4,12.6v-0.2c0-1.6,1-2.4,2.3-2.4c1.3,0,2.2,0.8,2.2,2.4v0.2c0,1.6-1,2.4-2.3,2.4S6.4,14.2,6.4,12.6z M9.9,12.7v-0.2
+		c0-1.1-0.3-1.7-1.2-1.7c-0.8,0-1.2,0.6-1.2,1.6v0.2c0,1.1,0.4,1.7,1.2,1.7C9.5,14.3,9.9,13.7,9.9,12.7z"/>
+</g>
+</svg>
diff --git a/src/main/resources/icons/language/go/go_template.svg b/src/main/resources/icons/language/go/go_template.svg
new file mode 100644
index 0000000..933d7c4
--- /dev/null
+++ b/src/main/resources/icons/language/go/go_template.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><rect id="frame" width="16" height="16" fill="none"/><g fill="none" fill-rule="evenodd"><path fill="#77BBE2" d="M12.16 10H10.8L7 12.6v2.157c-1.963-.363-3.286-1.783-3.286-3.466 0 0 .147-1.142.21-2.379h-.01c.022-.29.038-.612.038-.989 0-1.539-.238-3.368-.238-3.368C3.714 2.6 5.5 1 8 1c2.5 0 4.286 1.6 4.286 3.556 0 0-.238 2.085-.238 3.367 0 .353.02.684.045.989h-.012c.02.37.05.746.079 1.088z"/><path fill="#77BBE2" d="M12.16 10H10.8L7 12.6v2.157c-1.963-.363-3.286-1.783-3.286-3.466 0 0 .147-1.142.21-2.379h-.01c.022-.29.038-.612.038-.989 0-1.539-.238-3.368-.238-3.368C3.714 2.6 5.5 1 8 1c2.5 0 4.286 1.6 4.286 3.556 0 0-.238 2.085-.238 3.367 0 .353.02.684.045.989h-.012c.02.37.05.746.079 1.088z"/><path fill="#77BBE2" d="M13 3.18c0 .495-.373.897-.833.897-.46 0-.834-.402-.834-.898 0-.495.374-.897.834-.897.46 0 .833.402.833.897m-10 0c0 .496.373.898.833.898.46 0 .834-.402.834-.898 0-.495-.374-.897-.834-.897-.46 0-.833.402-.833.897"/><path fill="#FFF" d="M7.743 5.078c0 .836-.688 1.514-1.538 1.514-.85 0-1.538-.678-1.538-1.514s.688-1.514 1.538-1.514c.85 0 1.538.678 1.538 1.514"/><path fill="#FFF" d="M7.743 5.078c0 .836-.688 1.514-1.538 1.514-.85 0-1.538-.678-1.538-1.514s.688-1.514 1.538-1.514c.85 0 1.538.678 1.538 1.514"/><path fill="#000" d="M6.993 5.073c0 .409-.307.74-.687.74-.38 0-.687-.331-.687-.74 0-.408.308-.74.687-.74.38 0 .687.332.687.74"/><path fill="#FFF" d="M11.137 5.078c0 .836-.688 1.514-1.538 1.514-.85 0-1.539-.678-1.539-1.514S8.75 3.564 9.6 3.564s1.538.678 1.538 1.514"/><path fill="#000" d="M10.387 5.073c0 .409-.308.74-.687.74-.38 0-.687-.331-.687-.74 0-.408.307-.74.687-.74.38 0 .687.332.687.74"/><path fill="#FFF" d="M7.523 7.923h.953v-.769h-.953z"/><path fill="#B7937F" d="M8.953 6.897c0 .284-.427.513-.953.513s-.952-.23-.952-.513c0-.283.426-.512.952-.512s.953.23.953.513"/><path fill="#000" d="M8.476 6.633c0 .288-.216.264-.484.264-.267 0-.484.024-.484-.264 0-.289.217-.522.484-.522.268 0 .484.233.484.522"/><path fill="#B7937F" fill-rule="nonzero" d="M13 8.271c0 .354-.267.641-.595.641-.329 0-.596-.287-.596-.64 0-.355.267-.642.596-.642.328 0 .595.287.595.641m-8.81 0c0 .354-.266.641-.595.641-.328 0-.595-.287-.595-.64 0-.355.267-.642.595-.642.33 0 .596.287.596.641m1 5.934c0 .354-.267.641-.596.641-.328 0-.595-.287-.595-.641 0-.354.267-.64.595-.64.329 0 .596.286.596.64"/><path fill="#231F20" fill-opacity=".7" d="M8 14v-1l3-2v1l-2.2 1.5L11 15v1z"/><path fill="#231F20" fill-opacity=".7" d="M16 14v-1l-3-2v1l2.2 1.5L13 15v1z"/></g></svg>
\ No newline at end of file
diff --git a/src/main/resources/icons/language/go/go_template_dark.svg b/src/main/resources/icons/language/go/go_template_dark.svg
new file mode 100644
index 0000000..ffe6285
--- /dev/null
+++ b/src/main/resources/icons/language/go/go_template_dark.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><rect id="frame" width="16" height="16" fill="none"/><g fill="none" fill-rule="evenodd"><path fill="#77BBE2" d="M12.16 10H10.8L7 12.6v2.157c-1.963-.363-3.286-1.783-3.286-3.466 0 0 .147-1.142.21-2.379h-.01c.022-.29.038-.612.038-.989 0-1.539-.238-3.367-.238-3.367C3.714 2.6 5.5 1 8 1c2.5 0 4.286 1.6 4.286 3.556 0 0-.238 2.085-.238 3.367 0 .352.02.684.045.989h-.012c.02.37.05.746.079 1.088z"/><path fill="#77BBE2" d="M12.16 10H10.8L7 12.6v2.157c-1.963-.363-3.286-1.783-3.286-3.466 0 0 .147-1.142.21-2.379h-.01c.022-.29.038-.612.038-.989 0-1.539-.238-3.367-.238-3.367C3.714 2.6 5.5 1 8 1c2.5 0 4.286 1.6 4.286 3.556 0 0-.238 2.085-.238 3.367 0 .352.02.684.045.989h-.012c.02.37.05.746.079 1.088z"/><path fill="#77BBE2" d="M13 3.18c0 .495-.373.897-.833.897-.46 0-.834-.402-.834-.898 0-.495.374-.897.834-.897.46 0 .833.402.833.897m-10 0c0 .496.373.898.833.898.46 0 .834-.402.834-.898 0-.495-.374-.897-.834-.897-.46 0-.833.402-.833.897"/><path fill="#FFF" d="M7.743 5.078c0 .836-.688 1.514-1.538 1.514-.85 0-1.538-.678-1.538-1.514s.688-1.514 1.538-1.514c.85 0 1.538.678 1.538 1.514"/><path fill="#FFF" d="M7.743 5.078c0 .836-.688 1.514-1.538 1.514-.85 0-1.538-.678-1.538-1.514s.688-1.514 1.538-1.514c.85 0 1.538.678 1.538 1.514"/><path fill="#000" d="M6.993 5.073c0 .409-.307.74-.687.74-.38 0-.687-.331-.687-.74 0-.408.308-.74.687-.74.38 0 .687.332.687.74"/><path fill="#FFF" d="M11.137 5.078c0 .836-.688 1.514-1.538 1.514-.85 0-1.539-.678-1.539-1.514S8.75 3.564 9.6 3.564s1.538.678 1.538 1.514"/><path fill="#000" d="M10.387 5.073c0 .409-.308.74-.687.74-.38 0-.687-.331-.687-.74 0-.408.307-.74.687-.74.38 0 .687.332.687.74"/><path fill="#FFF" d="M7.523 7.923h.953v-.769h-.953z"/><path fill="#B7937F" d="M8.953 6.897c0 .284-.427.513-.953.513s-.952-.23-.952-.513c0-.283.426-.512.952-.512s.953.23.953.513"/><path fill="#000" d="M8.476 6.633c0 .288-.216.264-.484.264-.267 0-.484.024-.484-.264 0-.289.217-.522.484-.522.268 0 .484.233.484.522"/><path fill="#B7937F" fill-rule="nonzero" d="M13 8.271c0 .354-.267.641-.595.641-.329 0-.596-.287-.596-.64 0-.355.267-.642.596-.642.328 0 .595.287.595.641m-8.81 0c0 .354-.266.641-.595.641-.328 0-.595-.287-.595-.64 0-.355.267-.642.595-.642.33 0 .596.287.596.641m1 5.934c0 .354-.267.641-.596.641-.328 0-.595-.287-.595-.641 0-.354.267-.64.595-.64.329 0 .596.286.596.64"/><path fill="#9AA7B0" d="M8 14v-1l3-2v1l-2.2 1.5L11 15v1z"/><path fill="#9AA7B0" d="M16 14v-1l-3-2v1l2.2 1.5L13 15v1z"/></g></svg>
\ No newline at end of file
diff --git a/src/main/resources/icons/language/java/java 2.svg b/src/main/resources/icons/language/java/java 2.svg
new file mode 100644
index 0000000..1371d72
--- /dev/null
+++ b/src/main/resources/icons/language/java/java 2.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><rect id="frame" width="16" height="16" fill="none"/><g fill="none" fill-rule="evenodd"><path fill="#40B6E0" fill-opacity=".7" d="M1 16h15V9H1z"/><path fill="#9AA7B0" fill-opacity=".8" d="M7 1L3 5h4z"/><path fill="#9AA7B0" fill-opacity=".8" d="M8 1v5H3v2h10V1z"/><path fill="#231F20" fill-opacity=".7" d="M3.395 13.588c.23.25.443.412.892.412.526 0 .713-.412.713-.702V10h1v3.588C6 14.32 5.327 15 4.45 15c-.821 0-1.3-.237-1.65-.68l.595-.732z"/></g></svg>
\ No newline at end of file
diff --git a/src/main/resources/icons/language/java/java.svg b/src/main/resources/icons/language/java/java.svg
new file mode 100755
index 0000000..1371d72
--- /dev/null
+++ b/src/main/resources/icons/language/java/java.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><rect id="frame" width="16" height="16" fill="none"/><g fill="none" fill-rule="evenodd"><path fill="#40B6E0" fill-opacity=".7" d="M1 16h15V9H1z"/><path fill="#9AA7B0" fill-opacity=".8" d="M7 1L3 5h4z"/><path fill="#9AA7B0" fill-opacity=".8" d="M8 1v5H3v2h10V1z"/><path fill="#231F20" fill-opacity=".7" d="M3.395 13.588c.23.25.443.412.892.412.526 0 .713-.412.713-.702V10h1v3.588C6 14.32 5.327 15 4.45 15c-.821 0-1.3-.237-1.65-.68l.595-.732z"/></g></svg>
\ No newline at end of file
diff --git a/src/main/resources/icons/language/javascript/javascript.svg b/src/main/resources/icons/language/javascript/javascript.svg
new file mode 100755
index 0000000..5f6e654
--- /dev/null
+++ b/src/main/resources/icons/language/javascript/javascript.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><rect id="frame" width="16" height="16" fill="none"/><g fill="none" fill-rule="evenodd"><path fill="#F4AF3D" fill-opacity=".7" d="M1 16h15V9H1z"/><path fill="#9AA7B0" fill-opacity=".8" d="M7 1L3 5h4z"/><path fill="#9AA7B0" fill-opacity=".8" d="M8 1v5H3v2h10V1z"/><path fill="#231F20" fill-opacity=".7" d="M2.395 13.588c.23.25.443.412.892.412.526 0 .713-.412.713-.702V10h1v3.588C5 14.32 4.327 15 3.45 15c-.821 0-1.3-.237-1.65-.68l.595-.732z"/><path fill="#231F20" fill-opacity=".7" d="M6.973 11.502c0-.369.311-.502.872-.502H9v-1H7.86C6.778 10 6 10.46 6 11.45c0 .865.42 1.24 1.471 1.466.77.165 1.025.313 1.025.632 0 .32-.358.452-1.025.452H6.3v1h1.171C9.5 15 9.5 14 9.5 13.548c0-.632-.48-1.127-1.341-1.401-.862-.274-1.186-.277-1.186-.645z"/></g></svg>
\ No newline at end of file
diff --git a/src/main/resources/icons/language/javascript/js.svg b/src/main/resources/icons/language/javascript/js.svg
new file mode 100644
index 0000000..04e1d30
--- /dev/null
+++ b/src/main/resources/icons/language/javascript/js.svg
@@ -0,0 +1 @@
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><rect id="frame" width="16" height="16" fill="none"/><path fill-rule="evenodd" clip-rule="evenodd" d="M2 13h12V3H2v10z" fill="#F4AF3D" fill-opacity=".6"/><path fill-rule="evenodd" clip-rule="evenodd" d="M9.167 6.802c0-.442.374-.602 1.047-.602H11.6V5h-1.368C8.934 5 8 5.551 8 6.74c0 1.037.503 1.489 1.765 1.76.925.198 1.23.374 1.23.758 0 .383-.429.542-1.23.542H8.36V11h1.405C12.2 11 12.2 9.8 12.2 9.258c0-.759-.576-1.353-1.61-1.682-1.033-.328-1.423-.332-1.423-.774z" fill="#231F20" fill-opacity=".7"/><path fill-rule="evenodd" clip-rule="evenodd" d="M3.674 9.305c.275.3.531.495 1.07.495.632 0 .856-.495.856-.843V5h1.2v4.305C6.8 10.184 5.992 11 4.94 11c-.985 0-1.56-.285-1.98-.816l.714-.879z" fill="#231F20" fill-opacity=".7"/></svg>
\ No newline at end of file
diff --git a/src/main/resources/icons/language/javascript/nodejs.svg b/src/main/resources/icons/language/javascript/nodejs.svg
new file mode 100644
index 0000000..6e01ea5
--- /dev/null
+++ b/src/main/resources/icons/language/javascript/nodejs.svg
@@ -0,0 +1 @@
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><rect id="frame" width="16" height="16" fill="none"/><path fill-rule="evenodd" clip-rule="evenodd" d="M7.991 14.991a1.07 1.07 0 0 1-.533-.143l-1.697-1.004c-.254-.142-.13-.192-.046-.221a3.43 3.43 0 0 0 .767-.35c.038-.022.088-.014.126.009l1.304.774a.172.172 0 0 0 .158 0l5.085-2.935a.16.16 0 0 0 .077-.138V5.116c0-.059-.03-.11-.079-.14L8.072 2.044a.156.156 0 0 0-.157 0L2.833 4.976a.162.162 0 0 0-.08.139v5.867c0 .056.03.11.078.136l1.393.804c.756.378 1.218-.067 1.218-.515v-5.79c0-.084.065-.147.147-.147h.644c.08 0 .147.063.147.146v5.793c0 1.008-.55 1.588-1.506 1.588-.294 0-.526 0-1.172-.319l-1.334-.768a1.075 1.075 0 0 1-.533-.928V5.115c0-.382.203-.739.533-.927l5.09-2.936c.322-.182.75-.182 1.069 0l5.083 2.937c.328.19.533.545.533.927v5.867c0 .38-.205.736-.533.927l-5.083 2.936a1.068 1.068 0 0 1-.536.142" fill="#689F63"/><path d="M9.562 10.948c-2.225 0-2.691-1.021-2.691-1.878 0-.081.066-.146.147-.146h.657c.072 0 .133.053.145.124.1.67.394 1.007 1.74 1.007 1.071 0 1.527-.242 1.527-.81 0-.327-.13-.57-1.795-.733C7.9 8.374 7.04 8.068 7.04 6.954c0-1.026.865-1.637 2.315-1.637 1.628 0 2.435.565 2.536 1.78a.148.148 0 0 1-.147.16h-.66a.146.146 0 0 1-.143-.115c-.16-.704-.544-.929-1.589-.929-1.17 0-1.306.407-1.306.713 0 .37.16.478 1.739.687 1.563.208 2.305.5 2.305 1.599 0 1.107-.924 1.742-2.535 1.742l.007-.006z" fill="#689F63"/></svg>
\ No newline at end of file
diff --git a/src/main/resources/icons/language/php/localPHP_interpreter.svg b/src/main/resources/icons/language/php/localPHP_interpreter.svg
new file mode 100644
index 0000000..ff68b77
--- /dev/null
+++ b/src/main/resources/icons/language/php/localPHP_interpreter.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><rect id="frame" width="16" height="16" fill="none"/><g fill="none" fill-rule="evenodd" fill-opacity=".7"><path fill="#B99BF8" d="M0 13h16V3H0z"/><path fill="#231F20" d="M2.9 8.5c.664 0 1.1-.116 1.1-.75S3.557 7 2.9 7H2v1.5h.9zM1 6h1.971C4.142 6 5 6.642 5 7.65 5 9.286 3.971 9.5 2.872 9.5H2V11H1V6z"/><path fill="#231F20" d="M12.9 8.5c.664 0 1.1-.116 1.1-.75S13.557 7 12.9 7H12v1.5h.9zM11 6h1.971C14.142 6 15 6.642 15 7.65c0 1.636-1.029 1.85-2.128 1.85H12V11h-1V6z"/><path fill="#231F20" d="M6 6h1v2h2V6h1v5H9V9H7v2H6z"/></g></svg>
\ No newline at end of file
diff --git a/src/main/resources/icons/language/php/php-icon.svg b/src/main/resources/icons/language/php/php-icon.svg
new file mode 100644
index 0000000..0d1e04b
--- /dev/null
+++ b/src/main/resources/icons/language/php/php-icon.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><rect id="frame" width="16" height="16" fill="none"/><g fill="none" fill-rule="evenodd"><path fill="#B99BF8" fill-opacity=".7" d="M1 16h15V9H1z"/><path fill="#9AA7B0" fill-opacity=".8" d="M7 1L3 5h4z"/><path fill="#9AA7B0" fill-opacity=".8" d="M8 1v5H3v2h10V1z"/><path fill="#231F20" fill-opacity=".7" d="M3.9 12.5c.664 0 1.1-.116 1.1-.75S4.557 11 3.9 11H3v1.5h.9zM2 10h1.971C5.142 10 6 10.642 6 11.65c0 1.636-1.029 1.85-2.128 1.85H3V15H2v-5z"/><path fill="#231F20" fill-opacity=".7" d="M13.9 12.5c.664 0 1.1-.116 1.1-.75s-.443-.75-1.1-.75H13v1.5h.9zM12 10h1.971c1.171 0 2.029.642 2.029 1.65 0 1.636-1.029 1.85-2.128 1.85H13V15h-1v-5z"/><path fill="#231F20" fill-opacity=".7" d="M7 10h1v2h2v-2h1v5h-1v-2H8v2H7z"/></g></svg>
\ No newline at end of file
diff --git a/src/main/resources/icons/language/php/php.svg b/src/main/resources/icons/language/php/php.svg
new file mode 100644
index 0000000..77e8e9a
--- /dev/null
+++ b/src/main/resources/icons/language/php/php.svg
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 24.1.3, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
+<style type="text/css">
+	.st0{fill:none;}
+	.st1{fill-rule:evenodd;clip-rule:evenodd;fill:#2DE8C4;fill-opacity:0.7;}
+	.st2{fill-rule:evenodd;clip-rule:evenodd;fill:#9AA7B0;fill-opacity:0.8;}
+	.st3{fill-rule:evenodd;clip-rule:evenodd;fill:#A5D844;fill-opacity:0.7;}
+	.st4{fill-rule:evenodd;clip-rule:evenodd;fill:#E84242;fill-opacity:0.7;}
+	.st5{fill-rule:evenodd;clip-rule:evenodd;fill:#752EE5;fill-opacity:0.7;}
+	.st6{fill-rule:evenodd;clip-rule:evenodd;fill:#492A00;fill-opacity:0.7;}
+	.st7{fill-rule:evenodd;clip-rule:evenodd;fill:#ED6E06;fill-opacity:0.7;}
+	.st8{fill-rule:evenodd;clip-rule:evenodd;fill:#007535;fill-opacity:0.7;}
+	.st9{opacity:0.6;}
+</style>
+<rect id="frame_4_" x="0" class="st0" width="16" height="16"/>
+<path class="st8" d="M1,16h15V9H1V16z"/>
+<path class="st2" d="M7,1L3,5h4V1z"/>
+<path class="st2" d="M8,1v5H3v2h10V1H8z"/>
+<g class="st9">
+	<path d="M1.9,10.2h1.8c1.2,0,1.8,0.5,1.8,1.5c0,1-0.7,1.6-1.7,1.6H2.9V15h-1V10.2z M3.6,12.5c0.6,0,0.9-0.3,0.9-0.8
+		c0-0.6-0.2-0.8-0.8-0.8H2.9v1.6H3.6z"/>
+	<path d="M6.1,10.2h1v2h1.9v-2h1V15h-1v-2.1H7.1V15h-1V10.2z"/>
+	<path d="M11.1,10.2h1.8c1.2,0,1.8,0.5,1.8,1.5c0,1-0.7,1.6-1.7,1.6h-0.8V15h-1V10.2z M12.7,12.5c0.6,0,0.9-0.3,0.9-0.8
+		c0-0.6-0.2-0.8-0.8-0.8h-0.7v1.6H12.7z"/>
+</g>
+</svg>
diff --git a/src/main/resources/icons/language/python/py.svg b/src/main/resources/icons/language/python/py.svg
new file mode 100644
index 0000000..e231e14
--- /dev/null
+++ b/src/main/resources/icons/language/python/py.svg
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 24.1.3, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
+<style type="text/css">
+	.st0{fill:none;}
+	.st1{fill-rule:evenodd;clip-rule:evenodd;fill:#2DE8C4;fill-opacity:0.7;}
+	.st2{fill-rule:evenodd;clip-rule:evenodd;fill:#9AA7B0;fill-opacity:0.8;}
+	.st3{fill-rule:evenodd;clip-rule:evenodd;fill:#A5D844;fill-opacity:0.7;}
+	.st4{fill-rule:evenodd;clip-rule:evenodd;fill:#E84242;fill-opacity:0.7;}
+	.st5{fill-rule:evenodd;clip-rule:evenodd;fill:#752EE5;fill-opacity:0.7;}
+	.st6{fill-rule:evenodd;clip-rule:evenodd;fill:#492A00;fill-opacity:0.7;}
+	.st7{fill-rule:evenodd;clip-rule:evenodd;fill:#ED6E06;fill-opacity:0.7;}
+	.st8{fill-rule:evenodd;clip-rule:evenodd;fill:#007535;fill-opacity:0.7;}
+	.st9{opacity:0.6;}
+</style>
+<rect id="frame_3_" x="0" class="st0" width="16" height="16"/>
+<path class="st3" d="M1,16h15V9H1V16z"/>
+<path class="st2" d="M7,1L3,5h4V1z"/>
+<path class="st2" d="M8,1v5H3v2h10V1H8z"/>
+<g class="st9">
+	<path d="M1.8,10h1.8c1.2,0,1.8,0.5,1.8,1.5c0,1-0.7,1.6-1.7,1.6H2.8v1.8h-1V10z M3.5,12.4c0.6,0,0.9-0.3,0.9-0.8
+		c0-0.6-0.2-0.8-0.8-0.8H2.8v1.6H3.5z"/>
+	<path d="M7.2,13l-1.6-3h1.1l1,2.1h0l1-2.1h1l-1.6,3v1.9h-1V13z"/>
+</g>
+</svg>
diff --git a/src/main/resources/icons/language/python/python.svg b/src/main/resources/icons/language/python/python.svg
new file mode 100644
index 0000000..ef015b5
--- /dev/null
+++ b/src/main/resources/icons/language/python/python.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><rect id="frame" width="16" height="16" fill="none"/><g fill="none" fill-opacity=".9"><path fill="#40A0CF" d="M7.91 1.026c-.54 0-1.08.046-1.612.136-1.428.25-1.687.772-1.687 1.735v1.271h3.374v.424h-4.64a2.102 2.102 0 0 0-2.108 1.692 6.172 6.172 0 0 0 0 3.391 1.879 1.879 0 0 0 1.794 1.692h1.16V9.842A2.132 2.132 0 0 1 6.297 7.77h3.37a1.69 1.69 0 0 0 1.687-1.696V2.897a1.872 1.872 0 0 0-1.687-1.735 10.636 10.636 0 0 0-1.758-.136zM6.086 2.049A.636.636 0 1 1 6.09 3.32a.636.636 0 0 1-.005-1.271z"/><path fill="#FFC900" d="M11.775 6.074A2.152 2.152 0 0 1 9.668 8.19h-3.37a1.711 1.711 0 0 0-1.687 1.695v3.178c.11.883.804 1.58 1.687 1.695 1.098.34 2.272.34 3.37 0a1.925 1.925 0 0 0 1.687-1.695V11.79h-3.37v-.424h5.057c.98 0 1.345-.676 1.686-1.692a5.667 5.667 0 0 0 0-3.391 1.801 1.801 0 0 0-1.686-1.692h-1.267v1.482zM9.88 12.639a.636.636 0 1 1 .006 1.271.636.636 0 0 1-.006-1.271z"/></g></svg>
\ No newline at end of file
diff --git a/src/main/resources/icons/language/python/pythonFile.svg b/src/main/resources/icons/language/python/pythonFile.svg
new file mode 100644
index 0000000..1f17d0f
--- /dev/null
+++ b/src/main/resources/icons/language/python/pythonFile.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><rect id="frame" width="16" height="16" fill="none"/><g fill="none" fill-rule="evenodd"><path fill="#9AA7B0" fill-opacity=".8" d="M7 1L3 5h4z"/><path fill="#9AA7B0" fill-opacity=".8" d="M8 15v-.703c-.633-.097-1.117-.405-1.45-.923-.5-.777-.615-1.938-.352-2.886a2.49 2.49 0 0 1 2.384-1.97v-.214a2.081 2.081 0 0 1 .54-1.549c.384-.42.887-.743 1.457-.755.437-.074.916-.033 1.36-.033a5.49 5.49 0 0 1 1.061.11V1H8v5H3v9h5z"/><path fill="#40A0CF" fill-opacity=".9" fill-rule="nonzero" d="M11.491 6.878c-.35 0-.701.03-1.047.089-.928.162-1.097.501-1.097 1.127v.827h2.193v.275H8.524a1.366 1.366 0 0 0-1.37 1.1 4.011 4.011 0 0 0 0 2.204c.06.606.558 1.075 1.166 1.1h.754v-.991a1.386 1.386 0 0 1 1.37-1.347h2.19a1.099 1.099 0 0 0 1.096-1.103V8.094a1.217 1.217 0 0 0-1.096-1.127 6.913 6.913 0 0 0-1.143-.089zm-1.186.665a.413.413 0 1 1 .004.827.413.413 0 0 1-.004-.827z"/><path fill="#FFC900" fill-opacity=".9" fill-rule="nonzero" d="M14.004 9.196v.963a1.399 1.399 0 0 1-1.37 1.376h-2.19a1.112 1.112 0 0 0-1.097 1.102v2.065a1.27 1.27 0 0 0 1.097 1.102c.713.22 1.476.22 2.19 0a1.251 1.251 0 0 0 1.096-1.102v-.827h-2.19V13.6h3.286c.638 0 .875-.44 1.097-1.1a3.683 3.683 0 0 0 0-2.204 1.17 1.17 0 0 0-1.097-1.1h-.822zm-1.232 5.23a.413.413 0 1 1 .003.827.413.413 0 0 1-.003-.827z"/></g></svg>
\ No newline at end of file
diff --git a/src/main/resources/icons/language/ruby/rb.svg b/src/main/resources/icons/language/ruby/rb.svg
new file mode 100644
index 0000000..1e99135
--- /dev/null
+++ b/src/main/resources/icons/language/ruby/rb.svg
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 24.1.3, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
+<style type="text/css">
+	.st0{fill:none;}
+	.st1{fill-rule:evenodd;clip-rule:evenodd;fill:#2DE8C4;fill-opacity:0.7;}
+	.st2{fill-rule:evenodd;clip-rule:evenodd;fill:#9AA7B0;fill-opacity:0.8;}
+	.st3{fill-rule:evenodd;clip-rule:evenodd;fill:#A5D844;fill-opacity:0.7;}
+	.st4{fill-rule:evenodd;clip-rule:evenodd;fill:#E84242;fill-opacity:0.7;}
+	.st5{fill-rule:evenodd;clip-rule:evenodd;fill:#752EE5;fill-opacity:0.7;}
+	.st6{fill-rule:evenodd;clip-rule:evenodd;fill:#492A00;fill-opacity:0.7;}
+	.st7{fill-rule:evenodd;clip-rule:evenodd;fill:#ED6E06;fill-opacity:0.7;}
+	.st8{fill-rule:evenodd;clip-rule:evenodd;fill:#007535;fill-opacity:0.7;}
+	.st9{opacity:0.6;}
+</style>
+<rect id="frame" x="0" class="st0" width="16" height="16"/>
+<path class="st1" d="M1,16h15V9H1V16z"/>
+<path class="st2" d="M7,1L3,5h4V1z"/>
+<path class="st2" d="M8,1v5H3v2h10V1H8z"/>
+<g class="st9">
+	<path d="M1.8,10h1.7c1.1,0,1.8,0.5,1.8,1.4c0,0.7-0.3,1-0.9,1.2v0l1.2,2.1H4.5l-1-1.9H2.8v1.9h-1V10z M3.4,12.3
+		c0.6,0,0.8-0.3,0.8-0.8c0-0.5-0.2-0.8-0.8-0.8H2.8v1.5H3.4z"/>
+	<path d="M6.2,10H8c1.1,0,1.6,0.4,1.6,1.2c0,0.5-0.2,0.9-0.8,1.1v0c0.6,0.1,1,0.4,1,1.2c0,0.8-0.5,1.4-1.7,1.4H6.2V10z M7.8,12.1
+		c0.5,0,0.8-0.2,0.8-0.7c0-0.5-0.3-0.7-0.8-0.7H7.2v1.3H7.8z M7.9,14.2c0.6,0,0.9-0.2,0.9-0.8c0-0.5-0.3-0.7-0.9-0.7H7.2v1.5H7.9z"
+		/>
+</g>
+</svg>
diff --git a/src/main/resources/icons/language/ruby/ruby.svg b/src/main/resources/icons/language/ruby/ruby.svg
new file mode 100644
index 0000000..10b36c0
--- /dev/null
+++ b/src/main/resources/icons/language/ruby/ruby.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><rect id="frame" width="16" height="16" fill="none"/><path fill="#F26522" fill-opacity=".7" fill-rule="evenodd" d="M13.6 3H2.401L1 5.444 8 14l7-8.556z"/></svg>
\ No newline at end of file
diff --git a/src/main/resources/icons/language/ruby/ruby_file.svg b/src/main/resources/icons/language/ruby/ruby_file.svg
new file mode 100644
index 0000000..d882654
--- /dev/null
+++ b/src/main/resources/icons/language/ruby/ruby_file.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><rect id="frame" width="16" height="16" fill="none"/><g fill="none" fill-rule="evenodd"><path fill="#9AA7B0" fill-opacity=".8" d="M6.624 15l3.59-4.344L8.676 8H3V6h5V1h5v14H6.624zM7 1v4H3l4-4z"/><path fill="#F26522" fill-opacity=".7" d="M8.1 9H.901L0 10.556 4.5 16 9 10.556z"/></g></svg>
\ No newline at end of file
diff --git a/src/main/resources/icons/language/ruby/rxml.svg b/src/main/resources/icons/language/ruby/rxml.svg
new file mode 100644
index 0000000..6a81c61
--- /dev/null
+++ b/src/main/resources/icons/language/ruby/rxml.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><rect id="frame" width="16" height="16" fill="none"/><g fill="none" fill-rule="evenodd"><path fill="#9AA7B0" fill-opacity=".8" d="M7 1L3 5h4z"/><path fill="#9AA7B0" fill-opacity=".8" d="M8 1v5H3v2h10V1z"/><path fill="#F26522" fill-opacity=".6" d="M1 16h15V9H1z"/><path fill="#231F20" fill-opacity=".7" d="M3 15v-5h2c1.156 0 2 .6 2 1.613 0 1.014-.29 1.507-.978 1.706L7.263 15H6.127l-1.221-1.6H4V15H3zm1-2.531h1.111c.544 0 .89-.288.89-.732v-.012c0-.469-.334-.725-.896-.725H4v1.469z"/></g></svg>
\ No newline at end of file
diff --git a/src/main/resources/icons/language/swift/SwiftLang.svg b/src/main/resources/icons/language/swift/SwiftLang.svg
new file mode 100644
index 0000000..3d51565
--- /dev/null
+++ b/src/main/resources/icons/language/swift/SwiftLang.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><rect id="frame" width="16" height="16" fill="none"/><path fill="#EB4548" fill-rule="evenodd" d="M11.56 13.1c-1.65.988-3.917 1.09-6.198.076-1.847-.814-3.38-2.24-4.362-3.869.472.408 1.022.734 1.611 1.018 2.356 1.145 4.712 1.066 6.37.003-.002 0-.003-.001-.003-.003C6.62 8.452 4.615 6.01 3.122 4.013c-.314-.325-.55-.733-.786-1.1 1.808 1.711 4.677 3.87 5.698 4.48C5.874 5.032 3.947 2.1 4.026 2.182c3.419 3.584 6.602 5.62 6.602 5.62.106.061.187.113.252.158.07-.181.13-.37.18-.565.55-2.077-.078-4.44-1.454-6.394 3.183 1.995 5.07 5.742 4.284 8.878-.02.084-.043.168-.067.25l.028.035c1.571 2.036 1.14 4.194.943 3.787-.853-1.729-2.431-1.2-3.234-.85z"/></svg>
\ No newline at end of file
diff --git a/src/main/resources/icons/language/swift/sw.svg b/src/main/resources/icons/language/swift/sw.svg
new file mode 100644
index 0000000..b3853c8
--- /dev/null
+++ b/src/main/resources/icons/language/swift/sw.svg
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 24.1.3, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
+<style type="text/css">
+	.st0{fill:none;}
+	.st1{fill-rule:evenodd;clip-rule:evenodd;fill:#2DE8C4;fill-opacity:0.7;}
+	.st2{fill-rule:evenodd;clip-rule:evenodd;fill:#9AA7B0;fill-opacity:0.8;}
+	.st3{fill-rule:evenodd;clip-rule:evenodd;fill:#A5D844;fill-opacity:0.7;}
+	.st4{fill-rule:evenodd;clip-rule:evenodd;fill:#E84242;fill-opacity:0.7;}
+	.st5{fill-rule:evenodd;clip-rule:evenodd;fill:#752EE5;fill-opacity:0.7;}
+	.st6{fill-rule:evenodd;clip-rule:evenodd;fill:#492A00;fill-opacity:0.7;}
+	.st7{fill-rule:evenodd;clip-rule:evenodd;fill:#ED6E06;fill-opacity:0.7;}
+	.st8{fill-rule:evenodd;clip-rule:evenodd;fill:#007535;fill-opacity:0.7;}
+	.st9{opacity:0.6;}
+</style>
+<rect id="frame_5_" x="0" class="st0" width="16" height="16"/>
+<path class="st7" d="M1,16h15V9H1V16z"/>
+<path class="st2" d="M7,1L3,5h4V1z"/>
+<path class="st2" d="M8,1v5H3v2h10V1H8z"/>
+<g class="st9">
+	<path d="M1.8,14.7V14c0.4,0.2,0.9,0.3,1.4,0.3c0.6,0,0.9-0.3,0.9-0.6c0-0.4-0.2-0.5-0.7-0.7L3,12.8c-0.7-0.2-1.1-0.5-1.1-1.4
+		c0-0.8,0.5-1.4,1.7-1.4c0.7,0,1,0.1,1.3,0.3V11c-0.3-0.1-0.7-0.2-1.3-0.2c-0.5,0-0.8,0.2-0.8,0.6c0,0.3,0.2,0.5,0.6,0.6l0.4,0.1
+		c0.8,0.2,1.3,0.5,1.3,1.4c0,1-0.6,1.5-1.8,1.5C2.6,15,2.1,14.9,1.8,14.7z"/>
+	<path d="M5.4,10.1h1l0.8,3.4h0l0.9-3.4H9l0.9,3.4h0l0.8-3.4h0.9l-1.2,4.9h-1l-0.9-3.4h0l-0.9,3.4h-1L5.4,10.1z"/>
+</g>
+</svg>
diff --git a/src/main/resources/icons/openDiskHover.svg b/src/main/resources/icons/openDiskHover.svg
new file mode 100644
index 0000000..39d0a38
--- /dev/null
+++ b/src/main/resources/icons/openDiskHover.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><rect id="frame" width="16" height="16" fill="none"/><path fill="#7F8B91" fill-opacity=".9" fill-rule="evenodd" d="M3.49 7L1 11.885V3h4.999L7 5H14v2H3.49zm.615 1H16l-3.098 6H1l3.105-6z"/></svg>
\ No newline at end of file
diff --git a/src/main/resources/icons/run_anything.svg b/src/main/resources/icons/run_anything.svg
new file mode 100644
index 0000000..790a8fe
--- /dev/null
+++ b/src/main/resources/icons/run_anything.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><rect id="frame" width="16" height="16" fill="none"/><g fill="none" fill-rule="evenodd"><path fill="#6E6E6E" d="M1 2h14v12H1V2zm1 3v8h12V5H2z"/><path fill="#59A869" d="M4 6l4.667 3L4 12z"/></g></svg>
\ No newline at end of file
diff --git a/src/main/resources/icons/seq.svg b/src/main/resources/icons/seq.svg
new file mode 100644
index 0000000..bedff5e
--- /dev/null
+++ b/src/main/resources/icons/seq.svg
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 24.1.3, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
+<style type="text/css">
+	.st0{fill:none;}
+	.st1{fill-rule:evenodd;clip-rule:evenodd;fill:#2DE8C4;fill-opacity:0.7;}
+	.st2{fill-rule:evenodd;clip-rule:evenodd;fill:#9AA7B0;fill-opacity:0.8;}
+	.st3{fill-rule:evenodd;clip-rule:evenodd;fill:#A5D844;fill-opacity:0.7;}
+	.st4{fill-rule:evenodd;clip-rule:evenodd;fill:#E84242;fill-opacity:0.7;}
+	.st5{fill-rule:evenodd;clip-rule:evenodd;fill:#752EE5;fill-opacity:0.7;}
+	.st6{fill-rule:evenodd;clip-rule:evenodd;fill:#492A00;fill-opacity:0.7;}
+	.st7{fill-rule:evenodd;clip-rule:evenodd;fill:#ED6E06;fill-opacity:0.7;}
+	.st8{fill-rule:evenodd;clip-rule:evenodd;fill:#007535;fill-opacity:0.7;}
+	.st9{opacity:0.6;}
+</style>
+<rect id="frame_6_" x="0" class="st0" width="16" height="16"/>
+<path class="st6" d="M1,16h15V9H1V16z"/>
+<path class="st2" d="M7,1L3,5h4V1z"/>
+<path class="st2" d="M8,1v5H3v2h10V1H8z"/>
+<g class="st9">
+	<path d="M1.8,14.7V14c0.4,0.2,0.9,0.3,1.4,0.3c0.6,0,0.9-0.3,0.9-0.6c0-0.4-0.2-0.5-0.7-0.7L3,12.8c-0.7-0.2-1.1-0.5-1.1-1.4
+		c0-0.8,0.5-1.4,1.7-1.4c0.7,0,1,0.1,1.3,0.3V11c-0.3-0.1-0.7-0.2-1.3-0.2c-0.5,0-0.8,0.2-0.8,0.6c0,0.3,0.2,0.5,0.6,0.6l0.4,0.1
+		c0.8,0.2,1.3,0.5,1.3,1.4c0,1-0.6,1.5-1.8,1.5C2.6,15,2.1,14.9,1.8,14.7z"/>
+	<path d="M5.8,10.1h3.1v0.8H6.8v1.2h1.7v0.8H6.8v1.3H9v0.8H5.8V10.1z"/>
+	<path d="M12.7,14.7l1.5,0.3L14,15.8L11.3,15c-1-0.3-1.7-0.9-1.7-2.4v-0.2c0-1.6,1-2.4,2.3-2.4c1.3,0,2.2,0.8,2.2,2.4v0.1
+		C14.1,13.7,13.6,14.4,12.7,14.7z M13,12.7v-0.2c0-1.1-0.3-1.7-1.2-1.7c-0.8,0-1.2,0.6-1.2,1.6v0.3c0,1.1,0.4,1.7,1.2,1.7
+		C12.6,14.3,13,13.7,13,12.7z"/>
+</g>
+</svg>
diff --git a/src/main/resources/icons/sh.svg b/src/main/resources/icons/sh.svg
new file mode 100644
index 0000000..26473bf
--- /dev/null
+++ b/src/main/resources/icons/sh.svg
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 24.1.3, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
+<style type="text/css">
+	.st0{fill:none;}
+	.st1{fill-rule:evenodd;clip-rule:evenodd;fill:#2DE8C4;fill-opacity:0.7;}
+	.st2{fill-rule:evenodd;clip-rule:evenodd;fill:#9AA7B0;fill-opacity:0.8;}
+	.st3{fill-rule:evenodd;clip-rule:evenodd;fill:#A5D844;fill-opacity:0.7;}
+	.st4{fill-rule:evenodd;clip-rule:evenodd;fill:#E84242;fill-opacity:0.7;}
+	.st5{fill-rule:evenodd;clip-rule:evenodd;fill:#752EE5;fill-opacity:0.7;}
+	.st6{fill-rule:evenodd;clip-rule:evenodd;fill:#492A00;fill-opacity:0.7;}
+	.st7{fill-rule:evenodd;clip-rule:evenodd;fill:#ED6E06;fill-opacity:0.7;}
+	.st8{fill-rule:evenodd;clip-rule:evenodd;fill:#007535;fill-opacity:0.7;}
+	.st9{opacity:0.6;}
+</style>
+<rect id="frame_7_" x="0" class="st0" width="16" height="16"/>
+<path class="st5" d="M1,16h15V9H1V16z"/>
+<path class="st2" d="M7,1L3,5h4V1z"/>
+<path class="st2" d="M8,1v5H3v2h10V1H8z"/>
+<g class="st9">
+	<path d="M1.8,14.7V14c0.4,0.2,0.9,0.3,1.4,0.3c0.6,0,0.9-0.3,0.9-0.6c0-0.4-0.2-0.5-0.7-0.7L3,12.8c-0.7-0.2-1.1-0.5-1.1-1.4
+		c0-0.8,0.5-1.4,1.7-1.4c0.7,0,1,0.1,1.3,0.3V11c-0.3-0.1-0.7-0.2-1.3-0.2c-0.5,0-0.8,0.2-0.8,0.6c0,0.3,0.2,0.5,0.6,0.6l0.4,0.1
+		c0.8,0.2,1.3,0.5,1.3,1.4c0,1-0.6,1.5-1.8,1.5C2.6,15,2.1,14.9,1.8,14.7z"/>
+	<path d="M5.8,10.1h1v2h1.9v-2h1v4.9h-1v-2.1H6.8v2.1h-1V10.1z"/>
+</g>
+</svg>
diff --git a/src/main/resources/icons/ssh.svg b/src/main/resources/icons/ssh.svg
new file mode 100644
index 0000000..99c2add
--- /dev/null
+++ b/src/main/resources/icons/ssh.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><rect id="frame" width="16" height="16" fill="none"/><path fill="#9AA7B0" fill-opacity=".8" fill-rule="evenodd" d="M7.064 7.933L4.76 10.066l1.272 1.178 3.564-3.3-.012-.01.012-.012-3.564-3.3L4.76 5.8l2.304 2.133zM2 3h12v10H2V3z"/></svg>
\ No newline at end of file
diff --git a/src/main/resources/icons/yaml.svg b/src/main/resources/icons/yaml.svg
new file mode 100644
index 0000000..4a155c7
--- /dev/null
+++ b/src/main/resources/icons/yaml.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><rect id="frame" width="16" height="16" fill="none"/><g fill="none" fill-rule="evenodd"><path fill="#F98B9E" fill-opacity=".7" d="M1 16h15V9H1z"/><path fill="#9AA7B0" fill-opacity=".8" d="M7 1L3 5h4z"/><path fill="#9AA7B0" fill-opacity=".8" d="M8 1v5H3v2h10V1z"/><path fill="#231F20" fill-opacity=".7" d="M2.996 15v-1.971L1 10h1.05l1.446 2.207L4.95 10H6l-2 3.007V15z"/><path fill="#231F20" fill-opacity=".7" d="M6 10h.936L8.5 12l1.479-2H11v5h-1v-3.3l-1.5 1.8L7 11.7V15H6z"/><path fill="#231F20" fill-opacity=".7" d="M12 10h1v4h2.5v1H12z"/></g></svg>
\ No newline at end of file
diff --git a/src/main/resources/template/HOW-TO-DEPLOY.md b/src/main/resources/template/HOW-TO-DEPLOY.md
new file mode 100644
index 0000000..a63853b
--- /dev/null
+++ b/src/main/resources/template/HOW-TO-DEPLOY.md
@@ -0,0 +1,75 @@
+# What is wskdeploy
+
+`wskdeploy` is a utility to help you deploy any part of the OpenWhisk programming model using a Manifest file written in YAML. Use it to deploy all your OpenWhisk Packages, Actions, Triggers, and Rules!
+
+## Downloading wskdeploy
+
+Binaries of wskdeploy are available for download on the project's GitHub release page.
+https://github.com/apache/openwhisk-wskdeploy/releases
+
+## Getting started
+
+You can find various examples in the official documentation.
+https://github.com/apache/openwhisk-wskdeploy/blob/master/docs/programming_guide.md#getting-started
+
+
+# The "Hello World" sample wskdeploy project
+
+## Files included
+
+The following files are included in the sample project by default:
+- manifest.yaml : Manifest file for wskdeploy
+- src/index.js : Sample action code
+- src/index.test.js : Test code for the action code
+- src/package.json : package.json for NPM
+
+## How to deploy the project
+
+You can run the deploy command from the wskdeploy manifests explorer in the VSCode sidebar or run the following command in the terminal:
+
+```
+$ wskdeploy -m manifest.yaml
+```
+
+### Including dependencies
+
+The sample project deploys only the function code in the index.js file. But sometimes you need to add NPM or your dependencies.
+
+In this case, simply use a directory path as a function endpoint to compress and deploy all the files.
+
+```yaml
+packages:
+  hello_world_package:
+    actions:
+      hello_world:
+        # use src directory to deploy the action
+        # or deploy single code (function: index.js)
+        function: src
+        runtime: nodejs:default
+```
+
+Install NPM dependency modules and deploy wskdeploy project:
+(Note that wskdeploy does not automatically install npm dependencies.)
+
+```
+$ cd src && npm install --production
+$ wskdeploy -m manifest.yaml
+```
+
+## How to invoke the action
+
+You can invoke the action in the openwhisk explorer located in the activity bar or invoke it with the following command:
+
+```
+$ wsk action invoke hello_world_package/hello_world --blocking
+```
+
+## Test action code in your local environment
+
+The jest dependency is included by default. So you can test with `npm run test` command.
+
+```
+$ cd src
+$ npm install
+$ npm run test
+```
\ No newline at end of file
diff --git a/src/main/resources/template/manifest.yaml b/src/main/resources/template/manifest.yaml
new file mode 100644
index 0000000..4b6f5d0
--- /dev/null
+++ b/src/main/resources/template/manifest.yaml
@@ -0,0 +1,46 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
+#
+
+packages:
+  hello_world_package:
+    version: 1.0
+    license: Apache-2.0
+    actions:
+      hello_world:
+        function: src/index.js
+        runtime: nodejs:default
+        inputs:
+          name: string
+          place: string
+          children: integer
+          height: float
+        outputs:
+          greeting: string
+          details: string
+
+    triggers:
+      meetPerson:
+        inputs:
+          name: Sam
+          place: the Shire
+          children: 13
+          height: 1.2
+
+    rules:
+      meetPersonRule:
+        trigger: meetPerson
+        action: hello_world
\ No newline at end of file
diff --git a/src/main/resources/template/src/index.js b/src/main/resources/template/src/index.js
new file mode 100644
index 0000000..fc7490a
--- /dev/null
+++ b/src/main/resources/template/src/index.js
@@ -0,0 +1,7 @@
+function main(params) {
+    const msg = 'Hello, ' + params.name + ' from ' + params.place;
+    const family = 'You have ' + params.children + ' children ';
+    const stats = 'and are ' + params.height + ' m. tall.';
+    return { greeting: msg, details: family + stats };
+}
+module.exports.main = main;
\ No newline at end of file
diff --git a/src/main/resources/template/src/index.test.js b/src/main/resources/template/src/index.test.js
new file mode 100644
index 0000000..8f7ac66
--- /dev/null
+++ b/src/main/resources/template/src/index.test.js
@@ -0,0 +1,12 @@
+const { main } = require('.');
+
+test('results have a greeting and details property', () => {
+    const result = main({
+        name: 'name',
+        place: 'place',
+        children: 'children',
+        height: 'height',
+    });
+    expect(result).toHaveProperty('greeting');
+    expect(result).toHaveProperty('details');
+});
diff --git a/src/main/resources/template/src/package.json b/src/main/resources/template/src/package.json
new file mode 100644
index 0000000..2f89b86
--- /dev/null
+++ b/src/main/resources/template/src/package.json
@@ -0,0 +1,18 @@
+{
+  "name": "openwhisk-sample-app",
+  "version": "1.0.0",
+  "description": "",
+  "main": "index.js",
+  "engines": {
+    "node": ">=10.0.0",
+    "npm": ">=6.0.0"
+  },
+  "scripts": {
+    "test": "jest"
+  },
+  "author": "",
+  "license": "ISC",
+  "devDependencies": {
+    "jest": "^26.1.0"
+  }
+}
\ No newline at end of file
diff --git a/src/test/java/com/navercorp/openwhisk/intellij/common/utils/json/EmptyJsonParserUtilsTests.java b/src/test/java/com/navercorp/openwhisk/intellij/common/utils/json/EmptyJsonParserUtilsTests.java
new file mode 100644
index 0000000..96c2406
--- /dev/null
+++ b/src/test/java/com/navercorp/openwhisk/intellij/common/utils/json/EmptyJsonParserUtilsTests.java
@@ -0,0 +1,225 @@
+/**
+ * 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.json;
+
+import com.navercorp.openwhisk.intellij.common.utils.JsonParserUtils;
+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.WhiskAction;
+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.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 org.junit.Test;
+
+import java.io.IOException;
+import java.util.*;
+
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertFalse;
+
+public class EmptyJsonParserUtilsTests {
+
+    @Test
+    public void parseEmptyWhiskActions() throws IOException {
+        // given
+        String empty = "";
+
+        List<WhiskAction> expected = new ArrayList<>();
+
+        // when
+        List<WhiskActionMetaData> actual = JsonParserUtils.parseWhiskActions(empty);
+
+        // then
+        assertEquals(actual, expected);
+    }
+
+    @Test
+    public void parseEmtpyWhiskAction() throws IOException {
+        // given
+        String empty = "";
+
+        // when
+        Optional<ExecutableWhiskAction> actual = JsonParserUtils.parseWhiskAction(empty);
+
+        // then
+        assertFalse(actual.isPresent());
+    }
+
+    @Test
+    public void parseEmptyWhiskPackages() throws IOException {
+        // given
+        String empty = "";
+
+        List<WhiskPackage> expected = new ArrayList<>();
+
+        // when
+        List<WhiskPackage> actual = JsonParserUtils.parseWhiskPackages(empty);
+
+        // then
+        assertEquals(actual, expected);
+    }
+
+    @Test
+    public void parseEmtpyWhiskPackage() throws IOException {
+        // given
+        String empty = "";
+
+        // when
+        Optional<WhiskPackageWithActions> actual = JsonParserUtils.parseWhiskPackage(empty);
+
+        // then
+        assertFalse(actual.isPresent());
+    }
+
+    @Test
+    public void parseEmptyWhiskEndpoints() throws IOException {
+        // given
+        String empty = "";
+
+        List<WhiskEndpoint> expected = new ArrayList<>();
+
+        // when
+        List<WhiskEndpoint> actual = JsonParserUtils.parseWhiskEndpoints(empty);
+
+        // then
+        assertEquals(actual, expected);
+    }
+
+    @Test
+    public void parseEmptyWhiskNamespaces() throws IOException {
+        // given
+        String empty = "";
+
+        // when
+        String[] actual = JsonParserUtils.parseWhiskNamespace(empty);
+
+        // then
+        assertEquals(actual.length, 0);
+
+    }
+
+    @Test
+    public void parseEmptyWhiskActivations() throws IOException {
+        // given
+        String empty = "";
+
+        List<WhiskActivationMetaData> expected = new ArrayList<>();
+
+        // when
+        List<WhiskActivationMetaData> actual = JsonParserUtils.parseWhiskActivations(empty);
+
+        // then
+        assertEquals(actual, expected);
+    }
+
+    @Test
+    public void parseEmtpyActivation() throws IOException {
+        // given
+        String empty = "";
+
+        // when
+        Optional<WhiskActivationWithLogs> actual = JsonParserUtils.parseWhiskActivation(empty);
+
+        // then
+        assertFalse(actual.isPresent());
+    }
+
+    @Test
+    public void parseEmptyWhiskTriggers() throws IOException {
+        // given
+        String empty = "";
+
+        List<WhiskTriggerMetaData> expected = new ArrayList<>();
+
+        // when
+        List<WhiskTriggerMetaData> actual = JsonParserUtils.parseWhiskTriggers(empty);
+
+        // then
+        assertEquals(actual, expected);
+    }
+
+    @Test
+    public void parseEmtpyTrigger() throws IOException {
+        // given
+        String empty = "";
+
+        // when
+        Optional<ExecutableWhiskTrigger> actual = JsonParserUtils.parseWhiskTrigger(empty);
+
+        // then
+        assertFalse(actual.isPresent());
+    }
+
+    @Test
+    public void parseEmtpyRule() throws IOException {
+        // given
+        String empty = "";
+
+        // when
+        Optional<WhiskRule> actual = JsonParserUtils.parseWhiskRule(empty);
+
+        // then
+        assertFalse(actual.isPresent());
+    }
+
+    @Test
+    public void parseEmptyListMap() throws IOException {
+        // given
+        String empty = "";
+
+        List<Map<String, Object>> expected = new ArrayList<>();
+
+        // when
+        List<Map<String, Object>> actual = JsonParserUtils.parseListMap(empty);
+
+        // then
+        assertEquals(actual, expected);
+    }
+
+    @Test
+    public void parseEmptyMap() throws IOException {
+        // given
+        String empty = "";
+
+        Map<String, Object> expected = new LinkedHashMap<>();
+
+        // when
+        Map<String, Object> actual = JsonParserUtils.parseMap(empty);
+
+        // then
+        assertEquals(actual, expected);
+    }
+
+    @Test
+    public void beautifyEmptyJson() throws IOException {
+        // given
+        String empty = "";
+
+        // when
+        String actual = JsonParserUtils.beautifyJson(empty);
+
+        // then
+        assertEquals(actual, "");
+    }
+
+
+}
diff --git a/src/test/java/com/navercorp/openwhisk/intellij/common/utils/whisk/WhiskUtilsTest.java b/src/test/java/com/navercorp/openwhisk/intellij/common/utils/whisk/WhiskUtilsTest.java
new file mode 100644
index 0000000..8562dab
--- /dev/null
+++ b/src/test/java/com/navercorp/openwhisk/intellij/common/utils/whisk/WhiskUtilsTest.java
@@ -0,0 +1,34 @@
+/**
+ * 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.whisk;
+
+import com.navercorp.openwhisk.intellij.common.utils.WhiskUtils;
+import org.junit.Assert;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class WhiskUtilsTest {
+    @Test
+    public void testGetApihHostWithProtocol() {
+        String expected1 = "https://foo.com";
+        String expected2 = "http://foo.com";
+
+        Assert.assertEquals(expected1, WhiskUtils.getApihHostWithProtocol("foo.com"));
+        assertEquals(expected2, WhiskUtils.getApihHostWithProtocol("http://foo.com"));
+    }
+}
diff --git a/src/test/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskActionJsonTest.java b/src/test/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskActionJsonTest.java
new file mode 100644
index 0000000..aa44e5d
--- /dev/null
+++ b/src/test/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskActionJsonTest.java
@@ -0,0 +1,239 @@
+/**
+ * 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.JsonParserUtils;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.ExecutableWhiskAction;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.WhiskAction;
+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.model.exec.ExecMetaData;
+import junit.framework.Assert;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import static com.navercorp.openwhisk.intellij.utils.AnnotationHelper.createActionAnnotation;
+import static com.navercorp.openwhisk.intellij.utils.FileHelper.readFile;
+
+public class WhiskActionJsonTest {
+
+    @Test
+    public void testParseWhiskActionList() throws IOException {
+        // given
+        String actions = readFile("actions.json");
+
+        List<WhiskAction> expected = new ArrayList<>();
+        expected.add(new WhiskActionMetaData("testAct1", "testNs/testPkg1", "0.0.1", 1585756253499L, false,
+                createActionAnnotation(true, false, false, true, "nodejs:12"),
+                new Limits(1, 1, 256, 60000),
+                new ExecMetaData(true)));
+        expected.add(new WhiskActionMetaData("testAct2", "testNs/testPkg2", "0.0.1", 1585756252393L, false,
+                createActionAnnotation(true, false, false, true, "nodejs:12"),
+                new Limits(1, 1, 256, 60000),
+                new ExecMetaData(true)));
+        expected.add(new WhiskActionMetaData("testAct3", "testNs/testPkg3", "0.0.1", 1585756251289L, false,
+                createActionAnnotation(true, false, false, true, "nodejs:12"),
+                new Limits(1, 1, 256, 60000), new ExecMetaData(true)));
+        expected.add(new WhiskActionMetaData("testAct4", "testNs/testPkg4", "0.0.2", 1582709807073L, false,
+                createActionAnnotation(false, false, false, false, "blackbox"),
+                new Limits(1, 1, 256, 60000), new ExecMetaData(false)));
+        expected.add(new WhiskActionMetaData("testAct5", "testNs/testPkg5", "0.0.1", 1581316522182L, false,
+                createActionAnnotation(false, false, false, false, "python:3"),
+                new Limits(1, 1, 256, 60000), new ExecMetaData(false)));
+
+        // when
+        List<WhiskActionMetaData> actual = JsonParserUtils.parseWhiskActions(actions);
+
+        // then
+        Assert.assertEquals(expected, actual);
+    }
+
+
+    @Test
+    public void testParseNormalWhiskAction() throws IOException {
+        // given
+        String action = readFile("action.json");
+
+        List<Map<String, Object>> parameters = new ArrayList<>();
+        Map<String, Object> param1 = new LinkedHashMap<>();
+        param1.put("key", "name");
+        param1.put("value", "test");
+        parameters.add(param1);
+
+        Map<String, Object> param2 = new LinkedHashMap<>();
+        param2.put("key", "count");
+        param2.put("value", 1);
+        parameters.add(param2);
+
+        ExecutableWhiskAction expected = new ExecutableWhiskAction(
+                "testAct",
+                "testNs/testPkg",
+                "0.0.1",
+                1581316522182L,
+                false,
+                createActionAnnotation(false, false, false, false, "nodejs:12"),
+                new Limits(1, 1, 256, 60000),
+                new CodeExec(false, "nodejs:12", null, "function main(params) {}", null, new ArrayList<>()),
+                parameters);
+
+        // when
+        ExecutableWhiskAction actual = JsonParserUtils.parseWhiskAction(action).get();
+
+        // then
+        Assert.assertEquals(expected, actual);
+    }
+
+    @Test
+    public void testParseNormalBinaryAction() throws IOException {
+        // given
+        String action = readFile("binary-action.json");
+
+        List<Map<String, Object>> parameters = new ArrayList<>();
+        Map<String, Object> param1 = new LinkedHashMap<>();
+        param1.put("key", "name");
+        param1.put("value", "test");
+        parameters.add(param1);
+
+        Map<String, Object> param2 = new LinkedHashMap<>();
+        param2.put("key", "count");
+        param2.put("value", 1);
+        parameters.add(param2);
+
+        ExecutableWhiskAction expected = new ExecutableWhiskAction(
+                "testAct",
+                "testNs/testPkg",
+                "0.0.1",
+                1585756253499L,
+                false,
+                createActionAnnotation(false, false, false, false, "nodejs:12"),
+                new Limits(1, 1, 256, 60000),
+                new CodeExec(true, "nodejs:12", null, "aaaaa", null, new ArrayList<>()),
+                parameters);
+
+        // when
+        ExecutableWhiskAction actual = JsonParserUtils.parseWhiskAction(action).get();
+
+        // then
+        Assert.assertEquals(expected, actual);
+    }
+
+    @Test
+    public void testParseJavaAction() throws IOException {
+        // given
+        String action = readFile("java-action.json");
+
+        List<Map<String, Object>> parameters = new ArrayList<>();
+        Map<String, Object> param1 = new LinkedHashMap<>();
+        param1.put("key", "name");
+        param1.put("value", "test");
+        parameters.add(param1);
+
+        Map<String, Object> param2 = new LinkedHashMap<>();
+        param2.put("key", "count");
+        param2.put("value", 1);
+        parameters.add(param2);
+
+        ExecutableWhiskAction expected = new ExecutableWhiskAction(
+                "testAct",
+                "testNs/testPkg",
+                "0.0.1",
+                1585756253499L,
+                false,
+                createActionAnnotation(false, false, false, false, "java"),
+                new Limits(1, 1, 256, 60000),
+                new CodeExec(true, "java", "test", "aaaaa", null, new ArrayList<>()),
+                parameters);
+
+        // when
+        ExecutableWhiskAction actual = JsonParserUtils.parseWhiskAction(action).get();
+
+        // then
+        Assert.assertEquals(actual, expected);
+    }
+
+    @Test
+    public void testParseSequenceAction() throws IOException {
+        // given
+        String action = readFile("sequence-action.json");
+
+        List<String> components = new ArrayList<>();
+        components.add("/testNs/testAct1");
+        components.add("/testNs/testAct2");
+
+        List<Map<String, Object>> parameters = new ArrayList<>();
+        Map<String, Object> param1 = new LinkedHashMap<>();
+        param1.put("key", "_actions");
+        param1.put("components", components);
+        parameters.add(param1);
+
+        ExecutableWhiskAction expected = new ExecutableWhiskAction(
+                "testAct",
+                "testNs/testPkg",
+                "0.0.1",
+                1585756253499L,
+                false,
+                createActionAnnotation(false, false, false, false, "sequence"),
+                new Limits(1, 1, 256, 60000),
+                new CodeExec(false, "sequence", null, null, null, components),
+                parameters);
+
+        // when
+        ExecutableWhiskAction actual = JsonParserUtils.parseWhiskAction(action).get();
+
+        // then
+        Assert.assertEquals(actual, expected);
+    }
+
+    @Test
+    public void testParseBlackboxAction() throws IOException {
+        // given
+        String action = readFile("blackbox-action.json");
+
+        List<Map<String, Object>> parameters = new ArrayList<>();
+        Map<String, Object> param1 = new LinkedHashMap<>();
+        param1.put("key", "name");
+        param1.put("value", "test");
+        parameters.add(param1);
+
+        Map<String, Object> param2 = new LinkedHashMap<>();
+        param2.put("key", "count");
+        param2.put("value", 1);
+        parameters.add(param2);
+
+        ExecutableWhiskAction expected = new ExecutableWhiskAction(
+                "testAct",
+                "testNs/testPkg",
+                "0.0.1",
+                1585756253499L,
+                false,
+                createActionAnnotation(false, false, false, false, "blackbox"),
+                new Limits(1, 1, 256, 60000),
+                new CodeExec(true, "blackbox", null, null, "imageRepo/imageName", new ArrayList<>()),
+                parameters);
+
+        // when
+        ExecutableWhiskAction actual = JsonParserUtils.parseWhiskAction(action).get();
+
+        // then
+        Assert.assertEquals(expected, actual);
+    }
+}
diff --git a/src/test/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskActionTest.java b/src/test/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskActionTest.java
new file mode 100644
index 0000000..afce38c
--- /dev/null
+++ b/src/test/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskActionTest.java
@@ -0,0 +1,220 @@
+/**
+ * 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.Icons;
+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.model.exec.ExecMetaData;
+import com.navercorp.openwhisk.intellij.explorer.editor.model.ComboBoxEntityEntry;
+import com.navercorp.openwhisk.intellij.explorer.editor.model.ComboBoxEntityType;
+import org.junit.Test;
+
+import javax.swing.*;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import static com.navercorp.openwhisk.intellij.utils.AnnotationHelper.createActionAnnotation;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class WhiskActionTest {
+    private String name = "testName";
+    private String namespace = "testNamespace";
+    private String pkg = "testPkg";
+    private String version = "1.0.0";
+    private long updated = 1581316522182L;
+    private boolean publish = false;
+    private List<Map<String, Object>> annotations = createActionAnnotation(false, false, false, false, "nodejs:12");
+    private Limits limits = new Limits(1, 1, 256, 60000);
+    private ExecMetaData exec = new ExecMetaData(false);
+
+    @Test
+    public void testGetWhiskPackage() {
+        String expectedPkg = pkg;
+
+        WhiskActionMetaData action = new WhiskActionMetaData(name, namespace, version, updated, publish, annotations, limits, exec);
+        WhiskActionMetaData actionWithPkg = new WhiskActionMetaData(name, namespace + "/" + pkg, version, updated, publish, annotations, limits, exec);
+
+        assertEquals(Optional.empty(), action.getWhiskPackage());
+        assertEquals(Optional.of(expectedPkg), actionWithPkg.getWhiskPackage());
+    }
+
+
+    @Test
+    public void testGetNamespacePath() {
+        String expectedNamespace = namespace;
+
+        WhiskActionMetaData action = new WhiskActionMetaData(name, namespace, version, updated, publish, annotations, limits, exec);
+        WhiskActionMetaData actionWithPkg = new WhiskActionMetaData(name, namespace + "/" + pkg, version, updated, publish, annotations, limits, exec);
+
+        assertEquals(expectedNamespace, action.getNamespacePath());
+        assertEquals(expectedNamespace, actionWithPkg.getNamespacePath());
+    }
+
+    @Test
+    public void testGetFullyQualifiedName() {
+        String expectedFQN = namespace + "/" + name;
+        String expectedPkgFQN = namespace + "/" + pkg + "/" + name;
+
+        WhiskActionMetaData action = new WhiskActionMetaData(name, namespace, version, updated, publish, annotations, limits, exec);
+        WhiskActionMetaData actionWithPkg = new WhiskActionMetaData(name, namespace + "/" + pkg, version, updated, publish, annotations, limits, exec);
+
+        assertEquals(expectedFQN, action.getFullyQualifiedName());
+        assertEquals(expectedPkgFQN, actionWithPkg.getFullyQualifiedName());
+    }
+
+
+    @Test
+    public void testGetKind() {
+        String expectedNodeKind = "nodejs:12";
+        String expectedRubyKind = "ruby:2.5";
+        String expectedPhpKind = "php:7.3";
+
+        List<Map<String, Object>> nodeKindAnnotations = createActionAnnotation(false, false, false, false, expectedNodeKind);
+        List<Map<String, Object>> rubyKindAnnotations = createActionAnnotation(false, false, false, false, expectedRubyKind);
+        List<Map<String, Object>> phpKindAnnotations = createActionAnnotation(false, false, false, false, expectedPhpKind);
+
+        WhiskActionMetaData nodeAction = new WhiskActionMetaData(name, namespace, version, updated, publish, nodeKindAnnotations, limits, exec);
+        WhiskActionMetaData rubyAction = new WhiskActionMetaData(name, namespace, version, updated, publish, rubyKindAnnotations, limits, exec);
+        WhiskActionMetaData phpAction = new WhiskActionMetaData(name, namespace, version, updated, publish, phpKindAnnotations, limits, exec);
+
+        assertEquals(expectedNodeKind, nodeAction.getKind());
+        assertEquals(expectedRubyKind, rubyAction.getKind());
+        assertEquals(expectedPhpKind, phpAction.getKind());
+    }
+
+    @Test
+    public void testGetKindExtension() {
+        String expectedNodeExtension = ".js";
+        String expectedRubyExtension = ".rb";
+        String expectedPhpExtension = ".php";
+
+        String nodeKind = "nodejs:12";
+        String rubyKind = "ruby:2.5";
+        String phpKind = "php:7.3";
+
+        List<Map<String, Object>> nodeKindAnnotations = createActionAnnotation(false, false, false, false, nodeKind);
+        List<Map<String, Object>> rubyKindAnnotations = createActionAnnotation(false, false, false, false, rubyKind);
+        List<Map<String, Object>> phpKindAnnotations = createActionAnnotation(false, false, false, false, phpKind);
+
+        WhiskActionMetaData nodeAction = new WhiskActionMetaData(name, namespace, version, updated, publish, nodeKindAnnotations, limits, exec);
+        WhiskActionMetaData rubyAction = new WhiskActionMetaData(name, namespace, version, updated, publish, rubyKindAnnotations, limits, exec);
+        WhiskActionMetaData phpAction = new WhiskActionMetaData(name, namespace, version, updated, publish, phpKindAnnotations, limits, exec);
+
+        assertEquals(expectedNodeExtension, nodeAction.getKindExtension());
+        assertEquals(expectedRubyExtension, rubyAction.getKindExtension());
+        assertEquals(expectedPhpExtension, phpAction.getKindExtension());
+    }
+
+
+    @Test
+    public void testGetKindIcon() {
+        Icon expectedNodeIcon = Icons.KIND_JS;
+        Icon expectedRubyIcon = Icons.KIND_RUBY;
+        Icon expectedPhpIcon = Icons.KIND_PHP;
+
+        String nodeKind = "nodejs:12";
+        String rubyKind = "ruby:2.5";
+        String phpKind = "php:7.3";
+
+        List<Map<String, Object>> nodeKindAnnotations = createActionAnnotation(false, false, false, false, nodeKind);
+        List<Map<String, Object>> rubyKindAnnotations = createActionAnnotation(false, false, false, false, rubyKind);
+        List<Map<String, Object>> phpKindAnnotations = createActionAnnotation(false, false, false, false, phpKind);
+
+        WhiskActionMetaData nodeAction = new WhiskActionMetaData(name, namespace, version, updated, publish, nodeKindAnnotations, limits, exec);
+        WhiskActionMetaData rubyAction = new WhiskActionMetaData(name, namespace, version, updated, publish, rubyKindAnnotations, limits, exec);
+        WhiskActionMetaData phpAction = new WhiskActionMetaData(name, namespace, version, updated, publish, phpKindAnnotations, limits, exec);
+
+        assertEquals(expectedNodeIcon, nodeAction.getKindIcon());
+        assertEquals(expectedRubyIcon, rubyAction.getKindIcon());
+        assertEquals(expectedPhpIcon, phpAction.getKindIcon());
+    }
+
+
+    @Test
+    public void testIsSequenceAction() {
+        List<Map<String, Object>> sequenceAnnotations = createActionAnnotation(false, false, false, false, "sequence");
+
+        List<Map<String, Object>> parameters = new ArrayList<>();
+
+        List<String> components = new ArrayList<>();
+        components.add("/testNs/testAction1");
+        components.add("/testNs/testAction2");
+        components.add("/testNs/testAction3");
+
+        CodeExec codeExec = new CodeExec(false, "sequence", null, null, null, components);
+
+        ExecutableWhiskAction sequenceAction = new ExecutableWhiskAction(name, namespace, version, updated, publish,
+                sequenceAnnotations, limits, codeExec, parameters);
+
+        assertTrue(sequenceAction.isSequenceAction());
+    }
+
+    @Test
+    public void testWhiskActionWebActionOptions() {
+        List<Map<String, Object>> webAnnotations = createActionAnnotation(true, false, false, false, "nodejs:12");
+        List<Map<String, Object>> rawHttpAnnotations = createActionAnnotation(true, true, false, false, "nodejs:12");
+        List<Map<String, Object>> customOptionsAnnotations = createActionAnnotation(true, true, true, false, "nodejs:12");
+        List<Map<String, Object>> finalAnnotations = createActionAnnotation(true, false, false, true, "nodejs:12");
+
+        WhiskActionMetaData webAction = new WhiskActionMetaData(name, namespace, version, updated, publish, webAnnotations, limits, exec);
+        WhiskActionMetaData rawHttpAction = new WhiskActionMetaData(name, namespace, version, updated, publish, rawHttpAnnotations, limits, exec);
+        WhiskActionMetaData customOptionsAction = new WhiskActionMetaData(name, namespace, version, updated, publish, customOptionsAnnotations, limits, exec);
+        WhiskActionMetaData finalAction = new WhiskActionMetaData(name, namespace, version, updated, publish, finalAnnotations, limits, exec);
+
+        assertTrue(webAction.isWebAction());
+
+        assertTrue(rawHttpAction.isWebAction());
+        assertTrue(rawHttpAction.isRawHttp());
+
+        assertTrue(customOptionsAction.isWebAction());
+        assertTrue(customOptionsAction.isRawHttp());
+
+        assertTrue(finalAction.isWebAction());
+        assertTrue(finalAction.isFinalDefaultParameter());
+    }
+
+    @Test
+    public void testWhiskActionMetaDataToString() {
+        String expected = name;
+
+        WhiskActionMetaData action = new WhiskActionMetaData(name, namespace, version, updated, publish, annotations, limits, exec);
+
+        assertEquals(expected, action.toString());
+    }
+
+    @Test
+    public void testWhiskActionMetaDataWithPackageToString() {
+        String expected = pkg + "/" + name;
+
+        WhiskActionMetaData actionWithPackage = new WhiskActionMetaData(name, namespace + "/" + pkg, version, updated, publish, annotations, limits, exec);
+
+        assertEquals(expected, actionWithPackage.toString());
+    }
+
+    @Test
+    public void testWhiskActionMetaDataToCombBoxEntityEntry() {
+        WhiskActionMetaData actionMetaData = new WhiskActionMetaData(name, namespace, version, updated, publish, annotations, limits, exec);
+
+        ComboBoxEntityEntry expectedEntry = new ComboBoxEntityEntry(actionMetaData.toString(), ComboBoxEntityType.ACTION);
+
+        assertEquals(expectedEntry, actionMetaData.toCombBoxEntityEntry());
+    }
+}
diff --git a/src/test/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskActivationTest.java b/src/test/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskActivationTest.java
new file mode 100644
index 0000000..f57ce5e
--- /dev/null
+++ b/src/test/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskActivationTest.java
@@ -0,0 +1,124 @@
+/**
+ * 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.whisk.model.activation.WhiskActivationMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.model.activation.WhiskActivationWithLogs;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+
+public class WhiskActivationTest {
+    private String expectedActivationId = "969c2bf39e3b40599c2bf59e3b901911";
+    private String expectedName = "testName";
+    private String expectedNamespace = "testNamespace";
+    private String expectedVersion = "1.0.0";
+    private String expectedCause = "51adf3c3f875df3gadf3c0f875ef3eb9";
+    private long expectedStart = 1592555196109L;
+    private long expectedEnd = 1592555196153L;
+    private long expectedDuration = 50;
+    private boolean expectedPublish = false;
+
+    @Test
+    public void testWhiskActionMetaDataGetter() {
+
+        List<Map<String, Object>> expectedAnnotations = new ArrayList<>();
+        int expectedStatusCode = 0;
+
+        WhiskActivationMetaData activationMetaData = new WhiskActivationMetaData(
+                expectedActivationId, expectedName, expectedNamespace, expectedVersion, expectedCause,
+                expectedStart, expectedEnd, expectedDuration, expectedPublish, expectedAnnotations, expectedStatusCode);
+
+        assertEquals(expectedActivationId, activationMetaData.getActivationId());
+        assertEquals(expectedName, activationMetaData.getName());
+        assertEquals(expectedNamespace, activationMetaData.getNamespace());
+        assertEquals(expectedVersion, activationMetaData.getVersion());
+        assertEquals(expectedCause, activationMetaData.getCause());
+        assertEquals(expectedStart, activationMetaData.getStart());
+        assertEquals(expectedEnd, activationMetaData.getEnd());
+        assertEquals(expectedDuration, activationMetaData.getDuration());
+        assertEquals(expectedPublish, activationMetaData.isPublish());
+        assertEquals(expectedAnnotations, activationMetaData.getAnnotations());
+        assertEquals(expectedStatusCode, activationMetaData.getStatusCode());
+    }
+
+    @Test
+    public void testWhiskActionMetaDataGetStatus() {
+        List<Map<String, Object>> expectedAnnotations = new ArrayList<>();
+
+        int success = 0;
+        String expectedSuccessMessage0 = "success";
+        WhiskActivationMetaData activationMetaData0 = new WhiskActivationMetaData(
+                expectedActivationId, expectedName, expectedNamespace, expectedVersion, expectedCause,
+                expectedStart, expectedEnd, expectedDuration, expectedPublish, expectedAnnotations, success);
+        assertEquals(expectedSuccessMessage0, activationMetaData0.getStatus());
+
+
+        int applicationError = 1;
+        String expectedSuccessMessage1 = "application error";
+        WhiskActivationMetaData activationMetaData1 = new WhiskActivationMetaData(
+                expectedActivationId, expectedName, expectedNamespace, expectedVersion, expectedCause,
+                expectedStart, expectedEnd, expectedDuration, expectedPublish, expectedAnnotations, applicationError);
+        assertEquals(expectedSuccessMessage1, activationMetaData1.getStatus());
+
+        int developerError = 2;
+        String expectedSuccessMessage2 = "developer error";
+        WhiskActivationMetaData activationMetaData2 = new WhiskActivationMetaData(
+                expectedActivationId, expectedName, expectedNamespace, expectedVersion, expectedCause,
+                        expectedStart, expectedEnd, expectedDuration, expectedPublish, expectedAnnotations, developerError);
+        assertEquals(expectedSuccessMessage2, activationMetaData2.getStatus());
+
+
+        int internalError = 3;
+        String expectedSuccessMessage3 = "internal error";
+        WhiskActivationMetaData activationMetaData3 = new WhiskActivationMetaData(
+                expectedActivationId, expectedName, expectedNamespace, expectedVersion, expectedCause,
+                expectedStart, expectedEnd, expectedDuration, expectedPublish, expectedAnnotations, internalError);
+        assertEquals(expectedSuccessMessage3, activationMetaData3.getStatus());
+    }
+
+    @Test
+    public void testWhiskActivationWithLogsGetter() {
+        List<Map<String, Object>> expectedAnnotations = new ArrayList<>();
+        String expectedSubject = "testNamespace";
+        List<String> expectedLogs = new ArrayList<>();
+        Map<String, Object> expectedResponse = new LinkedHashMap<>();
+
+        WhiskActivationWithLogs activationWithLogs = new WhiskActivationWithLogs(
+                expectedActivationId, expectedName, expectedNamespace, expectedVersion, expectedCause,
+                expectedStart, expectedEnd, expectedDuration, expectedPublish, expectedAnnotations, expectedSubject, expectedLogs, expectedResponse);
+
+        assertEquals(expectedActivationId, activationWithLogs.getActivationId());
+        assertEquals(expectedName, activationWithLogs.getName());
+        assertEquals(expectedNamespace, activationWithLogs.getNamespace());
+        assertEquals(expectedVersion, activationWithLogs.getVersion());
+        assertEquals(expectedCause, activationWithLogs.getCause());
+        assertEquals(expectedStart, activationWithLogs.getStart());
+        assertEquals(expectedEnd, activationWithLogs.getEnd());
+        assertEquals(expectedDuration, activationWithLogs.getDuration());
+        assertEquals(expectedPublish, activationWithLogs.isPublish());
+        assertEquals(expectedAnnotations, activationWithLogs.getAnnotations());
+        assertEquals(expectedSubject, activationWithLogs.getSubject());
+        assertEquals(expectedLogs, activationWithLogs.getLogs());
+        assertEquals(expectedResponse, activationWithLogs.getResponse());
+    }
+}
diff --git a/src/test/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskAuthTest.java b/src/test/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskAuthTest.java
new file mode 100644
index 0000000..b11f3fb
--- /dev/null
+++ b/src/test/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskAuthTest.java
@@ -0,0 +1,47 @@
+/**
+ * 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 org.junit.Test;
+
+import java.util.Base64;
+
+import static org.junit.Assert.assertEquals;
+
+public class WhiskAuthTest {
+
+    @Test
+    public void testGetAuth() {
+        String apiKey = "test-auth";
+        WhiskAuth auth = new WhiskAuth(apiKey, "test-host");
+
+        // It is seemingly meaningless but it would help to guarantee sanity when the underlying logic is changed.
+        assertEquals("Basic " + Base64.getEncoder().encodeToString(apiKey.getBytes()), auth.getBasicAuthHeader());
+    }
+
+    @Test
+    public void testGetApihost() {
+        String expected1 = "https://test-host";
+        String expected2 = "http://test-host";
+        WhiskAuth auth1 = new WhiskAuth("test-auth", "test-host");
+        WhiskAuth auth2 = new WhiskAuth("test-auth", "http://test-host");
+
+        // It is seemingly meaningless but it would help to guarantee sanity when the underlying logic is changed.
+        assertEquals(expected1, auth1.getApihost());
+        assertEquals(expected2, auth2.getApihost());
+    }
+}
diff --git a/src/test/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskEndpointTest.java b/src/test/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskEndpointTest.java
new file mode 100644
index 0000000..afd5ee8
--- /dev/null
+++ b/src/test/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskEndpointTest.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 org.junit.Test;
+
+import java.util.ArrayList;
+
+import static org.junit.Assert.assertEquals;
+
+public class WhiskEndpointTest {
+
+    @Test
+    public void testWhiskEndpointWithConstructorGetApihost() {
+        String expected1 = "https://foo.com";
+        String expected2 = "http://foo.com";
+
+        WhiskEndpoint auth1 = new WhiskEndpoint("test-auth", "https://foo.com", new ArrayList<>());
+        WhiskEndpoint auth2 = new WhiskEndpoint("test-auth", "foo.com", new ArrayList<>());
+        WhiskEndpoint auth3 = new WhiskEndpoint("test-auth", "http://foo.com", new ArrayList<>());
+
+
+        assertEquals(expected1, auth1.getApihost());
+        assertEquals(expected1, auth2.getApihost());
+        assertEquals(expected2, auth3.getApihost());
+    }
+
+    @Test
+    public void testWhiskEndpointWithSetMethodGetApihost() {
+        String expected1 = "http://foo.com";
+        String expected2 = "https://bar.com";
+        String expected3 = "ftp://foo.com";
+
+        WhiskEndpoint auth1 = new WhiskEndpoint("test-auth", "test.com", new ArrayList<>());
+        WhiskEndpoint auth2 = new WhiskEndpoint("test-auth", "test.com", new ArrayList<>());
+        WhiskEndpoint auth3 = new WhiskEndpoint("test-auth", "test.com", new ArrayList<>());
+
+        auth1.setApihost("http://foo.com");
+        auth2.setApihost("bar.com");
+        auth3.setApihost("ftp://foo.com"); // the regular expression does not check the protocol itself but format.
+
+        assertEquals(expected1, auth1.getApihost());
+        assertEquals(expected2, auth2.getApihost());
+        assertEquals(expected3, auth3.getApihost());
+    }
+}
diff --git a/src/test/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskPackageJsonTest.java b/src/test/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskPackageJsonTest.java
new file mode 100644
index 0000000..b223c97
--- /dev/null
+++ b/src/test/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskPackageJsonTest.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.common.whisk.model;
+
+import com.navercorp.openwhisk.intellij.common.utils.JsonParserUtils;
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.CompactWhiskAction;
+import com.navercorp.openwhisk.intellij.common.whisk.model.pkg.WhiskPackage;
+import com.navercorp.openwhisk.intellij.common.whisk.model.pkg.WhiskPackageWithActions;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import static com.navercorp.openwhisk.intellij.utils.AnnotationHelper.*;
+import static com.navercorp.openwhisk.intellij.utils.FileHelper.readFile;
+import static junit.framework.Assert.assertEquals;
+
+public class WhiskPackageJsonTest {
+
+    @Test
+    public void parseWhiskPackages() throws IOException {
+        // given
+        String packages = readFile("packages.json");
+
+        List<WhiskPackage> expected = new ArrayList<>();
+        expected.add(new WhiskPackage("pkg1", "ns", false, 1586513535028L, "0.0.1", createPackageAnnotation("test", null), false));
+        expected.add(new WhiskPackage("pkg2", "ns", false, 1586513513922L, "0.0.1", createPackageAnnotation("test", null), false));
+        Map<String, String> binding = createBinding("sharedpackage", "whisk.system");
+        expected.add(new WhiskPackage("pkg3", "ns", false, 1557380799999L, "0.0.1", createPackageAnnotation("test", binding), binding));
+
+        // when
+        List<WhiskPackage> actual = JsonParserUtils.parseWhiskPackages(packages);
+
+        // then
+        assertEquals(expected, actual);
+    }
+
+
+    @Test
+    public void parseWhiskPackage() throws IOException {
+        // given
+        String pkg = readFile("package.json");
+
+        List<Map<String, Object>> parameters = new ArrayList<>();
+        Map<String, Object> param1 = new LinkedHashMap<>();
+        param1.put("key", "test");
+        param1.put("value", "test");
+        parameters.add(param1);
+
+        Map<String, Object> param2 = new LinkedHashMap<>();
+        param2.put("key", "test1");
+        param2.put("value", "test1");
+        parameters.add(param2);
+
+        List<CompactWhiskAction> actions = new ArrayList<>();
+        actions.add(new CompactWhiskAction("action1", "0.0.2", createActionAnnotation(false, false, false, false, "nodejs:10")));
+        actions.add(new CompactWhiskAction("action2", "0.0.2", createActionAnnotation(false, false, false, false, "nodejs:10")));
+        actions.add(new CompactWhiskAction("action3", "0.0.2", createActionAnnotation(false, false, false, false, "nodejs:10")));
+        WhiskPackageWithActions expected = new WhiskPackageWithActions("test", "testns", true, 1583828352890L, "0.0.6",
+                createPackageAnnotation("test", null), createEmptyBinding(), parameters, actions, new ArrayList<>());
+
+        // when
+        WhiskPackageWithActions actual = JsonParserUtils.parseWhiskPackage(pkg).get();
+
+        // then
+        assertEquals(expected, actual);
+    }
+}
diff --git a/src/test/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskPackageTest.java b/src/test/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskPackageTest.java
new file mode 100644
index 0000000..68230e1
--- /dev/null
+++ b/src/test/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskPackageTest.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;
+
+import com.navercorp.openwhisk.intellij.common.whisk.model.action.CompactWhiskAction;
+import com.navercorp.openwhisk.intellij.common.whisk.model.pkg.WhiskPackage;
+import com.navercorp.openwhisk.intellij.common.whisk.model.pkg.WhiskPackageWithActions;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import static com.navercorp.openwhisk.intellij.utils.AnnotationHelper.*;
+import static org.junit.Assert.*;
+
+public class WhiskPackageTest {
+    private String name = "testPkg";
+    private String namespace = "testNamespace";
+    private String version = "1.0.0";
+    private long updated = 1581316522182L;
+    private boolean publish = false;
+
+    @Test
+    public void testWhiskPackageGetBinding() {
+        List<Map<String, Object>> annotations = createPackageAnnotation("test-description", null);
+        WhiskPackage notBoundPackage = new WhiskPackage(name, namespace, publish, updated, version, annotations, createFalseBinding());
+
+        assertFalse(notBoundPackage.getBinding().isPresent());
+    }
+
+    @Test
+    public void testBoundWhiskPackageGetBinding() {
+        Binding binding = new Binding("boundPkg", "boundNamespace");
+
+        Map<String, String> bindingMap = createBinding(binding.getName(), binding.getNamespace());
+        List<Map<String, Object>> annotations = createPackageAnnotation("test-description", bindingMap);
+        WhiskPackage boundPackage = new WhiskPackage(name, namespace, publish, updated, version, annotations, bindingMap);
+
+        assertTrue(boundPackage.getBinding().isPresent());
+        assertEquals(binding, Optional.of(binding).get());
+    }
+
+
+    @Test
+    public void testWhiskPackageWithActionsGetBinding() {
+        List<Map<String, Object>> parameters = new ArrayList<>();
+        List<CompactWhiskAction> actions = new ArrayList<>();
+        List<Object> feeds = new ArrayList<>();
+
+        List<Map<String, Object>> annotations = createPackageAnnotation("test-description", null);
+        WhiskPackageWithActions notBoundPackage = new WhiskPackageWithActions(name, namespace, publish, updated, version, annotations,
+                createEmptyBinding(), parameters, actions, feeds);
+
+        assertFalse(notBoundPackage.getBinding().isPresent());
+    }
+
+    @Test
+    public void testBoundWhiskPackageWithActionsGetBinding() {
+        List<Map<String, Object>> parameters = new ArrayList<>();
+        List<CompactWhiskAction> actions = new ArrayList<>();
+        List<Object> feeds = new ArrayList<>();
+
+        Binding binding = new Binding("boundPkg", "boundNamespace");
+
+        Map<String, String> bindingMap = createBinding(binding.getName(), binding.getNamespace());
+        List<Map<String, Object>> annotations = createPackageAnnotation("test-description", bindingMap);
+        WhiskPackageWithActions boundPackage = new WhiskPackageWithActions(name, namespace, publish, updated, version,
+                annotations, bindingMap, parameters, actions, feeds);
+
+        assertTrue(boundPackage.getBinding().isPresent());
+        assertEquals(binding, Optional.of(binding).get());
+    }
+
+}
diff --git a/src/test/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskTriggerJsonTest.java b/src/test/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskTriggerJsonTest.java
new file mode 100644
index 0000000..49a0301
--- /dev/null
+++ b/src/test/java/com/navercorp/openwhisk/intellij/common/whisk/model/WhiskTriggerJsonTest.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;
+
+import com.navercorp.openwhisk.intellij.common.utils.JsonParserUtils;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.ExecutableWhiskTrigger;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.WhiskTriggerMetaData;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+
+import static com.navercorp.openwhisk.intellij.utils.AnnotationHelper.createRule;
+import static com.navercorp.openwhisk.intellij.utils.AnnotationHelper.createTriggerAlarmFeedAnnotation;
+import static com.navercorp.openwhisk.intellij.utils.FileHelper.readFile;
+import static junit.framework.Assert.assertEquals;
+
+public class WhiskTriggerJsonTest {
+
+    @Test
+    public void parseWhiskTriggers() throws IOException {
+        // given
+        String triggers = readFile("triggers.json");
+
+        List<WhiskTriggerMetaData> expected = new ArrayList<>();
+        expected.add(new WhiskTriggerMetaData("trigger1", "test", "0.0.2", 1586315510331L, false, createTriggerAlarmFeedAnnotation()));
+        expected.add(new WhiskTriggerMetaData("trigger2", "test", "0.0.2", 1586315245020L, false, createTriggerAlarmFeedAnnotation()));
+        expected.add(new WhiskTriggerMetaData("trigger3", "test", "0.0.2", 1582612773239L, false, new ArrayList<>()));
+
+        // when
+        List<WhiskTriggerMetaData> actual = JsonParserUtils.parseWhiskTriggers(triggers);
+
+        // then
+        assertEquals(actual, expected);
+    }
+
+    @Test
+    public void parseWhiskTrigger() throws IOException {
+        // given
+        String trigger = readFile("trigger.json");
+
+        ExecutableWhiskTrigger expected = new ExecutableWhiskTrigger(
+                "trigger1",
+                "test",
+                "0.0.2",
+                1586315245020L,
+                false,
+                createTriggerAlarmFeedAnnotation(),
+                new ArrayList<>(),
+                createRule("rule1", "test", "action1"),
+                new LinkedHashMap<>());
+
+        // when
+        ExecutableWhiskTrigger actual = JsonParserUtils.parseWhiskTrigger(trigger).get();
+
+        // then
+        assertEquals(actual, expected);
+    }
+}
diff --git a/src/test/java/com/navercorp/openwhisk/intellij/utils/AnnotationHelper.java b/src/test/java/com/navercorp/openwhisk/intellij/utils/AnnotationHelper.java
new file mode 100644
index 0000000..ebd0133
--- /dev/null
+++ b/src/test/java/com/navercorp/openwhisk/intellij/utils/AnnotationHelper.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.utils;
+
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.SimplifiedEntityMetaData;
+import com.navercorp.openwhisk.intellij.common.whisk.model.trigger.SimplifiedWhiskRule;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+public class AnnotationHelper {
+
+    protected AnnotationHelper() {
+        throw new UnsupportedOperationException("Utility classes should not have a public or default constructor.");
+    }
+
+    public static List<Map<String, Object>> createActionAnnotation(boolean webExport, boolean rawHttp, boolean customOption, boolean finalEntry, String kind) {
+        List<Map<String, Object>> annotations = new ArrayList<>();
+        if (webExport) {
+            Map<String, Object> entry1 = new LinkedHashMap<>();
+            entry1.put("key", "web-export");
+            entry1.put("value", webExport);
+            annotations.add(entry1);
+
+            Map<String, Object> entry2 = new LinkedHashMap<>();
+            entry2.put("key", "raw-http");
+            entry2.put("value", rawHttp);
+            annotations.add(entry2);
+
+            if (customOption) {
+                Map<String, Object> entry3 = new LinkedHashMap<>();
+                entry3.put("key", "web-custom-options");
+                entry3.put("value", customOption);
+                annotations.add(entry3);
+            }
+
+            Map<String, Object> entry4 = new LinkedHashMap<>();
+            entry4.put("key", "final");
+            entry4.put("value", finalEntry);
+            annotations.add(entry4);
+        }
+
+        Map<String, Object> entry5 = new LinkedHashMap<>();
+        entry5.put("key", "exec");
+        entry5.put("value", kind);
+        annotations.add(entry5);
+
+        return annotations;
+    }
+
+    public static List<Map<String, Object>> createPackageAnnotation(String description, Map<String, String> binding) {
+        List<Map<String, Object>> annotations = new ArrayList<>();
+
+        Map<String, Object> entry1 = new LinkedHashMap<>();
+        entry1.put("key", "description");
+        entry1.put("value", description);
+        annotations.add(entry1);
+
+        if (binding != null) {
+            Map<String, Object> entry2 = new LinkedHashMap<>();
+            entry2.put("key", "binding");
+            entry2.put("value", binding);
+            annotations.add(entry2);
+        }
+
+        return annotations;
+    }
+
+    public static Map<String, String> createBinding(String name, String namespace) {
+        Map<String, String> binding = new LinkedHashMap<>();
+        binding.put("name", name);
+        binding.put("namespace", namespace);
+        return binding;
+    }
+
+    public static Map<String, String> createEmptyBinding() {
+        return new LinkedHashMap<>();
+    }
+
+    public static boolean createFalseBinding() {
+        return false;
+    }
+
+    public static List<Map<String, Object>> createTriggerAlarmFeedAnnotation() {
+        List<Map<String, Object>> annotations = new ArrayList<>();
+
+        Map<String, Object> entry = new LinkedHashMap<>();
+        entry.put("key", "feed");
+        entry.put("value", "/whisk.system/alarms/alarm");
+        annotations.add(entry);
+        return annotations;
+    }
+
+    public static Map<String, SimplifiedWhiskRule> createRule(String ruleName, String namespace, String actionName) {
+        Map<String, SimplifiedWhiskRule> rules = new LinkedHashMap<>();
+        rules.put(namespace + "/" + ruleName, new SimplifiedWhiskRule(new SimplifiedEntityMetaData(namespace, actionName), "active"));
+        return rules;
+    }
+}
diff --git a/src/test/java/com/navercorp/openwhisk/intellij/utils/FileHelper.java b/src/test/java/com/navercorp/openwhisk/intellij/utils/FileHelper.java
new file mode 100644
index 0000000..c8c8f5e
--- /dev/null
+++ b/src/test/java/com/navercorp/openwhisk/intellij/utils/FileHelper.java
@@ -0,0 +1,22 @@
+package com.navercorp.openwhisk.intellij.utils;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.util.stream.Collectors;
+
+public class FileHelper {
+
+    protected FileHelper() {
+        throw new UnsupportedOperationException("Utility classes should not have a public or default constructor.");
+    }
+
+    public static String readFile(String filePath) {
+        InputStream inputStream = FileHelper.class.getClassLoader().getResourceAsStream(filePath);
+        return new BufferedReader(
+                new InputStreamReader(inputStream, StandardCharsets.UTF_8))
+                .lines()
+                .collect(Collectors.joining("\n"));
+    }
+}
diff --git a/src/test/resources/action.json b/src/test/resources/action.json
new file mode 100644
index 0000000..10c0886
--- /dev/null
+++ b/src/test/resources/action.json
@@ -0,0 +1,34 @@
+{
+  "annotations": [
+    {
+      "key": "exec",
+      "value": "nodejs:12"
+    }
+  ],
+  "exec": {
+    "kind": "nodejs:12",
+    "code": "function main(params) {}",
+    "binary": false
+  },
+  "limits": {
+    "concurrency": 1,
+    "logs": 1,
+    "memory": 256,
+    "timeout": 60000
+  },
+  "name": "testAct",
+  "namespace": "testNs/testPkg",
+  "parameters": [
+    {
+      "key": "name",
+      "value": "test"
+    },
+    {
+      "key": "count",
+      "value": 1
+    }
+  ],
+  "publish": false,
+  "updated": 1581316522182,
+  "version": "0.0.1"
+}
diff --git a/src/test/resources/actions.json b/src/test/resources/actions.json
new file mode 100644
index 0000000..a37da9f
--- /dev/null
+++ b/src/test/resources/actions.json
@@ -0,0 +1,148 @@
+[
+  {
+    "annotations": [
+      {
+        "key": "web-export",
+        "value": true
+      },
+      {
+        "key": "raw-http",
+        "value": false
+      },
+      {
+        "key": "final",
+        "value": true
+      },
+      {
+        "key": "exec",
+        "value": "nodejs:12"
+      }
+    ],
+    "exec": {
+      "binary": true
+    },
+    "limits": {
+      "concurrency": 1,
+      "logs": 1,
+      "memory": 256,
+      "timeout": 60000
+    },
+    "name": "testAct1",
+    "namespace": "testNs/testPkg1",
+    "publish": false,
+    "updated": 1585756253499,
+    "version": "0.0.1"
+  },
+  {
+    "annotations": [
+      {
+        "key": "web-export",
+        "value": true
+      },
+      {
+        "key": "raw-http",
+        "value": false
+      },
+      {
+        "key": "final",
+        "value": true
+      },
+      {
+        "key": "exec",
+        "value": "nodejs:12"
+      }
+    ],
+    "exec": {
+      "binary": true
+    },
+    "limits": {
+      "concurrency": 1,
+      "logs": 1,
+      "memory": 256,
+      "timeout": 60000
+    },
+    "name": "testAct2",
+    "namespace": "testNs/testPkg2",
+    "publish": false,
+    "updated": 1585756252393,
+    "version": "0.0.1"
+  },
+  {
+    "annotations": [
+      {
+        "key": "web-export",
+        "value": true
+      },
+      {
+        "key": "raw-http",
+        "value": false
+      },
+      {
+        "key": "final",
+        "value": true
+      },
+      {
+        "key": "exec",
+        "value": "nodejs:12"
+      }
+    ],
+    "exec": {
+      "binary": true
+    },
+    "limits": {
+      "concurrency": 1,
+      "logs": 1,
+      "memory": 256,
+      "timeout": 60000
+    },
+    "name": "testAct3",
+    "namespace": "testNs/testPkg3",
+    "publish": false,
+    "updated": 1585756251289,
+    "version": "0.0.1"
+  },
+  {
+    "annotations": [
+      {
+        "key": "exec",
+        "value": "blackbox"
+      }
+    ],
+    "exec": {
+      "binary": false
+    },
+    "limits": {
+      "concurrency": 1,
+      "logs": 1,
+      "memory": 256,
+      "timeout": 60000
+    },
+    "name": "testAct4",
+    "namespace": "testNs/testPkg4",
+    "publish": false,
+    "updated": 1582709807073,
+    "version": "0.0.2"
+  },
+  {
+    "annotations": [
+      {
+        "key": "exec",
+        "value": "python:3"
+      }
+    ],
+    "exec": {
+      "binary": false
+    },
+    "limits": {
+      "concurrency": 1,
+      "logs": 1,
+      "memory": 256,
+      "timeout": 60000
+    },
+    "name": "testAct5",
+    "namespace": "testNs/testPkg5",
+    "publish": false,
+    "updated": 1581316522182,
+    "version": "0.0.1"
+  }
+]
diff --git a/src/test/resources/activation_with_log.json b/src/test/resources/activation_with_log.json
new file mode 100644
index 0000000..f598265
--- /dev/null
+++ b/src/test/resources/activation_with_log.json
@@ -0,0 +1,55 @@
+{
+  "activationId": "26760d5e868f4223b60d5e868f02233c",
+  "annotations": [
+    {
+      "key": "path",
+      "value": "ns1/action1"
+    },
+    {
+      "key": "waitTime",
+      "value": 468
+    },
+    {
+      "key": "kind",
+      "value": "nodejs:6"
+    },
+    {
+      "key": "timeout",
+      "value": false
+    },
+    {
+      "key": "limits",
+      "value": {
+        "concurrency": 1,
+        "logs": 1,
+        "memory": 256,
+        "timeout": 60000
+      }
+    },
+    {
+      "key": "initTime",
+      "value": 241
+    }
+  ],
+  "duration": 253,
+  "end": 1591082866561,
+  "logs": [],
+  "name": "action1",
+  "namespace": "ns1",
+  "publish": false,
+  "response": {
+    "result": {
+      "payload": "Hello",
+      "test": {
+        "test1": "test1",
+        "test2": "test2"
+      }
+    },
+    "size": 72,
+    "status": "success",
+    "success": true
+  },
+  "start": 1591082866308,
+  "subject": "ns1",
+  "version": "0.0.10"
+}
diff --git a/src/test/resources/activations.json b/src/test/resources/activations.json
new file mode 100644
index 0000000..95dda46
--- /dev/null
+++ b/src/test/resources/activations.json
@@ -0,0 +1,86 @@
+[
+  {
+    "activationId": "969c2bf59e3b40199c2bf59e3b901940",
+    "annotations": [
+      {
+        "key": "path",
+        "value": "ns1/action1"
+      },
+      {
+        "key": "waitTime",
+        "value": 56
+      },
+      {
+        "key": "kind",
+        "value": "nodejs:10"
+      },
+      {
+        "key": "timeout",
+        "value": false
+      },
+      {
+        "key": "limits",
+        "value": {
+          "concurrency": 1,
+          "logs": 1,
+          "memory": 256,
+          "timeout": 50000
+        }
+      },
+      {
+        "key": "initTime",
+        "value": 37
+      }
+    ],
+    "duration": 44,
+    "end": 1592555196153,
+    "name": "action1",
+    "namespace": "ns1",
+    "publish": false,
+    "start": 1592555196109,
+    "statusCode": 0,
+    "version": "0.0.8"
+  },
+  {
+    "activationId": "51adf3c0f8754f3eadf3c0f875ef3eb9",
+    "annotations": [
+      {
+        "key": "path",
+        "value": "ns1/action1"
+      },
+      {
+        "key": "waitTime",
+        "value": 61
+      },
+      {
+        "key": "kind",
+        "value": "nodejs:10"
+      },
+      {
+        "key": "timeout",
+        "value": false
+      },
+      {
+        "key": "limits",
+        "value": {
+          "concurrency": 1,
+          "logs": 1,
+          "memory": 256,
+          "timeout": 50000
+        }
+      },
+      {
+        "key": "initTime",
+        "value": 23
+      }
+    ],
+    "duration": 28,
+    "end": 1592555184779,
+    "name": "action1",
+    "namespace": "ns1",
+    "publish": false,
+    "start": 1592555184751,
+    "statusCode": 0,
+    "version": "0.0.7"
+  }
+]
\ No newline at end of file
diff --git a/src/test/resources/binary-action.json b/src/test/resources/binary-action.json
new file mode 100644
index 0000000..87cc4c8
--- /dev/null
+++ b/src/test/resources/binary-action.json
@@ -0,0 +1,34 @@
+{
+  "annotations": [
+    {
+      "key": "exec",
+      "value": "nodejs:12"
+    }
+  ],
+  "exec": {
+    "kind": "nodejs:12",
+    "code": "aaaaa",
+    "binary": true
+  },
+  "limits": {
+    "concurrency": 1,
+    "logs": 1,
+    "memory": 256,
+    "timeout": 60000
+  },
+  "name": "testAct",
+  "namespace": "testNs/testPkg",
+  "parameters": [
+    {
+      "key": "name",
+      "value": "test"
+    },
+    {
+      "key": "count",
+      "value": 1
+    }
+  ],
+  "publish": false,
+  "updated": 1585756253499,
+  "version": "0.0.1"
+}
\ No newline at end of file
diff --git a/src/test/resources/blackbox-action.json b/src/test/resources/blackbox-action.json
new file mode 100644
index 0000000..a545b8e
--- /dev/null
+++ b/src/test/resources/blackbox-action.json
@@ -0,0 +1,34 @@
+{
+  "annotations": [
+    {
+      "key": "exec",
+      "value": "blackbox"
+    }
+  ],
+  "exec": {
+    "kind": "blackbox",
+    "image": "imageRepo/imageName",
+    "binary": true
+  },
+  "limits": {
+    "concurrency": 1,
+    "logs": 1,
+    "memory": 256,
+    "timeout": 60000
+  },
+  "name": "testAct",
+  "namespace": "testNs/testPkg",
+  "parameters": [
+    {
+      "key": "name",
+      "value": "test"
+    },
+    {
+      "key": "count",
+      "value": 1
+    }
+  ],
+  "publish": false,
+  "updated": 1585756253499,
+  "version": "0.0.1"
+}
\ No newline at end of file
diff --git a/src/test/resources/java-action.json b/src/test/resources/java-action.json
new file mode 100644
index 0000000..3e4e939
--- /dev/null
+++ b/src/test/resources/java-action.json
@@ -0,0 +1,35 @@
+{
+  "annotations": [
+    {
+      "key": "exec",
+      "value": "java"
+    }
+  ],
+  "exec": {
+    "kind": "java",
+    "main": "test",
+    "binary": true,
+    "code": "aaaaa"
+  },
+  "limits": {
+    "concurrency": 1,
+    "logs": 1,
+    "memory": 256,
+    "timeout": 60000
+  },
+  "name": "testAct",
+  "namespace": "testNs/testPkg",
+  "parameters": [
+    {
+      "key": "name",
+      "value": "test"
+    },
+    {
+      "key": "count",
+      "value": 1
+    }
+  ],
+  "publish": false,
+  "updated": 1585756253499,
+  "version": "0.0.1"
+}
\ No newline at end of file
diff --git a/src/test/resources/package.json b/src/test/resources/package.json
new file mode 100644
index 0000000..edd7b10
--- /dev/null
+++ b/src/test/resources/package.json
@@ -0,0 +1,57 @@
+{
+  "namespace": "testns",
+  "name": "test",
+  "version": "0.0.6",
+  "updated": 1583828352890,
+  "publish": true,
+  "annotations": [
+    {
+      "key": "description",
+      "value": "test"
+    }
+  ],
+  "parameters": [
+    {
+      "key": "test",
+      "value": "test"
+    },
+    {
+      "key": "test1",
+      "value": "test1"
+    }
+  ],
+  "binding": {},
+  "feeds": [],
+  "actions": [
+    {
+      "name": "action1",
+      "version": "0.0.2",
+      "annotations": [
+        {
+          "key": "exec",
+          "value": "nodejs:10"
+        }
+      ]
+    },
+    {
+      "name": "action2",
+      "version": "0.0.2",
+      "annotations": [
+        {
+          "key": "exec",
+          "value": "nodejs:10"
+        }
+      ]
+    },
+    {
+      "name": "action3",
+      "version": "0.0.2",
+      "annotations": [
+        {
+          "key": "exec",
+          "value": "nodejs:10"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/src/test/resources/packages.json b/src/test/resources/packages.json
new file mode 100644
index 0000000..8142167
--- /dev/null
+++ b/src/test/resources/packages.json
@@ -0,0 +1,54 @@
+[
+  {
+    "annotations": [
+      {
+        "key": "description",
+        "value": "test"
+      }
+    ],
+    "binding": false,
+    "name": "pkg1",
+    "namespace": "ns",
+    "publish": false,
+    "updated": 1586513535028,
+    "version": "0.0.1"
+  },
+  {
+    "annotations": [
+      {
+        "key": "description",
+        "value": "test"
+      }
+    ],
+    "binding": false,
+    "name": "pkg2",
+    "namespace": "ns",
+    "publish": false,
+    "updated": 1586513513922,
+    "version": "0.0.1"
+  },
+  {
+    "annotations": [
+      {
+        "key": "description",
+        "value": "test"
+      },
+      {
+        "key": "binding",
+        "value": {
+          "name": "sharedpackage",
+          "namespace": "whisk.system"
+        }
+      }
+    ],
+    "binding": {
+      "name": "sharedpackage",
+      "namespace": "whisk.system"
+    },
+    "name": "pkg3",
+    "namespace": "ns",
+    "publish": false,
+    "updated": 1557380799999,
+    "version": "0.0.1"
+  }
+]
diff --git a/src/test/resources/sequence-action.json b/src/test/resources/sequence-action.json
new file mode 100644
index 0000000..7fd66d9
--- /dev/null
+++ b/src/test/resources/sequence-action.json
@@ -0,0 +1,35 @@
+{
+  "annotations": [
+    {
+      "key": "exec",
+      "value": "sequence"
+    }
+  ],
+  "exec": {
+    "kind": "sequence",
+    "components": [
+      "/testNs/testAct1",
+      "/testNs/testAct2"
+    ]
+  },
+  "limits": {
+    "concurrency": 1,
+    "logs": 1,
+    "memory": 256,
+    "timeout": 60000
+  },
+  "name": "testAct",
+  "namespace": "testNs/testPkg",
+  "parameters": [
+    {
+      "key": "_actions",
+      "components": [
+        "/testNs/testAct1",
+        "/testNs/testAct2"
+      ]
+    }
+  ],
+  "publish": false,
+  "updated": 1585756253499,
+  "version": "0.0.1"
+}
\ No newline at end of file
diff --git a/src/test/resources/trigger.json b/src/test/resources/trigger.json
new file mode 100644
index 0000000..9efe83f
--- /dev/null
+++ b/src/test/resources/trigger.json
@@ -0,0 +1,24 @@
+{
+  "annotations": [
+    {
+      "key": "feed",
+      "value": "/whisk.system/alarms/alarm"
+    }
+  ],
+  "limits": {},
+  "name": "trigger1",
+  "namespace": "test",
+  "parameters": [],
+  "publish": false,
+  "rules": {
+    "test/rule1": {
+      "action": {
+        "name": "action1",
+        "path": "test"
+      },
+      "status": "active"
+    }
+  },
+  "updated": 1586315245020,
+  "version": "0.0.2"
+}
\ No newline at end of file
diff --git a/src/test/resources/triggers.json b/src/test/resources/triggers.json
new file mode 100644
index 0000000..6860863
--- /dev/null
+++ b/src/test/resources/triggers.json
@@ -0,0 +1,36 @@
+[
+  {
+    "annotations": [
+      {
+        "key": "feed",
+        "value": "/whisk.system/alarms/alarm"
+      }
+    ],
+    "name": "trigger1",
+    "namespace": "test",
+    "publish": false,
+    "updated": 1586315510331,
+    "version": "0.0.2"
+  },
+  {
+    "annotations": [
+      {
+        "key": "feed",
+        "value": "/whisk.system/alarms/alarm"
+      }
+    ],
+    "name": "trigger2",
+    "namespace": "test",
+    "publish": false,
+    "updated": 1586315245020,
+    "version": "0.0.2"
+  },
+  {
+    "annotations": [],
+    "name": "trigger3",
+    "namespace": "test",
+    "publish": false,
+    "updated": 1582612773239,
+    "version": "0.0.2"
+  }
+]
\ No newline at end of file