├── .dockerignore
├── .editorconfig
├── .gitignore
├── Dockerfile
├── Makefile
├── README.md
├── bin
└── cli.js
├── docs
└── main.png
├── index.js
├── kubernetes
├── redis-cluster-ip.yml
└── redis-deployment.yml
├── manager
├── Dockerfile
├── kubernetes
│ ├── server-deployment.yml
│ └── server-load-balancer-service.yml
└── pkg
│ ├── config.go
│ ├── config
│ └── config.go
│ ├── kubernetes
│ ├── client.go
│ └── watchtower_service.go
│ ├── server
│ ├── index.go
│ ├── response.go
│ ├── server.go
│ └── watchtower_router.go
│ └── watchtower.go
├── package.json
├── src
├── clients
│ ├── redis.js
│ ├── twilio.js
│ └── twitter.js
├── utils.js
└── watchtower.js
└── yarn.lock
/.dockerignore:
--------------------------------------------------------------------------------
1 | manager
2 | node_modules
3 | configmap.yml
4 | .env
5 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = lf
5 | charset = utf-8
6 | trim_trailing_whitespace = true
7 | insert_final_newline = true
8 | indent_style = tab
9 | indent_size = 2
10 |
11 | [*.{diff,md}]
12 | trim_trailing_whitespace = false
13 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_STORE
2 | app
3 | .env
4 | node_modules
5 | configmap.yml
6 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM mhart/alpine-node:11
2 |
3 | WORKDIR /app
4 | COPY package.json /app
5 | RUN npm install
6 | COPY . /app
7 | CMD node ./bin/cli
8 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | .PHONY: install test build serve clean pack deploy ship
2 |
3 | TAG?=$(shell git rev-list HEAD --max-count=1 --abbrev-commit)
4 |
5 | export TAG
6 |
7 | install:
8 | go get ./manager/cmd/app
9 |
10 | test: install
11 | go test ./...
12 |
13 | build: install
14 | go build -ldflags "-X main.version=$(TAG)" -o manager/app manager/cmd/app/*
15 |
16 | serve: build
17 | ./app
18 |
19 | clean:
20 | rm ./manager/app
21 |
22 | clean-watchtowers:
23 | kubectl delete deployment -n watchtower --selector=app=watchtower
24 |
25 | pack:
26 | GOOS=linux make build
27 | docker build -t mattgarnett/auto-k8s:$(TAG) manager
28 |
29 | upload:
30 | docker push mattgarnett/auto-k8s:$(TAG)
31 |
32 | deploy:
33 | kubectl delete deployment -n watchtower server-deployment
34 | sed 's/REPLACE_WITH_TAG/$(TAG)/' manager/kubernetes/server-deployment.yml | kubectl apply -f -
35 | kubectl apply -f manager/kubernetes/server-load-balancer-service.yml
36 |
37 | ship: pack upload deploy clean
38 |
39 |
40 | build-watchtower:
41 | docker build -t mattgarnett/watchtower:$(TAG) .
42 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Watchtower
2 |
3 | Watchtower is a monitoring solution for [Plasma](https://medium.com/@argongroup/ethereum-plasma-explained-608720d3c60e) that is able to notify registered users of malicious activity occuring on the chain in real time.
4 |
5 | *We built our project around [Plasma Group](https://plasma.group)'s implementation of Plasma Cash.*
6 |
7 | ### Forks and other related repos:
8 | * https://github.com/villanuevawill/plasma-chain-operator
9 | * https://github.com/villanuevawill/plasma-core
10 | * https://github.com/villanuevawill/plasma-client
11 | * https://github.com/villanuevawill/plasma-js-lib
12 | * https://github.com/c-o-l-o-r/standalone-tor-proxy
13 |
14 | 
15 |
--------------------------------------------------------------------------------
/bin/cli.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | require('dotenv').config()
4 |
5 | const program = require('commander')
6 | const colors = require('colors')
7 | const Watchtower = require('../src/watchtower')
8 |
9 | program
10 | .version('0.0.1')
11 | .option('-a, --address
', 'Public address to monitor')
12 | .option('-p, --phone ', 'Phone number to recieve SMS notifications on')
13 | .parse(process.argv)
14 |
15 | // const debug = program.debug ? 'debug:*' : ''
16 |
17 | // const options = {
18 | // finalityDepth: program.finality,
19 | // port: program.port,
20 | // ethereumEndpoint: program.ethereum,
21 | // debug: `service:*,${debug}`,
22 | // contractProvider: PlasmaCore.providers.ContractProviders.ContractProvider,
23 | // walletProvider: wallets[program.wallet],
24 | // operatorProvider: PlasmaCore.providers.OperatorProviders.HttpOperatorProvider,
25 | // plasmaChainName: program.chain,
26 | // registryAddress: program.registry
27 | // }
28 |
29 | const options = {
30 | address: program.address,
31 | phone: program.phone,
32 | }
33 |
34 | const watchtower = new Watchtower(options)
35 |
36 | // (async () => {
37 | // // const latest = await latestVersion(pkg.name)
38 | // // if (pkg.version !== latest) {
39 | // // console.log(colors.red('ERROR: ') + 'Your plasma-client is out of date.')
40 | // // console.log('Please update to the latest version by running:')
41 | // // console.log(colors.green('npm install -g --upgrade plasma-client'))
42 | // // console.log()
43 | // // console.log(`You might also want to reset your database (this won't delete your accounts):`)
44 | // // console.log(colors.green('plasma-cli killdb'))
45 | // // return
46 | // // }
47 |
48 |
49 |
50 | // // console.log('Plasma Client v' + pkg.version + ' 🎉 🎉 🎉 ')
51 |
52 | // // console.log(getSectionTitle('DISCLAIMER'))
53 | // // console.log('Plasma Client is alpha software and will probably break.')
54 | // // console.log(`Please do NOT use this application with real money (unless you're willing to lose it).`)
55 |
56 | // // console.log(getSectionTitle('Available Accounts'))
57 | // // const accounts = await client.getAccounts()
58 | // // accounts.forEach((account, i) => {
59 | // // const maxDigits = (accounts.length - 1).toString().length
60 | // // const accountNumber = i.toString().padStart(maxDigits, '0')
61 | // // console.log(`(${accountNumber}) ${account}`)
62 | // // })
63 |
64 | // // console.log(getSectionTitle('Client Information'))
65 | // // console.log(`Plasma Chain: ${program.chain}`)
66 | // // console.log(`Ethereum Node: ${program.ethereum}`)
67 | // // console.log(`Listening on: http://${program.hostname}:${program.port}`)
68 |
69 | // // console.log(getSectionTitle('Logs'))
70 | // })()
71 |
72 |
73 |
74 | async function main() {
75 | await watchtower.start()
76 | }
77 |
78 | main().then(() => console.log('done'))
79 |
--------------------------------------------------------------------------------
/docs/main.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lightclient/watchtower/89c1c437b793e1cd403d7c7626c09867955135d1/docs/main.png
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | const Watchtower = require('./src/watchtower')
2 |
3 | module.exports = Watchtower
4 |
--------------------------------------------------------------------------------
/kubernetes/redis-cluster-ip.yml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: redis-cluster-ip-service
5 | namespace: watchtower
6 | spec:
7 | type: ClusterIP
8 | selector:
9 | component: redis
10 | ports:
11 | - port: 6379
12 | targetPort: 6379
13 |
--------------------------------------------------------------------------------
/kubernetes/redis-deployment.yml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: redis-deployment
5 | namespace: watchtower
6 | spec:
7 | replicas: 1
8 | selector:
9 | matchLabels:
10 | component: redis
11 | template:
12 | metadata:
13 | labels:
14 | component: redis
15 | spec:
16 | containers:
17 | - name: redis
18 | image: redis
19 | ports:
20 | - containerPort: 6379
21 |
--------------------------------------------------------------------------------
/manager/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM alpine:3.4
2 | ADD app /bin/app
3 | CMD ["app"]
4 | EXPOSE 8080
5 |
--------------------------------------------------------------------------------
/manager/kubernetes/server-deployment.yml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: server-deployment
5 | namespace: watchtower
6 | spec:
7 | replicas: 1
8 | selector:
9 | matchLabels:
10 | component: server
11 | template:
12 | metadata:
13 | labels:
14 | component: server
15 | spec:
16 | containers:
17 | - name: server
18 | image: mattgarnett/auto-k8s:REPLACE_WITH_TAG
19 | command: ["app"]
20 | ports:
21 | - containerPort: 8080
22 | imagePullPolicy: Always
23 |
--------------------------------------------------------------------------------
/manager/kubernetes/server-load-balancer-service.yml:
--------------------------------------------------------------------------------
1 | kind: Service
2 | apiVersion: v1
3 | metadata:
4 | name: server-load-balancer
5 | namespace: watchtower
6 | spec:
7 | type: LoadBalancer
8 | ports:
9 | - port: 80
10 | targetPort: 8080
11 | selector:
12 | component: server
13 |
--------------------------------------------------------------------------------
/manager/pkg/config.go:
--------------------------------------------------------------------------------
1 | package root
2 |
3 | // type MongoConfig struct {
4 | // Ip string `json:"ip"`
5 | // DbName string `json:"dbName"`
6 | // }
7 |
8 | type ServerConfig struct {
9 | Port string `json:"port"`
10 | }
11 |
12 | type KubernetesConfig struct {
13 | Namespace string `json:"namespace"`
14 | }
15 |
16 | // type AuthConfig struct {
17 | // Secret string `json:"secret"`
18 | // }
19 |
20 | type Config struct {
21 | Server *ServerConfig `json:"server"`
22 | Kubernetes *KubernetesConfig `json:"kubernetes"`
23 | }
24 |
--------------------------------------------------------------------------------
/manager/pkg/config/config.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "os"
5 |
6 | root "github.com/c-o-l-o-r/watchtower/manager/pkg"
7 | )
8 |
9 | func GetConfig() *root.Config {
10 | return &root.Config{
11 | Server: &root.ServerConfig{Port: envOrDefaultString("watchtower:server:port", ":8080")},
12 | Kubernetes: &root.KubernetesConfig{Namespace: envOrDefaultString("watchtower:namespace", "watchtower")},
13 | }
14 | }
15 |
16 | func envOrDefaultString(envVar string, defaultValue string) string {
17 | value := os.Getenv(envVar)
18 | if value == "" {
19 | return defaultValue
20 | }
21 |
22 | return value
23 | }
24 |
--------------------------------------------------------------------------------
/manager/pkg/kubernetes/client.go:
--------------------------------------------------------------------------------
1 | package kubernetes
2 |
3 | import (
4 | root "github.com/c-o-l-o-r/watchtower/manager/pkg"
5 | "k8s.io/client-go/kubernetes"
6 | appsv1 "k8s.io/client-go/kubernetes/typed/apps/v1"
7 | "k8s.io/client-go/rest"
8 | )
9 |
10 | type Client struct {
11 | deployments appsv1.DeploymentInterface
12 | }
13 |
14 | func NewClient(config *root.KubernetesConfig) (*Client, error) {
15 | clusterConfig, err := rest.InClusterConfig()
16 | if err != nil {
17 | panic(err.Error())
18 | }
19 |
20 | clientset, err := kubernetes.NewForConfig(clusterConfig)
21 | if err != nil {
22 | panic(err.Error())
23 | }
24 |
25 | return &Client{deployments: clientset.AppsV1().Deployments(config.Namespace)}, err
26 | }
27 |
--------------------------------------------------------------------------------
/manager/pkg/kubernetes/watchtower_service.go:
--------------------------------------------------------------------------------
1 | package kubernetes
2 |
3 | import (
4 | "fmt"
5 | "strings"
6 |
7 | root "github.com/c-o-l-o-r/watchtower/manager/pkg"
8 | appsv1 "k8s.io/api/apps/v1"
9 | apiv1 "k8s.io/api/core/v1"
10 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
11 | )
12 |
13 | type WatchtowerService struct {
14 | client *Client
15 | }
16 |
17 | func NewWatchtowerService(c *Client) *WatchtowerService {
18 | return &WatchtowerService{c}
19 | }
20 |
21 | func (p *WatchtowerService) CreateWatchtower(a root.WatchtowerAttributes) error {
22 |
23 | // Address provided to kubernetes must be lowercase in order to adhere to RFC 1123
24 | lowercaseAddress := strings.ToLower(a.Address)
25 |
26 | deployment := &appsv1.Deployment{
27 | ObjectMeta: metav1.ObjectMeta{
28 | Name: fmt.Sprintf("watchtower-%s", lowercaseAddress),
29 | Namespace: "watchtower",
30 | },
31 | Spec: appsv1.DeploymentSpec{
32 | Replicas: int32Ptr(1),
33 | Selector: &metav1.LabelSelector{
34 | MatchLabels: map[string]string{
35 | "app": "watchtower",
36 | },
37 | },
38 | Template: apiv1.PodTemplateSpec{
39 | ObjectMeta: metav1.ObjectMeta{
40 | Labels: map[string]string{
41 | "app": "watchtower",
42 | },
43 | },
44 | Spec: apiv1.PodSpec{
45 | Containers: []apiv1.Container{
46 | {
47 | Name: "web",
48 | Image: "mattgarnett/watchtower:89ae292",
49 | Command: []string{"node"},
50 | ImagePullPolicy: "Always",
51 | Args: []string{"./bin/cli.js", "-p", a.Phone, "-a", a.Address},
52 | Ports: []apiv1.ContainerPort{
53 | {
54 | Name: "http",
55 | Protocol: apiv1.ProtocolTCP,
56 | ContainerPort: 80,
57 | },
58 | },
59 | Env: []apiv1.EnvVar{
60 | {
61 | Name: "REDIS_HOST",
62 | Value: "redis://redis-cluster-ip-service:6379",
63 | },
64 | {
65 | Name: "TWILIO_AUTH_TOKEN",
66 | ValueFrom: &apiv1.EnvVarSource{
67 | ConfigMapKeyRef: &apiv1.ConfigMapKeySelector{
68 | LocalObjectReference: apiv1.LocalObjectReference{
69 | Name: "watchtower-config",
70 | },
71 | Key: "TWILIO_AUTH_TOKEN",
72 | },
73 | },
74 | },
75 | {
76 | Name: "TWILIO_SID",
77 | ValueFrom: &apiv1.EnvVarSource{
78 | ConfigMapKeyRef: &apiv1.ConfigMapKeySelector{
79 | LocalObjectReference: apiv1.LocalObjectReference{
80 | Name: "watchtower-config",
81 | },
82 | Key: "TWILIO_SID",
83 | },
84 | },
85 | },
86 | {
87 | Name: "TWILIO_FROM_NUMBER",
88 | ValueFrom: &apiv1.EnvVarSource{
89 | ConfigMapKeyRef: &apiv1.ConfigMapKeySelector{
90 | LocalObjectReference: apiv1.LocalObjectReference{
91 | Name: "watchtower-config",
92 | },
93 | Key: "TWILIO_FROM_NUMBER",
94 | },
95 | },
96 | },
97 | {
98 | Name: "TWITTER_CONSUMER_KEY",
99 | ValueFrom: &apiv1.EnvVarSource{
100 | ConfigMapKeyRef: &apiv1.ConfigMapKeySelector{
101 | LocalObjectReference: apiv1.LocalObjectReference{
102 | Name: "watchtower-config",
103 | },
104 | Key: "TWITTER_CONSUMER_KEY",
105 | },
106 | },
107 | },
108 | {
109 | Name: "TWITTER_CONSUMER_SECRET",
110 | ValueFrom: &apiv1.EnvVarSource{
111 | ConfigMapKeyRef: &apiv1.ConfigMapKeySelector{
112 | LocalObjectReference: apiv1.LocalObjectReference{
113 | Name: "watchtower-config",
114 | },
115 | Key: "TWITTER_CONSUMER_SECRET",
116 | },
117 | },
118 | },
119 | {
120 | Name: "TWITTER_ACCESS_TOKEN",
121 | ValueFrom: &apiv1.EnvVarSource{
122 | ConfigMapKeyRef: &apiv1.ConfigMapKeySelector{
123 | LocalObjectReference: apiv1.LocalObjectReference{
124 | Name: "watchtower-config",
125 | },
126 | Key: "TWITTER_ACCESS_TOKEN",
127 | },
128 | },
129 | },
130 | {
131 | Name: "TWITTER_ACCESS_TOKEN_SECRET",
132 | ValueFrom: &apiv1.EnvVarSource{
133 | ConfigMapKeyRef: &apiv1.ConfigMapKeySelector{
134 | LocalObjectReference: apiv1.LocalObjectReference{
135 | Name: "watchtower-config",
136 | },
137 | Key: "TWITTER_ACCESS_TOKEN_SECRET",
138 | },
139 | },
140 | },
141 | },
142 | },
143 | },
144 | },
145 | },
146 | },
147 | }
148 |
149 | _, err := p.client.deployments.Create(deployment)
150 | return err
151 | }
152 |
153 | func int32Ptr(i int32) *int32 { return &i }
154 |
--------------------------------------------------------------------------------
/manager/pkg/server/index.go:
--------------------------------------------------------------------------------
1 | package server
2 |
3 | const Index string = `
4 |
5 |
6 |
7 |
235 |
236 |
237 |
240 |
294 |
295 |
296 | `
297 |
--------------------------------------------------------------------------------
/manager/pkg/server/response.go:
--------------------------------------------------------------------------------
1 | package server
2 |
3 | import (
4 | "encoding/json"
5 | "net/http"
6 | )
7 |
8 | func Error(w http.ResponseWriter, code int, message string) {
9 | Json(w, code, map[string]string{"error": message})
10 | }
11 |
12 | func Json(w http.ResponseWriter, code int, payload interface{}) {
13 | response, _ := json.Marshal(payload)
14 |
15 | w.Header().Set("Content-Type", "application/json")
16 | w.WriteHeader(code)
17 | w.Write(response)
18 | }
19 |
--------------------------------------------------------------------------------
/manager/pkg/server/server.go:
--------------------------------------------------------------------------------
1 | package server
2 |
3 | import (
4 | "html/template"
5 | "log"
6 | "net/http"
7 | "os"
8 |
9 | root "github.com/c-o-l-o-r/watchtower/manager/pkg"
10 | "github.com/gorilla/handlers"
11 | "github.com/gorilla/mux"
12 | )
13 |
14 | type Server struct {
15 | router *mux.Router
16 | config *root.ServerConfig
17 | }
18 |
19 | func NewServer(w root.WatchtowerService, config *root.Config) *Server {
20 | s := Server{
21 | router: mux.NewRouter(),
22 | config: config.Server,
23 | }
24 |
25 | NewWatchtowerRouter(w, s.getSubrouter("/watchtower"))
26 | s.router.HandleFunc("/", IndexHandler)
27 | return &s
28 | }
29 |
30 | func (s *Server) Start() {
31 | log.Println("Listening on port " + s.config.Port)
32 | if err := http.ListenAndServe(s.config.Port, handlers.LoggingHandler(os.Stdout, s.router)); err != nil {
33 | log.Fatal("http.ListenAndServe: ", err)
34 | }
35 | }
36 |
37 | func (s *Server) getSubrouter(path string) *mux.Router {
38 | return s.router.PathPrefix(path).Subrouter()
39 | }
40 |
41 | func IndexHandler(w http.ResponseWriter, r *http.Request) {
42 | t := template.New("index")
43 | t, err := t.Parse(Index)
44 | if err != nil {
45 | panic(err)
46 | }
47 |
48 | t.Execute(w, nil)
49 | }
50 |
--------------------------------------------------------------------------------
/manager/pkg/server/watchtower_router.go:
--------------------------------------------------------------------------------
1 | package server
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "net/http"
7 |
8 | root "github.com/c-o-l-o-r/watchtower/manager/pkg"
9 | "github.com/gorilla/mux"
10 | "github.com/gorilla/schema"
11 | )
12 |
13 | type watchtowerRouter struct {
14 | watchtowerService root.WatchtowerService
15 | }
16 |
17 | func NewWatchtowerRouter(w root.WatchtowerService, router *mux.Router) *mux.Router {
18 | watchtowerRouter := watchtowerRouter{w}
19 |
20 | router.HandleFunc("/", watchtowerRouter.createWatchtowerJSONHandler).
21 | HeadersRegexp("Content-Type", "application/(text|json)").
22 | Methods("POST")
23 |
24 | router.HandleFunc("/", watchtowerRouter.createWatchtowerFormHandler).
25 | HeadersRegexp("Content-Type", "application/x-www-form-urlencoded").
26 | Methods("POST")
27 |
28 | return router
29 | }
30 |
31 | func (wt *watchtowerRouter) createWatchtowerJSONHandler(w http.ResponseWriter, r *http.Request) {
32 | decoder := json.NewDecoder(r.Body)
33 | var watchtowerAttributes root.WatchtowerAttributes
34 | err := decoder.Decode(&watchtowerAttributes)
35 | if err != nil {
36 | panic(err)
37 | }
38 |
39 | err = wt.watchtowerService.CreateWatchtower(watchtowerAttributes)
40 | if err != nil {
41 | panic(err)
42 | }
43 |
44 | Json(w, http.StatusOK, "success")
45 | }
46 |
47 | func (wt *watchtowerRouter) createWatchtowerFormHandler(w http.ResponseWriter, r *http.Request) {
48 | refer := r.Header.Get("Referer")
49 |
50 | err := r.ParseForm()
51 | if err != nil {
52 | redirectFail(w, r, refer)
53 | }
54 |
55 | decoder := schema.NewDecoder()
56 | var watchtowerAttributes root.WatchtowerAttributes
57 | err = decoder.Decode(&watchtowerAttributes, r.PostForm)
58 | if err != nil {
59 | redirectFail(w, r, refer)
60 | }
61 |
62 | err = wt.watchtowerService.CreateWatchtower(watchtowerAttributes)
63 | if err != nil {
64 | redirectFail(w, r, refer)
65 | }
66 |
67 | redirectURL := fmt.Sprintf("%s?success=true&address=%s", refer, watchtowerAttributes.Address)
68 | http.Redirect(w, r, redirectURL, 302)
69 | }
70 |
71 | func redirectFail(w http.ResponseWriter, r *http.Request, url string) {
72 | http.Redirect(w, r, fmt.Sprintf("%s?success=false", url), 302)
73 | }
74 |
--------------------------------------------------------------------------------
/manager/pkg/watchtower.go:
--------------------------------------------------------------------------------
1 | package root
2 |
3 | type WatchtowerAttributes struct {
4 | Address string
5 | Email string
6 | Phone string
7 | }
8 |
9 | type WatchtowerService interface {
10 | CreateWatchtower(a WatchtowerAttributes) error
11 | }
12 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "watchtower",
3 | "version": "1.0.0",
4 | "main": "index.js",
5 | "repository": "git@github.com:c-o-l-o-r/watchtower.git",
6 | "author": "Matt Garnett ",
7 | "license": "MIT",
8 | "dependencies": {
9 | "axios": "^0.18.0",
10 | "colors": "^1.3.3",
11 | "commander": "^2.19.0",
12 | "dotenv": "^6.2.0",
13 | "inquirer": "^6.2.2",
14 | "redis": "^2.8.0",
15 | "twilio": "^3.28.0",
16 | "twit": "^2.2.11",
17 | "uuid": "^3.3.2"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/clients/redis.js:
--------------------------------------------------------------------------------
1 | const redis = require('redis');
2 | const { promisify } = require('util');
3 |
4 | const client = redis.createClient({ url: process.env['REDIS_HOST'] });
5 |
6 | const get = promisify(client.get).bind(client);
7 | const set = promisify(client.set).bind(client);
8 |
9 | module.exports = { get, set }
10 |
--------------------------------------------------------------------------------
/src/clients/twilio.js:
--------------------------------------------------------------------------------
1 | const accountSid = process.env.TWILIO_SID;
2 | const authToken = process.env.TWILIO_AUTH_TOKEN;
3 | const client = require('twilio')(accountSid, authToken);
4 |
5 | function send(message, to) {
6 | return client.messages.create({
7 | body: message,
8 | from: process.env.TWILIO_FROM_NUMBER,
9 | to: to
10 | })
11 | }
12 |
13 | module.exports = { send }
14 |
--------------------------------------------------------------------------------
/src/clients/twitter.js:
--------------------------------------------------------------------------------
1 | const Twit = require('twit')
2 |
3 | const T = new Twit({
4 | consumer_key: process.env.TWITTER_CONSUMER_KEY,
5 | consumer_secret: process.env.TWITTER_CONSUMER_SECRET,
6 | access_token: process.env.TWITTER_ACCESS_TOKEN,
7 | access_token_secret: process.env.TWITTER_ACCESS_TOKEN_SECRET,
8 | timeout_ms: 60*1000, // optional HTTP request timeout to apply to all requests.
9 | strictSSL: true, // optional - requires SSL certificates to be valid.
10 | })
11 |
12 | function post(status) {
13 | return new Promise((resolve, reject) => {
14 | T.post('statuses/update', { status }, function(err, data, response) {
15 | if (err) {
16 | reject(err)
17 | }
18 |
19 | resolve(data)
20 | })
21 | })
22 | }
23 |
24 | module.exports = { post }
25 |
--------------------------------------------------------------------------------
/src/utils.js:
--------------------------------------------------------------------------------
1 | function sleep(ms) {
2 | return new Promise(resolve => setTimeout(resolve, ms));
3 | }
4 |
5 | module.exports = { sleep }
6 |
--------------------------------------------------------------------------------
/src/watchtower.js:
--------------------------------------------------------------------------------
1 | const axios = require('axios')
2 | const uuidv4 = require('uuid/v4')
3 |
4 | const twilio = require('./clients/twilio')
5 | const twitter = require('./clients/twitter')
6 | const redis = require('./clients/redis')
7 |
8 | const utils = require('./utils')
9 |
10 | // const defaultOptions = {
11 | // port: '9898',
12 | // dbProvider: LevelDBProvider,
13 | // operatorProvider: PlasmaCore.providers.OperatorProviders.HttpOperatorProvider,
14 | // contractProvider: PlasmaCore.providers.ContractProviders.ContractProvider,
15 | // walletProvider: PlasmaCore.providers.WalletProviders.LocalWalletProvider,
16 | // dbPath: dbPaths.CHAIN_DB_PATH
17 | // }
18 |
19 | /**
20 | * Class that houses the Watchtower.
21 | */
22 | class Watchtower {
23 | constructor (options) {
24 | this.options = Object.assign({}, options)
25 | console.log(options)
26 | }
27 |
28 | /**
29 | * Starts the node.
30 | */
31 | async start () {
32 | this.started = true
33 | await this._initConnection()
34 | await this._sendRPC('pg_monitorAccount', this.options.address)
35 | this._pollNode()
36 | }
37 |
38 | /**
39 | * Stops the node.
40 | */
41 | stop () {
42 | this.started = false
43 | }
44 |
45 | /**
46 | * Polls the node for invalid transactions and notifies the account owner if
47 | * someone attempts to transfer a coinrange they own
48 | */
49 | async _pollNode() {
50 | if (!this.started) return
51 |
52 | try {
53 | console.log('polling')
54 |
55 | const response = await this._sendRPC(
56 | 'pg_getMaliciousTransactions',
57 | this.options.address
58 | )
59 |
60 | console.log('got this from node: ', response)
61 |
62 | for (element in response.result) {
63 | const [txHash, error] = element
64 |
65 | const notified = await redis.get(txHash)
66 |
67 | if (!notified) {
68 | redis.set(txHash, 'true')
69 | this._notifyOwner()
70 | }
71 | }
72 | } finally {
73 | await utils.sleep(10000)
74 | this._pollOperator()
75 | }
76 | }
77 |
78 | /**
79 | * Sends the owner a text message regarding the invalid transfer attempt and
80 | * posts a tweet to the Plasma chain's Twitter account
81 | */
82 | async _notifyOwner() {
83 | await Promise.all([
84 | this._sendSMS(),
85 | this._sendTweet(),
86 | ])
87 | }
88 |
89 | /**
90 | * Sends a SMS message to the account owner
91 | */
92 | async _sendSMS(data) {
93 | console.log('sending sms')
94 | try {
95 | await twilio.send('Your coins are running away!', this.options.phone)
96 | } catch (e) {
97 | console.log(e)
98 | }
99 | }
100 |
101 | /**
102 | * Post a Tweet to the Plasma chain's Twitter account
103 | */
104 | async _sendTweet(data) {
105 | console.log('sending tweet')
106 | try {
107 | await twitter.post('Bad stuff is happening on the chain!')
108 | } catch(e) {
109 | console.log(e)
110 | }
111 | }
112 |
113 | /**
114 | * Initializes axios http connection to the Plasma node
115 | */
116 | async _initConnection () {
117 | this.endpoint = 'http://plasma-node-cluster-ip-service:9898'
118 | this.http = axios.create({
119 | baseURL: this.endpoint.startsWith('http')
120 | ? this.endpoint
121 | : `https://${this.endpoint}`
122 | })
123 | }
124 |
125 | /**
126 | * Sends an RPC call over http to the Plasma node
127 | * @param {string} method
128 | * @param {string | Object} params
129 | */
130 | async _sendRPC(method, params) {
131 | try {
132 | const response = await this.http.post('/', {
133 | jsonrpc: '2.0',
134 | method: method,
135 | params: params,
136 | id: uuidv4()
137 | })
138 |
139 | return JSON.parse(response.data)
140 | } catch(e) {
141 | throw e
142 | }
143 | }
144 | }
145 |
146 | module.exports = Watchtower
147 |
--------------------------------------------------------------------------------
/yarn.lock:
--------------------------------------------------------------------------------
1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2 | # yarn lockfile v1
3 |
4 |
5 | "@types/body-parser@*":
6 | version "1.17.0"
7 | resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.17.0.tgz#9f5c9d9bd04bb54be32d5eb9fc0d8c974e6cf58c"
8 | integrity sha512-a2+YeUjPkztKJu5aIF2yArYFQQp8d51wZ7DavSHjFuY1mqVgidGyzEQ41JIVNy82fXj8yPgy2vJmfIywgESW6w==
9 | dependencies:
10 | "@types/connect" "*"
11 | "@types/node" "*"
12 |
13 | "@types/connect@*":
14 | version "3.4.32"
15 | resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.32.tgz#aa0e9616b9435ccad02bc52b5b454ffc2c70ba28"
16 | integrity sha512-4r8qa0quOvh7lGD0pre62CAb1oni1OO6ecJLGCezTmhQ8Fz50Arx9RUszryR8KlgK6avuSXvviL6yWyViQABOg==
17 | dependencies:
18 | "@types/node" "*"
19 |
20 | "@types/express-serve-static-core@*":
21 | version "4.16.1"
22 | resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.16.1.tgz#35df7b302299a4ab138a643617bd44078e74d44e"
23 | integrity sha512-QgbIMRU1EVRry5cIu1ORCQP4flSYqLM1lS5LYyGWfKnFT3E58f0gKto7BR13clBFVrVZ0G0rbLZ1hUpSkgQQOA==
24 | dependencies:
25 | "@types/node" "*"
26 | "@types/range-parser" "*"
27 |
28 | "@types/express@^4.11.1":
29 | version "4.16.1"
30 | resolved "https://registry.yarnpkg.com/@types/express/-/express-4.16.1.tgz#d756bd1a85c34d87eaf44c888bad27ba8a4b7cf0"
31 | integrity sha512-V0clmJow23WeyblmACoxbHBu2JKlE5TiIme6Lem14FnPW9gsttyHtk6wq7njcdIWH1njAaFgR8gW09lgY98gQg==
32 | dependencies:
33 | "@types/body-parser" "*"
34 | "@types/express-serve-static-core" "*"
35 | "@types/serve-static" "*"
36 |
37 | "@types/mime@*":
38 | version "2.0.1"
39 | resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.1.tgz#dc488842312a7f075149312905b5e3c0b054c79d"
40 | integrity sha512-FwI9gX75FgVBJ7ywgnq/P7tw+/o1GUbtP0KzbtusLigAOgIgNISRK0ZPl4qertvXSIE8YbsVJueQ90cDt9YYyw==
41 |
42 | "@types/node@*":
43 | version "11.9.4"
44 | resolved "https://registry.yarnpkg.com/@types/node/-/node-11.9.4.tgz#ceb0048a546db453f6248f2d1d95e937a6f00a14"
45 | integrity sha512-Zl8dGvAcEmadgs1tmSPcvwzO1YRsz38bVJQvH1RvRqSR9/5n61Q1ktcDL0ht3FXWR+ZpVmXVwN1LuH4Ax23NsA==
46 |
47 | "@types/range-parser@*":
48 | version "1.2.3"
49 | resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.3.tgz#7ee330ba7caafb98090bece86a5ee44115904c2c"
50 | integrity sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==
51 |
52 | "@types/serve-static@*":
53 | version "1.13.2"
54 | resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.2.tgz#f5ac4d7a6420a99a6a45af4719f4dcd8cd907a48"
55 | integrity sha512-/BZ4QRLpH/bNYgZgwhKEh+5AsboDBcUdlBYgzoLX0fpj3Y2gp6EApyOlM3bK53wQS/OE1SrdSYBAbux2D1528Q==
56 | dependencies:
57 | "@types/express-serve-static-core" "*"
58 | "@types/mime" "*"
59 |
60 | ajv@^6.5.5:
61 | version "6.9.1"
62 | resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.9.1.tgz#a4d3683d74abc5670e75f0b16520f70a20ea8dc1"
63 | integrity sha512-XDN92U311aINL77ieWHmqCcNlwjoP5cHXDxIxbf2MaPYuCXOHS7gHH8jktxeK5omgd52XbSTX6a4Piwd1pQmzA==
64 | dependencies:
65 | fast-deep-equal "^2.0.1"
66 | fast-json-stable-stringify "^2.0.0"
67 | json-schema-traverse "^0.4.1"
68 | uri-js "^4.2.2"
69 |
70 | ansi-escapes@^3.2.0:
71 | version "3.2.0"
72 | resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b"
73 | integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==
74 |
75 | ansi-regex@^3.0.0:
76 | version "3.0.0"
77 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
78 | integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=
79 |
80 | ansi-regex@^4.0.0:
81 | version "4.0.0"
82 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.0.0.tgz#70de791edf021404c3fd615aa89118ae0432e5a9"
83 | integrity sha512-iB5Dda8t/UqpPI/IjsejXu5jOGDrzn41wJyljwPH65VCIbk6+1BzFIMJGFwTNrYXT1CrD+B4l19U7awiQ8rk7w==
84 |
85 | ansi-styles@^3.2.1:
86 | version "3.2.1"
87 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
88 | integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
89 | dependencies:
90 | color-convert "^1.9.0"
91 |
92 | asap@^2.0.0:
93 | version "2.0.6"
94 | resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
95 | integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=
96 |
97 | asn1@~0.2.3:
98 | version "0.2.4"
99 | resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136"
100 | integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==
101 | dependencies:
102 | safer-buffer "~2.1.0"
103 |
104 | assert-plus@1.0.0, assert-plus@^1.0.0:
105 | version "1.0.0"
106 | resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
107 | integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=
108 |
109 | asynckit@^0.4.0:
110 | version "0.4.0"
111 | resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
112 | integrity sha1-x57Zf380y48robyXkLzDZkdLS3k=
113 |
114 | aws-sign2@~0.7.0:
115 | version "0.7.0"
116 | resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
117 | integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=
118 |
119 | aws4@^1.8.0:
120 | version "1.8.0"
121 | resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f"
122 | integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==
123 |
124 | axios@^0.18.0:
125 | version "0.18.0"
126 | resolved "https://registry.yarnpkg.com/axios/-/axios-0.18.0.tgz#32d53e4851efdc0a11993b6cd000789d70c05102"
127 | integrity sha1-MtU+SFHv3AoRmTts0AB4nXDAUQI=
128 | dependencies:
129 | follow-redirects "^1.3.0"
130 | is-buffer "^1.1.5"
131 |
132 | bcrypt-pbkdf@^1.0.0:
133 | version "1.0.2"
134 | resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e"
135 | integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=
136 | dependencies:
137 | tweetnacl "^0.14.3"
138 |
139 | bluebird@^3.1.5:
140 | version "3.5.3"
141 | resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.3.tgz#7d01c6f9616c9a51ab0f8c549a79dfe6ec33efa7"
142 | integrity sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw==
143 |
144 | buffer-equal-constant-time@1.0.1:
145 | version "1.0.1"
146 | resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819"
147 | integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=
148 |
149 | caseless@~0.12.0:
150 | version "0.12.0"
151 | resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
152 | integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=
153 |
154 | chalk@^2.4.2:
155 | version "2.4.2"
156 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
157 | integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
158 | dependencies:
159 | ansi-styles "^3.2.1"
160 | escape-string-regexp "^1.0.5"
161 | supports-color "^5.3.0"
162 |
163 | chardet@^0.7.0:
164 | version "0.7.0"
165 | resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
166 | integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==
167 |
168 | cli-cursor@^2.1.0:
169 | version "2.1.0"
170 | resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5"
171 | integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=
172 | dependencies:
173 | restore-cursor "^2.0.0"
174 |
175 | cli-width@^2.0.0:
176 | version "2.2.0"
177 | resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639"
178 | integrity sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=
179 |
180 | color-convert@^1.9.0:
181 | version "1.9.3"
182 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
183 | integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
184 | dependencies:
185 | color-name "1.1.3"
186 |
187 | color-name@1.1.3:
188 | version "1.1.3"
189 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
190 | integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
191 |
192 | colors@^1.3.3:
193 | version "1.3.3"
194 | resolved "https://registry.yarnpkg.com/colors/-/colors-1.3.3.tgz#39e005d546afe01e01f9c4ca8fa50f686a01205d"
195 | integrity sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg==
196 |
197 | combined-stream@^1.0.6, combined-stream@~1.0.6:
198 | version "1.0.7"
199 | resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.7.tgz#2d1d24317afb8abe95d6d2c0b07b57813539d828"
200 | integrity sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==
201 | dependencies:
202 | delayed-stream "~1.0.0"
203 |
204 | commander@^2.19.0:
205 | version "2.19.0"
206 | resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a"
207 | integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==
208 |
209 | core-util-is@1.0.2:
210 | version "1.0.2"
211 | resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
212 | integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
213 |
214 | dashdash@^1.12.0:
215 | version "1.14.1"
216 | resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
217 | integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=
218 | dependencies:
219 | assert-plus "^1.0.0"
220 |
221 | debug@^3.2.6:
222 | version "3.2.6"
223 | resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
224 | integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==
225 | dependencies:
226 | ms "^2.1.1"
227 |
228 | delayed-stream@~1.0.0:
229 | version "1.0.0"
230 | resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
231 | integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk=
232 |
233 | deprecate@1.0.0:
234 | version "1.0.0"
235 | resolved "https://registry.yarnpkg.com/deprecate/-/deprecate-1.0.0.tgz#661490ed2428916a6c8883d8834e5646f4e4a4a8"
236 | integrity sha1-ZhSQ7SQokWpsiIPYg05WRvTkpKg=
237 |
238 | dotenv@^6.2.0:
239 | version "6.2.0"
240 | resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-6.2.0.tgz#941c0410535d942c8becf28d3f357dbd9d476064"
241 | integrity sha512-HygQCKUBSFl8wKQZBSemMywRWcEDNidvNbjGVyZu3nbZ8qq9ubiPoGLMdRDpfSrpkkm9BXYFkpKxxFX38o/76w==
242 |
243 | double-ended-queue@^2.1.0-0:
244 | version "2.1.0-0"
245 | resolved "https://registry.yarnpkg.com/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz#103d3527fd31528f40188130c841efdd78264e5c"
246 | integrity sha1-ED01J/0xUo9AGIEwyEHv3XgmTlw=
247 |
248 | ecc-jsbn@~0.1.1:
249 | version "0.1.2"
250 | resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9"
251 | integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=
252 | dependencies:
253 | jsbn "~0.1.0"
254 | safer-buffer "^2.1.0"
255 |
256 | ecdsa-sig-formatter@1.0.10:
257 | version "1.0.10"
258 | resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.10.tgz#1c595000f04a8897dfb85000892a0f4c33af86c3"
259 | integrity sha1-HFlQAPBKiJffuFAAiSoPTDOvhsM=
260 | dependencies:
261 | safe-buffer "^5.0.1"
262 |
263 | escape-string-regexp@^1.0.5:
264 | version "1.0.5"
265 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
266 | integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
267 |
268 | extend@~3.0.2:
269 | version "3.0.2"
270 | resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
271 | integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
272 |
273 | external-editor@^3.0.3:
274 | version "3.0.3"
275 | resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.0.3.tgz#5866db29a97826dbe4bf3afd24070ead9ea43a27"
276 | integrity sha512-bn71H9+qWoOQKyZDo25mOMVpSmXROAsTJVVVYzrrtol3d4y+AsKjf4Iwl2Q+IuT0kFSQ1qo166UuIwqYq7mGnA==
277 | dependencies:
278 | chardet "^0.7.0"
279 | iconv-lite "^0.4.24"
280 | tmp "^0.0.33"
281 |
282 | extsprintf@1.3.0:
283 | version "1.3.0"
284 | resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05"
285 | integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=
286 |
287 | extsprintf@^1.2.0:
288 | version "1.4.0"
289 | resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f"
290 | integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8=
291 |
292 | fast-deep-equal@^2.0.1:
293 | version "2.0.1"
294 | resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49"
295 | integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=
296 |
297 | fast-json-stable-stringify@^2.0.0:
298 | version "2.0.0"
299 | resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2"
300 | integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I=
301 |
302 | figures@^2.0.0:
303 | version "2.0.0"
304 | resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962"
305 | integrity sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=
306 | dependencies:
307 | escape-string-regexp "^1.0.5"
308 |
309 | follow-redirects@^1.3.0:
310 | version "1.7.0"
311 | resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.7.0.tgz#489ebc198dc0e7f64167bd23b03c4c19b5784c76"
312 | integrity sha512-m/pZQy4Gj287eNy94nivy5wchN3Kp+Q5WgUPNy5lJSZ3sgkVKSYV/ZChMAQVIgx1SqfZ2zBZtPA2YlXIWxxJOQ==
313 | dependencies:
314 | debug "^3.2.6"
315 |
316 | forever-agent@~0.6.1:
317 | version "0.6.1"
318 | resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
319 | integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=
320 |
321 | form-data@~2.3.2:
322 | version "2.3.3"
323 | resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6"
324 | integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==
325 | dependencies:
326 | asynckit "^0.4.0"
327 | combined-stream "^1.0.6"
328 | mime-types "^2.1.12"
329 |
330 | getpass@^0.1.1:
331 | version "0.1.7"
332 | resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa"
333 | integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=
334 | dependencies:
335 | assert-plus "^1.0.0"
336 |
337 | har-schema@^2.0.0:
338 | version "2.0.0"
339 | resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
340 | integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=
341 |
342 | har-validator@~5.1.0:
343 | version "5.1.3"
344 | resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080"
345 | integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==
346 | dependencies:
347 | ajv "^6.5.5"
348 | har-schema "^2.0.0"
349 |
350 | has-flag@^3.0.0:
351 | version "3.0.0"
352 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
353 | integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
354 |
355 | http-signature@~1.2.0:
356 | version "1.2.0"
357 | resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1"
358 | integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=
359 | dependencies:
360 | assert-plus "^1.0.0"
361 | jsprim "^1.2.2"
362 | sshpk "^1.7.0"
363 |
364 | iconv-lite@^0.4.24:
365 | version "0.4.24"
366 | resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
367 | integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
368 | dependencies:
369 | safer-buffer ">= 2.1.2 < 3"
370 |
371 | inquirer@^6.2.2:
372 | version "6.2.2"
373 | resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.2.2.tgz#46941176f65c9eb20804627149b743a218f25406"
374 | integrity sha512-Z2rREiXA6cHRR9KBOarR3WuLlFzlIfAEIiB45ll5SSadMg7WqOh1MKEjjndfuH5ewXdixWCxqnVfGOQzPeiztA==
375 | dependencies:
376 | ansi-escapes "^3.2.0"
377 | chalk "^2.4.2"
378 | cli-cursor "^2.1.0"
379 | cli-width "^2.0.0"
380 | external-editor "^3.0.3"
381 | figures "^2.0.0"
382 | lodash "^4.17.11"
383 | mute-stream "0.0.7"
384 | run-async "^2.2.0"
385 | rxjs "^6.4.0"
386 | string-width "^2.1.0"
387 | strip-ansi "^5.0.0"
388 | through "^2.3.6"
389 |
390 | is-buffer@^1.1.5:
391 | version "1.1.6"
392 | resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
393 | integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
394 |
395 | is-fullwidth-code-point@^2.0.0:
396 | version "2.0.0"
397 | resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
398 | integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=
399 |
400 | is-promise@^2.1.0:
401 | version "2.1.0"
402 | resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa"
403 | integrity sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=
404 |
405 | is-typedarray@~1.0.0:
406 | version "1.0.0"
407 | resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
408 | integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=
409 |
410 | isstream@~0.1.2:
411 | version "0.1.2"
412 | resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
413 | integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=
414 |
415 | jsbn@~0.1.0:
416 | version "0.1.1"
417 | resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
418 | integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM=
419 |
420 | json-schema-traverse@^0.4.1:
421 | version "0.4.1"
422 | resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
423 | integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
424 |
425 | json-schema@0.2.3:
426 | version "0.2.3"
427 | resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13"
428 | integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=
429 |
430 | json-stringify-safe@~5.0.1:
431 | version "5.0.1"
432 | resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
433 | integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=
434 |
435 | jsonwebtoken@^8.1.0:
436 | version "8.4.0"
437 | resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.4.0.tgz#8757f7b4cb7440d86d5e2f3becefa70536c8e46a"
438 | integrity sha512-coyXjRTCy0pw5WYBpMvWOMN+Kjaik2MwTUIq9cna/W7NpO9E+iYbumZONAz3hcr+tXFJECoQVrtmIoC3Oz0gvg==
439 | dependencies:
440 | jws "^3.1.5"
441 | lodash.includes "^4.3.0"
442 | lodash.isboolean "^3.0.3"
443 | lodash.isinteger "^4.0.4"
444 | lodash.isnumber "^3.0.3"
445 | lodash.isplainobject "^4.0.6"
446 | lodash.isstring "^4.0.1"
447 | lodash.once "^4.0.0"
448 | ms "^2.1.1"
449 |
450 | jsprim@^1.2.2:
451 | version "1.4.1"
452 | resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2"
453 | integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=
454 | dependencies:
455 | assert-plus "1.0.0"
456 | extsprintf "1.3.0"
457 | json-schema "0.2.3"
458 | verror "1.10.0"
459 |
460 | jwa@^1.2.0:
461 | version "1.2.0"
462 | resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.2.0.tgz#606da70c1c6d425cad329c77c99f2df2a981489a"
463 | integrity sha512-Grku9ZST5NNQ3hqNUodSkDfEBqAmGA1R8yiyPHOnLzEKI0GaCQC/XhFmsheXYuXzFQJdILbh+lYBiliqG5R/Vg==
464 | dependencies:
465 | buffer-equal-constant-time "1.0.1"
466 | ecdsa-sig-formatter "1.0.10"
467 | safe-buffer "^5.0.1"
468 |
469 | jws@^3.1.5:
470 | version "3.2.1"
471 | resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.1.tgz#d79d4216a62c9afa0a3d5e8b5356d75abdeb2be5"
472 | integrity sha512-bGA2omSrFUkd72dhh05bIAN832znP4wOU3lfuXtRBuGTbsmNmDXMQg28f0Vsxaxgk4myF5YkKQpz6qeRpMgX9g==
473 | dependencies:
474 | jwa "^1.2.0"
475 | safe-buffer "^5.0.1"
476 |
477 | lodash.includes@^4.3.0:
478 | version "4.3.0"
479 | resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f"
480 | integrity sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=
481 |
482 | lodash.isboolean@^3.0.3:
483 | version "3.0.3"
484 | resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6"
485 | integrity sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=
486 |
487 | lodash.isinteger@^4.0.4:
488 | version "4.0.4"
489 | resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343"
490 | integrity sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=
491 |
492 | lodash.isnumber@^3.0.3:
493 | version "3.0.3"
494 | resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc"
495 | integrity sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=
496 |
497 | lodash.isplainobject@^4.0.6:
498 | version "4.0.6"
499 | resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb"
500 | integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=
501 |
502 | lodash.isstring@^4.0.1:
503 | version "4.0.1"
504 | resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451"
505 | integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=
506 |
507 | lodash.once@^4.0.0:
508 | version "4.1.1"
509 | resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac"
510 | integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=
511 |
512 | lodash@^4.17.10, lodash@^4.17.11:
513 | version "4.17.11"
514 | resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
515 | integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==
516 |
517 | mime-db@~1.38.0:
518 | version "1.38.0"
519 | resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.38.0.tgz#1a2aab16da9eb167b49c6e4df2d9c68d63d8e2ad"
520 | integrity sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg==
521 |
522 | mime-types@^2.1.12, mime-types@~2.1.19:
523 | version "2.1.22"
524 | resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.22.tgz#fe6b355a190926ab7698c9a0556a11199b2199bd"
525 | integrity sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog==
526 | dependencies:
527 | mime-db "~1.38.0"
528 |
529 | mime@^1.3.4:
530 | version "1.6.0"
531 | resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
532 | integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
533 |
534 | mimic-fn@^1.0.0:
535 | version "1.2.0"
536 | resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022"
537 | integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==
538 |
539 | moment@2.19.3:
540 | version "2.19.3"
541 | resolved "https://registry.yarnpkg.com/moment/-/moment-2.19.3.tgz#bdb99d270d6d7fda78cc0fbace855e27fe7da69f"
542 | integrity sha1-vbmdJw1tf9p4zA+6zoVeJ/59pp8=
543 |
544 | ms@^2.1.1:
545 | version "2.1.1"
546 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
547 | integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
548 |
549 | mute-stream@0.0.7:
550 | version "0.0.7"
551 | resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"
552 | integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=
553 |
554 | oauth-sign@~0.9.0:
555 | version "0.9.0"
556 | resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
557 | integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==
558 |
559 | onetime@^2.0.0:
560 | version "2.0.1"
561 | resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4"
562 | integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=
563 | dependencies:
564 | mimic-fn "^1.0.0"
565 |
566 | os-tmpdir@~1.0.2:
567 | version "1.0.2"
568 | resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
569 | integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=
570 |
571 | performance-now@^2.1.0:
572 | version "2.1.0"
573 | resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
574 | integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
575 |
576 | pop-iterate@^1.0.1:
577 | version "1.0.1"
578 | resolved "https://registry.yarnpkg.com/pop-iterate/-/pop-iterate-1.0.1.tgz#ceacfdab4abf353d7a0f2aaa2c1fc7b3f9413ba3"
579 | integrity sha1-zqz9q0q/NT16DyqqLB/Hs/lBO6M=
580 |
581 | psl@^1.1.24:
582 | version "1.1.31"
583 | resolved "https://registry.yarnpkg.com/psl/-/psl-1.1.31.tgz#e9aa86d0101b5b105cbe93ac6b784cd547276184"
584 | integrity sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==
585 |
586 | punycode@^1.4.1:
587 | version "1.4.1"
588 | resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
589 | integrity sha1-wNWmOycYgArY4esPpSachN1BhF4=
590 |
591 | punycode@^2.1.0:
592 | version "2.1.1"
593 | resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
594 | integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
595 |
596 | q@2.0.x:
597 | version "2.0.3"
598 | resolved "https://registry.yarnpkg.com/q/-/q-2.0.3.tgz#75b8db0255a1a5af82f58c3f3aaa1efec7d0d134"
599 | integrity sha1-dbjbAlWhpa+C9Yw/Oqoe/sfQ0TQ=
600 | dependencies:
601 | asap "^2.0.0"
602 | pop-iterate "^1.0.1"
603 | weak-map "^1.0.5"
604 |
605 | qs@~6.5.2:
606 | version "6.5.2"
607 | resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
608 | integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
609 |
610 | redis-commands@^1.2.0:
611 | version "1.4.0"
612 | resolved "https://registry.yarnpkg.com/redis-commands/-/redis-commands-1.4.0.tgz#52f9cf99153efcce56a8f86af986bd04e988602f"
613 | integrity sha512-cu8EF+MtkwI4DLIT0x9P8qNTLFhQD4jLfxLR0cCNkeGzs87FN6879JOJwNQR/1zD7aSYNbU0hgsV9zGY71Itvw==
614 |
615 | redis-parser@^2.6.0:
616 | version "2.6.0"
617 | resolved "https://registry.yarnpkg.com/redis-parser/-/redis-parser-2.6.0.tgz#52ed09dacac108f1a631c07e9b69941e7a19504b"
618 | integrity sha1-Uu0J2srBCPGmMcB+m2mUHnoZUEs=
619 |
620 | redis@^2.8.0:
621 | version "2.8.0"
622 | resolved "https://registry.yarnpkg.com/redis/-/redis-2.8.0.tgz#202288e3f58c49f6079d97af7a10e1303ae14b02"
623 | integrity sha512-M1OkonEQwtRmZv4tEWF2VgpG0JWJ8Fv1PhlgT5+B+uNq2cA3Rt1Yt/ryoR+vQNOQcIEgdCdfH0jr3bDpihAw1A==
624 | dependencies:
625 | double-ended-queue "^2.1.0-0"
626 | redis-commands "^1.2.0"
627 | redis-parser "^2.6.0"
628 |
629 | request@^2.68.0, request@^2.87.0:
630 | version "2.88.0"
631 | resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef"
632 | integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==
633 | dependencies:
634 | aws-sign2 "~0.7.0"
635 | aws4 "^1.8.0"
636 | caseless "~0.12.0"
637 | combined-stream "~1.0.6"
638 | extend "~3.0.2"
639 | forever-agent "~0.6.1"
640 | form-data "~2.3.2"
641 | har-validator "~5.1.0"
642 | http-signature "~1.2.0"
643 | is-typedarray "~1.0.0"
644 | isstream "~0.1.2"
645 | json-stringify-safe "~5.0.1"
646 | mime-types "~2.1.19"
647 | oauth-sign "~0.9.0"
648 | performance-now "^2.1.0"
649 | qs "~6.5.2"
650 | safe-buffer "^5.1.2"
651 | tough-cookie "~2.4.3"
652 | tunnel-agent "^0.6.0"
653 | uuid "^3.3.2"
654 |
655 | restore-cursor@^2.0.0:
656 | version "2.0.0"
657 | resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf"
658 | integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368=
659 | dependencies:
660 | onetime "^2.0.0"
661 | signal-exit "^3.0.2"
662 |
663 | rootpath@0.1.2:
664 | version "0.1.2"
665 | resolved "https://registry.yarnpkg.com/rootpath/-/rootpath-0.1.2.tgz#5b379a87dca906e9b91d690a599439bef267ea6b"
666 | integrity sha1-Wzeah9ypBum5HWkKWZQ5vvJn6ms=
667 |
668 | run-async@^2.2.0:
669 | version "2.3.0"
670 | resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0"
671 | integrity sha1-A3GrSuC91yDUFm19/aZP96RFpsA=
672 | dependencies:
673 | is-promise "^2.1.0"
674 |
675 | rxjs@^6.4.0:
676 | version "6.4.0"
677 | resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.4.0.tgz#f3bb0fe7bda7fb69deac0c16f17b50b0b8790504"
678 | integrity sha512-Z9Yfa11F6B9Sg/BK9MnqnQ+aQYicPLtilXBp2yUtDt2JRCE0h26d33EnfO3ZxoNxG0T92OUucP3Ct7cpfkdFfw==
679 | dependencies:
680 | tslib "^1.9.0"
681 |
682 | safe-buffer@^5.0.1, safe-buffer@^5.1.2:
683 | version "5.1.2"
684 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
685 | integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
686 |
687 | "safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
688 | version "2.1.2"
689 | resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
690 | integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
691 |
692 | scmp@2.0.0:
693 | version "2.0.0"
694 | resolved "https://registry.yarnpkg.com/scmp/-/scmp-2.0.0.tgz#247110ef22ccf897b13a3f0abddb52782393cd6a"
695 | integrity sha1-JHEQ7yLM+JexOj8KvdtSeCOTzWo=
696 |
697 | signal-exit@^3.0.2:
698 | version "3.0.2"
699 | resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
700 | integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=
701 |
702 | sshpk@^1.7.0:
703 | version "1.16.1"
704 | resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877"
705 | integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==
706 | dependencies:
707 | asn1 "~0.2.3"
708 | assert-plus "^1.0.0"
709 | bcrypt-pbkdf "^1.0.0"
710 | dashdash "^1.12.0"
711 | ecc-jsbn "~0.1.1"
712 | getpass "^0.1.1"
713 | jsbn "~0.1.0"
714 | safer-buffer "^2.0.2"
715 | tweetnacl "~0.14.0"
716 |
717 | string-width@^2.1.0:
718 | version "2.1.1"
719 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
720 | integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==
721 | dependencies:
722 | is-fullwidth-code-point "^2.0.0"
723 | strip-ansi "^4.0.0"
724 |
725 | strip-ansi@^4.0.0:
726 | version "4.0.0"
727 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f"
728 | integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8=
729 | dependencies:
730 | ansi-regex "^3.0.0"
731 |
732 | strip-ansi@^5.0.0:
733 | version "5.0.0"
734 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.0.0.tgz#f78f68b5d0866c20b2c9b8c61b5298508dc8756f"
735 | integrity sha512-Uu7gQyZI7J7gn5qLn1Np3G9vcYGTVqB+lFTytnDJv83dd8T22aGH451P3jueT2/QemInJDfxHB5Tde5OzgG1Ow==
736 | dependencies:
737 | ansi-regex "^4.0.0"
738 |
739 | supports-color@^5.3.0:
740 | version "5.5.0"
741 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
742 | integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
743 | dependencies:
744 | has-flag "^3.0.0"
745 |
746 | through@^2.3.6:
747 | version "2.3.8"
748 | resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
749 | integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=
750 |
751 | tmp@^0.0.33:
752 | version "0.0.33"
753 | resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
754 | integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==
755 | dependencies:
756 | os-tmpdir "~1.0.2"
757 |
758 | tough-cookie@~2.4.3:
759 | version "2.4.3"
760 | resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781"
761 | integrity sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==
762 | dependencies:
763 | psl "^1.1.24"
764 | punycode "^1.4.1"
765 |
766 | tslib@^1.9.0:
767 | version "1.9.3"
768 | resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286"
769 | integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==
770 |
771 | tunnel-agent@^0.6.0:
772 | version "0.6.0"
773 | resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"
774 | integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=
775 | dependencies:
776 | safe-buffer "^5.0.1"
777 |
778 | tweetnacl@^0.14.3, tweetnacl@~0.14.0:
779 | version "0.14.5"
780 | resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
781 | integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=
782 |
783 | twilio@^3.28.0:
784 | version "3.28.0"
785 | resolved "https://registry.yarnpkg.com/twilio/-/twilio-3.28.0.tgz#8ad8b88529ac0cdf0d571f88bf22dc60d1caa002"
786 | integrity sha512-wsBU2+4bWpuhVl4ZjYPaR7WYD2U27ghALclK2Jg9pp4RgtD0EhPMIhTFWkGJ8+zUqdIW/ZkYA/4UocDx9vdoQw==
787 | dependencies:
788 | "@types/express" "^4.11.1"
789 | deprecate "1.0.0"
790 | jsonwebtoken "^8.1.0"
791 | lodash "^4.17.10"
792 | moment "2.19.3"
793 | q "2.0.x"
794 | request "^2.87.0"
795 | rootpath "0.1.2"
796 | scmp "2.0.0"
797 | xmlbuilder "9.0.1"
798 |
799 | twit@^2.2.11:
800 | version "2.2.11"
801 | resolved "https://registry.yarnpkg.com/twit/-/twit-2.2.11.tgz#554343d1cf343ddf503280db821f61be5ab407c3"
802 | integrity sha512-BkdwvZGRVoUTcEBp0zuocuqfih4LB+kEFUWkWJOVBg6pAE9Ebv9vmsYTTrfXleZGf45Bj5H3A1/O9YhF2uSYNg==
803 | dependencies:
804 | bluebird "^3.1.5"
805 | mime "^1.3.4"
806 | request "^2.68.0"
807 |
808 | uri-js@^4.2.2:
809 | version "4.2.2"
810 | resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0"
811 | integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==
812 | dependencies:
813 | punycode "^2.1.0"
814 |
815 | uuid@^3.3.2:
816 | version "3.3.2"
817 | resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131"
818 | integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==
819 |
820 | verror@1.10.0:
821 | version "1.10.0"
822 | resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400"
823 | integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=
824 | dependencies:
825 | assert-plus "^1.0.0"
826 | core-util-is "1.0.2"
827 | extsprintf "^1.2.0"
828 |
829 | weak-map@^1.0.5:
830 | version "1.0.5"
831 | resolved "https://registry.yarnpkg.com/weak-map/-/weak-map-1.0.5.tgz#79691584d98607f5070bd3b70a40e6bb22e401eb"
832 | integrity sha1-eWkVhNmGB/UHC9O3CkDmuyLkAes=
833 |
834 | xmlbuilder@9.0.1:
835 | version "9.0.1"
836 | resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.1.tgz#91cd70897755363eba57c12ddeeab4a341a61f65"
837 | integrity sha1-kc1wiXdVNj66V8Et3uq0o0GmH2U=
838 |
--------------------------------------------------------------------------------