{"_id":"@barbershopio/sprucebot-skills-kit-server","_rev":"17-669844458cbe24e83f903d902e8fc005","name":"@barbershopio/sprucebot-skills-kit-server","dist-tags":{"latest":"6.7.0"},"versions":{"3.0.0":{"name":"@barbershopio/sprucebot-skills-kit-server","publishConfig":{"access":"public"},"description":"The engine that drives your Sprucebot Skill! ⚙️🌲🤖","version":"3.0.0","homepage":"https://github.com/sprucelabsai/sprucebot-skills-kit","bugs":{"url":"https://github.com/sprucelabsai/sprucebot-skills-kit/issues"},"keywords":["node","sprucebot","sprucelabs"],"scripts":{"lint":"eslint '**/**/*{.js}'","test":"jest","precommit":"npm run lint","prepush":"npm run lint && npm run test"},"dependencies":{"@koa/cors":"2","config":"^1.28.0","cookies":"^0.7.1","debug":"^3.1.0","glob":"^7.1.2","jsonwebtoken":"^8.1.0","koa":"^2.4.1","koa-bodyparser":"^4.2.0","koa-router":"^7.2.1","koa-static":"^4.0.1","next":"^5.0.0","node-cron":"^1.2.1","pg":"^7.4.1","react":"^16.2.0","react-dom":"^16.0.0","sequelize":"^4.33.4","umzug":"^2.1.0"},"devDependencies":{"add":"^2.0.6","aws-sdk":"^2.153.0","eslint":"^4.10.0","eslint-config-prettier":"^2.6.0","eslint-plugin-prettier":"^2.3.1","jest":"^22.0.5","prettier":"^1.7.4","yarn":"^1.2.1"},"jest":{"clearMocks":true,"testPathIgnorePatterns":["/config/"],"collectCoverageFrom":["**/*.js","!**/node_modules/**","!**/__TEST__/**"],"coverageDirectory":"./coverage/","collectCoverage":true},"_id":"@barbershopio/sprucebot-skills-kit-server@3.0.0","_npmVersion":"5.6.0","_nodeVersion":"8.10.0","_npmUser":{"name":"kengoldfarb","email":"hello@kengoldfarb.com"},"dist":{"integrity":"sha512-/C9/xGJ+Wvz/Gd+tUIj3tD64Go+cyx2Z71EguRP6QMRLTasDC1DbTAnQaUGRknRl1m78jzOciC9jkkb2Talsrg==","shasum":"995471fab7ffe77305a5d84713ee6b3119a5914d","tarball":"https://registry.npmjs.org/@barbershopio/sprucebot-skills-kit-server/-/sprucebot-skills-kit-server-3.0.0.tgz","fileCount":26,"unpackedSize":46433,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIG8cBfGyLV4Ik18koubShcmplU63GZGNZYzqHNW4Gu6oAiEAtOSYqHHqTzPqH9Tan4/8r96Iy2YSQ3xPw+/5xS9jB4s="}]},"maintainers":[{"name":"barbershop","email":"hello@barbershop.io"},{"name":"casey1449","email":"caseymetz@gmail.com"},{"name":"kengoldfarb","email":"hello@kengoldfarb.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sprucebot-skills-kit-server_3.0.0_1522434865003_0.16402797309033335"},"_hasShrinkwrap":false},"3.0.1":{"name":"@barbershopio/sprucebot-skills-kit-server","publishConfig":{"access":"public"},"description":"The engine that drives your Sprucebot Skill! ⚙️🌲🤖","version":"3.0.1","homepage":"https://github.com/sprucelabsai/sprucebot-skills-kit","bugs":{"url":"https://github.com/sprucelabsai/sprucebot-skills-kit/issues"},"keywords":["node","sprucebot","sprucelabs"],"scripts":{"lint":"eslint '**/**/*{.js}'","test":"jest","precommit":"npm run lint","prepush":"npm run lint && npm run test"},"dependencies":{"@koa/cors":"2","config":"^1.28.0","cookies":"^0.7.1","debug":"^3.1.0","glob":"^7.1.2","jsonwebtoken":"^8.1.0","koa":"^2.4.1","koa-bodyparser":"^4.2.0","koa-router":"^7.2.1","koa-static":"^4.0.1","next":"^5.0.0","node-cron":"^1.2.1","pg":"^7.4.1","react":"^16.2.0","react-dom":"^16.0.0","sequelize":"^4.33.4","umzug":"^2.1.0"},"devDependencies":{"add":"^2.0.6","aws-sdk":"^2.153.0","eslint":"^4.10.0","eslint-config-prettier":"^2.6.0","eslint-plugin-prettier":"^2.3.1","jest":"^22.0.5","prettier":"^1.7.4","yarn":"^1.2.1"},"jest":{"clearMocks":true,"testPathIgnorePatterns":["/config/"],"collectCoverageFrom":["**/*.js","!**/node_modules/**","!**/__TEST__/**"],"coverageDirectory":"./coverage/","collectCoverage":true},"_id":"@barbershopio/sprucebot-skills-kit-server@3.0.1","_npmVersion":"5.6.0","_nodeVersion":"8.10.0","_npmUser":{"name":"kengoldfarb","email":"hello@kengoldfarb.com"},"dist":{"integrity":"sha512-ynnWtWljymvE+kGai+vYJo5DVXIlOBirXNY/X+hPq4moXGFaQsCAnsCMiEMf1WVIV5bzAFGlV2m4j3NKxnP1Zg==","shasum":"8be36b62586e9c0b23efbaf82cc7ab90d5e6c6dd","tarball":"https://registry.npmjs.org/@barbershopio/sprucebot-skills-kit-server/-/sprucebot-skills-kit-server-3.0.1.tgz","fileCount":26,"unpackedSize":46451,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDsiLBvRRl6Vt5hmHCytzYcRRC8r83KHjAibuOZ86V8kAIgC+qPR4Qnou2LPaJWoVyt4+fr9cgedEpIq4V5plpHwtM="}]},"maintainers":[{"email":"hello@barbershop.io","name":"barbershop"},{"email":"brandonminch@gmail.com","name":"bminch"},{"email":"caseymetz@gmail.com","name":"casey1449"},{"email":"hartigan.hm@gmail.com","name":"hartiganhm"},{"email":"hello@kengoldfarb.com","name":"kengoldfarb"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sprucebot-skills-kit-server_3.0.1_1523030670281_0.18220780693666927"},"_hasShrinkwrap":false},"3.0.2":{"name":"@barbershopio/sprucebot-skills-kit-server","publishConfig":{"access":"public"},"description":"The engine that drives your Sprucebot Skill! ⚙️🌲🤖","version":"3.0.2","homepage":"https://github.com/sprucelabsai/sprucebot-skills-kit","bugs":{"url":"https://github.com/sprucelabsai/sprucebot-skills-kit/issues"},"keywords":["node","sprucebot","sprucelabs"],"scripts":{"lint":"eslint '**/**/*{.js}'","test":"jest","precommit":"npm run lint","prepush":"npm run lint && npm run test"},"dependencies":{"@koa/cors":"2","config":"^1.28.0","cookies":"^0.7.1","debug":"^3.1.0","glob":"^7.1.2","jsonwebtoken":"^8.1.0","koa":"^2.4.1","koa-bodyparser":"^4.2.0","koa-router":"^7.2.1","koa-static":"^4.0.1","next":"^5.0.0","node-cron":"^1.2.1","pg":"^7.4.1","react":"^16.2.0","react-dom":"^16.0.0","sequelize":"^4.33.4","umzug":"^2.1.0"},"devDependencies":{"add":"^2.0.6","aws-sdk":"^2.153.0","eslint":"^4.10.0","eslint-config-prettier":"^2.6.0","eslint-plugin-prettier":"^2.3.1","jest":"^22.0.5","prettier":"^1.7.4","yarn":"^1.2.1"},"jest":{"clearMocks":true,"testPathIgnorePatterns":["/config/"],"collectCoverageFrom":["**/*.js","!**/node_modules/**","!**/__TEST__/**"],"coverageDirectory":"./coverage/","collectCoverage":true},"_id":"@barbershopio/sprucebot-skills-kit-server@3.0.2","_npmVersion":"5.6.0","_nodeVersion":"8.10.0","_npmUser":{"name":"kengoldfarb","email":"hello@kengoldfarb.com"},"dist":{"integrity":"sha512-PxRLRVJm7jQgNdF42uqGbd2PWBygZ5LcFdxGNT10lXVDC5s6fLI8waAQgyHUOzSv9jWYkSNqtoy8CJP7/orr0w==","shasum":"332147121fb05bcf1e7f5e41c03f51e0c75956b9","tarball":"https://registry.npmjs.org/@barbershopio/sprucebot-skills-kit-server/-/sprucebot-skills-kit-server-3.0.2.tgz","fileCount":26,"unpackedSize":46519,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDQmMsXFumQ5YEfYYP0anJ5mO5LET0t6/572CuDU1Ps9gIhAOZkCHQUE45JFcvdSOytpmBmfBRVCmeKL+TKZf4X7TVY"}]},"maintainers":[{"email":"hello@barbershop.io","name":"barbershop"},{"email":"brandonminch@gmail.com","name":"bminch"},{"email":"caseymetz@gmail.com","name":"casey1449"},{"email":"hartigan.hm@gmail.com","name":"hartiganhm"},{"email":"hello@kengoldfarb.com","name":"kengoldfarb"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sprucebot-skills-kit-server_3.0.2_1523219926860_0.17839944447090672"},"_hasShrinkwrap":false},"6.4.2":{"name":"@barbershopio/sprucebot-skills-kit-server","publishConfig":{"access":"public"},"description":"The engine that drives your Sprucebot Skill! ⚙️🌲🤖","version":"6.4.2","homepage":"https://github.com/sprucelabsai/sprucebot-skills-kit","bugs":{"url":"https://github.com/sprucelabsai/sprucebot-skills-kit/issues"},"keywords":["node","sprucebot","sprucelabs"],"scripts":{"lint":"eslint '**/**/*{.js}'","test":"jest","precommit":"npm run lint","prepush":"npm run lint && npm run test"},"dependencies":{"@koa/cors":"2","config":"^1.28.0","cookies":"^0.7.1","debug":"^3.1.0","glob":"^7.1.2","jsonwebtoken":"^8.1.0","koa":"^2.4.1","koa-bodyparser":"^4.2.0","koa-router":"^7.2.1","koa-static":"^4.0.1","next":"^5.1.0","node-cron":"^1.2.1","pg":"^7.4.1","react":"^16.2.0","react-dom":"^16.0.0","sequelize":"^4.33.4","umzug":"^2.1.0"},"devDependencies":{"add":"^2.0.6","aws-sdk":"^2.153.0","eslint":"^4.10.0","eslint-config-prettier":"^2.6.0","eslint-plugin-prettier":"^2.3.1","jest":"^22.0.5","prettier":"^1.7.4","yarn":"^1.2.1"},"jest":{"clearMocks":true,"testPathIgnorePatterns":["/config/"],"collectCoverageFrom":["**/*.js","!**/node_modules/**","!**/__TEST__/**"],"coverageDirectory":"./coverage/","collectCoverage":true},"_id":"@barbershopio/sprucebot-skills-kit-server@6.4.2","_npmVersion":"5.6.0","_nodeVersion":"8.10.0","_npmUser":{"name":"kengoldfarb","email":"hello@kengoldfarb.com"},"dist":{"integrity":"sha512-lnvWHyw1PO4+ofCKwe9PPaT7NrU8N7T40hfBQAN8rya47/9IZz881eVdJCVGtC9Lea/zJZyMyNJLYrzIlTFa+w==","shasum":"cbc06f0123c7af881218dadf7d98b14de7e228f4","tarball":"https://registry.npmjs.org/@barbershopio/sprucebot-skills-kit-server/-/sprucebot-skills-kit-server-6.4.2.tgz","fileCount":26,"unpackedSize":46558,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa2VpVCRA9TVsSAnZWagAA8XsP/0JtC8ZYRzP7uPIEVtcn\n+i1EEoY1GZPmWQ1eer4XqW0OVrchMi5U61qYnNSpBSMd/tbkGPvHVj8jYbn2\npk8bz7r7EfNy5dek2UE/MUVb+ytmhKdCligF5uSWeRCpbW42hyYLB++Pu6R0\nHS9zSf+aHLCMU0TOorHSkT7wmXUq0eaLmHcwaJRoZI83O++vb/42I9VFG1eD\nSZjCkQyF9h956sgAzDZH1VoxkiOQxEm3dOdm1GNZ/NicFbIhA5VwHEr2SRct\nsETGy2l/+zYdZ6oGvDGhESogeibVRsPi+8bich1d+W/hdCjf7j92Na1RwBDS\nExvfndUyCbBEw2PFy8Bu3SK95hlA/KM5AGXEdcfxmGygl4HUlW/+dvbYGwpv\ne6hbuUC2x8KxoovFbLH33ztj4l/iOMzXHmRPGWH2YDwX276S7nc87WRoH+YU\nWevq83M/tM3FJJ8jx6lZX8E8GrESxUwxNzeVa1L2/PXJ+0UR+QunJcrGDT9v\nKA+J3AWScomAcW8Iz9ioUb1NDAM6995esyialw2RRLf8KWlQhWFs5QXsp7Mc\noaeSdugTMov70PMRiLSZfvfediopa6zGD4+CKGmDxsvmodYwEKRGcSawXcwS\ndUgPHsT3gMWXnSbGCWoUkHFn2MjDD8vnzQxvXT4TRZ3VaBCfRvb3+XjhxOY6\nGeb+\r\n=Ghiy\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGAjMKTSClywaT9Vz57MJoj7RtfXDklCzSiDOnMhF3QiAiEArqulwRMi4v2T9SMOE0d+I4DkPCchZTMxxgM8+eR0JjM="}]},"maintainers":[{"email":"hello@barbershop.io","name":"barbershop"},{"email":"brandonminch@gmail.com","name":"bminch"},{"email":"caseymetz@gmail.com","name":"casey1449"},{"email":"hartigan.hm@gmail.com","name":"hartiganhm"},{"email":"hello@kengoldfarb.com","name":"kengoldfarb"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sprucebot-skills-kit-server_6.4.2_1524193875772_0.24351562760123824"},"_hasShrinkwrap":false},"6.4.3":{"name":"@barbershopio/sprucebot-skills-kit-server","publishConfig":{"access":"public"},"description":"The engine that drives your Sprucebot Skill! ⚙️🌲🤖","version":"6.4.3","homepage":"https://github.com/sprucelabsai/sprucebot-skills-kit","bugs":{"url":"https://github.com/sprucelabsai/sprucebot-skills-kit/issues"},"keywords":["node","sprucebot","sprucelabs"],"scripts":{"lint":"eslint '**/**/*{.js}'","test":"jest","precommit":"npm run lint","prepush":"npm run lint && npm run test"},"dependencies":{"@koa/cors":"2","config":"^1.28.0","cookies":"^0.7.1","debug":"^3.1.0","glob":"^7.1.2","jsonwebtoken":"^8.1.0","koa":"^2.4.1","koa-bodyparser":"^4.2.0","koa-router":"^7.2.1","koa-static":"^4.0.1","next":"^5.1.0","node-cron":"^1.2.1","pg":"^7.4.1","react":"^16.2.0","react-dom":"^16.0.0","sequelize":"^4.33.4","umzug":"^2.1.0"},"devDependencies":{"add":"^2.0.6","aws-sdk":"^2.153.0","eslint":"^4.10.0","eslint-config-prettier":"^2.6.0","eslint-plugin-prettier":"^2.3.1","jest":"^22.0.5","prettier":"^1.7.4","yarn":"^1.2.1"},"jest":{"clearMocks":true,"testPathIgnorePatterns":["/config/"],"collectCoverageFrom":["**/*.js","!**/node_modules/**","!**/__TEST__/**"],"coverageDirectory":"./coverage/","collectCoverage":true},"_id":"@barbershopio/sprucebot-skills-kit-server@6.4.3","_npmVersion":"5.6.0","_nodeVersion":"8.10.0","_npmUser":{"name":"kengoldfarb","email":"hello@kengoldfarb.com"},"dist":{"integrity":"sha512-J6AYkHRJR+1BDcGG6ObntOTnWC30hwteyuWajHnrV7trAtsBGJtIeGEiVq+Q1Qy9bKkPDkm0mqmRzBMy0KxLbA==","shasum":"e8b36965ae8afd33bffaa870ebfed3077c202fd0","tarball":"https://registry.npmjs.org/@barbershopio/sprucebot-skills-kit-server/-/sprucebot-skills-kit-server-6.4.3.tgz","fileCount":26,"unpackedSize":46252,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa27cLCRA9TVsSAnZWagAAOBwP/0ihjwtdb489GJJ4+T7x\nsUN3ohtrESCFEVWI043yGTAEHQo+Leu/fQ+XLNGLapR6HEZNRpmjo3GOicK7\nJd83tgwngK97Uq1Dej5rSKb3VmnnMukj2j7CsXK50gjSsoP1b3FaLBPZ2MXQ\nyrnKtpTf63EHY26jXvURyKdNiyWVEEXZegl64FuASSn+4jBH+2e5E17RFhiL\ncyFqLW4zU/89hlXX9aIvwVaG1IpBG/OOJf90o4cEAQedgboKYDN/GHEYWNro\npv+O/zp1bMZoo6hcbIxCspP5bd1SmVI9fh2edUjPCuRVs8b4MmrHJgFO7QUV\nhpBQBODHzCdTEnXhJ8EFzLxbdfnHce6Pp+YZxBHAC73cgnoSuQB1CNANrYAr\n1HNOomtPEgi/cZ+fV302mzj5MOvByn+E6i2OruQo7eBIw9QLMPgH+RxeQXFJ\nyXi6EdW3T3V6axl29PoEwyJLb9hTiDAoSLcBuPyorE5Zj+l6/xa38cO/wWmW\n7LZW8Nc4Nb/qBfEshyLQsJ0RRIIRa8KXOtpEIBVPCSd/hp/C8qiPzKmsHgKu\nyJl73cR2IQMuMU87nZTMF0ebOpgIb0hWMeZJ9Wftuu6sYLTJ5nQ4D7llHu07\n1cezhuOf5eVp58taSgbspx0YDZ2I1EqQM7O7TQOqn6PrIcYcFNwdcu/eq1NJ\nLwtN\r\n=hefU\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEdq3NEyoQt/ESHss/GglftyUjNjLWNbsMa5gv83DctzAiBN0sd7PfC45vM+vwpSfD6NgtRuOKxtnjJF/bFp7FFKIg=="}]},"maintainers":[{"email":"hello@barbershop.io","name":"barbershop"},{"email":"brandonminch@gmail.com","name":"bminch"},{"email":"caseymetz@gmail.com","name":"casey1449"},{"email":"hartigan.hm@gmail.com","name":"hartiganhm"},{"email":"hello@kengoldfarb.com","name":"kengoldfarb"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sprucebot-skills-kit-server_6.4.3_1524348680954_0.15614601573333498"},"_hasShrinkwrap":false},"6.6.0":{"name":"@barbershopio/sprucebot-skills-kit-server","description":"The engine that drives your Sprucebot Skill! ⚙️🌲🤖","version":"6.6.0","homepage":"https://github.com/sprucelabsai/sprucebot-skills-kit","bugs":{"url":"https://github.com/sprucelabsai/sprucebot-skills-kit/issues"},"keywords":["node","sprucebot","sprucelabs"],"scripts":{"lint":"eslint '**/**/*{.js}'","test":"jest","precommit":"npm run lint","prepush":"npm run lint && npm run test"},"dependencies":{"@koa/cors":"2","config":"^1.28.0","cookies":"^0.7.1","debug":"^3.1.0","glob":"^7.1.2","jsonwebtoken":"^8.1.0","koa":"^2.4.1","koa-bodyparser":"^4.2.0","koa-router":"^7.2.1","koa-static":"^4.0.1","next":"^5.1.0","node-cron":"^1.2.1","pg":"^7.4.1","react":"^16.2.0","react-dom":"^16.0.0","sequelize":"^4.33.4","umzug":"^2.1.0"},"devDependencies":{"add":"^2.0.6","aws-sdk":"^2.153.0","eslint":"^4.10.0","eslint-config-prettier":"^2.9.0","eslint-plugin-prettier":"^2.3.1","jest":"^22.0.5","prettier":"^1.13","yarn":"^1.2.1"},"jest":{"clearMocks":true,"testPathIgnorePatterns":["/config/"],"collectCoverageFrom":["**/*.js","!**/node_modules/**","!**/__TEST__/**"],"coverageDirectory":"./coverage/","collectCoverage":true},"_id":"@barbershopio/sprucebot-skills-kit-server@6.6.0","_npmVersion":"5.6.0","_nodeVersion":"8.11.2","_npmUser":{"name":"kengoldfarb","email":"hello@kengoldfarb.com"},"dist":{"integrity":"sha512-40qeTTGMBsKhTeSzN1+GeGtUXUwcG07clkbA9Ls+n6I1MXx5LeOiXBd1fd06ykzeyj6Vvxw+Cuu9ls8/YqnnAg==","shasum":"95e782b00e2dabf563fefb3a41d4af6449c4cce6","tarball":"https://registry.npmjs.org/@barbershopio/sprucebot-skills-kit-server/-/sprucebot-skills-kit-server-6.6.0.tgz","fileCount":28,"unpackedSize":48075,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbDy+WCRA9TVsSAnZWagAAX0QP/0a0iLsus2HlpVpzUAIE\nsIEX1gbkVZ7YZowYc96ph7HGfIYbqAol1uoAfrFf6lCziodyXCFWbaBUaPcS\niFfP5JG4g3xjLRsiO4Yj98lVoanVjQk5O0otsx+uXUAgqt5PDCiHW4EeYcbk\nVzQ+bpTi/M7cu5SJtduJPhVDUkwVDFPABaF1Zruvr/6hLGtFmjqeLjhoAH/+\nvtOYnGjQomo08aIiRhGC66fMG7ydk9iq8mfYlf7Notr41Y/+tR9gc9Rs0MQv\nf+zFtIiWMRhRzp7Ln9PBEhzXophPaGG14zBoZXNcHrn6IXnZKexQ4TGmsmaw\nSJBcB2NbBruYxXeGJm7tuiK8oacflTEUgpI9zNn9BU2TZhtbw8fQh7xJAO8c\nubrPfOuE0eCikzA4U301I3osETXqBCvWQhhW9q5IbV+ahy1MbzqWwL6g1Rg/\nE0PlUVekOGBpBOKSEzdD5KxnE9FBHNEoP22Vf+IkqSIwDdtGWduV1PCPFYC/\nLAb/0H68Lid2tE8ADeiW1miLHfg09U7841juLwJy4+WruzDR6S/al+lxDPUs\nuBs+Ayp2LwNZtk8f90nyYDu0xxPLc2GCOPRA3b8vjG0SZM+rHUtryPlfWVBQ\netliVsfcghZGFCdAAWiyj8xu/6O7f5YLaLU133m2OoBz7Xd4mJ9zzJjgOAsE\n7AOi\r\n=Xe2n\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD3qNVnU/Ov4UyPWKBOGlBMXRUY1rYz0VwbFMY1L2iJHAIhAOSAAEe2Yt5lNwZhuksn2vvWYPB5si3gc7el7xFTnvix"}]},"maintainers":[{"email":"hello@barbershop.io","name":"barbershop"},{"email":"brandonminch@gmail.com","name":"bminch"},{"email":"caseymetz@gmail.com","name":"casey1449"},{"email":"hartigan.hm@gmail.com","name":"hartiganhm"},{"email":"hello@kengoldfarb.com","name":"kengoldfarb"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sprucebot-skills-kit-server_6.6.0_1527721876547_0.7693756881616449"},"_hasShrinkwrap":false},"6.6.1":{"name":"@barbershopio/sprucebot-skills-kit-server","description":"The engine that drives your Sprucebot Skill! ⚙️🌲🤖","version":"6.6.1","homepage":"https://github.com/sprucelabsai/sprucebot-skills-kit","bugs":{"url":"https://github.com/sprucelabsai/sprucebot-skills-kit/issues"},"keywords":["node","sprucebot","sprucelabs"],"scripts":{"lint":"eslint '**/**/*{.js}'","test":"jest","precommit":"npm run lint","prepush":"npm run lint && npm run test"},"dependencies":{"@koa/cors":"2","config":"^1.28.0","cookies":"^0.7.1","debug":"^3.1.0","glob":"^7.1.2","jsonwebtoken":"^8.1.0","koa":"^2.4.1","koa-bodyparser":"^4.2.0","koa-router":"^7.2.1","koa-static":"^4.0.1","next":"^5.1.0","node-cron":"^1.2.1","pg":"^7.4.1","react":"^16.2.0","react-dom":"^16.0.0","sequelize":"^4.33.4","umzug":"^2.1.0"},"devDependencies":{"add":"^2.0.6","aws-sdk":"^2.153.0","eslint":"^4.10.0","eslint-config-prettier":"^2.9.0","eslint-plugin-prettier":"^2.3.1","jest":"^22.0.5","prettier":"^1.13","yarn":"^1.2.1"},"jest":{"clearMocks":true,"testPathIgnorePatterns":["/config/"],"collectCoverageFrom":["**/*.js","!**/node_modules/**","!**/__TEST__/**"],"coverageDirectory":"./coverage/","collectCoverage":true},"_id":"@barbershopio/sprucebot-skills-kit-server@6.6.1","_npmVersion":"5.6.0","_nodeVersion":"8.11.2","_npmUser":{"name":"kengoldfarb","email":"hello@kengoldfarb.com"},"dist":{"integrity":"sha512-mQlp2yV4cATUX0DfBpD3MNpJ1h1owm3SZVM3/jHcpqjmYa/prMzbgjReGzuMg+q3vDGMRDGG4k9/bl5Chsju5A==","shasum":"8c29fb252c394a681c268a7efcdc96d5e646a090","tarball":"https://registry.npmjs.org/@barbershopio/sprucebot-skills-kit-server/-/sprucebot-skills-kit-server-6.6.1.tgz","fileCount":28,"unpackedSize":48075,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbEDCuCRA9TVsSAnZWagAA9SYP/25HNfUERKKhUrwJTdgz\nIwPtnFOqyOIyr4+Gg7vgsyGJiTkWze0HRNe82+eu2kYruScY20sIirC8J8Dt\nX/xu1/+U9ZYOu3VrnxtDhorRCBw+xy371fMtcSA2q9+KRjQjHps2viUyhGjX\nCnvYAY40WVqO7YkUWj4zLw9zVUjZ0xeU3kD429AN8aFZc2tP3VXtNbpRJ/oN\nGugEV0Nni7CAv72EC1mGyRwZgnRkYGG52i0/phJpFM+IMaF/Bw4aCaFTte6J\nlC56n+AfwY3xmSH0uRfHyEBHh5PdUOIzC5uWDSHIOIMGkPNK/ABSIHID+yC4\nQ3U26my5alLl3csyajP6tLRSBz0uucF9lvuMV8tAL7PEp77gbtUbBQfnWTl5\n06S6NOFp2SfjEMz+9jFqshuE0EHRDElv2QUx96jOzwGqxaCpXbyr315Y1Glb\n8YOcNF3j24dW+NT+LVn6ZMPyeBLnlzvMQSUt4iIHEGWk1ETLE/l+7BRxFWQE\n5m3w8sNkV6lpKDMt8Qmh/l8oNu3ob9PC+QnOkQNFXj/7gEAlTitELJfo1V1P\nY11ELy9A/XVw/VjHpaoywqs4tJcWBp7M0BHf68DyTTZtm/9tv3RAp9pJN6NU\nJW/ftuWySB/30LLyAYZewbnTi9AlJPwEvHKGMuCVnSs0oVLzc8W51ferpc3v\n/aGL\r\n=PCVW\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCcJTbSMXG+Pw3JZ/eOXqlKr+5NNIbJilwbARRQkd4x4gIhAL7wZmer3V5oL/h/F+2L+vZVHySMKNnrb8WcI6finbxZ"}]},"maintainers":[{"email":"hello@barbershop.io","name":"barbershop"},{"email":"brandonminch@gmail.com","name":"bminch"},{"email":"caseymetz@gmail.com","name":"casey1449"},{"email":"hartigan.hm@gmail.com","name":"hartiganhm"},{"email":"hello@kengoldfarb.com","name":"kengoldfarb"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sprucebot-skills-kit-server_6.6.1_1527787692928_0.1907902656147662"},"_hasShrinkwrap":false},"6.6.2":{"name":"@barbershopio/sprucebot-skills-kit-server","description":"The engine that drives your Sprucebot Skill! ⚙️🌲🤖","version":"6.6.2","homepage":"https://github.com/sprucelabsai/sprucebot-skills-kit","bugs":{"url":"https://github.com/sprucelabsai/sprucebot-skills-kit/issues"},"keywords":["node","sprucebot","sprucelabs"],"scripts":{"lint":"eslint '**/**/*{.js}'","test":"jest","precommit":"npm run lint","prepush":"npm run lint && npm run test"},"dependencies":{"@koa/cors":"2","config":"^1.28.0","cookies":"^0.7.1","debug":"^3.1.0","glob":"^7.1.2","jsonwebtoken":"^8.1.0","koa":"^2.4.1","koa-bodyparser":"^4.2.0","koa-router":"^7.2.1","koa-static":"^4.0.1","next":"^5.1.0","node-cron":"^1.2.1","pg":"^7.4.1","react":"^16.2.0","react-dom":"^16.0.0","sequelize":"^4.33.4","umzug":"^2.1.0"},"devDependencies":{"add":"^2.0.6","aws-sdk":"^2.153.0","eslint":"^4.10.0","eslint-config-prettier":"^2.9.0","eslint-plugin-prettier":"^2.3.1","jest":"^22.0.5","prettier":"^1.13","yarn":"^1.2.1"},"jest":{"clearMocks":true,"testPathIgnorePatterns":["/config/"],"collectCoverageFrom":["**/*.js","!**/node_modules/**","!**/__TEST__/**"],"coverageDirectory":"./coverage/","collectCoverage":true},"_id":"@barbershopio/sprucebot-skills-kit-server@6.6.2","_npmVersion":"5.6.0","_nodeVersion":"8.11.3","_npmUser":{"name":"kengoldfarb","email":"hello@kengoldfarb.com"},"dist":{"integrity":"sha512-tPlj3xj2wFoC9vls6gIbNMwdr7zj2Tz5uOndPZjF7D7Cau21Ox4ROcK2bncAZI8FmuvJ/GG329FtNHMr5t/1Iw==","shasum":"a1b37f91cf3cefc132c759be87e64a2e334c92c7","tarball":"https://registry.npmjs.org/@barbershopio/sprucebot-skills-kit-server/-/sprucebot-skills-kit-server-6.6.2.tgz","fileCount":28,"unpackedSize":48692,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbMX5MCRA9TVsSAnZWagAAlH0P/AgKsVIPIzZx6V0PTDJH\n1OhrkAUygyMUn/toQpVSXdlBCZbL1S9fY5gejGeAcJx7Zur7BuLYtVO/Fn7V\n2QC9l5GKZAAGRm23W4MOW8EX45RZbK8J4mF2YXWajLfXANVm5Y/N8WdM+jlh\nX5HxJ0f88N153nzB+Y3ekQmIH5Qi79JYZekaxV3JdKXqhjdXWQ566QFhC61C\neovU0IqMM+CQoCPvj+RAwNS3xu5Q8HGmUJHSOZWKJwkRfZtUZb28F64urcCB\nqzDEIRSu6Qv4xvaTfME4u4+P5QYf20diurf8d8iH7eHDJKxESnOUqB4eG0mJ\nODwBedmpTQL2kXf5YoPnn9/OzN8qn4RCUDSXEa1rT/P4VaU/zmtFnpOSoNGJ\nQZeXrpRcwaQzKpsvX3BX+/0pVJrV5ChOMDgyMJ/qac6n6Qi59vIef/gDfrbj\nMI8BqXznKP0WlutF/OyL2VwNFHRDM5/X9hEXl6cBWEdYaHdvNMhhv6mwe9II\nXZLe8N2PW9B6afWhlw8+lC5D31O1wImfkagCqFpoytegkbnHsqAAN5LDNK8x\nP5R2p/es73tl74jXvLEfnLql8PRooVh+rOyCR7wwtxdZjtOy8vPaWX2bNv6/\n1ah5upTEG7uh/849/4UUDW7naI9hADih28u9i6ZF9pIE3zlpv4qj6l8bEEwf\nKEVB\r\n=IbRS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBoMrPr6JG+P070PcZRvFOB6Ut0MiyHqGrNoszAsrFAAAiATIm4aJSxVjwfk49wDVc3ap9TOfRH8CIWhmDWvYKi0Lg=="}]},"maintainers":[{"email":"hello@barbershop.io","name":"barbershop"},{"email":"brandonminch@gmail.com","name":"bminch"},{"email":"caseymetz@gmail.com","name":"casey1449"},{"email":"hartigan.hm@gmail.com","name":"hartiganhm"},{"email":"hello@kengoldfarb.com","name":"kengoldfarb"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sprucebot-skills-kit-server_6.6.2_1529970252602_0.402676770006567"},"_hasShrinkwrap":false},"6.6.3":{"name":"@barbershopio/sprucebot-skills-kit-server","description":"The engine that drives your Sprucebot Skill! ⚙️🌲🤖","version":"6.6.3","homepage":"https://github.com/sprucelabsai/sprucebot-skills-kit","bugs":{"url":"https://github.com/sprucelabsai/sprucebot-skills-kit/issues"},"keywords":["node","sprucebot","sprucelabs"],"scripts":{"lint":"eslint '**/**/*{.js}'","test":"jest","precommit":"npm run lint","prepush":"npm run lint && npm run test"},"dependencies":{"@koa/cors":"2","config":"^1.28.0","cookies":"^0.7.1","debug":"^3.1.0","glob":"^7.1.2","jsonwebtoken":"^8.1.0","koa":"^2.4.1","koa-bodyparser":"^4.2.0","koa-router":"^7.2.1","koa-static":"^4.0.1","next":"^5.1.0","node-cron":"^1.2.1","pg":"^7.4.1","react":"^16.2.0","react-dom":"^16.0.0","sequelize":"^4.33.4","umzug":"^2.1.0"},"devDependencies":{"add":"^2.0.6","aws-sdk":"^2.153.0","eslint":"^4.10.0","eslint-config-prettier":"^2.9.0","eslint-plugin-prettier":"^2.3.1","jest":"^22.0.5","prettier":"^1.13","yarn":"^1.2.1"},"jest":{"clearMocks":true,"testPathIgnorePatterns":["/config/"],"collectCoverageFrom":["**/*.js","!**/node_modules/**","!**/__TEST__/**"],"coverageDirectory":"./coverage/","collectCoverage":true},"_id":"@barbershopio/sprucebot-skills-kit-server@6.6.3","_npmVersion":"5.6.0","_nodeVersion":"8.11.3","_npmUser":{"name":"kengoldfarb","email":"hello@kengoldfarb.com"},"dist":{"integrity":"sha512-x5tmK8SQJlTDPiM+vAGE/Aa+oKb0RdYRuwmGxbXYxn8NfOfKXHGahLXYZSL4qmPlrYJAPblTNPuS/9G18FqISQ==","shasum":"2793545ddf45b13ceda38dc8cac1eddb3bece388","tarball":"https://registry.npmjs.org/@barbershopio/sprucebot-skills-kit-server/-/sprucebot-skills-kit-server-6.6.3.tgz","fileCount":28,"unpackedSize":48700,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbMYF8CRA9TVsSAnZWagAAIJAP/3F5mS0rPkRaYy4rq7lK\nA/hMbr+U+7Ro3tp9OebdK+xp80co8T6CPDA0ZCfp4uzx04vrXuSfgj2R3+MF\n+obKkdZ+vCnisuKDeQzmIQ7EaaAfbCZM+f+PJuSILbuSRGRV7q+EYkI5slOm\nTtedVPJpJ7NKNwUFqKcdlI6SL4q+zgAZhe4Di5imn7NseS6JwealZZB5s18L\no4lMak5bcVcA87rN3O2Kr9P3GylYZlZTADwLHDVQhYxzQRJlVGL2FvvBzt0b\np/GaXaNxGWPmJnCHr3MhC6ARtVSk9GGg8TP9CA+tU1ga234/Ml873p9MIYsl\n2shminh0cvXlX46FC3Hg8ibB037urhGIYdS2TXHGdpwWtaYCP8xRXKdrOUKK\nB8xAa86F79qwOEiGe0AeV/SzDC9+pTHH2byHdbnWqElFWfeGR+0NZDylbZf5\n0zvqV0vPApD3w26D6gICkqLhy+EUMsBaQ/w1lhmz+d46YmPwZSZSGIFH3TQk\nWxvKrwJDTz6qF3V2sS7hR0d1wRQhlpnZlTuJ3ckeiVoFDMcQv3Jw0ugzeSlc\n//IdTr4asSAof1xUq5czYNU1RWVokm2lqz5gc8y5iU+gr0yn7HAnNYlhp/7k\nKpQux0B+YJvaF6ouU0EWeXHF3JQIeP33j8/TP+fZSB3QPO0M02oynra+lxFr\nQpUm\r\n=vZsW\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHAp10LmPLB8mcxGG5Qz8q5KIe9vSTRxJHjAYcaDjs8wAiA7Kv9gc8PeKWCwEZs3+81MoJnEKjm3fpqYd43UaAtQGQ=="}]},"maintainers":[{"email":"hello@barbershop.io","name":"barbershop"},{"email":"brandonminch@gmail.com","name":"bminch"},{"email":"caseymetz@gmail.com","name":"casey1449"},{"email":"hartigan.hm@gmail.com","name":"hartiganhm"},{"email":"hello@kengoldfarb.com","name":"kengoldfarb"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sprucebot-skills-kit-server_6.6.3_1529971068635_0.09774671546831981"},"_hasShrinkwrap":false},"6.7.0":{"name":"@barbershopio/sprucebot-skills-kit-server","description":"The engine that drives your Sprucebot Skill! ⚙️🌲🤖","version":"6.7.0","homepage":"https://github.com/sprucelabsai/sprucebot-skills-kit","bugs":{"url":"https://github.com/sprucelabsai/sprucebot-skills-kit/issues"},"keywords":["node","sprucebot","sprucelabs"],"scripts":{"lint":"eslint '**/**/*{.js}'","test":"jest","precommit":"npm run lint","prepush":"npm run lint && npm run test"},"dependencies":{"@koa/cors":"2","config":"^1.28.0","cookies":"^0.7.1","debug":"^3.1.0","glob":"^7.1.2","jsonwebtoken":"^8.1.0","koa":"^2.4.1","koa-bodyparser":"^4.2.0","koa-router":"^7.2.1","koa-static":"^4.0.1","next":"^5.1.0","node-cron":"^1.2.1","pg":"^7.4.1","react":"^16.2.0","react-dom":"^16.0.0","sequelize":"^4.33.4","umzug":"^2.1.0"},"devDependencies":{"add":"^2.0.6","aws-sdk":"^2.153.0","eslint":"^4.10.0","eslint-config-prettier":"^2.9.0","eslint-plugin-prettier":"^2.3.1","jest":"^22.0.5","prettier":"^1.13","yarn":"^1.2.1"},"jest":{"clearMocks":true,"testPathIgnorePatterns":["/config/"],"collectCoverageFrom":["**/*.js","!**/node_modules/**","!**/__TEST__/**"],"coverageDirectory":"./coverage/","collectCoverage":true},"_id":"@barbershopio/sprucebot-skills-kit-server@6.7.0","_npmVersion":"5.6.0","_nodeVersion":"8.11.3","_npmUser":{"name":"kengoldfarb","email":"hello@kengoldfarb.com"},"dist":{"integrity":"sha512-GjbSfDOu006QxwpFw79WCNBcPp8OCedFosb3Bh5tYUfb6Zp+wtJc3kcw3L+9OvPpGnIBxitRkZ0j/xtQ3+xdEA==","shasum":"f07bd0c846c9742c2818e32b11010a00726d10c6","tarball":"https://registry.npmjs.org/@barbershopio/sprucebot-skills-kit-server/-/sprucebot-skills-kit-server-6.7.0.tgz","fileCount":28,"unpackedSize":49082,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbPpuBCRA9TVsSAnZWagAAjbQP/AnUEOt1KLb5PJ1Hy5GX\no3nsLEEjE4QAz3FDc9SvbFqRTnLwmv6wF3RegRuxJUYYNHwcSz01hAB/NrPf\n295VYA0y63UkJC8Epx2Y92TlTE1YSYzEz2/Gwj0MQfPerdRJCQ82FlFiXI7Q\nvj27KvtzTcpUAN3HaXgpG1ZFc63A4hAteWhcREVqLK/an32zKNw0TU+Une8R\nIebIVMmNEMyIaCCCyFdhAaD+PbjKBW/Iq/e0YjehyfYnDFqsuKHOSSHy5Zze\nKBlE020Dvxhpd3n4nKMLlOkxRJp/h3MwqoOZx0d2Qfj+FMHLzs/BvpX4sdGP\nVPD01DyaeF+gpWLzxLhLAmsr6GJHW/kaOYeoMeJ9HV+zwk+ru21vYUzhZoSr\nb2ke16wusizX3/goThiNxBNqog5NBi4afawHgTcZvoQGs0zHe6qA+uLNxr15\no60UVIJ/koLQtFsAVdNTx5KpGs6Mw/0x0qzcQeThLTgcXoFwEgjFB+uDGKVY\nVwlNfENCHfyTSaB8GrY7Z9oXFIL2HevEDpuOsaPqVlzDyhWq9nsTwLvlRtbj\nD7jSF+y/EJY9ctkT8oSNnNRBaotsjxqeK+AFXIG9wtgYLAk0WZPEu4PvSFGK\nIgf0UcKVqtO+wjzmrTNpLBEuo38/i3bkl/QXzbxIg6PPeAYIz97ya7LR/Yse\nBRlI\r\n=7CT7\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFPSOiArMmG0CEPgWQtGkkhXTT2YByVf/MtDgzFbV2YoAiEA6y/Ai5r1eNSOcyDgSmLirxaNWA18RJmYCDgdCx7Rfjo="}]},"maintainers":[{"email":"hello@barbershop.io","name":"barbershop"},{"email":"brandonminch@gmail.com","name":"bminch"},{"email":"caseymetz@gmail.com","name":"casey1449"},{"email":"hartigan.hm@gmail.com","name":"hartiganhm"},{"email":"hello@kengoldfarb.com","name":"kengoldfarb"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sprucebot-skills-kit-server_6.7.0_1530829697118_0.6395245625014254"},"_hasShrinkwrap":false}},"time":{"created":"2018-03-30T18:32:59.757Z","7.0.0":"2018-03-30T18:32:59.867Z","modified":"2022-04-04T17:43:10.673Z","3.0.0":"2018-03-30T18:34:25.100Z","3.0.1":"2018-04-06T16:04:30.439Z","3.0.2":"2018-04-08T20:38:46.979Z","6.4.2":"2018-04-20T03:11:15.937Z","6.4.3":"2018-04-21T22:11:21.031Z","6.6.0":"2018-05-30T23:11:16.657Z","6.6.1":"2018-05-31T17:28:13.018Z","6.6.2":"2018-06-25T23:44:12.679Z","6.6.3":"2018-06-25T23:57:48.721Z","6.7.0":"2018-07-05T22:28:17.198Z"},"maintainers":[{"email":"brandonminch@gmail.com","name":"bminch"},{"email":"hello@barbershop.io","name":"barbershop"},{"email":"hello@kengoldfarb.com","name":"kengoldfarb"}],"description":"The engine that drives your Sprucebot Skill! ⚙️🌲🤖","homepage":"https://github.com/sprucelabsai/sprucebot-skills-kit","keywords":["node","sprucebot","sprucelabs"],"bugs":{"url":"https://github.com/sprucelabsai/sprucebot-skills-kit/issues"},"readme":"# `sprucebot-skills-kit-server`\n\n[![Greenkeeper badge](https://badges.greenkeeper.io/sprucelabsai/sprucebot-skills-kit-server.svg)](https://greenkeeper.io/)\nThis module relies heavily on [koajs](http://koajs.com) and [koa-router](https://github.com/alexmingoia/koa-router). It can help to be familiar with those modules, but it's probably not 100% necessary.\n\n## Where to start?\nIf you haven't already, you should totally checkout the [sprucebot-skills-kit's documentation](https://github.com/sprucelabsai/sprucebot-skills-kit/blob/dev/docs). In fact, this readme is assuming you already read it.  \n\n## File structure\nIt is probably a good idea to go through each file to understand how they work. It'll help a lot when building your skill.\n\n * `.vscode` - Settings for your favorite IDE.\n * `controllers` - For built in [`controllers`](https://github.com/sprucelabsai/sprucebot-skills-kit/blob/dev/docs/server.md#controllers) that are made available in every skill.\n    * `auth.js` - An [authentication](https://github.com/sprucelabsai/sprucebot-skills-kit/blob/dev/docs/server.md#auth) endpoint. Also, a condition role set for when `DEV_MODE` is enabled in your skill.\n* `factories` - Factories for helping us setup and run your skill.\n    * `context.js` - Reusable factory for dropping things onto your `ctx`. Used to populate `services` and `utilities`.\n    * `listeners.js` - Sets up all your [`listeners`](https://github.com/sprucelabsai/sprucebot-skills-kit/blob/dev/docs/events.md), which are `.js` files that exist in `server/events` in your skill.\n    * `routes.js` - Sets up your `controllers`.\n    * `wares.js` - Sets up your [middleware](https://github.com/sprucelabsai/sprucebot-skills-kit/blob/dev/docs/server.md#middleware).\n* `helpers` - Simple `utilities` we make available to your skill.\n    * `lang.js` - Handles [language support](https://github.com/sprucelabsai/sprucebot-skills-kit/blob/dev/docs/lang.md). **TODO: move to separate module and import**.\n* `middleware` - Built-in middleware that works on all skills.\n    * `auth.js` - Handles authorization, i.e. locks routes by role.\n* `node_modules` - Nodejs stuff.\n* `services` - Built-in [`services`](https://github.com/liquidg3/sprucebot-skills-kit/blob/dev/docs/server.md#services).\n    * `uploads` - Built in upload adapters.\n        * `s3.js` - For uploads to [S3](https://aws.amazon.com/s3).\n    * `uploads.js` - Handles picking the upload adapter and passing it your file.\n* `support` - Built-in configs and settings made available to your skill.\n    * `errors.js` - Built-in errors.\n* `utilities` - Built-in `utilities` made available to your skill.\n    * `auth.js` - Helpful methods for checking role hierarchy.\n\n## Checking permissions\nLets say you want to send an alert to the team when a `user` arrives. But, you have rules around how it should work. \n\n* `guest` arrives -> notify `teammates` and `owners`\n* `teammate` arrives -> notify `owners`\n* `owner` arrives -> no notification\n\nUsing the built-in `auth` `utility`, you have the following.\n\n * `auth.isAbove(teammate, guest)` \n * `auth.isAboveOrEqual(teammate, guest)`\n\nYou should check the source of `utilities/auth` in this module to see how it works.\n\nFor the rules defined above, we'll use `auth.isAbove()`.\n\n```js\n// server/events/did-enter.js\nmodule.exports = async (ctx, next) => {\n    next() // let sprucebot to it's usual (which is nothing on did-enter)\n    try {\n        // we are probs gonna want special error reporting here so we can know the context\n        // of the failure. remember, everything good goes in utilities or services\n        await ctx.services.alerts.send(ctx.event)\n\n    } catch (err) {\n\n        // a helpful message about the error to help us track it down from the logs\n        console.error('did-enter failed to send alert')\n\n        // followed be the actual error\n        console.error(err)\n    }\n}\n```\n\nNow we'll create our `service` for sending the alerts.\n\n```js\n// server/services/alerts.js\nmodule.exports = {\n\n    // an event object mirrors a user object, so this works 100%\n    async send(user) {\n\n        // load all teammates\n        const teammates = await this.sb.users(locationId, { role: 'ownerteammate' })\n        const sendTo = teammates.reduce((sendTo, teammate) => {\n\n            // use built in auth utilities to check role. Honors rules above.\n            if (this.utilities.auth.isAbove(teammate, user)) {\n                sendTo.push(teammate)\n            }\n\n            return sendTo\n\n        }, [])\n\n        //send to everyone\n        await Promise.all(sendTo.map(teammate => {\n            return this.sb.message(teammate.Location.id, teammate.User.id, this.utilities.lang.getText('arrivalAlert', { teammate, user }))\n        }))\n\n    }\n}\n```\n\nFor the sake of it, lets define our lang.\n```js\n// lang/default.js\nmodule.exports = {\n    arrivalAlert ({ teammate, user }) => `Hey ${teammate.user.firstName || teammate.user.name}, ${teammate.user.name} has arrived!`\n}\n```\n\n## Uploading files\nCurrently the only data store built-in is S3. You can add your own very easily. Lets start by setting up S3 and along the way talk about how to specify your own.\n\nWe'll start on the `interface` with a file input. We're gonna make the file input hidden because it's ugly. Instead, we'll prompt the `user` to upload a file after they tap a fancy `<Button />`.\n\nWe're going to depend on newer browser features, including `FileReader` to make this work. Also, we'll only let them upload a pdf.\n\n```js\n// interface/pages/owner/index.js\nimport { Container, Button } from 'react-sprucebot'\n\nclass OwnerDashboard extends Component {\n\n    constructor (props) {\n        super(props)\n        this.state = {\n            errorMessage: undefined\n        }\n    }\n    // setup the file reader when client side\n    componentDidMount() {\n\t\t// is browser out-to-date\n\t\tif (typeof FileReader === 'undefined') {\n\t\t\tthis.setState({\n\t\t\t\terrorMessage: this.props.lang.getText('outOfDateBrowserMessage')\n\t\t\t})\n\t\t} else {\n\t\t\t// setup file reader, we're\n\t\t\tthis.reader = new FileReader()\n\t\t\tthis.reader.onload = this.onFileReaderLoadFile.bind(this)\n\t\t\tthis.reader.onerror = this.onFileReaderLoadFileFail.bind(this)\n        }\n        \n        this.props.skill.read()\n        this.props.actions.files.fetch()\n\t}\n    \n    // tiggered when clicking our nice <Button />\n    selectFile() {\n\n        // triggers the \"select file\" prompt\n        this.fileInput.click()\n    }\n\n    // triggered when a file is selected\n    onFileSelect(e) {\n        // pull the first file (only one at a time for this example)\n        const file = e.target.files[0]\n\n        // always good to do a mime-type check\n        if (file.type !== 'application/pdf') {\n\t\t\tthis.setState({\n                errorMessage: this.props.lang.getText('badFileFormatErrorMessage')\n            })\n\t\t\treturn\n\t\t}\n\n\n        // read the file using the reader\n        this.reader.readAsDataURL(file)\n    }\n\n    // called when the FileReader has read the whole file\n    onFileReaderLoadFile(e) {\n        const content = e.target.result\n        const name = e.target.name\n\n     \n        // defined in our actions in the code sample below\n        this.props.actions.files.upload(content, name)\n\t}\n\n    // if the FileReader fails for some reason\n\tonFileReaderLoadImageFail(err) {\n\t\tconsole.error(err)\n\t\tthis.setState({ errorMessage: this.props.lang.getText('uploadImageFailedMessage') })\n\t}\n\n\n    render() {\n\n        const { lang, files } = this.props\n        const { errorMessage } = this.state\n\n        // errors can be set in our state or by an action failing\n        const error = errorMessage || (files.uploadError && files.uploadingError.friendlyMessage)\n\n        return (\n            <Container className=\"ownerDashboard\">\n                {!error && (\n                    <BotText>{lang.getText('ownerDashboardBotText')}</BotText>\n                )}\n                \n                {error && (\n                    <BotText>{error}</BotText>\n                )}\n\n                {files.file.value && (\n                    <BotText>{`Current file url: ${files.file.value}`}</BotText>\n                )}\n                \n                <Button busy={files.uploading} primary onClick={this.selectFile.bind(this)}>\n                    {lang.getText('uploadFileButtonLabel')}\n                </Button>\n\n                <input \n                    type=\"file\" \n                    ref={input => {\n                        this.fileInput = input\n                    }}\n                    onChange={this.onFileSelect.bind(this)}\n                    style={{ display:'none' }} \n                />\n            </Container>\n        )\n    }\n}\n\n```\nSome things to notice in the above example:\n * We can manually set an error using `state`, but also errors in `actions` are reported through `props`. So, we check both places. This can feel tedious until you actually want to handle different errors differently, then it's a life saver.\n * We use `<Button busy={files.uploading}>` to show a nice `<Loader />` inside the button while the upload is in progress.\n * We check `files.file.value` for the currently uploaded file. This is actually the `url` of the file which is saved as `meta` after upload (which is why we check `value`)\n\nLets move into the `action` for this upload process.\n\n```js\n// interface/store/actions/files.js\nexport const FETCH_FILE_REQUEST = 'files/FETCH_FILE_REQUEST'\nexport const FETCH_FILE_SUCCESS = 'files/FETCH_FILE_SUCCESS'\nexport const FETCH_FILE_ERROR = 'files/FETCH_FILE_ERROR'\n\nexport const UPLOAD_FILE_REQUEST = 'files/UPLOAD_FILE_REQUEST'\nexport const UPLOAD_FILE_SUCCESS = 'files/UPLOAD_FILE_SUCCESS'\nexport const UPLOAD_FILE_ERROR = 'files/UPLOAD_FILE_ERROR'\n\nexport function fetch() {\n    return {\n        types: [\n            FETCH_FILE_REQUEST,\n            FETCH_FILE_SUCCESS,\n            FETCH_FILE_ERROR\n        ],\n        promise: (client, auth) => client.get(`/api/1.0/owner/files/file.json`)\n    }\n}\n\nexport function upload(content, name) {\n    return {\n        types: [\n            UPLOAD_FILE_REQUEST,\n            UPLOAD_FILE_SUCCESS,\n            UPLOAD_FILE_ERROR\n        ],\n        promise: (client, auth) =>\n            client.post(`/api/1.0/owner/files/upload.json`, {\n            body: {\n                content,\n                name\n            }\n        })\n    }\n}\n```\nDon't forget to let your `interface` know your new action exists.\n\n```js\n// interface/store/actions/index.js\nimport * as users from './users'\nimport * as locations from './locations'\nimport * as files from './files'\n\nmodule.exports = {\n    users,\n    locations,\n    files\n}\n\n```\nOk, time for the `reducer`.\n```js\n// interface/store/reducers/files.js\nimport {\n    FETCH_FILE_REQUEST,\n    FETCH_FILE_SUCCESS,\n    FETCH_FILE_ERROR,\n    UPLOAD_FILE_REQUEST,\n    UPLOAD_FILE_SUCCESS,\n    UPLOAD_FILE_ERROR\n} from '../actions/files'\n\nexport default function reducer(state = null, action) {\n    switch (action.type) {\n        case FETCH_FILE_REQUEST:\n            return {\n                ...state,\n                fetching: true\n            }\n        case FETCH_FILE_SUCCESS:\n            return {\n                ...state,\n                file: action.result,\n                fetchError: false,\n                fetching: false\n            }\n        case FETCH_FILE_ERROR:\n            return {\n                ...state,\n                fetchError: action.error,\n                fetching: false\n            }\n        case UPLOAD_FILE_REQUEST:\n            return {\n                ...state,\n                uploading: true\n            }\n        case UPLOAD_FILE_SUCCESS:\n            return {\n                ...state,\n                file: action.result,\n                uploadError: false,\n                uploading: false\n            }\n        case UPLOAD_FILE_ERROR:\n            return {\n                ...state,\n                uploadError: action.error,\n                uploading: false\n            }\n        default:\n            return state\n\t}\n}\n\n```\nExpose your `reducer` to the `interface`.\n```js\n// interface/store/reducers/index.js\nimport users from './users'\nimport locations from './locations'\nimport files from './files'\n\nmodule.exports = {\n    users,\n    locations,\n    files\n}\n```\nOk, `interface` is good to go. Lets setup the `controller` on the `server` to receive the file and pass it to S3 (or whatever storage platform we want). We're going to store the files URL in `meta` for when we want it later. In this example, we're going to save the file for the `location`.\n\n```js\n// controllers/owner/files.js\nmodule.exports = router => {\n\n    router.get('/api/1.0/owner/files/files.json', async (ctx, next) {\n\n        // check if the file has been uploaded yet\n        const meta = await ctx.sb.meta('file', {\n            locationId: ctx.auth.Location.id\n        })\n\n        ctx.body = meta || {}\n\n        await next()\n\n    })\n\n    router.post('/api/1.0/owner/files/upload.json', async (ctx, next) {\n\n        // ensure file and name exist\n        ctx.assert(typeof(ctx.body.content) === 'string', 'FILE_MISSING')\n        ctx.assert(typeof(ctx.body.name) === 'string', 'NAME_MISSING')\n\n        // stop race condition if multiple owners are uploading files\n        // with this is place, last upload will win, but we'll never\n        // end up with duplicates\n        const key = `saving-file-for-${ctx.auth.Location.id}`\n        await ctx.sb.wait(key)\n\n        try {\n\n            // upload the file (THIS WILL overwrite a file be the same name)\n            // this is using s3, which is is defined in `config/default.js`\n            const url = await ctx.services.uploads.upload(ctx.body.content, {\n                Key: `uploads/${ctx.body.name}`,\n                ACL: 'public-read'\n            })\n\n            // save the image to meta for later\n            const meta = ctx.sb.upsertMeta('file', url, {\n                locationId: ctx.auth.Location.Id\n            })\n\n            ctx.body = meta\n\n        } catch(err) {\n\n            console.error(error)\n            ctx.throw('UPLOAD_FAILED')\n\n        } finally {\n\n            // always unblock\n            ctx.sb.go(key)\n            await next()\n        }\n\n    })\n}\n```\n\nOk, we're almost there! We need to configure our uploads `service` to work properly.\n\n```js\n// config/default.js\nmodule.exports = {\n    ...,\n    services: {\n\t\tuploads: {\n\t\t\tuploader: './uploads/s3.js',\n\t\t\toptions: {\n\t\t\t\tBucket: 'my-bucket-name',\n\t\t\t\taccessKeyId: process.env.AWS_ACCESS_KEY_ID,\n\t\t\t\tsecretAccessKey: process.env.AWS_SECRET_ACCESS_KEY\n\t\t\t}\n\t\t}\n\t}\n}\n\n```\nThat's it! Now, if you want to create your own upload `service`, you could do this.\n\n```js\n// config/default.js\nmodule.exports = {\n    ...,\n    services: {\n        uploads: {\n            uploader: path.join(__dirname, '../server/services/ftp.js'),\n            options: {\n                endpoint: process.env.FTP_ENDPOINT,\n                path: process.env.FTP_PATH\n            }\n        }\n    }\n}\n\n```\nNow, when you call `ctx.services.files.upload()` it'll invoke your `service`'s `upload()` method. \n\nNote: Make sure you define `init(options)` in your uploader. It'll receive whatever is defined in `config/default.js` -> `services.uploads.options`.\n\n# What's next?\nHmm, tbd on this one.","readmeFilename":"README.md"}