├── .circleci └── config.yml ├── .eslintignore ├── .eslintrc ├── .github ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .npmignore ├── .prettierignore ├── .prettierrc ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── CONTRIBUTORS ├── LICENSE ├── README.md ├── package-lock.json ├── package.json ├── rollup.config.ts ├── samples ├── README.md ├── audio.js ├── credentials.js ├── package-lock.json ├── package.json ├── quickstart.js ├── resources │ ├── common.res │ ├── hey_google.pmdl │ └── ok_google.pmdl └── text.js ├── src ├── assistant.ts ├── audio-conversation.ts ├── common.ts ├── conversation.ts ├── index.ts ├── proto.ts └── text-conversation.ts ├── test └── assistant.spec.ts └── tsconfig.json /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | node8: 4 | docker: 5 | - image: circleci/node:8 6 | steps: 7 | - checkout 8 | - restore_cache: 9 | keys: 10 | - v1-npm-deps-{{ checksum "package-lock.json" }} 11 | # fallback to using the latest cache if no exact match is found 12 | - v1-npm-deps- 13 | - run: npm install 14 | - save_cache: 15 | key: v1-npm-deps-{{ checksum "package-lock.json" }} 16 | paths: 17 | - node_modules 18 | - run: npm run lint 19 | - run: npm test 20 | - run: npm run build 21 | node10: 22 | docker: 23 | - image: circleci/node:10 24 | steps: 25 | - checkout 26 | - restore_cache: 27 | keys: 28 | - v1-npm-deps-{{ checksum "package-lock.json" }} 29 | # fallback to using the latest cache if no exact match is found 30 | - v1-npm-deps- 31 | - run: npm install 32 | - save_cache: 33 | key: v1-npm-deps-{{ checksum "package-lock.json" }} 34 | paths: 35 | - node_modules 36 | - run: npm run lint 37 | - run: npm test 38 | - run: npm run build 39 | node12: 40 | docker: 41 | - image: circleci/node:12 42 | steps: 43 | - checkout 44 | - restore_cache: 45 | keys: 46 | - v1-npm-deps-{{ checksum "package-lock.json" }} 47 | # fallback to using the latest cache if no exact match is found 48 | - v1-npm-deps- 49 | - run: npm install 50 | - save_cache: 51 | key: v1-npm-deps-{{ checksum "package-lock.json" }} 52 | paths: 53 | - node_modules 54 | - run: npm run lint 55 | - run: npm test 56 | - run: npm run build 57 | docs: 58 | docker: 59 | - image: circleci/node:12 60 | steps: 61 | - checkout 62 | - restore_cache: 63 | keys: 64 | - v1-npm-deps-{{ checksum "package-lock.json" }} 65 | # fallback to using the latest cache if no exact match is found 66 | - v1-npm-deps- 67 | - run: npm install 68 | - save_cache: 69 | key: v1-npm-deps-{{ checksum "package-lock.json" }} 70 | paths: 71 | - node_modules 72 | - run: 73 | name: Generate docs 74 | command: | 75 | npm run generate-docs 76 | mkdir -p /tmp/assistant/samples 77 | mv api /tmp/assistant 78 | git reset --hard 79 | cp {README.md,CODE_OF_CONDUCT.md,CONTRIBUTING.md} /tmp/assistant 80 | cp samples/README.md /tmp/assistant/samples 81 | git checkout gh-pages 82 | shopt -s extglob 83 | rm -rf !(.|..|.git|_config.yml) 84 | mv /tmp/assistant/* . 85 | sed -nie '/## Index/,$p' api/README.md && rm api/README.mde 86 | - deploy: 87 | name: Deploy docs 88 | command: | 89 | git diff-index --quiet HEAD -- && echo "No changes in docs, no need to deploy." && exit 0 90 | echo "Detected changes in docs, deploying new docs version." 91 | git config credential.helper 'cache --timeout=120' 92 | git config user.name "${GITHUB_NAME}" 93 | git config user.email "${GITHUB_EMAIL}" 94 | git add . 95 | git commit -m "docs: Automatic generation" 96 | echo "Pushing to gh-pages branch..." 97 | git push -q https://${GITHUB_TOKEN}@github.com/Dabolus/nodejs-assistant.git gh-pages 98 | workflows: 99 | version: 2 100 | run_pipeline: 101 | jobs: 102 | - node8: 103 | filters: 104 | branches: 105 | only: master 106 | - node10: 107 | filters: 108 | branches: 109 | only: master 110 | - node12: 111 | filters: 112 | branches: 113 | only: master 114 | - docs: 115 | filters: 116 | branches: 117 | only: master 118 | requires: 119 | - node8 120 | - node10 121 | - node12 122 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | node_modules/ 3 | lib/ 4 | dev/ 5 | .rpt2_cache/ 6 | client_secret*.json 7 | credentials.json 8 | api/ 9 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@typescript-eslint/parser", 3 | "parserOptions": { 4 | "project": "tsconfig.json", 5 | "ecmaVersion": 2018 6 | }, 7 | "extends": [ 8 | "eslint:recommended", 9 | "plugin:@typescript-eslint/recommended", 10 | "prettier", 11 | "prettier/@typescript-eslint" 12 | ], 13 | "plugins": [ 14 | "@typescript-eslint", 15 | "prettier" 16 | ], 17 | "env": { 18 | "node": true, 19 | "es6": true 20 | }, 21 | "rules": { 22 | "prettier/prettier": "error", 23 | "no-console": "error", 24 | "@typescript-eslint/explicit-function-return-type": "off" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Thanks for stopping by to let us know something could be better! 2 | 3 | Please, make sure you've tried to search [the already opened issues](https://github.com/Dabolus/nodejs-assistant/issues) before opening a new one. 4 | 5 | If you are still having issues, please be sure to include as much information as 6 | possible: 7 | 8 | #### Environment details 9 | 10 | - OS: 11 | - Node.js version: 12 | - npm version: 13 | - `nodejs-assistant` version: 14 | 15 | #### Steps to reproduce 16 | 17 | 1. ??? 18 | 2. ??? 19 | 20 | Following these steps will guarantee the quickest resolution possible. 21 | 22 | Thanks! 23 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Fixes # (it's a good idea to open an issue first for discussion) 2 | 3 | - [ ] Tests and linter pass 4 | - [ ] Code coverage does not decrease (if any source code was changed) 5 | - [ ] Appropriate docs were updated (if necessary) 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | node_modules/ 3 | lib/ 4 | dev/ 5 | .rpt2_cache/ 6 | client_secret*.json 7 | credentials.json 8 | api/ 9 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .circleci/ 3 | .github/ 4 | node_modules/ 5 | samples/ 6 | src/ 7 | dev/ 8 | api/ 9 | test/ 10 | .rpt2_cache/ 11 | .* 12 | *.log 13 | ts*.json 14 | CODE_OF_CONDUCT.md 15 | CONTRIBUTORS 16 | rollup.config.ts 17 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | node_modules/ 3 | lib/ 4 | dev/ 5 | .rpt2_cache/ 6 | client_secret*.json 7 | credentials.json 8 | api/ 9 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all" 4 | } 5 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Code of Conduct 2 | 3 | As contributors and maintainers of this project, 4 | and in the interest of fostering an open and welcoming community, 5 | we pledge to respect all people who contribute through reporting issues, 6 | posting feature requests, updating documentation, 7 | submitting pull requests or patches, and other activities. 8 | 9 | We are committed to making participation in this project 10 | a harassment-free experience for everyone, 11 | regardless of level of experience, gender, gender identity and expression, 12 | sexual orientation, disability, personal appearance, 13 | body size, race, ethnicity, age, religion, or nationality. 14 | 15 | Examples of unacceptable behavior by participants include: 16 | 17 | * The use of sexualized language or imagery 18 | * Personal attacks 19 | * Trolling or insulting/derogatory comments 20 | * Public or private harassment 21 | * Publishing other's private information, 22 | such as physical or electronic 23 | addresses, without explicit permission 24 | * Other unethical or unprofessional conduct. 25 | 26 | Project maintainers have the right and responsibility to remove, edit, or reject 27 | comments, commits, code, wiki edits, issues, and other contributions 28 | that are not aligned to this Code of Conduct. 29 | By adopting this Code of Conduct, 30 | project maintainers commit themselves to fairly and consistently 31 | applying these principles to every aspect of managing this project. 32 | Project maintainers who do not follow or enforce the Code of Conduct 33 | may be permanently removed from the project team. 34 | 35 | This code of conduct applies both within project spaces and in public spaces 36 | when an individual is representing the project or its community. 37 | 38 | Instances of abusive, harassing, or otherwise unacceptable behavior 39 | may be reported by opening an issue 40 | or contacting one or more of the project maintainers. 41 | 42 | This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.2.0, 43 | available at [http://contributor-covenant.org/version/1/2/0/](http://contributor-covenant.org/version/1/2/0/) 44 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to become a contributor and submit your own code 2 | 3 | **Table of contents** 4 | 5 | * [Contributing a patch](#contributing-a-patch) 6 | * [Running the tests](#running-the-tests) 7 | * [Releasing the library](#releasing-the-library) 8 | 9 | ## Contributing A Patch 10 | 11 | 1. Read the [Code of Conduct](CODE_OF_CONDUCT.md). 12 | 2. Submit an issue describing your proposed change to the repo in question. 13 | 3. The repo owner will respond to your issue promptly. 14 | 4. If your proposed change is accepted, fork the desired repo, develop and 15 | test your code changes. 16 | 5. Ensure that your code adheres to the existing style in the code to which 17 | you are contributing. 18 | 6. Ensure that your code has an appropriate set of tests which all pass. 19 | 7. Submit a pull request. 20 | 21 | ## Running the tests 22 | 23 | 1. [Prepare your environment for Node.js setup][setup]. 24 | 25 | 2. Install dependencies: 26 | 27 | npm install 28 | 29 | 3. Run the tests: 30 | 31 | npm test 32 | 33 | [setup]: https://cloud.google.com/nodejs/docs/setup 34 | -------------------------------------------------------------------------------- /CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | # The names of individuals who have contributed to this project. 2 | # 3 | # Names are formatted as: 4 | # name 5 | # 6 | Giorgio Garasto 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Giorgio Garasto 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Google Assistant logo 2 | 3 | # [Google Assistant SDK: Node.js Client](https://github.com/Dabolus/nodejs-assistant) 4 | 5 | 6 | 7 | > Node.js idiomatic client for [Google Assistant SDK][product-docs] (unofficial). 8 | 9 | The [Google Assistant SDK][product-docs] lets you add hotword detection, voice 10 | control, natural language understanding and Google’s smarts to your devices. 11 | Your device captures an utterance (a spoken audio request, such as 12 | _What's on my calendar?_), sends it to the Google Assistant, and receives a 13 | spoken audio response in addition to the raw text of the utterance. 14 | 15 | 16 | * [Google Assistant SDK Node.js Client API Reference][client-docs] 17 | * [github.com/Dabolus/nodejs-assistant](https://github.com/Dabolus/nodejs-assistant) 18 | * [Google Assistant SDK documentation][product-docs] 19 | 20 | Read more about the client libraries for Cloud APIs, including the older 21 | Google APIs Client Libraries, in [Client Libraries Explained][explained]. 22 | 23 | [explained]: https://cloud.google.com/apis/docs/client-libraries-explained 24 | 25 | **Table of contents:** 26 | 27 | * [Quickstart](#quickstart) 28 | * [Before you begin](#before-you-begin) 29 | * [Installing the client library](#installing-the-client-library) 30 | * [Using the client library](#using-the-client-library) 31 | * [Samples](#samples) 32 | * [Contributing](#contributing) 33 | * [License](#license) 34 | 35 | ## Quickstart 36 | 37 | ### Before you begin 38 | 39 | Some configuration steps are needed to make this library work correctly, for 40 | example creating a Cloud Platform project, creating a device and generating 41 | OAuth2 credentials. Please, follow 42 | [the Google Assistant Library for Python guide](https://developers.google.com/assistant/sdk/guides/library/python/) 43 | to get started quickly. 44 | 45 | ### Installing the client library 46 | 47 | npm install --save nodejs-assistant 48 | 49 | ### Using the client library 50 | 51 | ```javascript 52 | // Imports the Google Cloud client library 53 | const {Assistant, AssistantLanguage} = require('nodejs-assistant'); 54 | 55 | // Your credentials 56 | const credentials = require('path-to-your-credentials.json'); 57 | 58 | // Creates a client 59 | const assistant = new Assistant(/* required credentials */ { 60 | type: 'authorized_user', 61 | client_id: credentials.client_id, 62 | client_secret: credentials.client_secret, 63 | refresh_token: credentials.refresh_token, 64 | }, /* additional, optional options */ { 65 | locale: AssistantLanguage.ITALIAN, // Defaults to AssistantLanguage.ENGLISH (en-US) 66 | deviceId: 'your device id', 67 | deviceModelId: 'your device model id', 68 | }); 69 | 70 | // Sends a text query to the assistant 71 | assistant.query('Hi!') 72 | .then(response => { 73 | // response contains all the fields returned by the assistant, such as the text and audio 74 | console.log(`Response: ${response.text}`); 75 | // response.audio is a Buffer containing the audio response by the assistant 76 | }) 77 | .catch(err => { 78 | console.error('ERROR: ', err); 79 | }); 80 | ``` 81 | 82 | Head over the [API docs](https://dabolus.github.io/nodejs-assistant/api/) for 83 | more information. 84 | 85 | ## Samples 86 | 87 | Samples are in the [`samples/`](samples/) directory. The samples' `README.md` 88 | has instructions for running the samples. 89 | 90 | | Sample | Source Code | Try it | 91 | | --------------------------- | --------------------------------- | ------ | 92 | | Quickstart | [source code](https://github.com/Dabolus/nodejs-assistant/blob/master/samples/quickstart.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/Dabolus/nodejs-assistant&page=editor&open_in_editor=samples/quickstart.js,samples/README.md) | 93 | | Text Conversation | [source code](https://github.com/Dabolus/nodejs-assistant/blob/master/samples/text.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/Dabolus/nodejs-assistant&page=editor&open_in_editor=samples/text.js,samples/README.md) | 94 | | Audio Conversation | [source code](https://github.com/Dabolus/nodejs-assistant/blob/master/samples/audio.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/Dabolus/nodejs-assistant&page=editor&open_in_editor=samples/audio.js,samples/README.md) | 95 | 96 | You might also want to check out [Google Assistant Desktop _(Unofficial)_](https://github.com/Dabolus/google-assistant-desktop-unofficial) 97 | for a more concrete example. 98 | 99 | ## Contributing 100 | 101 | Contributions welcome! See the [Contributing Guide](CONTRIBUTING.md). 102 | 103 | ## License 104 | 105 | MIT 106 | 107 | See [LICENSE](https://github.com/Dabolus/nodejs-assistant/blob/master/LICENSE) 108 | 109 | [client-docs]: https://dabolus.github.io/nodejs-assistant/ 110 | [product-docs]: https://developers.google.com/assistant/sdk/ 111 | [shell_img]: https://gstatic.com/cloudssh/images/open-btn.png 112 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nodejs-assistant", 3 | "description": "Google Assistant SDK for Node.js", 4 | "version": "0.8.0", 5 | "main": "lib/index.js", 6 | "module": "lib/index.mjs", 7 | "types": "lib/index.d.ts", 8 | "author": "Giorgio Garasto ", 9 | "license": "MIT", 10 | "scripts": { 11 | "build": "rm -rf lib && rollup -c rollup.config.ts", 12 | "test": "jest", 13 | "lint": "eslint --ext .ts --ext .js 'src/**/*.ts'", 14 | "format": "npm run lint -- --fix", 15 | "generate-docs": "typedoc src --out api --mode file --name 'Assistant SDK for Node.js' --theme markdown --hideSources", 16 | "prepublishOnly": "npm run build" 17 | }, 18 | "dependencies": { 19 | "@grpc/proto-loader": "^0.5.3", 20 | "google-auth-library": "^5.10.0", 21 | "google-proto-files": "^1.1.2", 22 | "grpc": "^1.24.2" 23 | }, 24 | "devDependencies": { 25 | "@types/jest": "^25.1.3", 26 | "@types/node": "^13.7.4", 27 | "@typescript-eslint/eslint-plugin": "^2.20.0", 28 | "@typescript-eslint/parser": "^2.20.0", 29 | "eslint": "^6.8.0", 30 | "eslint-config-prettier": "^6.10.0", 31 | "eslint-plugin-prettier": "^3.1.2", 32 | "jest": "^25.1.0", 33 | "prettier": "^1.19.1", 34 | "rollup": "^1.31.1", 35 | "rollup-plugin-typescript2": "^0.26.0", 36 | "ts-jest": "^25.2.1", 37 | "typedoc": "^0.16.10", 38 | "typedoc-plugin-markdown": "^2.2.16", 39 | "typescript": "^3.8.2" 40 | }, 41 | "repository": "Dabolus/nodejs-assistant", 42 | "contributors": [ 43 | "Giorgio Garasto " 44 | ], 45 | "jest": { 46 | "preset": "ts-jest", 47 | "testEnvironment": "node", 48 | "moduleFileExtensions": [ 49 | "ts", 50 | "js", 51 | "json" 52 | ], 53 | "transform": { 54 | "^.+\\.ts$": "ts-jest" 55 | }, 56 | "transformIgnorePatterns": [ 57 | "/node_modules/" 58 | ], 59 | "collectCoverage": true, 60 | "collectCoverageFrom": [ 61 | "/src/**/*.ts", 62 | "!/src/**/*.d.ts" 63 | ], 64 | "coverageDirectory": "/coverage", 65 | "testMatch": [ 66 | "/test/**/*.spec.ts" 67 | ] 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /rollup.config.ts: -------------------------------------------------------------------------------- 1 | // tslint:disable:object-literal-sort-keys 2 | import fs from 'fs'; 3 | import typescript from 'rollup-plugin-typescript2'; 4 | 5 | const inputs = fs 6 | .readdirSync('src') 7 | .filter((file) => !file.endsWith('.d.ts')) 8 | .map((file) => file.slice(0, -3)); 9 | 10 | export default inputs.map((input) => ({ 11 | input: `src/${input}.ts`, 12 | output: [{ 13 | file: `lib/${input}.js`, 14 | format: 'cjs', 15 | }, { 16 | file: `lib/${input}.mjs`, 17 | format: 'es', 18 | }], 19 | plugins: [ 20 | typescript(), 21 | ], 22 | // Make all dependencies external 23 | external: () => true, 24 | })); 25 | -------------------------------------------------------------------------------- /samples/README.md: -------------------------------------------------------------------------------- 1 | Google Assistant logo 2 | 3 | # Google Assistant SDK: Node.js Samples 4 | 5 | [![Open in Cloud Shell][shell_img]][shell_link] 6 | 7 | The [Google Assistant SDK](https://developers.google.com/assistant/sdk/) lets you add hotword detection, voice 8 | control, natural language understanding and Google’s smarts to your devices. 9 | Your device captures an utterance (a spoken audio request, such as 10 | _What's on my calendar?_), sends it to the Google Assistant, and receives a 11 | spoken audio response in addition to the raw text of the utterance. 12 | 13 | ## Table of Contents 14 | 15 | * [Before you begin](#before-you-begin) 16 | * [Samples](#samples) 17 | * [Quickstart](#quickstart) 18 | * [Text Conversation](#text-conversation) 19 | * [Audio Conversation](#audio-conversation) 20 | 21 | ## Before you begin 22 | 23 | Before running the samples, make sure you've followed the steps in the 24 | [Before you begin section](../README.md#before-you-begin) of the client 25 | library's README. 26 | 27 | ## Samples 28 | 29 | ### Quickstart 30 | 31 | A really basic example that sends a text query to the Assistant using the 32 | `query` method and prints its response. 33 | 34 | View the [source code][quickstart_code]. 35 | 36 | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/Dabolus/nodejs-assistant&page=editor&open_in_editor=samples/quickstart.js,samples/README.md) 37 | 38 | __Usage:__ `node quickstart.js` 39 | 40 | [quickstart_code]: https://github.com/Dabolus/nodejs-assistant/blob/master/samples/quickstart.js 41 | 42 | ### Text Conversation 43 | 44 | An example that shows how to open a text conversation with the Assistant, send 45 | text queries and receive text responses. 46 | 47 | View the [source code][text_code]. 48 | 49 | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/Dabolus/nodejs-assistant&page=editor&open_in_editor=samples/text.js,samples/README.md) 50 | 51 | __Usage:__ `node text.js` 52 | 53 | [text_code]: https://github.com/Dabolus/nodejs-assistant/blob/master/samples/text.js 54 | 55 | ### Audio Conversation 56 | 57 | An example that shows how to detect an hotword using [Snowboy](https://snowboy.kitt.ai/), 58 | open an audio conversation with the Assistant, send audio queries and receive 59 | audio responses. Note that the two hotwords ("Ok Google" and "Hey Google") were 60 | trained against my voice, so you might want to train your own hotwords from 61 | Snowboy's dashboard. The great thing is that Snowboy lets you train any keyword 62 | you want, not just "Ok Google" and "Hey Google". 63 | 64 | View the [source code][audio_code]. 65 | 66 | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/Dabolus/nodejs-assistant&page=editor&open_in_editor=samples/audio.js,samples/README.md) 67 | 68 | __Usage:__ `node audio.js` 69 | 70 | [audio_code]: https://github.com/Dabolus/nodejs-assistant/blob/master/samples/audio.js 71 | 72 | [shell_img]: https://gstatic.com/cloudssh/images/open-btn.png 73 | [shell_link]: https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/Dabolus/nodejs-assistant&page=editor&open_in_editor=samples/README.md 74 | -------------------------------------------------------------------------------- /samples/audio.js: -------------------------------------------------------------------------------- 1 | const record = require('node-record-lpcm16'); 2 | const Speaker = require('speaker'); 3 | const { Assistant, AssistantLanguage, AudioInEncoding, AudioOutEncoding } = require('nodejs-assistant'); 4 | const { Detector, Models } = require('snowboy'); 5 | const { getCredentials } = require('./credentials'); 6 | 7 | const startAudioAssistant = async (mic) => { 8 | console.log('Speak now!'); 9 | const credentials = await getCredentials(); 10 | const assistant = new Assistant(credentials, { 11 | deviceId: 'test device', 12 | deviceModelId: 'test device model', 13 | locale: AssistantLanguage.ENGLISH, 14 | }); 15 | let spokenResponseLength = 0; 16 | let speakerOpenTime = 0; 17 | let speakerTimer; 18 | 19 | // Setup the speaker 20 | const speaker = new Speaker({ 21 | sampleRate: 16000, 22 | channels: 1, 23 | }); 24 | // Setup the conversation with the Assistant 25 | const conversation = assistant.startAudioConversation( 26 | // Audio input config 27 | { 28 | encoding: AudioInEncoding.LINEAR16, 29 | sampleRateHertz: 16000, 30 | }, 31 | // Audio output config 32 | { 33 | encoding: AudioOutEncoding.LINEAR16, 34 | sampleRateHertz: 16000, 35 | volumePercentage: 100, 36 | }, 37 | ); 38 | 39 | conversation.on('audio', (audio) => { 40 | const now = Date.now(); 41 | speaker.write(audio); 42 | spokenResponseLength += audio.length; 43 | const audioTime = spokenResponseLength / (16000 * 16 / 8) * 1000; 44 | if (speakerTimer) { 45 | clearTimeout(speakerTimer); 46 | } 47 | speakerTimer = setTimeout( 48 | () => speaker.end(), 49 | audioTime - Math.max(0, now - speakerOpenTime), 50 | ); 51 | }); 52 | 53 | mic.on('data', (data) => conversation.send(data)); 54 | 55 | speaker 56 | .on('open', () => { 57 | if (speakerTimer) { 58 | clearTimeout(speakerTimer); 59 | } 60 | spokenResponseLength = 0; 61 | speakerOpenTime = Date.now(); 62 | }) 63 | .on('close', () => void 0); 64 | }; 65 | 66 | const waitForHotword = () => { 67 | const models = new Models(); 68 | models.add({ 69 | file: 'resources/ok_google.pmdl', 70 | sensitivity: '0.5', 71 | hotwords: 'Ok Google', 72 | }); 73 | models.add({ 74 | file: 'resources/hey_google.pmdl', 75 | sensitivity: '0.5', 76 | hotwords: 'Hey Google', 77 | }); 78 | 79 | const detector = new Detector({ 80 | models, 81 | resource: 'resources/common.res', 82 | audioGain: 1, 83 | applyFrontend: false, 84 | }); 85 | 86 | // Setup the mic 87 | const mic = record.start({ 88 | sampleRate: 16000, 89 | threshold: 0, 90 | }); 91 | 92 | detector.on('hotword', () => startAudioAssistant(mic)); 93 | mic.pipe(detector); 94 | }; 95 | 96 | waitForHotword(); 97 | -------------------------------------------------------------------------------- /samples/credentials.js: -------------------------------------------------------------------------------- 1 | const { writeFileSync } = require('fs'); 2 | const { OAuth2Client } = require('google-auth-library'); 3 | 4 | const getAuthorizationCode = (oAuth2Client) => new Promise((resolve) => { 5 | // Generate the url that will be used for the consent dialog. 6 | const authorizeUrl = oAuth2Client.generateAuthUrl({ 7 | access_type: 'offline', 8 | scope: 'https://www.googleapis.com/auth/assistant-sdk-prototype', 9 | }); 10 | 11 | console.log(`Open your browser at: ${authorizeUrl}`) 12 | console.log(`When you're done, paste your authorization code down here:`); 13 | 14 | const stdin = process.openStdin(); 15 | stdin.once('data', (data) => resolve(data.toString().trim())); 16 | }); 17 | 18 | const getRefreshToken = async (oAuth2Client) => { 19 | const code = await getAuthorizationCode(oAuth2Client); 20 | const { tokens } = await oAuth2Client.getToken(code); 21 | return tokens.refresh_token; 22 | } 23 | 24 | const getCredentials = async () => { 25 | try { 26 | return require('./credentials.json'); 27 | } catch (e) { 28 | const { 29 | installed: { client_id, client_secret }, 30 | } = require('./client_secret.json'); 31 | 32 | // Create an oAuth client. Secrets are kept in a `client_secret_<...>.json` 33 | // file, which should be downloaded from the Google Developers Console. 34 | const oAuth2Client = new OAuth2Client( 35 | client_id, 36 | client_secret, 37 | 'urn:ietf:wg:oauth:2.0:oob', 38 | ); 39 | const refresh_token = await getRefreshToken(oAuth2Client); 40 | const credentials = { 41 | type: 'authorized_user', 42 | client_id, 43 | client_secret, 44 | refresh_token, 45 | }; 46 | writeFileSync('./credentials.json', JSON.stringify(credentials)); 47 | return credentials; 48 | } 49 | }; 50 | 51 | module.exports = { 52 | getAuthorizationCode, 53 | getRefreshToken, 54 | getCredentials, 55 | }; 56 | -------------------------------------------------------------------------------- /samples/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nodejs-docs-samples-assistant", 3 | "version": "0.0.1", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@grpc/proto-loader": { 8 | "version": "0.5.3", 9 | "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.5.3.tgz", 10 | "integrity": "sha512-8qvUtGg77G2ZT2HqdqYoM/OY97gQd/0crSG34xNmZ4ZOsv3aQT/FQV9QfZPazTGna6MIoyUd+u6AxsoZjJ/VMQ==", 11 | "requires": { 12 | "lodash.camelcase": "^4.3.0", 13 | "protobufjs": "^6.8.6" 14 | } 15 | }, 16 | "@protobufjs/aspromise": { 17 | "version": "1.1.2", 18 | "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", 19 | "integrity": "sha1-m4sMxmPWaafY9vXQiToU00jzD78=" 20 | }, 21 | "@protobufjs/base64": { 22 | "version": "1.1.2", 23 | "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", 24 | "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" 25 | }, 26 | "@protobufjs/codegen": { 27 | "version": "2.0.4", 28 | "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", 29 | "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" 30 | }, 31 | "@protobufjs/eventemitter": { 32 | "version": "1.1.0", 33 | "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", 34 | "integrity": "sha1-NVy8mLr61ZePntCV85diHx0Ga3A=" 35 | }, 36 | "@protobufjs/fetch": { 37 | "version": "1.1.0", 38 | "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", 39 | "integrity": "sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=", 40 | "requires": { 41 | "@protobufjs/aspromise": "^1.1.1", 42 | "@protobufjs/inquire": "^1.1.0" 43 | } 44 | }, 45 | "@protobufjs/float": { 46 | "version": "1.0.2", 47 | "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", 48 | "integrity": "sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E=" 49 | }, 50 | "@protobufjs/inquire": { 51 | "version": "1.1.0", 52 | "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", 53 | "integrity": "sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik=" 54 | }, 55 | "@protobufjs/path": { 56 | "version": "1.1.2", 57 | "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", 58 | "integrity": "sha1-bMKyDFya1q0NzP0hynZz2Nf79o0=" 59 | }, 60 | "@protobufjs/pool": { 61 | "version": "1.1.0", 62 | "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", 63 | "integrity": "sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q=" 64 | }, 65 | "@protobufjs/utf8": { 66 | "version": "1.1.0", 67 | "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", 68 | "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=" 69 | }, 70 | "@types/bytebuffer": { 71 | "version": "5.0.40", 72 | "resolved": "https://registry.npmjs.org/@types/bytebuffer/-/bytebuffer-5.0.40.tgz", 73 | "integrity": "sha512-h48dyzZrPMz25K6Q4+NCwWaxwXany2FhQg/ErOcdZS1ZpsaDnDMZg8JYLMTGz7uvXKrcKGJUZJlZObyfgdaN9g==", 74 | "requires": { 75 | "@types/long": "*", 76 | "@types/node": "*" 77 | } 78 | }, 79 | "@types/long": { 80 | "version": "4.0.1", 81 | "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz", 82 | "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==" 83 | }, 84 | "@types/node": { 85 | "version": "10.17.16", 86 | "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.16.tgz", 87 | "integrity": "sha512-A4283YSA1OmnIivcpy/4nN86YlnKRiQp8PYwI2KdPCONEBN093QTb0gCtERtkLyVNGKKIGazTZ2nAmVzQU51zA==" 88 | }, 89 | "abbrev": { 90 | "version": "1.1.1", 91 | "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", 92 | "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" 93 | }, 94 | "abort-controller": { 95 | "version": "3.0.0", 96 | "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", 97 | "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", 98 | "requires": { 99 | "event-target-shim": "^5.0.0" 100 | } 101 | }, 102 | "agent-base": { 103 | "version": "6.0.0", 104 | "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.0.tgz", 105 | "integrity": "sha512-j1Q7cSCqN+AwrmDd+pzgqc0/NpC655x2bUf5ZjRIO77DcNBFmh+OgRNzF6OKdCC9RSCb19fGd99+bhXFdkRNqw==", 106 | "requires": { 107 | "debug": "4" 108 | } 109 | }, 110 | "ajv": { 111 | "version": "4.11.8", 112 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", 113 | "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", 114 | "requires": { 115 | "co": "^4.6.0", 116 | "json-stable-stringify": "^1.0.1" 117 | } 118 | }, 119 | "ansi-regex": { 120 | "version": "2.1.1", 121 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", 122 | "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" 123 | }, 124 | "aproba": { 125 | "version": "1.2.0", 126 | "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", 127 | "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" 128 | }, 129 | "are-we-there-yet": { 130 | "version": "1.1.5", 131 | "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", 132 | "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", 133 | "requires": { 134 | "delegates": "^1.0.0", 135 | "readable-stream": "^2.0.6" 136 | } 137 | }, 138 | "arrify": { 139 | "version": "2.0.1", 140 | "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", 141 | "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==" 142 | }, 143 | "ascli": { 144 | "version": "1.0.1", 145 | "resolved": "https://registry.npmjs.org/ascli/-/ascli-1.0.1.tgz", 146 | "integrity": "sha1-vPpZdKYvGOgcq660lzKrSoj5Brw=", 147 | "requires": { 148 | "colour": "~0.7.1", 149 | "optjs": "~3.2.2" 150 | } 151 | }, 152 | "asn1": { 153 | "version": "0.2.4", 154 | "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", 155 | "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", 156 | "requires": { 157 | "safer-buffer": "~2.1.0" 158 | } 159 | }, 160 | "assert-plus": { 161 | "version": "0.2.0", 162 | "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", 163 | "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=" 164 | }, 165 | "asynckit": { 166 | "version": "0.4.0", 167 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 168 | "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" 169 | }, 170 | "aws-sign2": { 171 | "version": "0.6.0", 172 | "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", 173 | "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=" 174 | }, 175 | "aws4": { 176 | "version": "1.9.1", 177 | "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz", 178 | "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==" 179 | }, 180 | "balanced-match": { 181 | "version": "1.0.0", 182 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 183 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" 184 | }, 185 | "base64-js": { 186 | "version": "1.3.1", 187 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", 188 | "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" 189 | }, 190 | "bcrypt-pbkdf": { 191 | "version": "1.0.2", 192 | "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", 193 | "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", 194 | "requires": { 195 | "tweetnacl": "^0.14.3" 196 | } 197 | }, 198 | "bignumber.js": { 199 | "version": "7.2.1", 200 | "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-7.2.1.tgz", 201 | "integrity": "sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ==" 202 | }, 203 | "bindings": { 204 | "version": "1.5.0", 205 | "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", 206 | "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", 207 | "requires": { 208 | "file-uri-to-path": "1.0.0" 209 | } 210 | }, 211 | "block-stream": { 212 | "version": "0.0.9", 213 | "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", 214 | "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", 215 | "requires": { 216 | "inherits": "~2.0.0" 217 | } 218 | }, 219 | "boom": { 220 | "version": "2.10.1", 221 | "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", 222 | "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", 223 | "requires": { 224 | "hoek": "2.x.x" 225 | } 226 | }, 227 | "brace-expansion": { 228 | "version": "1.1.11", 229 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 230 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 231 | "requires": { 232 | "balanced-match": "^1.0.0", 233 | "concat-map": "0.0.1" 234 | } 235 | }, 236 | "buffer-alloc": { 237 | "version": "1.2.0", 238 | "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", 239 | "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", 240 | "requires": { 241 | "buffer-alloc-unsafe": "^1.1.0", 242 | "buffer-fill": "^1.0.0" 243 | } 244 | }, 245 | "buffer-alloc-unsafe": { 246 | "version": "1.1.0", 247 | "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", 248 | "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==" 249 | }, 250 | "buffer-equal-constant-time": { 251 | "version": "1.0.1", 252 | "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", 253 | "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" 254 | }, 255 | "buffer-fill": { 256 | "version": "1.0.0", 257 | "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", 258 | "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=" 259 | }, 260 | "bytebuffer": { 261 | "version": "5.0.1", 262 | "resolved": "https://registry.npmjs.org/bytebuffer/-/bytebuffer-5.0.1.tgz", 263 | "integrity": "sha1-WC7qSxqHO20CCkjVjfhfC7ps/d0=", 264 | "requires": { 265 | "long": "~3" 266 | }, 267 | "dependencies": { 268 | "long": { 269 | "version": "3.2.0", 270 | "resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz", 271 | "integrity": "sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s=" 272 | } 273 | } 274 | }, 275 | "camelcase": { 276 | "version": "2.1.1", 277 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", 278 | "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=" 279 | }, 280 | "caseless": { 281 | "version": "0.12.0", 282 | "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", 283 | "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" 284 | }, 285 | "cliui": { 286 | "version": "3.2.0", 287 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", 288 | "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", 289 | "requires": { 290 | "string-width": "^1.0.1", 291 | "strip-ansi": "^3.0.1", 292 | "wrap-ansi": "^2.0.0" 293 | } 294 | }, 295 | "co": { 296 | "version": "4.6.0", 297 | "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", 298 | "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" 299 | }, 300 | "code-point-at": { 301 | "version": "1.1.0", 302 | "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", 303 | "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" 304 | }, 305 | "colour": { 306 | "version": "0.7.1", 307 | "resolved": "https://registry.npmjs.org/colour/-/colour-0.7.1.tgz", 308 | "integrity": "sha1-nLFpkX7F0SwHNtPoaFdG3xyt93g=" 309 | }, 310 | "combined-stream": { 311 | "version": "1.0.8", 312 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 313 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 314 | "requires": { 315 | "delayed-stream": "~1.0.0" 316 | } 317 | }, 318 | "concat-map": { 319 | "version": "0.0.1", 320 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 321 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" 322 | }, 323 | "console-control-strings": { 324 | "version": "1.1.0", 325 | "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", 326 | "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" 327 | }, 328 | "core-util-is": { 329 | "version": "1.0.2", 330 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 331 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" 332 | }, 333 | "cryptiles": { 334 | "version": "2.0.5", 335 | "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", 336 | "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", 337 | "requires": { 338 | "boom": "2.x.x" 339 | } 340 | }, 341 | "dashdash": { 342 | "version": "1.14.1", 343 | "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", 344 | "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", 345 | "requires": { 346 | "assert-plus": "^1.0.0" 347 | }, 348 | "dependencies": { 349 | "assert-plus": { 350 | "version": "1.0.0", 351 | "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", 352 | "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" 353 | } 354 | } 355 | }, 356 | "debug": { 357 | "version": "4.1.1", 358 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", 359 | "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", 360 | "requires": { 361 | "ms": "^2.1.1" 362 | } 363 | }, 364 | "decamelize": { 365 | "version": "1.2.0", 366 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", 367 | "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" 368 | }, 369 | "deep-extend": { 370 | "version": "0.6.0", 371 | "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", 372 | "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" 373 | }, 374 | "delayed-stream": { 375 | "version": "1.0.0", 376 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 377 | "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" 378 | }, 379 | "delegates": { 380 | "version": "1.0.0", 381 | "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", 382 | "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" 383 | }, 384 | "detect-libc": { 385 | "version": "1.0.3", 386 | "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", 387 | "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" 388 | }, 389 | "ecc-jsbn": { 390 | "version": "0.1.2", 391 | "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", 392 | "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", 393 | "requires": { 394 | "jsbn": "~0.1.0", 395 | "safer-buffer": "^2.1.0" 396 | } 397 | }, 398 | "ecdsa-sig-formatter": { 399 | "version": "1.0.11", 400 | "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", 401 | "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", 402 | "requires": { 403 | "safe-buffer": "^5.0.1" 404 | } 405 | }, 406 | "event-target-shim": { 407 | "version": "5.0.1", 408 | "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", 409 | "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" 410 | }, 411 | "extend": { 412 | "version": "3.0.2", 413 | "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", 414 | "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" 415 | }, 416 | "extsprintf": { 417 | "version": "1.3.0", 418 | "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", 419 | "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" 420 | }, 421 | "fast-text-encoding": { 422 | "version": "1.0.0", 423 | "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.0.tgz", 424 | "integrity": "sha512-R9bHCvweUxxwkDwhjav5vxpFvdPGlVngtqmx4pIZfSUhM/Q4NiIUHB456BAf+Q1Nwu3HEZYONtu+Rya+af4jiQ==" 425 | }, 426 | "file-uri-to-path": { 427 | "version": "1.0.0", 428 | "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", 429 | "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" 430 | }, 431 | "forever-agent": { 432 | "version": "0.6.1", 433 | "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", 434 | "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" 435 | }, 436 | "form-data": { 437 | "version": "2.1.4", 438 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", 439 | "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", 440 | "requires": { 441 | "asynckit": "^0.4.0", 442 | "combined-stream": "^1.0.5", 443 | "mime-types": "^2.1.12" 444 | } 445 | }, 446 | "fs.realpath": { 447 | "version": "1.0.0", 448 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 449 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" 450 | }, 451 | "fstream": { 452 | "version": "1.0.12", 453 | "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", 454 | "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", 455 | "requires": { 456 | "graceful-fs": "^4.1.2", 457 | "inherits": "~2.0.0", 458 | "mkdirp": ">=0.5 0", 459 | "rimraf": "2" 460 | } 461 | }, 462 | "fstream-ignore": { 463 | "version": "1.0.5", 464 | "resolved": "https://registry.npmjs.org/fstream-ignore/-/fstream-ignore-1.0.5.tgz", 465 | "integrity": "sha1-nDHa40dnAY/h0kmyTa2mfQktoQU=", 466 | "requires": { 467 | "fstream": "^1.0.0", 468 | "inherits": "2", 469 | "minimatch": "^3.0.0" 470 | } 471 | }, 472 | "gauge": { 473 | "version": "2.7.4", 474 | "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", 475 | "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", 476 | "requires": { 477 | "aproba": "^1.0.3", 478 | "console-control-strings": "^1.0.0", 479 | "has-unicode": "^2.0.0", 480 | "object-assign": "^4.1.0", 481 | "signal-exit": "^3.0.0", 482 | "string-width": "^1.0.1", 483 | "strip-ansi": "^3.0.1", 484 | "wide-align": "^1.1.0" 485 | } 486 | }, 487 | "gaxios": { 488 | "version": "2.3.1", 489 | "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-2.3.1.tgz", 490 | "integrity": "sha512-DQOesWEx59/bm63lTX0uHDDXpGTW9oKqNsoigwCoRe2lOb5rFqxzHjLTa6aqEBecLcz69dHLw7rbS068z1fvIQ==", 491 | "requires": { 492 | "abort-controller": "^3.0.0", 493 | "extend": "^3.0.2", 494 | "https-proxy-agent": "^5.0.0", 495 | "is-stream": "^2.0.0", 496 | "node-fetch": "^2.3.0" 497 | } 498 | }, 499 | "gcp-metadata": { 500 | "version": "3.4.0", 501 | "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-3.4.0.tgz", 502 | "integrity": "sha512-fizmBtCXHp8b7FZuzbgKaixO8DzsSYoEVmMgZIna7x8t6cfBF3eqirODWYxVbgmasA5qudCAKiszfB7yVwroIQ==", 503 | "requires": { 504 | "gaxios": "^2.1.0", 505 | "json-bigint": "^0.3.0" 506 | } 507 | }, 508 | "getpass": { 509 | "version": "0.1.7", 510 | "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", 511 | "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", 512 | "requires": { 513 | "assert-plus": "^1.0.0" 514 | }, 515 | "dependencies": { 516 | "assert-plus": { 517 | "version": "1.0.0", 518 | "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", 519 | "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" 520 | } 521 | } 522 | }, 523 | "glob": { 524 | "version": "7.1.6", 525 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", 526 | "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", 527 | "requires": { 528 | "fs.realpath": "^1.0.0", 529 | "inflight": "^1.0.4", 530 | "inherits": "2", 531 | "minimatch": "^3.0.4", 532 | "once": "^1.3.0", 533 | "path-is-absolute": "^1.0.0" 534 | } 535 | }, 536 | "google-auth-library": { 537 | "version": "5.10.1", 538 | "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-5.10.1.tgz", 539 | "integrity": "sha512-rOlaok5vlpV9rSiUu5EpR0vVpc+PhN62oF4RyX/6++DG1VsaulAFEMlDYBLjJDDPI6OcNOCGAKy9UVB/3NIDXg==", 540 | "requires": { 541 | "arrify": "^2.0.0", 542 | "base64-js": "^1.3.0", 543 | "ecdsa-sig-formatter": "^1.0.11", 544 | "fast-text-encoding": "^1.0.0", 545 | "gaxios": "^2.1.0", 546 | "gcp-metadata": "^3.4.0", 547 | "gtoken": "^4.1.0", 548 | "jws": "^4.0.0", 549 | "lru-cache": "^5.0.0" 550 | } 551 | }, 552 | "google-p12-pem": { 553 | "version": "2.0.4", 554 | "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-2.0.4.tgz", 555 | "integrity": "sha512-S4blHBQWZRnEW44OcR7TL9WR+QCqByRvhNDZ/uuQfpxywfupikf/miba8js1jZi6ZOGv5slgSuoshCWh6EMDzg==", 556 | "requires": { 557 | "node-forge": "^0.9.0" 558 | } 559 | }, 560 | "google-proto-files": { 561 | "version": "1.1.2", 562 | "resolved": "https://registry.npmjs.org/google-proto-files/-/google-proto-files-1.1.2.tgz", 563 | "integrity": "sha512-j5z6RH5Ke50T0vAgSQsSvsIhJzgXp5Q3v1hWnqf+8TFFixpgjZFQMQhRheglYE23BTfxL3LL1UIXdx/obhixiQ==", 564 | "requires": { 565 | "protobufjs": "^6.8.0", 566 | "walkdir": "^0.4.0" 567 | } 568 | }, 569 | "graceful-fs": { 570 | "version": "4.2.3", 571 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", 572 | "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==" 573 | }, 574 | "grpc": { 575 | "version": "1.24.2", 576 | "resolved": "https://registry.npmjs.org/grpc/-/grpc-1.24.2.tgz", 577 | "integrity": "sha512-EG3WH6AWMVvAiV15d+lr+K77HJ/KV/3FvMpjKjulXHbTwgDZkhkcWbwhxFAoTdxTkQvy0WFcO3Nog50QBbHZWw==", 578 | "requires": { 579 | "@types/bytebuffer": "^5.0.40", 580 | "lodash.camelcase": "^4.3.0", 581 | "lodash.clone": "^4.5.0", 582 | "nan": "^2.13.2", 583 | "node-pre-gyp": "^0.14.0", 584 | "protobufjs": "^5.0.3" 585 | }, 586 | "dependencies": { 587 | "abbrev": { 588 | "version": "1.1.1", 589 | "bundled": true 590 | }, 591 | "ansi-regex": { 592 | "version": "2.1.1", 593 | "bundled": true 594 | }, 595 | "aproba": { 596 | "version": "1.2.0", 597 | "bundled": true 598 | }, 599 | "are-we-there-yet": { 600 | "version": "1.1.5", 601 | "bundled": true, 602 | "requires": { 603 | "delegates": "^1.0.0", 604 | "readable-stream": "^2.0.6" 605 | } 606 | }, 607 | "balanced-match": { 608 | "version": "1.0.0", 609 | "bundled": true 610 | }, 611 | "brace-expansion": { 612 | "version": "1.1.11", 613 | "bundled": true, 614 | "requires": { 615 | "balanced-match": "^1.0.0", 616 | "concat-map": "0.0.1" 617 | } 618 | }, 619 | "chownr": { 620 | "version": "1.1.3", 621 | "bundled": true 622 | }, 623 | "code-point-at": { 624 | "version": "1.1.0", 625 | "bundled": true 626 | }, 627 | "concat-map": { 628 | "version": "0.0.1", 629 | "bundled": true 630 | }, 631 | "console-control-strings": { 632 | "version": "1.1.0", 633 | "bundled": true 634 | }, 635 | "core-util-is": { 636 | "version": "1.0.2", 637 | "bundled": true 638 | }, 639 | "debug": { 640 | "version": "3.2.6", 641 | "bundled": true, 642 | "requires": { 643 | "ms": "^2.1.1" 644 | } 645 | }, 646 | "deep-extend": { 647 | "version": "0.6.0", 648 | "bundled": true 649 | }, 650 | "delegates": { 651 | "version": "1.0.0", 652 | "bundled": true 653 | }, 654 | "detect-libc": { 655 | "version": "1.0.3", 656 | "bundled": true 657 | }, 658 | "fs-minipass": { 659 | "version": "1.2.7", 660 | "bundled": true, 661 | "requires": { 662 | "minipass": "^2.6.0" 663 | } 664 | }, 665 | "fs.realpath": { 666 | "version": "1.0.0", 667 | "bundled": true 668 | }, 669 | "gauge": { 670 | "version": "2.7.4", 671 | "bundled": true, 672 | "requires": { 673 | "aproba": "^1.0.3", 674 | "console-control-strings": "^1.0.0", 675 | "has-unicode": "^2.0.0", 676 | "object-assign": "^4.1.0", 677 | "signal-exit": "^3.0.0", 678 | "string-width": "^1.0.1", 679 | "strip-ansi": "^3.0.1", 680 | "wide-align": "^1.1.0" 681 | } 682 | }, 683 | "glob": { 684 | "version": "7.1.4", 685 | "bundled": true, 686 | "requires": { 687 | "fs.realpath": "^1.0.0", 688 | "inflight": "^1.0.4", 689 | "inherits": "2", 690 | "minimatch": "^3.0.4", 691 | "once": "^1.3.0", 692 | "path-is-absolute": "^1.0.0" 693 | } 694 | }, 695 | "has-unicode": { 696 | "version": "2.0.1", 697 | "bundled": true 698 | }, 699 | "iconv-lite": { 700 | "version": "0.4.24", 701 | "bundled": true, 702 | "requires": { 703 | "safer-buffer": ">= 2.1.2 < 3" 704 | } 705 | }, 706 | "ignore-walk": { 707 | "version": "3.0.3", 708 | "bundled": true, 709 | "requires": { 710 | "minimatch": "^3.0.4" 711 | } 712 | }, 713 | "inflight": { 714 | "version": "1.0.6", 715 | "bundled": true, 716 | "requires": { 717 | "once": "^1.3.0", 718 | "wrappy": "1" 719 | } 720 | }, 721 | "inherits": { 722 | "version": "2.0.4", 723 | "bundled": true 724 | }, 725 | "ini": { 726 | "version": "1.3.5", 727 | "bundled": true 728 | }, 729 | "is-fullwidth-code-point": { 730 | "version": "1.0.0", 731 | "bundled": true, 732 | "requires": { 733 | "number-is-nan": "^1.0.0" 734 | } 735 | }, 736 | "isarray": { 737 | "version": "1.0.0", 738 | "bundled": true 739 | }, 740 | "minimatch": { 741 | "version": "3.0.4", 742 | "bundled": true, 743 | "requires": { 744 | "brace-expansion": "^1.1.7" 745 | } 746 | }, 747 | "minimist": { 748 | "version": "1.2.0", 749 | "bundled": true 750 | }, 751 | "minipass": { 752 | "version": "2.9.0", 753 | "bundled": true, 754 | "requires": { 755 | "safe-buffer": "^5.1.2", 756 | "yallist": "^3.0.0" 757 | } 758 | }, 759 | "minizlib": { 760 | "version": "1.3.3", 761 | "bundled": true, 762 | "requires": { 763 | "minipass": "^2.9.0" 764 | } 765 | }, 766 | "mkdirp": { 767 | "version": "0.5.1", 768 | "bundled": true, 769 | "requires": { 770 | "minimist": "0.0.8" 771 | }, 772 | "dependencies": { 773 | "minimist": { 774 | "version": "0.0.8", 775 | "bundled": true 776 | } 777 | } 778 | }, 779 | "ms": { 780 | "version": "2.1.2", 781 | "bundled": true 782 | }, 783 | "needle": { 784 | "version": "2.4.0", 785 | "bundled": true, 786 | "requires": { 787 | "debug": "^3.2.6", 788 | "iconv-lite": "^0.4.4", 789 | "sax": "^1.2.4" 790 | } 791 | }, 792 | "node-pre-gyp": { 793 | "version": "0.14.0", 794 | "bundled": true, 795 | "requires": { 796 | "detect-libc": "^1.0.2", 797 | "mkdirp": "^0.5.1", 798 | "needle": "^2.2.1", 799 | "nopt": "^4.0.1", 800 | "npm-packlist": "^1.1.6", 801 | "npmlog": "^4.0.2", 802 | "rc": "^1.2.7", 803 | "rimraf": "^2.6.1", 804 | "semver": "^5.3.0", 805 | "tar": "^4.4.2" 806 | } 807 | }, 808 | "nopt": { 809 | "version": "4.0.1", 810 | "bundled": true, 811 | "requires": { 812 | "abbrev": "1", 813 | "osenv": "^0.1.4" 814 | } 815 | }, 816 | "npm-bundled": { 817 | "version": "1.0.6", 818 | "bundled": true 819 | }, 820 | "npm-packlist": { 821 | "version": "1.4.6", 822 | "bundled": true, 823 | "requires": { 824 | "ignore-walk": "^3.0.1", 825 | "npm-bundled": "^1.0.1" 826 | } 827 | }, 828 | "npmlog": { 829 | "version": "4.1.2", 830 | "bundled": true, 831 | "requires": { 832 | "are-we-there-yet": "~1.1.2", 833 | "console-control-strings": "~1.1.0", 834 | "gauge": "~2.7.3", 835 | "set-blocking": "~2.0.0" 836 | } 837 | }, 838 | "number-is-nan": { 839 | "version": "1.0.1", 840 | "bundled": true 841 | }, 842 | "object-assign": { 843 | "version": "4.1.1", 844 | "bundled": true 845 | }, 846 | "once": { 847 | "version": "1.4.0", 848 | "bundled": true, 849 | "requires": { 850 | "wrappy": "1" 851 | } 852 | }, 853 | "os-homedir": { 854 | "version": "1.0.2", 855 | "bundled": true 856 | }, 857 | "os-tmpdir": { 858 | "version": "1.0.2", 859 | "bundled": true 860 | }, 861 | "osenv": { 862 | "version": "0.1.5", 863 | "bundled": true, 864 | "requires": { 865 | "os-homedir": "^1.0.0", 866 | "os-tmpdir": "^1.0.0" 867 | } 868 | }, 869 | "path-is-absolute": { 870 | "version": "1.0.1", 871 | "bundled": true 872 | }, 873 | "process-nextick-args": { 874 | "version": "2.0.1", 875 | "bundled": true 876 | }, 877 | "protobufjs": { 878 | "version": "5.0.3", 879 | "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-5.0.3.tgz", 880 | "integrity": "sha512-55Kcx1MhPZX0zTbVosMQEO5R6/rikNXd9b6RQK4KSPcrSIIwoXTtebIczUrXlwaSrbz4x8XUVThGPob1n8I4QA==", 881 | "requires": { 882 | "ascli": "~1", 883 | "bytebuffer": "~5", 884 | "glob": "^7.0.5", 885 | "yargs": "^3.10.0" 886 | } 887 | }, 888 | "rc": { 889 | "version": "1.2.8", 890 | "bundled": true, 891 | "requires": { 892 | "deep-extend": "^0.6.0", 893 | "ini": "~1.3.0", 894 | "minimist": "^1.2.0", 895 | "strip-json-comments": "~2.0.1" 896 | } 897 | }, 898 | "readable-stream": { 899 | "version": "2.3.6", 900 | "bundled": true, 901 | "requires": { 902 | "core-util-is": "~1.0.0", 903 | "inherits": "~2.0.3", 904 | "isarray": "~1.0.0", 905 | "process-nextick-args": "~2.0.0", 906 | "safe-buffer": "~5.1.1", 907 | "string_decoder": "~1.1.1", 908 | "util-deprecate": "~1.0.1" 909 | } 910 | }, 911 | "rimraf": { 912 | "version": "2.7.1", 913 | "bundled": true, 914 | "requires": { 915 | "glob": "^7.1.3" 916 | } 917 | }, 918 | "safe-buffer": { 919 | "version": "5.1.2", 920 | "bundled": true 921 | }, 922 | "safer-buffer": { 923 | "version": "2.1.2", 924 | "bundled": true 925 | }, 926 | "sax": { 927 | "version": "1.2.4", 928 | "bundled": true 929 | }, 930 | "semver": { 931 | "version": "5.7.1", 932 | "bundled": true 933 | }, 934 | "set-blocking": { 935 | "version": "2.0.0", 936 | "bundled": true 937 | }, 938 | "signal-exit": { 939 | "version": "3.0.2", 940 | "bundled": true 941 | }, 942 | "string-width": { 943 | "version": "1.0.2", 944 | "bundled": true, 945 | "requires": { 946 | "code-point-at": "^1.0.0", 947 | "is-fullwidth-code-point": "^1.0.0", 948 | "strip-ansi": "^3.0.0" 949 | } 950 | }, 951 | "string_decoder": { 952 | "version": "1.1.1", 953 | "bundled": true, 954 | "requires": { 955 | "safe-buffer": "~5.1.0" 956 | } 957 | }, 958 | "strip-ansi": { 959 | "version": "3.0.1", 960 | "bundled": true, 961 | "requires": { 962 | "ansi-regex": "^2.0.0" 963 | } 964 | }, 965 | "strip-json-comments": { 966 | "version": "2.0.1", 967 | "bundled": true 968 | }, 969 | "tar": { 970 | "version": "4.4.13", 971 | "bundled": true, 972 | "requires": { 973 | "chownr": "^1.1.1", 974 | "fs-minipass": "^1.2.5", 975 | "minipass": "^2.8.6", 976 | "minizlib": "^1.2.1", 977 | "mkdirp": "^0.5.0", 978 | "safe-buffer": "^5.1.2", 979 | "yallist": "^3.0.3" 980 | } 981 | }, 982 | "util-deprecate": { 983 | "version": "1.0.2", 984 | "bundled": true 985 | }, 986 | "wide-align": { 987 | "version": "1.1.3", 988 | "bundled": true, 989 | "requires": { 990 | "string-width": "^1.0.2 || 2" 991 | } 992 | }, 993 | "wrappy": { 994 | "version": "1.0.2", 995 | "bundled": true 996 | }, 997 | "yallist": { 998 | "version": "3.1.1", 999 | "bundled": true 1000 | } 1001 | } 1002 | }, 1003 | "gtoken": { 1004 | "version": "4.1.4", 1005 | "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-4.1.4.tgz", 1006 | "integrity": "sha512-VxirzD0SWoFUo5p8RDP8Jt2AGyOmyYcT/pOUgDKJCK+iSw0TMqwrVfY37RXTNmoKwrzmDHSk0GMT9FsgVmnVSA==", 1007 | "requires": { 1008 | "gaxios": "^2.1.0", 1009 | "google-p12-pem": "^2.0.0", 1010 | "jws": "^4.0.0", 1011 | "mime": "^2.2.0" 1012 | } 1013 | }, 1014 | "har-schema": { 1015 | "version": "1.0.5", 1016 | "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", 1017 | "integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=" 1018 | }, 1019 | "har-validator": { 1020 | "version": "4.2.1", 1021 | "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", 1022 | "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=", 1023 | "requires": { 1024 | "ajv": "^4.9.1", 1025 | "har-schema": "^1.0.5" 1026 | } 1027 | }, 1028 | "has-unicode": { 1029 | "version": "2.0.1", 1030 | "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", 1031 | "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" 1032 | }, 1033 | "hawk": { 1034 | "version": "3.1.3", 1035 | "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", 1036 | "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", 1037 | "requires": { 1038 | "boom": "2.x.x", 1039 | "cryptiles": "2.x.x", 1040 | "hoek": "2.x.x", 1041 | "sntp": "1.x.x" 1042 | } 1043 | }, 1044 | "hoek": { 1045 | "version": "2.16.3", 1046 | "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", 1047 | "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=" 1048 | }, 1049 | "http-signature": { 1050 | "version": "1.1.1", 1051 | "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", 1052 | "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", 1053 | "requires": { 1054 | "assert-plus": "^0.2.0", 1055 | "jsprim": "^1.2.2", 1056 | "sshpk": "^1.7.0" 1057 | } 1058 | }, 1059 | "https-proxy-agent": { 1060 | "version": "5.0.0", 1061 | "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", 1062 | "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", 1063 | "requires": { 1064 | "agent-base": "6", 1065 | "debug": "4" 1066 | } 1067 | }, 1068 | "inflight": { 1069 | "version": "1.0.6", 1070 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 1071 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 1072 | "requires": { 1073 | "once": "^1.3.0", 1074 | "wrappy": "1" 1075 | } 1076 | }, 1077 | "inherits": { 1078 | "version": "2.0.4", 1079 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 1080 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 1081 | }, 1082 | "ini": { 1083 | "version": "1.3.5", 1084 | "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", 1085 | "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" 1086 | }, 1087 | "invert-kv": { 1088 | "version": "1.0.0", 1089 | "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", 1090 | "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" 1091 | }, 1092 | "is-fullwidth-code-point": { 1093 | "version": "1.0.0", 1094 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", 1095 | "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", 1096 | "requires": { 1097 | "number-is-nan": "^1.0.0" 1098 | } 1099 | }, 1100 | "is-stream": { 1101 | "version": "2.0.0", 1102 | "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", 1103 | "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" 1104 | }, 1105 | "is-typedarray": { 1106 | "version": "1.0.0", 1107 | "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", 1108 | "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" 1109 | }, 1110 | "isarray": { 1111 | "version": "1.0.0", 1112 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 1113 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" 1114 | }, 1115 | "isstream": { 1116 | "version": "0.1.2", 1117 | "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", 1118 | "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" 1119 | }, 1120 | "jsbn": { 1121 | "version": "0.1.1", 1122 | "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", 1123 | "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" 1124 | }, 1125 | "json-bigint": { 1126 | "version": "0.3.0", 1127 | "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-0.3.0.tgz", 1128 | "integrity": "sha1-DM2RLEuCcNBfBW+9E4FLU9OCWx4=", 1129 | "requires": { 1130 | "bignumber.js": "^7.0.0" 1131 | } 1132 | }, 1133 | "json-schema": { 1134 | "version": "0.2.3", 1135 | "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", 1136 | "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" 1137 | }, 1138 | "json-stable-stringify": { 1139 | "version": "1.0.1", 1140 | "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", 1141 | "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", 1142 | "requires": { 1143 | "jsonify": "~0.0.0" 1144 | } 1145 | }, 1146 | "json-stringify-safe": { 1147 | "version": "5.0.1", 1148 | "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", 1149 | "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" 1150 | }, 1151 | "jsonify": { 1152 | "version": "0.0.0", 1153 | "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", 1154 | "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=" 1155 | }, 1156 | "jsprim": { 1157 | "version": "1.4.1", 1158 | "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", 1159 | "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", 1160 | "requires": { 1161 | "assert-plus": "1.0.0", 1162 | "extsprintf": "1.3.0", 1163 | "json-schema": "0.2.3", 1164 | "verror": "1.10.0" 1165 | }, 1166 | "dependencies": { 1167 | "assert-plus": { 1168 | "version": "1.0.0", 1169 | "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", 1170 | "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" 1171 | } 1172 | } 1173 | }, 1174 | "jwa": { 1175 | "version": "2.0.0", 1176 | "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", 1177 | "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", 1178 | "requires": { 1179 | "buffer-equal-constant-time": "1.0.1", 1180 | "ecdsa-sig-formatter": "1.0.11", 1181 | "safe-buffer": "^5.0.1" 1182 | } 1183 | }, 1184 | "jws": { 1185 | "version": "4.0.0", 1186 | "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", 1187 | "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", 1188 | "requires": { 1189 | "jwa": "^2.0.0", 1190 | "safe-buffer": "^5.0.1" 1191 | } 1192 | }, 1193 | "lcid": { 1194 | "version": "1.0.0", 1195 | "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", 1196 | "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", 1197 | "requires": { 1198 | "invert-kv": "^1.0.0" 1199 | } 1200 | }, 1201 | "lodash.camelcase": { 1202 | "version": "4.3.0", 1203 | "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", 1204 | "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=" 1205 | }, 1206 | "lodash.clone": { 1207 | "version": "4.5.0", 1208 | "resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-4.5.0.tgz", 1209 | "integrity": "sha1-GVhwRQ9aExkkeN9Lw9I9LeoZB7Y=" 1210 | }, 1211 | "long": { 1212 | "version": "4.0.0", 1213 | "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", 1214 | "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" 1215 | }, 1216 | "lru-cache": { 1217 | "version": "5.1.1", 1218 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", 1219 | "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", 1220 | "requires": { 1221 | "yallist": "^3.0.2" 1222 | } 1223 | }, 1224 | "mime": { 1225 | "version": "2.4.4", 1226 | "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", 1227 | "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==" 1228 | }, 1229 | "mime-db": { 1230 | "version": "1.43.0", 1231 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz", 1232 | "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==" 1233 | }, 1234 | "mime-types": { 1235 | "version": "2.1.26", 1236 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz", 1237 | "integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==", 1238 | "requires": { 1239 | "mime-db": "1.43.0" 1240 | } 1241 | }, 1242 | "minimatch": { 1243 | "version": "3.0.4", 1244 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 1245 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 1246 | "requires": { 1247 | "brace-expansion": "^1.1.7" 1248 | } 1249 | }, 1250 | "minimist": { 1251 | "version": "0.0.8", 1252 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 1253 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" 1254 | }, 1255 | "mkdirp": { 1256 | "version": "0.5.1", 1257 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 1258 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 1259 | "requires": { 1260 | "minimist": "0.0.8" 1261 | } 1262 | }, 1263 | "ms": { 1264 | "version": "2.1.2", 1265 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 1266 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 1267 | }, 1268 | "nan": { 1269 | "version": "2.14.0", 1270 | "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", 1271 | "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==" 1272 | }, 1273 | "node-fetch": { 1274 | "version": "2.6.0", 1275 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", 1276 | "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==" 1277 | }, 1278 | "node-forge": { 1279 | "version": "0.9.1", 1280 | "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.9.1.tgz", 1281 | "integrity": "sha512-G6RlQt5Sb4GMBzXvhfkeFmbqR6MzhtnT7VTHuLadjkii3rdYHNdw0m8zA4BTxVIh68FicCQ2NSUANpsqkr9jvQ==" 1282 | }, 1283 | "node-pre-gyp": { 1284 | "version": "0.6.39", 1285 | "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.6.39.tgz", 1286 | "integrity": "sha512-OsJV74qxnvz/AMGgcfZoDaeDXKD3oY3QVIbBmwszTFkRisTSXbMQyn4UWzUMOtA5SVhrBZOTp0wcoSBgfMfMmQ==", 1287 | "requires": { 1288 | "detect-libc": "^1.0.2", 1289 | "hawk": "3.1.3", 1290 | "mkdirp": "^0.5.1", 1291 | "nopt": "^4.0.1", 1292 | "npmlog": "^4.0.2", 1293 | "rc": "^1.1.7", 1294 | "request": "2.81.0", 1295 | "rimraf": "^2.6.1", 1296 | "semver": "^5.3.0", 1297 | "tar": "^2.2.1", 1298 | "tar-pack": "^3.4.0" 1299 | } 1300 | }, 1301 | "node-record-lpcm16": { 1302 | "version": "1.0.1", 1303 | "resolved": "https://registry.npmjs.org/node-record-lpcm16/-/node-record-lpcm16-1.0.1.tgz", 1304 | "integrity": "sha512-H75GMOP8ErnF67m21+qSgj4USnzv5RLfm7OkEItdIi+soNKoJZpMQPX6umM8Cn9nVPSgd/dBUtc1msst5MmABA==", 1305 | "requires": { 1306 | "debug": "^2.6.8" 1307 | }, 1308 | "dependencies": { 1309 | "debug": { 1310 | "version": "2.6.9", 1311 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 1312 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 1313 | "requires": { 1314 | "ms": "2.0.0" 1315 | } 1316 | }, 1317 | "ms": { 1318 | "version": "2.0.0", 1319 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 1320 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 1321 | } 1322 | } 1323 | }, 1324 | "nodejs-assistant": { 1325 | "version": "0.8.0", 1326 | "resolved": "https://registry.npmjs.org/nodejs-assistant/-/nodejs-assistant-0.8.0.tgz", 1327 | "integrity": "sha512-vOHGrXeqJZyIvv+LEKeTrR0e4hdGM9b8ACt3KwwXdT+w+XmrsLyGkkGr7/P4K5bBep4B+JBpJA73cIXsemJREA==", 1328 | "requires": { 1329 | "@grpc/proto-loader": "^0.5.3", 1330 | "google-auth-library": "^5.10.0", 1331 | "google-proto-files": "^1.1.2", 1332 | "grpc": "^1.24.2" 1333 | } 1334 | }, 1335 | "nopt": { 1336 | "version": "4.0.1", 1337 | "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", 1338 | "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", 1339 | "requires": { 1340 | "abbrev": "1", 1341 | "osenv": "^0.1.4" 1342 | } 1343 | }, 1344 | "npmlog": { 1345 | "version": "4.1.2", 1346 | "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", 1347 | "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", 1348 | "requires": { 1349 | "are-we-there-yet": "~1.1.2", 1350 | "console-control-strings": "~1.1.0", 1351 | "gauge": "~2.7.3", 1352 | "set-blocking": "~2.0.0" 1353 | } 1354 | }, 1355 | "number-is-nan": { 1356 | "version": "1.0.1", 1357 | "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", 1358 | "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" 1359 | }, 1360 | "oauth-sign": { 1361 | "version": "0.8.2", 1362 | "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", 1363 | "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" 1364 | }, 1365 | "object-assign": { 1366 | "version": "4.1.1", 1367 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 1368 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" 1369 | }, 1370 | "once": { 1371 | "version": "1.4.0", 1372 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1373 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 1374 | "requires": { 1375 | "wrappy": "1" 1376 | } 1377 | }, 1378 | "optjs": { 1379 | "version": "3.2.2", 1380 | "resolved": "https://registry.npmjs.org/optjs/-/optjs-3.2.2.tgz", 1381 | "integrity": "sha1-aabOicRCpEQDFBrS+bNwvVu29O4=" 1382 | }, 1383 | "os-homedir": { 1384 | "version": "1.0.2", 1385 | "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", 1386 | "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" 1387 | }, 1388 | "os-locale": { 1389 | "version": "1.4.0", 1390 | "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", 1391 | "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", 1392 | "requires": { 1393 | "lcid": "^1.0.0" 1394 | } 1395 | }, 1396 | "os-tmpdir": { 1397 | "version": "1.0.2", 1398 | "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", 1399 | "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" 1400 | }, 1401 | "osenv": { 1402 | "version": "0.1.5", 1403 | "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", 1404 | "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", 1405 | "requires": { 1406 | "os-homedir": "^1.0.0", 1407 | "os-tmpdir": "^1.0.0" 1408 | } 1409 | }, 1410 | "path-is-absolute": { 1411 | "version": "1.0.1", 1412 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 1413 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" 1414 | }, 1415 | "performance-now": { 1416 | "version": "0.2.0", 1417 | "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", 1418 | "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=" 1419 | }, 1420 | "process-nextick-args": { 1421 | "version": "2.0.1", 1422 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", 1423 | "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" 1424 | }, 1425 | "protobufjs": { 1426 | "version": "6.8.8", 1427 | "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.8.8.tgz", 1428 | "integrity": "sha512-AAmHtD5pXgZfi7GMpllpO3q1Xw1OYldr+dMUlAnffGTAhqkg72WdmSY71uKBF/JuyiKs8psYbtKrhi0ASCD8qw==", 1429 | "requires": { 1430 | "@protobufjs/aspromise": "^1.1.2", 1431 | "@protobufjs/base64": "^1.1.2", 1432 | "@protobufjs/codegen": "^2.0.4", 1433 | "@protobufjs/eventemitter": "^1.1.0", 1434 | "@protobufjs/fetch": "^1.1.0", 1435 | "@protobufjs/float": "^1.0.2", 1436 | "@protobufjs/inquire": "^1.1.0", 1437 | "@protobufjs/path": "^1.1.2", 1438 | "@protobufjs/pool": "^1.1.0", 1439 | "@protobufjs/utf8": "^1.1.0", 1440 | "@types/long": "^4.0.0", 1441 | "@types/node": "^10.1.0", 1442 | "long": "^4.0.0" 1443 | } 1444 | }, 1445 | "punycode": { 1446 | "version": "1.4.1", 1447 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", 1448 | "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" 1449 | }, 1450 | "qs": { 1451 | "version": "6.4.0", 1452 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", 1453 | "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=" 1454 | }, 1455 | "rc": { 1456 | "version": "1.2.8", 1457 | "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", 1458 | "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", 1459 | "requires": { 1460 | "deep-extend": "^0.6.0", 1461 | "ini": "~1.3.0", 1462 | "minimist": "^1.2.0", 1463 | "strip-json-comments": "~2.0.1" 1464 | }, 1465 | "dependencies": { 1466 | "minimist": { 1467 | "version": "1.2.0", 1468 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", 1469 | "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" 1470 | } 1471 | } 1472 | }, 1473 | "readable-stream": { 1474 | "version": "2.3.7", 1475 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", 1476 | "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", 1477 | "requires": { 1478 | "core-util-is": "~1.0.0", 1479 | "inherits": "~2.0.3", 1480 | "isarray": "~1.0.0", 1481 | "process-nextick-args": "~2.0.0", 1482 | "safe-buffer": "~5.1.1", 1483 | "string_decoder": "~1.1.1", 1484 | "util-deprecate": "~1.0.1" 1485 | }, 1486 | "dependencies": { 1487 | "safe-buffer": { 1488 | "version": "5.1.2", 1489 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 1490 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 1491 | } 1492 | } 1493 | }, 1494 | "request": { 1495 | "version": "2.81.0", 1496 | "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", 1497 | "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=", 1498 | "requires": { 1499 | "aws-sign2": "~0.6.0", 1500 | "aws4": "^1.2.1", 1501 | "caseless": "~0.12.0", 1502 | "combined-stream": "~1.0.5", 1503 | "extend": "~3.0.0", 1504 | "forever-agent": "~0.6.1", 1505 | "form-data": "~2.1.1", 1506 | "har-validator": "~4.2.1", 1507 | "hawk": "~3.1.3", 1508 | "http-signature": "~1.1.0", 1509 | "is-typedarray": "~1.0.0", 1510 | "isstream": "~0.1.2", 1511 | "json-stringify-safe": "~5.0.1", 1512 | "mime-types": "~2.1.7", 1513 | "oauth-sign": "~0.8.1", 1514 | "performance-now": "^0.2.0", 1515 | "qs": "~6.4.0", 1516 | "safe-buffer": "^5.0.1", 1517 | "stringstream": "~0.0.4", 1518 | "tough-cookie": "~2.3.0", 1519 | "tunnel-agent": "^0.6.0", 1520 | "uuid": "^3.0.0" 1521 | } 1522 | }, 1523 | "rimraf": { 1524 | "version": "2.7.1", 1525 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", 1526 | "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", 1527 | "requires": { 1528 | "glob": "^7.1.3" 1529 | } 1530 | }, 1531 | "safe-buffer": { 1532 | "version": "5.2.0", 1533 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", 1534 | "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" 1535 | }, 1536 | "safer-buffer": { 1537 | "version": "2.1.2", 1538 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 1539 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 1540 | }, 1541 | "semver": { 1542 | "version": "5.7.1", 1543 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", 1544 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" 1545 | }, 1546 | "set-blocking": { 1547 | "version": "2.0.0", 1548 | "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", 1549 | "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" 1550 | }, 1551 | "signal-exit": { 1552 | "version": "3.0.2", 1553 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", 1554 | "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" 1555 | }, 1556 | "snowboy": { 1557 | "version": "1.3.1", 1558 | "resolved": "https://registry.npmjs.org/snowboy/-/snowboy-1.3.1.tgz", 1559 | "integrity": "sha512-YvwJffy3b000IKCbqsyMWrQ6ga/qZRECgNoGb9mSkbFGX1QIG4Kv0S4pVEsUpbM1Gt4b+14BOufkdicZCsTt4g==", 1560 | "requires": { 1561 | "node-pre-gyp": "^0.6.30" 1562 | } 1563 | }, 1564 | "sntp": { 1565 | "version": "1.0.9", 1566 | "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", 1567 | "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", 1568 | "requires": { 1569 | "hoek": "2.x.x" 1570 | } 1571 | }, 1572 | "speaker": { 1573 | "version": "0.5.1", 1574 | "resolved": "https://registry.npmjs.org/speaker/-/speaker-0.5.1.tgz", 1575 | "integrity": "sha512-/9/ZGeGXLWNhEUqdXZmzgNr9QLNHWFkhKvwtJAKjlFx8mqGmyUPmBwiqw463KlRnUh7oXiHlmn6gqvkAEhtPnQ==", 1576 | "requires": { 1577 | "bindings": "^1.3.0", 1578 | "buffer-alloc": "^1.1.0", 1579 | "debug": "^4.0.0" 1580 | } 1581 | }, 1582 | "sshpk": { 1583 | "version": "1.16.1", 1584 | "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", 1585 | "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", 1586 | "requires": { 1587 | "asn1": "~0.2.3", 1588 | "assert-plus": "^1.0.0", 1589 | "bcrypt-pbkdf": "^1.0.0", 1590 | "dashdash": "^1.12.0", 1591 | "ecc-jsbn": "~0.1.1", 1592 | "getpass": "^0.1.1", 1593 | "jsbn": "~0.1.0", 1594 | "safer-buffer": "^2.0.2", 1595 | "tweetnacl": "~0.14.0" 1596 | }, 1597 | "dependencies": { 1598 | "assert-plus": { 1599 | "version": "1.0.0", 1600 | "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", 1601 | "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" 1602 | } 1603 | } 1604 | }, 1605 | "string-width": { 1606 | "version": "1.0.2", 1607 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", 1608 | "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", 1609 | "requires": { 1610 | "code-point-at": "^1.0.0", 1611 | "is-fullwidth-code-point": "^1.0.0", 1612 | "strip-ansi": "^3.0.0" 1613 | } 1614 | }, 1615 | "string_decoder": { 1616 | "version": "1.1.1", 1617 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", 1618 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", 1619 | "requires": { 1620 | "safe-buffer": "~5.1.0" 1621 | }, 1622 | "dependencies": { 1623 | "safe-buffer": { 1624 | "version": "5.1.2", 1625 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 1626 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 1627 | } 1628 | } 1629 | }, 1630 | "stringstream": { 1631 | "version": "0.0.6", 1632 | "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.6.tgz", 1633 | "integrity": "sha512-87GEBAkegbBcweToUrdzf3eLhWNg06FJTebl4BVJz/JgWy8CvEr9dRtX5qWphiynMSQlxxi+QqN0z5T32SLlhA==" 1634 | }, 1635 | "strip-ansi": { 1636 | "version": "3.0.1", 1637 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", 1638 | "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", 1639 | "requires": { 1640 | "ansi-regex": "^2.0.0" 1641 | } 1642 | }, 1643 | "strip-json-comments": { 1644 | "version": "2.0.1", 1645 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", 1646 | "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" 1647 | }, 1648 | "tar": { 1649 | "version": "2.2.2", 1650 | "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz", 1651 | "integrity": "sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==", 1652 | "requires": { 1653 | "block-stream": "*", 1654 | "fstream": "^1.0.12", 1655 | "inherits": "2" 1656 | } 1657 | }, 1658 | "tar-pack": { 1659 | "version": "3.4.1", 1660 | "resolved": "https://registry.npmjs.org/tar-pack/-/tar-pack-3.4.1.tgz", 1661 | "integrity": "sha512-PPRybI9+jM5tjtCbN2cxmmRU7YmqT3Zv/UDy48tAh2XRkLa9bAORtSWLkVc13+GJF+cdTh1yEnHEk3cpTaL5Kg==", 1662 | "requires": { 1663 | "debug": "^2.2.0", 1664 | "fstream": "^1.0.10", 1665 | "fstream-ignore": "^1.0.5", 1666 | "once": "^1.3.3", 1667 | "readable-stream": "^2.1.4", 1668 | "rimraf": "^2.5.1", 1669 | "tar": "^2.2.1", 1670 | "uid-number": "^0.0.6" 1671 | }, 1672 | "dependencies": { 1673 | "debug": { 1674 | "version": "2.6.9", 1675 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 1676 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 1677 | "requires": { 1678 | "ms": "2.0.0" 1679 | } 1680 | }, 1681 | "ms": { 1682 | "version": "2.0.0", 1683 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 1684 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 1685 | } 1686 | } 1687 | }, 1688 | "tough-cookie": { 1689 | "version": "2.3.4", 1690 | "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", 1691 | "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==", 1692 | "requires": { 1693 | "punycode": "^1.4.1" 1694 | } 1695 | }, 1696 | "tunnel-agent": { 1697 | "version": "0.6.0", 1698 | "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", 1699 | "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", 1700 | "requires": { 1701 | "safe-buffer": "^5.0.1" 1702 | } 1703 | }, 1704 | "tweetnacl": { 1705 | "version": "0.14.5", 1706 | "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", 1707 | "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" 1708 | }, 1709 | "uid-number": { 1710 | "version": "0.0.6", 1711 | "resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz", 1712 | "integrity": "sha1-DqEOgDXo61uOREnwbaHHMGY7qoE=" 1713 | }, 1714 | "util-deprecate": { 1715 | "version": "1.0.2", 1716 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 1717 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" 1718 | }, 1719 | "uuid": { 1720 | "version": "3.4.0", 1721 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", 1722 | "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" 1723 | }, 1724 | "verror": { 1725 | "version": "1.10.0", 1726 | "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", 1727 | "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", 1728 | "requires": { 1729 | "assert-plus": "^1.0.0", 1730 | "core-util-is": "1.0.2", 1731 | "extsprintf": "^1.2.0" 1732 | }, 1733 | "dependencies": { 1734 | "assert-plus": { 1735 | "version": "1.0.0", 1736 | "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", 1737 | "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" 1738 | } 1739 | } 1740 | }, 1741 | "walkdir": { 1742 | "version": "0.4.1", 1743 | "resolved": "https://registry.npmjs.org/walkdir/-/walkdir-0.4.1.tgz", 1744 | "integrity": "sha512-3eBwRyEln6E1MSzcxcVpQIhRG8Q1jLvEqRmCZqS3dsfXEDR/AhOF4d+jHg1qvDCpYaVRZjENPQyrVxAkQqxPgQ==" 1745 | }, 1746 | "wide-align": { 1747 | "version": "1.1.3", 1748 | "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", 1749 | "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", 1750 | "requires": { 1751 | "string-width": "^1.0.2 || 2" 1752 | } 1753 | }, 1754 | "window-size": { 1755 | "version": "0.1.4", 1756 | "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.4.tgz", 1757 | "integrity": "sha1-+OGqHuWlPsW/FR/6CXQqatdpeHY=" 1758 | }, 1759 | "wrap-ansi": { 1760 | "version": "2.1.0", 1761 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", 1762 | "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", 1763 | "requires": { 1764 | "string-width": "^1.0.1", 1765 | "strip-ansi": "^3.0.1" 1766 | } 1767 | }, 1768 | "wrappy": { 1769 | "version": "1.0.2", 1770 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1771 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" 1772 | }, 1773 | "y18n": { 1774 | "version": "3.2.1", 1775 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", 1776 | "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=" 1777 | }, 1778 | "yallist": { 1779 | "version": "3.1.1", 1780 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", 1781 | "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" 1782 | }, 1783 | "yargs": { 1784 | "version": "3.32.0", 1785 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.32.0.tgz", 1786 | "integrity": "sha1-AwiOnr+edWtpdRYR0qXvWRSCyZU=", 1787 | "requires": { 1788 | "camelcase": "^2.0.1", 1789 | "cliui": "^3.0.3", 1790 | "decamelize": "^1.1.1", 1791 | "os-locale": "^1.4.0", 1792 | "string-width": "^1.0.1", 1793 | "window-size": "^0.1.4", 1794 | "y18n": "^3.2.0" 1795 | } 1796 | } 1797 | } 1798 | } 1799 | -------------------------------------------------------------------------------- /samples/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nodejs-docs-samples-assistant", 3 | "version": "0.0.1", 4 | "private": true, 5 | "repository": { 6 | "type": "git", 7 | "url": "git+https://github.com/Dabolus/nodejs-assistant.git" 8 | }, 9 | "author": "Giorgio Garasto ", 10 | "license": "MIT", 11 | "dependencies": { 12 | "google-auth-library": "^5.10.1", 13 | "node-record-lpcm16": "^1.0.1", 14 | "nodejs-assistant": "^0.8.0", 15 | "snowboy": "^1.3.1-alpha.1", 16 | "speaker": "^0.5.1" 17 | }, 18 | "devDependencies": {} 19 | } 20 | -------------------------------------------------------------------------------- /samples/quickstart.js: -------------------------------------------------------------------------------- 1 | const { Assistant, AssistantLanguage } = require('nodejs-assistant'); 2 | const { getCredentials } = require('./credentials'); 3 | 4 | const startAssistant = async () => { 5 | const credentials = await getCredentials(); 6 | const assistant = new Assistant(credentials, { 7 | deviceId: 'test device', 8 | deviceModelId: 'test device model', 9 | locale: AssistantLanguage.ENGLISH, 10 | }); 11 | 12 | const response = await assistant.query('Hi!'); 13 | console.log('Assistant response: ', response); 14 | }; 15 | 16 | startAssistant(); 17 | -------------------------------------------------------------------------------- /samples/resources/common.res: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dabolus/nodejs-assistant/9bea870fee607a0adab534954f7b77e528c8af2b/samples/resources/common.res -------------------------------------------------------------------------------- /samples/resources/hey_google.pmdl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dabolus/nodejs-assistant/9bea870fee607a0adab534954f7b77e528c8af2b/samples/resources/hey_google.pmdl -------------------------------------------------------------------------------- /samples/resources/ok_google.pmdl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dabolus/nodejs-assistant/9bea870fee607a0adab534954f7b77e528c8af2b/samples/resources/ok_google.pmdl -------------------------------------------------------------------------------- /samples/text.js: -------------------------------------------------------------------------------- 1 | const { Assistant, AssistantLanguage } = require('nodejs-assistant'); 2 | const { getCredentials } = require('./credentials'); 3 | 4 | const startTextAssistant = async () => { 5 | const credentials = await getCredentials(); 6 | const assistant = new Assistant(credentials, { 7 | deviceId: 'test device', 8 | deviceModelId: 'test device model', 9 | locale: AssistantLanguage.ENGLISH, 10 | }); 11 | 12 | const conversation = assistant.startTextConversation(); 13 | 14 | conversation 15 | .on('message', (text) => console.log('Assistant: ', text)) 16 | .send('Hi!'); 17 | }; 18 | 19 | startTextAssistant(); 20 | -------------------------------------------------------------------------------- /src/assistant.ts: -------------------------------------------------------------------------------- 1 | import { JWTInput, UserRefreshClient } from 'google-auth-library'; 2 | import * as grpc from 'grpc'; 3 | import { AudioConversation } from './audio-conversation'; 4 | import { 5 | AssistantLanguage, 6 | AssistantOptions, 7 | AssistantResponse, 8 | mapAssistResponseToAssistantResponse, 9 | mapAssistantRequestToAssistRequest, 10 | } from './common'; 11 | import { 12 | AssistResponse, 13 | AudioInConfig, 14 | AudioInEncoding, 15 | AudioOutConfig, 16 | AudioOutEncoding, 17 | EmbeddedAssistant as EmbeddedAssistantInstance, 18 | embeddedAssistantPb as EmbeddedAssistant, 19 | } from './proto'; 20 | import { TextConversation } from './text-conversation'; 21 | 22 | export interface AssistantQueryOptions { 23 | conversationState?: Buffer; 24 | audioInConfig?: AudioInConfig; 25 | audioOutConfig?: AudioOutConfig; 26 | } 27 | 28 | /** 29 | * The base class to connect with the Assistant. 30 | * @author Giorgio Garasto 31 | * @license MIT 32 | * @class 33 | */ 34 | export class Assistant { 35 | public locale: AssistantLanguage; 36 | public deviceId: string; 37 | public deviceModelId: string; 38 | private _endpoint = 'embeddedassistant.googleapis.com'; 39 | private _client: EmbeddedAssistantInstance; 40 | 41 | /** 42 | * Creates a new connection with the assistant. 43 | * @param credentials - The credentials to use to authenticate with the Assistant. 44 | * @param options - Some additional (optional) options. 45 | * @param options.deviceId - The device ID to use in the conversations with the Assistant. 46 | * @param options.deviceModelId - The device model ID to use in the conversations with the Assistant. 47 | * @param options.locale - The locale to use in the conversations with the Assistant. 48 | * @constructor 49 | */ 50 | constructor( 51 | credentials: JWTInput, 52 | options: AssistantOptions = { 53 | deviceId: 'default', 54 | deviceModelId: 'default', 55 | locale: AssistantLanguage.ENGLISH, 56 | }, 57 | ) { 58 | this.locale = options.locale; 59 | this.deviceId = options.deviceId; 60 | this.deviceModelId = options.deviceModelId; 61 | this._client = this._createClient(credentials); 62 | } 63 | 64 | /** 65 | * Starts a new text conversation with the Assistant. 66 | * @param audioOutConfig - The audio output configuration. 67 | * @returns The new text conversation. 68 | */ 69 | public startTextConversation( 70 | audioOutConfig: AudioOutConfig = { 71 | encoding: AudioOutEncoding.LINEAR16, 72 | sampleRateHertz: 16000, 73 | volumePercentage: 100, 74 | }, 75 | ): TextConversation { 76 | return new TextConversation( 77 | this._client.assist(), 78 | this.deviceId, 79 | this.deviceModelId, 80 | this.locale, 81 | audioOutConfig, 82 | ); 83 | } 84 | 85 | /** 86 | * Starts a new audio conversation with the Assistant. 87 | * @param audioInConfig - The audio input configuration. 88 | * @param audioOutConfig - The audio output configuration. 89 | * @returns The new audio conversation. 90 | */ 91 | public startAudioConversation( 92 | audioInConfig: AudioInConfig = { 93 | encoding: AudioInEncoding.LINEAR16, 94 | sampleRateHertz: 16000, 95 | }, 96 | audioOutConfig: AudioOutConfig = { 97 | encoding: AudioOutEncoding.LINEAR16, 98 | sampleRateHertz: 16000, 99 | volumePercentage: 100, 100 | }, 101 | ): AudioConversation { 102 | return new AudioConversation( 103 | this._client.assist(), 104 | this.deviceId, 105 | this.deviceModelId, 106 | this.locale, 107 | audioInConfig, 108 | audioOutConfig, 109 | ); 110 | } 111 | 112 | /** 113 | * Sends a single text query to the Assistant and wait for its response. 114 | * @param textOrAudio - The text query or the audio buffer to send to the Assistant. 115 | * @param options - The additional query options. 116 | * @returns A promise that resolves to the Assistant response. 117 | */ 118 | public query( 119 | textOrAudio: string | Buffer, 120 | { 121 | conversationState, 122 | audioInConfig, 123 | audioOutConfig, 124 | }: AssistantQueryOptions = { 125 | audioInConfig: { 126 | encoding: AudioInEncoding.LINEAR16, 127 | sampleRateHertz: 16000, 128 | }, 129 | audioOutConfig: { 130 | encoding: AudioOutEncoding.LINEAR16, 131 | sampleRateHertz: 16000, 132 | volumePercentage: 100, 133 | }, 134 | }, 135 | ): Promise { 136 | const conversation = this._client.assist(); 137 | return new Promise((resolve, reject) => { 138 | const response: AssistantResponse = {}; 139 | conversation.on('data', (data: AssistResponse) => { 140 | const mappedData = mapAssistResponseToAssistantResponse(data); 141 | if (typeof mappedData.action !== 'undefined') { 142 | response.action = { 143 | ...response.action, 144 | ...mappedData.action, 145 | }; 146 | } 147 | if (typeof mappedData.actionOnGoogle !== 'undefined') { 148 | response.actionOnGoogle = { 149 | ...response.actionOnGoogle, 150 | ...mappedData.actionOnGoogle, 151 | }; 152 | } 153 | if (typeof mappedData.audio !== 'undefined') { 154 | response.audio = response.audio 155 | ? Buffer.concat([response.audio, mappedData.audio]) 156 | : mappedData.audio; 157 | } 158 | if (typeof mappedData.conversationEnded !== 'undefined') { 159 | response.conversationEnded = mappedData.conversationEnded; 160 | } 161 | if (typeof mappedData.conversationState !== 'undefined') { 162 | response.conversationState = mappedData.conversationState; 163 | } 164 | if (typeof mappedData.html !== 'undefined') { 165 | response.html = response.html 166 | ? `${response.html} ${mappedData.html}` 167 | : mappedData.html; 168 | } 169 | if (typeof mappedData.newVolume !== 'undefined') { 170 | response.newVolume = mappedData.newVolume; 171 | } 172 | if (typeof mappedData.speechRecognitionResults !== 'undefined') { 173 | response.speechRecognitionResults = [ 174 | ...(response.speechRecognitionResults || []), 175 | ...mappedData.speechRecognitionResults, 176 | ]; 177 | } 178 | if (typeof mappedData.text !== 'undefined') { 179 | response.text = response.text 180 | ? `${response.text} ${mappedData.text}` 181 | : mappedData.text; 182 | } 183 | }); 184 | conversation.on('end', () => { 185 | // Response ended, resolve with the whole response. 186 | resolve(response); 187 | }); 188 | conversation.on('error', reject); 189 | conversation.write( 190 | mapAssistantRequestToAssistRequest({ 191 | locale: this.locale, 192 | deviceId: this.deviceId, 193 | deviceModelId: this.deviceModelId, 194 | audioOutConfig, 195 | conversationState, 196 | html: true, 197 | ...(typeof textOrAudio === 'string' 198 | ? { 199 | text: textOrAudio, 200 | } 201 | : { 202 | audioInConfig, 203 | }), 204 | }), 205 | ); 206 | if (typeof textOrAudio !== 'string') { 207 | conversation.write( 208 | mapAssistantRequestToAssistRequest({ audio: textOrAudio }), 209 | ); 210 | } 211 | conversation.end(); 212 | }); 213 | } 214 | 215 | private _createClient(credentials: JWTInput): EmbeddedAssistantInstance { 216 | const sslCreds = grpc.credentials.createSsl(); 217 | const refresh = new UserRefreshClient(); 218 | refresh.fromJSON(credentials); 219 | const callCreds = grpc.credentials.createFromGoogleCredential(refresh); 220 | const combinedCreds = grpc.credentials.combineChannelCredentials( 221 | sslCreds, 222 | callCreds, 223 | ); 224 | const client = new EmbeddedAssistant(this._endpoint, combinedCreds); 225 | return client; 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /src/audio-conversation.ts: -------------------------------------------------------------------------------- 1 | import { ClientDuplexStream } from 'grpc'; 2 | import { AssistantLanguage } from './common'; 3 | import { Conversation } from './conversation'; 4 | import { 5 | AssistRequest, 6 | AssistResponse, 7 | AudioInConfig, 8 | AudioOutConfig, 9 | } from './proto'; 10 | 11 | /** 12 | * Represents an audio conversation with the Assistant. 13 | * @author Giorgio Garasto 14 | * @license MIT 15 | * @class 16 | */ 17 | export class AudioConversation extends Conversation { 18 | /** 19 | * Creates a new audio conversation. 20 | * @param _stream - The duplex stream to use to communicate with the Assistant SDK. 21 | * @param _deviceId - The device ID to use during this conversation. 22 | * @param _deviceModelId - The device model ID to use during this conversation. 23 | * @param locale - The locale to use during this conversation. 24 | * @param audioInConfig - The audio input configuration. 25 | * @param audioOutConfig - The audio output configuration. 26 | * @constructor 27 | */ 28 | constructor( 29 | _stream: ClientDuplexStream, 30 | _deviceId: string, 31 | _deviceModelId: string, 32 | locale: AssistantLanguage, 33 | audioInConfig: AudioInConfig, 34 | audioOutConfig: AudioOutConfig, 35 | ) { 36 | super(_stream, _deviceId, _deviceModelId, locale); 37 | this.sendRequest({ 38 | audioInConfig, 39 | audioOutConfig, 40 | deviceId: _deviceId, 41 | deviceModelId: _deviceModelId, 42 | isNewConversation: true, 43 | locale, 44 | }); 45 | } 46 | 47 | /** 48 | * Sends audio to the Assistant. 49 | * @param audio - The audio buffer to send to the Assistant. 50 | * @returns A boolean that tells whether the audio buffer was successfully sent or not. 51 | */ 52 | public send(audio: Buffer): boolean { 53 | return this.sendRequest({ audio }); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/common.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AssistRequest, 3 | AssistResponse, 4 | AssistResponseEventType, 5 | AudioInConfig, 6 | AudioOutConfig, 7 | LatLng, 8 | MicrophoneMode, 9 | ScreenMode, 10 | ScreenOutFormat, 11 | } from './proto'; 12 | 13 | export interface AssistantOptions { 14 | locale: AssistantLanguage; 15 | deviceId: string; 16 | deviceModelId: string; 17 | } 18 | 19 | export interface AssistantSpeechRecognitionResult { 20 | transcript: string; 21 | stability: number; 22 | } 23 | 24 | export type AssistantRequest = 25 | | { 26 | audio?: Buffer; 27 | audioInConfig?: never; 28 | audioOutConfig?: never; 29 | debug?: never; 30 | deviceId?: never; 31 | deviceModelId?: never; 32 | conversationState?: never; 33 | deviceLocation?: never; 34 | isNewConversation?: never; 35 | locale?: never; 36 | html?: never; 37 | text?: never; 38 | } 39 | | ({ 40 | audioOutConfig: AudioOutConfig; 41 | html?: boolean; 42 | conversationState?: Buffer; 43 | locale: AssistantLanguage; 44 | deviceLocation?: LatLng; 45 | isNewConversation?: boolean; 46 | deviceId: string; 47 | deviceModelId: string; 48 | debug?: boolean; 49 | audio?: never; 50 | } & ( 51 | | { 52 | audioInConfig: AudioInConfig; 53 | text?: never; 54 | } 55 | | { 56 | text: string; 57 | audioInConfig?: never; 58 | } 59 | )); 60 | 61 | // TODO: replace with more specific types 62 | export type Action = { [key: string]: unknown }; 63 | 64 | // TODO: replace with more specific types 65 | export type ActionOnGoogle = { [key: string]: unknown }; 66 | 67 | export interface AssistantResponse { 68 | action?: Action; 69 | actionOnGoogle?: ActionOnGoogle; 70 | audio?: Buffer; 71 | conversationEnded?: boolean; 72 | conversationState?: Buffer; 73 | html?: string; 74 | newVolume?: number; 75 | speechRecognitionResults?: AssistantSpeechRecognitionResult[]; 76 | text?: string; 77 | utteranceEnded?: boolean; 78 | } 79 | 80 | export enum AssistantLanguage { 81 | GERMAN = 'de-DE', 82 | ENGLISH_AU = 'en-AU', 83 | ENGLISH_CA = 'en-CA', 84 | ENGLISH_UK = 'en-GB', 85 | ENGLISH_IN = 'en-IN', 86 | ENGLISH_US = 'en-US', 87 | ENGLISH = 'en-US', 88 | FRENCH_CA = 'fr-CA', 89 | FRENCH_FR = 'fr-FR', 90 | FRENCH = 'fr-FR', 91 | ITALIAN = 'it-IT', 92 | JAPANESE = 'ja-JP', 93 | SPANISH_ES = 'es-ES', 94 | SPANISH_MX = 'es-MX', 95 | SPANISH = 'es-ES', 96 | KOREAN = 'ko-KR', 97 | PORTUGUESE = 'pt-BR', 98 | } 99 | 100 | export function mapAssistantRequestToAssistRequest({ 101 | audio, 102 | audioInConfig, 103 | audioOutConfig, 104 | debug, 105 | deviceId, 106 | deviceModelId, 107 | conversationState, 108 | deviceLocation, 109 | isNewConversation, 110 | locale, 111 | html, 112 | text, 113 | }: AssistantRequest): AssistRequest { 114 | if (audio) { 115 | return { audioIn: audio }; 116 | } 117 | return { 118 | config: { 119 | audioOutConfig, 120 | ...(html && { 121 | screenOutConfig: { 122 | screenMode: ScreenMode.PLAYING, 123 | }, 124 | }), 125 | deviceConfig: { 126 | deviceId, 127 | deviceModelId, 128 | }, 129 | dialogStateIn: { 130 | ...(conversationState && { conversationState }), 131 | languageCode: locale, 132 | ...(deviceLocation && { 133 | deviceLocation: { 134 | coordinates: deviceLocation, 135 | }, 136 | }), 137 | ...(isNewConversation && { isNewConversation }), 138 | }, 139 | ...(debug && { 140 | debugConfig: { 141 | returnDebugInfo: debug, 142 | }, 143 | }), 144 | ...(audioInConfig ? { audioInConfig } : { textQuery: text }), 145 | }, 146 | }; 147 | } 148 | 149 | export function mapAssistResponseToAssistantResponse({ 150 | audioOut, 151 | debugInfo, 152 | deviceAction, 153 | dialogStateOut, 154 | eventType, 155 | screenOut, 156 | speechResults, 157 | }: AssistResponse): AssistantResponse { 158 | return { 159 | ...(audioOut && { audio: audioOut.audioData }), 160 | ...(debugInfo && { 161 | actionOnGoogle: JSON.parse(debugInfo.aogAgentToAssistantJson), 162 | }), 163 | ...(deviceAction && { action: JSON.parse(deviceAction.deviceRequestJson) }), 164 | ...(dialogStateOut && { 165 | conversationEnded: 166 | dialogStateOut.microphoneMode === MicrophoneMode.CLOSE_MICROPHONE, 167 | conversationState: dialogStateOut.conversationState, 168 | text: dialogStateOut.supplementalDisplayText, 169 | ...(dialogStateOut.volumePercentage && { 170 | newVolume: dialogStateOut.volumePercentage, 171 | }), 172 | }), 173 | ...(screenOut && 174 | screenOut.format === ScreenOutFormat.HTML && { 175 | html: screenOut.data.toString(), 176 | }), 177 | ...(speechResults && 178 | speechResults.length && { speechRecognitionResults: speechResults }), 179 | utteranceEnded: eventType === AssistResponseEventType.END_OF_UTTERANCE, 180 | }; 181 | } 182 | -------------------------------------------------------------------------------- /src/conversation.ts: -------------------------------------------------------------------------------- 1 | import { EventEmitter } from 'events'; 2 | import { ClientDuplexStream } from 'grpc'; 3 | import { 4 | AssistantLanguage, 5 | AssistantRequest, 6 | AssistantResponse, 7 | AssistantSpeechRecognitionResult, 8 | mapAssistantRequestToAssistRequest, 9 | mapAssistResponseToAssistantResponse, 10 | } from './common'; 11 | import { AssistRequest, AssistResponse } from './proto'; 12 | 13 | /** 14 | * Represents a conversation with the Assistant. 15 | * @author Giorgio Garasto 16 | * @license MIT 17 | * @class 18 | */ 19 | export class Conversation extends EventEmitter { 20 | /** 21 | * Creates a new conversation. 22 | * @param _stream - The duplex stream to use to communicate with the Assistant SDK. 23 | * @param _deviceId - The device ID to use during this conversation. 24 | * @param _deviceModelId - The device model ID to use during this conversation. 25 | * @param locale - The locale to use during this conversation. 26 | * @constructor 27 | */ 28 | constructor( 29 | private _stream: ClientDuplexStream, 30 | protected _deviceId: string, 31 | protected _deviceModelId: string, 32 | public locale: AssistantLanguage, 33 | ) { 34 | super(); 35 | this._setupEvents(); 36 | } 37 | 38 | public sendRawRequest(rawRequest: AssistRequest): boolean { 39 | return this._stream.write(rawRequest); 40 | } 41 | 42 | public sendRequest(request: AssistantRequest): boolean { 43 | const finalRequest = request.audio 44 | ? request 45 | : ({ 46 | deviceId: this._deviceId, 47 | deviceModelId: this._deviceModelId, 48 | locale: this.locale, 49 | ...request, 50 | } as AssistantRequest); 51 | 52 | return this.sendRawRequest( 53 | mapAssistantRequestToAssistRequest(finalRequest), 54 | ); 55 | } 56 | 57 | public end(): Promise { 58 | return new Promise((resolve, reject) => { 59 | this._stream.end((err: Error) => { 60 | if (err) { 61 | reject(err); 62 | } else { 63 | resolve(); 64 | } 65 | }); 66 | }); 67 | } 68 | 69 | private _setupEvents(): void { 70 | this._stream 71 | .on('data', (data: AssistResponse) => { 72 | const mappedResponse = mapAssistResponseToAssistantResponse(data); 73 | this.emit('data', mappedResponse); 74 | if (mappedResponse.action) { 75 | this.emit('action', mappedResponse.action); 76 | } 77 | if (mappedResponse.actionOnGoogle) { 78 | this.emit('actionongoogle', mappedResponse.actionOnGoogle); 79 | } 80 | if (mappedResponse.audio) { 81 | this.emit('audio', mappedResponse.audio); 82 | } 83 | if (mappedResponse.conversationEnded) { 84 | this.emit('conversationend', mappedResponse); 85 | } 86 | if (mappedResponse.html) { 87 | this.emit('html', mappedResponse.html); 88 | } 89 | if (mappedResponse.newVolume) { 90 | this.emit('volume', mappedResponse.newVolume); 91 | } 92 | if (mappedResponse.speechRecognitionResults) { 93 | this.emit( 94 | 'speechrecognition', 95 | mappedResponse.speechRecognitionResults, 96 | ); 97 | } 98 | if (mappedResponse.text) { 99 | this.emit('message', mappedResponse.text); 100 | } 101 | if (mappedResponse.utteranceEnded) { 102 | this.emit('utteranceend', mappedResponse); 103 | } 104 | }) 105 | .on('end', () => this.emit('end')) 106 | .on('close', () => this.emit('close')) 107 | .on('error', err => this.emit('error', err)); 108 | } 109 | } 110 | 111 | /***** TYPINGS ************************************************************************************/ 112 | // The following are just some TypeScript goodies to make it much easier to write code 113 | // for both library users and developers. 114 | 115 | export type ConversationEvent = 116 | | 'data' 117 | | 'action' 118 | | 'actionongoogle' 119 | | 'audio' 120 | | 'conversationend' 121 | | 'message' 122 | | 'html' 123 | | 'volume' 124 | | 'speechrecognition' 125 | | 'utteranceend'; 126 | 127 | export declare interface Conversation { 128 | /** 129 | * Adds an event listener that will be triggered when the Assistant sends any type of data. 130 | * 131 | * @param event - The 'data' event. 132 | * @param listener - A callback that will receive the Assistant data as param. 133 | * @returns The conversation. 134 | */ 135 | addListener(event: 'data', listener: (data: AssistantResponse) => void): this; 136 | 137 | /** 138 | * Adds an event listener that will be triggered if the user has triggered a Device Action. 139 | * For example, a device which supports the query *Turn on the light* would receive an 140 | * object containing the semantics of the request. 141 | * 142 | * @param event - The 'action' event. 143 | * @param listener - A callback that will receive the object containing the semantics of the request as param. 144 | * @returns The conversation. 145 | */ 146 | addListener(event: 'action', listener: (action: unknown) => void): this; 147 | 148 | /** 149 | * Adds an event listener that will be triggered with the original response from an 150 | * Action-on-Google agent to Google server. To be able to receive this data, the conversation 151 | * has to be in debug mode, the request maker has to own the AoG project and the AoG project 152 | * has to be in preview mode. 153 | * 154 | * @param event - The 'actionongoogle' event. 155 | * @param listener - A callback that will receive the original response from the AoG agent as param. 156 | * @returns The conversation. 157 | */ 158 | addListener( 159 | event: 'actionongoogle', 160 | listener: (actionOnGoogle: unknown) => void, 161 | ): this; 162 | 163 | /** 164 | * Adds an event listener that will be triggered when the Assistant sends an audio message. 165 | * 166 | * @param event - The 'audio' event. 167 | * @param listener - A callback that will receive the Assistant audio message as param. 168 | * @returns The conversation. 169 | */ 170 | addListener(event: 'audio', listener: (audio: Buffer) => void): this; 171 | 172 | /** 173 | * Adds an event listener that will be triggered when the conversation with the Assistant ends. 174 | * 175 | * @param event - The 'conversationend' event. 176 | * @param listener - A callback that will receive the latest Assistant response as param. 177 | * @returns The conversation. 178 | */ 179 | addListener( 180 | event: 'conversationend', 181 | listener: (latestData: AssistantResponse) => void, 182 | ): this; 183 | 184 | /** 185 | * Adds an event listener that will be triggered when the Assistant sends a text message. 186 | * 187 | * @param event - The 'message' event. 188 | * @param listener - A callback that will receive the Assistant text message as param. 189 | * @returns The conversation. 190 | */ 191 | addListener(event: 'message', listener: (text: string) => void): this; 192 | 193 | /** 194 | * Adds an event listener that will be triggered when the Assistant sends HTML data. 195 | * 196 | * @param event - The 'html' event. 197 | * @param listener - A callback that will receive the HTML data as param. 198 | * @returns The conversation. 199 | */ 200 | addListener(event: 'html', listener: (html: string) => void): this; 201 | 202 | /** 203 | * Adds an event listener that will be triggered when the Assistant sends the new device volume level. 204 | * 205 | * @param event - The 'volume' event. 206 | * @param listener - A callback that will receive the new device volume level as param. 207 | * @returns The conversation. 208 | */ 209 | addListener(event: 'volume', listener: (newVolume: number) => void): this; 210 | 211 | /** 212 | * Adds an event listener that will be triggered with the Assistant speech recognition results. 213 | * 214 | * @param event - The 'speechrecognition' event. 215 | * @param listener - A callback that will receive the Assistant speech recognition results as param. 216 | * @returns The conversation. 217 | */ 218 | addListener( 219 | event: 'speechrecognition', 220 | listener: ( 221 | speechRecognitionResults: AssistantSpeechRecognitionResult[], 222 | ) => void, 223 | ): this; 224 | 225 | /** 226 | * Adds an event listener that will be triggered when the Assistant detects the end of the user's 227 | * speech utterance and expects no additional speech. Therefore, it will not process additional audio 228 | * (although it may subsequently return additional results). 229 | * 230 | * @param event - The 'utteranceend' event. 231 | * @param listener - A callback that will receive the latest Assistant response as param. 232 | * @returns The conversation. 233 | */ 234 | addListener( 235 | event: 'utteranceend', 236 | listener: (latestData: AssistantResponse) => void, 237 | ): this; 238 | 239 | /** 240 | * Adds an event listener that will be triggered when the Assistant closes the connection, 241 | * thus not sending any more data. 242 | * 243 | * @param event - The 'end' event. 244 | * @param listener - A callback with no params. 245 | * @returns The conversation. 246 | */ 247 | addListener(event: 'end', listener: () => void): this; 248 | 249 | /** 250 | * Adds an event listener that will be triggered when the connection with the Assistant is 251 | * fully closed. 252 | * 253 | * @param event - The 'end' event. 254 | * @param listener - A callback with no params. 255 | * @returns The conversation. 256 | */ 257 | addListener(event: 'close', listener: () => void): this; 258 | 259 | /** 260 | * Adds an event listener that will be triggered when any type of error occurs. 261 | * 262 | * @param event - The 'error' event. 263 | * @param listener - A callback that will receive the error as param. 264 | * @returns The conversation. 265 | */ 266 | addListener(event: 'error', listener: (error: Error) => void): this; 267 | 268 | /** 269 | * Adds an event listener that will be triggered when the Assistant sends any type of data. 270 | * 271 | * @param event - The 'data' event. 272 | * @param listener - A callback that will receive the Assistant data as param. 273 | * @returns The conversation. 274 | */ 275 | on(event: 'data', listener: (data: AssistantResponse) => void): this; 276 | 277 | /** 278 | * Adds an event listener that will be triggered if the user has triggered a Device Action. 279 | * For example, a device which supports the query *Turn on the light* would receive an 280 | * object containing the semantics of the request. 281 | * 282 | * @param event - The 'action' event. 283 | * @param listener - A callback that will receive the object containing the semantics of the request as param. 284 | * @returns The conversation. 285 | */ 286 | on(event: 'action', listener: (action: unknown) => void): this; 287 | 288 | /** 289 | * Adds an event listener that will be triggered with the original response from an 290 | * Action-on-Google agent to Google server. To be able to receive this data, the conversation 291 | * has to be in debug mode, the request maker has to own the AoG project and the AoG project 292 | * has to be in preview mode. 293 | * 294 | * @param event - The 'actionongoogle' event. 295 | * @param listener - A callback that will receive the original response from the AoG agent as param. 296 | * @returns The conversation. 297 | */ 298 | on( 299 | event: 'actionongoogle', 300 | listener: (actionOnGoogle: unknown) => void, 301 | ): this; 302 | 303 | /** 304 | * Adds an event listener that will be triggered when the Assistant sends an audio message. 305 | * 306 | * @param event - The 'audio' event. 307 | * @param listener - A callback that will receive the Assistant audio message as param. 308 | * @returns The conversation. 309 | */ 310 | on(event: 'audio', listener: (audio: Buffer) => void): this; 311 | 312 | /** 313 | * Adds an event listener that will be triggered when the conversation with the Assistant ends. 314 | * 315 | * @param event - The 'conversationend' event. 316 | * @param listener - A callback that will receive the latest Assistant response as param. 317 | * @returns The conversation. 318 | */ 319 | on( 320 | event: 'conversationend', 321 | listener: (latestData: AssistantResponse) => void, 322 | ): this; 323 | 324 | /** 325 | * Adds an event listener that will be triggered when the Assistant sends a text message. 326 | * 327 | * @param event - The 'message' event. 328 | * @param listener - A callback that will receive the Assistant text message as param. 329 | * @returns The conversation. 330 | */ 331 | on(event: 'message', listener: (text: string) => void): this; 332 | 333 | /** 334 | * Adds an event listener that will be triggered when the Assistant sends HTML data. 335 | * 336 | * @param event - The 'html' event. 337 | * @param listener - A callback that will receive the HTML data as param. 338 | * @returns The conversation. 339 | */ 340 | on(event: 'html', listener: (html: string) => void): this; 341 | 342 | /** 343 | * Adds an event listener that will be triggered when the Assistant sends the new device volume level. 344 | * 345 | * @param event - The 'volume' event. 346 | * @param listener - A callback that will receive the new device volume level as param. 347 | * @returns The conversation. 348 | */ 349 | on(event: 'volume', listener: (newVolume: number) => void): this; 350 | 351 | /** 352 | * Adds an event listener that will be triggered with the Assistant speech recognition results. 353 | * 354 | * @param event - The 'speechrecognition' event. 355 | * @param listener - A callback that will receive the Assistant speech recognition results as param. 356 | * @returns The conversation. 357 | */ 358 | on( 359 | event: 'speechrecognition', 360 | listener: ( 361 | speechRecognitionResults: AssistantSpeechRecognitionResult[], 362 | ) => void, 363 | ): this; 364 | 365 | /** 366 | * Adds an event listener that will be triggered when the Assistant detects the end of the user's 367 | * speech utterance and expects no additional speech. Therefore, it will not process additional audio 368 | * (although it may subsequently return additional results). 369 | * 370 | * @param event - The 'utteranceend' event. 371 | * @param listener - A callback that will receive the latest Assistant response as param. 372 | * @returns The conversation. 373 | */ 374 | on( 375 | event: 'utteranceend', 376 | listener: (latestData: AssistantResponse) => void, 377 | ): this; 378 | 379 | /** 380 | * Adds an event listener that will be triggered when the Assistant closes the connection, 381 | * thus not sending any more data. 382 | * 383 | * @param event - The 'end' event. 384 | * @param listener - A callback with no params. 385 | * @returns The conversation. 386 | */ 387 | on(event: 'end', listener: () => void): this; 388 | 389 | /** 390 | * Adds an event listener that will be triggered when the connection with the Assistant is 391 | * fully closed. 392 | * 393 | * @param event - The 'end' event. 394 | * @param listener - A callback with no params. 395 | * @returns The conversation. 396 | */ 397 | on(event: 'close', listener: () => void): this; 398 | 399 | /** 400 | * Adds an event listener that will be triggered when any type of error occurs. 401 | * 402 | * @param event - The 'error' event. 403 | * @param listener - A callback that will receive the error as param. 404 | * @returns The conversation. 405 | */ 406 | on(event: 'error', listener: (error: Error) => void): this; 407 | 408 | /** 409 | * Adds a one time event listener that will be triggered when the Assistant sends any type of data. 410 | * 411 | * @param event - The 'data' event. 412 | * @param listener - A callback that will receive the Assistant data as param. 413 | * @returns The conversation. 414 | */ 415 | once(event: 'data', listener: (data: AssistantResponse) => void): this; 416 | 417 | /** 418 | * Adds a one time event listener that will be triggered if the user has triggered a Device Action. 419 | * For example, a device which supports the query *Turn on the light* would receive an 420 | * object containing the semantics of the request. 421 | * 422 | * @param event - The 'action' event. 423 | * @param listener - A callback that will receive the object containing the semantics of the request as param. 424 | * @returns The conversation. 425 | */ 426 | once(event: 'action', listener: (action: unknown) => void): this; 427 | 428 | /** 429 | * Adds a one time event listener that will be triggered with the original response from an 430 | * Action-on-Google agent to Google server. To be able to receive this data, the conversation 431 | * has to be in debug mode, the request maker has to own the AoG project and the AoG project 432 | * has to be in preview mode. 433 | * 434 | * @param event - The 'actionongoogle' event. 435 | * @param listener - A callback that will receive the original response from the AoG agent as param. 436 | * @returns The conversation. 437 | */ 438 | once( 439 | event: 'actionongoogle', 440 | listener: (actionOnGoogle: unknown) => void, 441 | ): this; 442 | 443 | /** 444 | * Adds a one time event listener that will be triggered when the Assistant sends an audio message. 445 | * 446 | * @param event - The 'audio' event. 447 | * @param listener - A callback that will receive the Assistant audio message as param. 448 | * @returns The conversation. 449 | */ 450 | once(event: 'audio', listener: (audio: Buffer) => void): this; 451 | 452 | /** 453 | * Adds a one time event listener that will be triggered when the conversation with the Assistant ends. 454 | * 455 | * @param event - The 'conversationend' event. 456 | * @param listener - A callback that will receive the latest Assistant response as param. 457 | * @returns The conversation. 458 | */ 459 | once( 460 | event: 'conversationend', 461 | listener: (latestData: AssistantResponse) => void, 462 | ): this; 463 | 464 | /** 465 | * Adds a one time event listener that will be triggered when the Assistant sends a text message. 466 | * 467 | * @param event - The 'message' event. 468 | * @param listener - A callback that will receive the Assistant text message as param. 469 | * @returns The conversation. 470 | */ 471 | once(event: 'message', listener: (text: string) => void): this; 472 | 473 | /** 474 | * Adds a one time event listener that will be triggered when the Assistant sends HTML data. 475 | * 476 | * @param event - The 'html' event. 477 | * @param listener - A callback that will receive the HTML data as param. 478 | * @returns The conversation. 479 | */ 480 | once(event: 'html', listener: (html: string) => void): this; 481 | 482 | /** 483 | * Adds a one time event listener that will be triggered when the Assistant sends the new device volume level. 484 | * 485 | * @param event - The 'volume' event. 486 | * @param listener - A callback that will receive the new device volume level as param. 487 | * @returns The conversation. 488 | */ 489 | once(event: 'volume', listener: (newVolume: number) => void): this; 490 | 491 | /** 492 | * Adds a one time event listener that will be triggered with the Assistant speech recognition results. 493 | * 494 | * @param event - The 'speechrecognition' event. 495 | * @param listener - A callback that will receive the Assistant speech recognition results as param. 496 | * @returns The conversation. 497 | */ 498 | once( 499 | event: 'speechrecognition', 500 | listener: ( 501 | speechRecognitionResults: AssistantSpeechRecognitionResult[], 502 | ) => void, 503 | ): this; 504 | 505 | /** 506 | * Adds a one time event listener that will be triggered when the Assistant detects the end of the user's 507 | * speech utterance and expects no additional speech. Therefore, it will not process additional audio 508 | * (although it may subsequently return additional results). 509 | * 510 | * @param event - The 'utteranceend' event. 511 | * @param listener - A callback that will receive the latest Assistant response as param. 512 | * @returns The conversation. 513 | */ 514 | once( 515 | event: 'utteranceend', 516 | listener: (latestData: AssistantResponse) => void, 517 | ): this; 518 | 519 | /** 520 | * Adds a one time event listener that will be triggered when the Assistant closes the connection, 521 | * thus not sending any more data. 522 | * 523 | * @param event - The 'end' event. 524 | * @param listener - A callback with no params. 525 | * @returns The conversation. 526 | */ 527 | once(event: 'end', listener: () => void): this; 528 | 529 | /** 530 | * Adds a one time event listener that will be triggered when the connection with the Assistant is 531 | * fully closed. 532 | * 533 | * @param event - The 'end' event. 534 | * @param listener - A callback with no params. 535 | * @returns The conversation. 536 | */ 537 | once(event: 'close', listener: () => void): this; 538 | 539 | /** 540 | * Adds a one time event listener that will be triggered when any type of error occurs. 541 | * 542 | * @param event - The 'error' event. 543 | * @param listener - A callback that will receive the error as param. 544 | * @returns The conversation. 545 | */ 546 | once(event: 'error', listener: (error: Error) => void): this; 547 | 548 | // TODO: add good docs to these methods too. 549 | prependListener( 550 | event: ConversationEvent, 551 | listener: (...args: any[]) => void, 552 | ): this; 553 | prependOnceListener( 554 | event: ConversationEvent, 555 | listener: (...args: any[]) => void, 556 | ): this; 557 | removeListener( 558 | event: ConversationEvent, 559 | listener: (...args: any[]) => void, 560 | ): this; 561 | removeAllListeners(event?: ConversationEvent): this; 562 | setMaxListeners(n: number): this; 563 | getMaxListeners(): number; 564 | listeners(event: ConversationEvent): Array<() => void>; 565 | rawListeners(event: ConversationEvent): Array<() => void>; 566 | 567 | emit(event: 'data', data: AssistantResponse): boolean; 568 | emit(event: 'action', action: unknown): boolean; 569 | emit(event: 'actionongoogle', actionOnGoogle: unknown): boolean; 570 | emit(event: 'audio', audio: Buffer): boolean; 571 | emit(event: 'conversationend', latestData: AssistantResponse): boolean; 572 | emit(event: 'message', text: string): boolean; 573 | emit(event: 'html', html: string): boolean; 574 | emit(event: 'volume', newVolume: number): boolean; 575 | emit( 576 | event: 'speechrecognition', 577 | speechRecognitionResults: AssistantSpeechRecognitionResult[], 578 | ): boolean; 579 | emit(event: 'utteranceend', latestData: AssistantResponse): boolean; 580 | emit(event: 'end'): boolean; 581 | emit(event: 'close'): boolean; 582 | emit(event: 'error', error: Error): boolean; 583 | 584 | eventNames(): ConversationEvent[]; 585 | listenerCount(type: ConversationEvent): number; 586 | } 587 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './common'; 2 | export * from './assistant'; 3 | export * from './conversation'; 4 | export * from './text-conversation'; 5 | export * from './audio-conversation'; 6 | export * from './proto'; 7 | -------------------------------------------------------------------------------- /src/proto.ts: -------------------------------------------------------------------------------- 1 | import { loadSync } from '@grpc/proto-loader'; 2 | import { getProtoPath } from 'google-proto-files'; 3 | import { 4 | ChannelCredentials, 5 | ClientDuplexStream, 6 | loadPackageDefinition, 7 | } from 'grpc'; 8 | 9 | // Service that implements the Google Assistant API. 10 | export declare class EmbeddedAssistant { 11 | constructor(endpoint: string, credentials: ChannelCredentials); 12 | 13 | // Initiates or continues a conversation with the embedded Assistant Service. 14 | // Each call performs one round-trip, sending an audio request to the service 15 | // and receiving the audio response. Uses bidirectional streaming to receive 16 | // results, such as the `END_OF_UTTERANCE` event, while sending audio. 17 | // 18 | // A conversation is one or more gRPC connections, each consisting of several 19 | // streamed requests and responses. 20 | // For example, the user says *Add to my shopping list* and the Assistant 21 | // responds *What do you want to add?*. The sequence of streamed requests and 22 | // responses in the first gRPC message could be: 23 | // 24 | // * AssistRequest.config 25 | // * AssistRequest.audioIn 26 | // * AssistRequest.audioIn 27 | // * AssistRequest.audioIn 28 | // * AssistRequest.audioIn 29 | // * AssistResponse.eventType.END_OF_UTTERANCE 30 | // * AssistResponse.speechResults.transcript "add to my shopping list" 31 | // * AssistResponse.dialogStateOut.microphoneMode.DIALOG_FOLLOW_ON 32 | // * AssistResponse.audioOut 33 | // * AssistResponse.audioOut 34 | // * AssistResponse.audioOut 35 | // 36 | // 37 | // The user then says *bagels* and the Assistant responds 38 | // *OK, I've added bagels to your shopping list*. This is sent as another gRPC 39 | // connection call to the `Assist` method, again with streamed requests and 40 | // responses, such as: 41 | // 42 | // * AssistRequest.config 43 | // * AssistRequest.audioIn 44 | // * AssistRequest.audioIn 45 | // * AssistRequest.audioIn 46 | // * AssistResponse.eventType.END_OF_UTTERANCE 47 | // * AssistResponse.dialogStateOut.microphoneMode.CLOSE_MICROPHONE 48 | // * AssistResponse.audioOut 49 | // * AssistResponse.audioOut 50 | // * AssistResponse.audioOut 51 | // * AssistResponse.audioOut 52 | // 53 | // Although the precise order of responses is not guaranteed, sequential 54 | // `AssistResponse.audioOut` messages will always contain sequential portions 55 | // of audio. 56 | public assist(): ClientDuplexStream; 57 | } 58 | 59 | // The top-level message sent by the client. Clients must send at least two, and 60 | // typically numerous `AssistRequest` messages. The first message must 61 | // contain a `config` message and must not contain `audioIn` data. All 62 | // subsequent messages must contain `audioIn` data and must not contain a 63 | // `config` message. 64 | export type AssistRequest = 65 | | { 66 | // The `config` message provides information to the recognizer that 67 | // specifies how to process the request. 68 | // The first `AssistRequest` message must contain a `config` message. 69 | config: AssistConfig; 70 | 71 | audioIn?: never; 72 | } 73 | | { 74 | // The audio data to be recognized. Sequential chunks of audio data are sent 75 | // in sequential `AssistRequest` messages. The first `AssistRequest` 76 | // message must not contain `audioIn` data and all subsequent 77 | // `AssistRequest` messages must contain `audioIn` data. The audio bytes 78 | // must be encoded as specified in `AudioInConfig`. 79 | // Audio must be sent at approximately real-time (16000 samples per second). 80 | // An error will be returned if audio is sent significantly faster or 81 | // slower. 82 | audioIn: Buffer; 83 | 84 | config?: never; 85 | }; 86 | 87 | // Indicates the type of event. 88 | export enum AssistResponseEventType { 89 | // No event specified. 90 | EVENT_TYPE_UNSPECIFIED = 0, 91 | 92 | // This event indicates that the server has detected the end of the user's 93 | // speech utterance and expects no additional speech. Therefore, the server 94 | // will not process additional audio (although it may subsequently return 95 | // additional results). The client should stop sending additional audio 96 | // data, half-close the gRPC connection, and wait for any additional results 97 | // until the server closes the gRPC connection. 98 | END_OF_UTTERANCE = 1, 99 | } 100 | 101 | // The top-level message received by the client. A series of one or more 102 | // `AssistResponse` messages are streamed back to the client. 103 | export interface AssistResponse { 104 | // *Output-only* Indicates the type of event. 105 | eventType: AssistResponseEventType; 106 | 107 | // *Output-only* The audio containing the Assistant's response to the query. 108 | audioOut?: AudioOut; 109 | 110 | // *Output-only* Contains the Assistant's visual response to the query. 111 | screenOut?: ScreenOut; 112 | 113 | // *Output-only* Contains the action triggered by the query with the 114 | // appropriate payloads and semantic parsing. 115 | deviceAction?: DeviceAction; 116 | 117 | // *Output-only* This repeated list contains zero or more speech recognition 118 | // results that correspond to consecutive portions of the audio currently 119 | // being processed, starting with the portion corresponding to the earliest 120 | // audio (and most stable portion) to the portion corresponding to the most 121 | // recent audio. The strings can be concatenated to view the full 122 | // in-progress response. When the speech recognition completes, this list 123 | // will contain one item with `stability` of `1.0`. 124 | speechResults?: SpeechRecognitionResult[]; 125 | 126 | // *Output-only* Contains output related to the user's query. 127 | dialogStateOut?: DialogStateOut; 128 | 129 | // *Output-only* Debugging info for developer. Only returned if request set 130 | // `returnDebugInfo` to true. 131 | debugInfo?: DebugInfo; 132 | } 133 | 134 | // Debug info for developer. Only returned if request set `returnDebugInfo` 135 | // to true. 136 | export interface DebugInfo { 137 | // The original JSON response from an Action-on-Google agent to Google server. 138 | // See 139 | // https://developers.google.com/actions/reference/rest/Shared.Types/AppResponse. 140 | // It will only be populated if the request maker owns the AoG project and the 141 | // AoG project is in preview mode. 142 | aogAgentToAssistantJson: string; 143 | } 144 | 145 | // Specifies how to process the `AssistRequest` messages. 146 | export type AssistConfig = { 147 | // *Required* Specifies how to format the audio that will be returned. 148 | audioOutConfig: AudioOutConfig; 149 | 150 | // *Optional* Specifies the desired format to use when server returns a 151 | // visual screen response. 152 | screenOutConfig?: ScreenOutConfig; 153 | 154 | // *Required* Represents the current dialog state. 155 | dialogStateIn: DialogStateIn; 156 | 157 | // Device configuration that uniquely identifies a specific device. 158 | deviceConfig: DeviceConfig; 159 | 160 | // *Optional* Debugging parameters for the whole `Assist` RPC. 161 | debugConfig?: DebugConfig; 162 | } & ( 163 | | { 164 | // Specifies how to process the subsequent incoming audio. Required if 165 | // [AssistRequest.audioIn][google.assistant.embedded.v1alpha2.AssistRequest.audioIn] 166 | // bytes will be provided in subsequent requests. 167 | audioInConfig: AudioInConfig; 168 | 169 | textQuery?: never; 170 | } 171 | | { 172 | // The text input to be sent to the Assistant. This can be populated from a 173 | // text interface if audio input is not available. 174 | textQuery: string; 175 | 176 | audioInConfig?: never; 177 | } 178 | ); 179 | 180 | // Audio encoding of the data sent in the audio message. 181 | // Audio must be one-channel (mono). 182 | export enum AudioInEncoding { 183 | // Not specified. Will return result [google.rpc.Code.INVALID_ARGUMENT][]. 184 | ENCODING_UNSPECIFIED = 0, 185 | 186 | // Uncompressed 16-bit signed little-endian samples (Linear PCM). 187 | // This encoding includes no header, only the raw audio bytes. 188 | LINEAR16 = 1, 189 | 190 | // [`FLAC`](https://xiph.org/flac/documentation.html) (Free Lossless Audio 191 | // Codec) is the recommended encoding because it is 192 | // lossless--therefore recognition is not compromised--and 193 | // requires only about half the bandwidth of `LINEAR16`. This encoding 194 | // includes the `FLAC` stream header followed by audio data. It supports 195 | // 16-bit and 24-bit samples, however, not all fields in `STREAMINFO` are 196 | // supported. 197 | FLAC = 2, 198 | } 199 | 200 | // Specifies how to process the `audioIn` data that will be provided in 201 | // subsequent requests. For recommended settings, see the Google Assistant SDK 202 | // [best practices](https://developers.google.com/assistant/sdk/guides/service/python/best-practices/audio). 203 | export interface AudioInConfig { 204 | // *Required* Encoding of audio data sent in all `audioIn` messages. 205 | encoding: AudioInEncoding; 206 | 207 | // *Required* Sample rate (in Hertz) of the audio data sent in all `audioIn` 208 | // messages. Valid values are from 16000-24000, but 16000 is optimal. 209 | // For best results, set the sampling rate of the audio source to 16000 Hz. 210 | // If that's not possible, use the native sample rate of the audio source 211 | // (instead of re-sampling). 212 | sampleRateHertz: number; 213 | } 214 | 215 | // Audio encoding of the data returned in the audio message. All encodings are 216 | // raw audio bytes with no header, except as indicated below. 217 | export enum AudioOutEncoding { 218 | // Not specified. Will return result [google.rpc.Code.INVALID_ARGUMENT][]. 219 | ENCODING_UNSPECIFIED = 0, 220 | 221 | // Uncompressed 16-bit signed little-endian samples (Linear PCM). 222 | LINEAR16 = 1, 223 | 224 | // MP3 audio encoding. The sample rate is encoded in the payload. 225 | MP3 = 2, 226 | 227 | // Opus-encoded audio wrapped in an ogg container. The result will be a 228 | // file which can be played natively on Android and in some browsers (such 229 | // as Chrome). The quality of the encoding is considerably higher than MP3 230 | // while using the same bitrate. The sample rate is encoded in the payload. 231 | OPUS_IN_OGG = 3, 232 | } 233 | 234 | // Specifies the desired format for the server to use when it returns 235 | // `audioOut` messages. 236 | export interface AudioOutConfig { 237 | // *Required* The encoding of audio data to be returned in all `audioOut` 238 | // messages. 239 | encoding: AudioOutEncoding; 240 | 241 | // *Required* The sample rate in Hertz of the audio data returned in 242 | // `audioOut` messages. Valid values are: 16000-24000. 243 | sampleRateHertz: number; 244 | 245 | // *Required* Current volume setting of the device's audio output. 246 | // Valid values are 1 to 100 (corresponding to 1% to 100%). 247 | volumePercentage: number; 248 | } 249 | 250 | // Possible modes for visual screen-output on the device. 251 | export enum ScreenMode { 252 | // No video mode specified. 253 | // The Assistant may respond as if in `OFF` mode. 254 | SCREEN_MODE_UNSPECIFIED = 0, 255 | 256 | // Screen is off (or has brightness or other settings set so low it is 257 | // not visible). The Assistant will typically not return a screen response 258 | // in this mode. 259 | OFF = 1, 260 | 261 | // The Assistant will typically return a partial-screen response in this 262 | // mode. 263 | PLAYING = 3, 264 | } 265 | 266 | // Specifies the desired format for the server to use when it returns 267 | // `screenOut` response. 268 | export interface ScreenOutConfig { 269 | // Current visual screen-mode for the device while issuing the query. 270 | screenMode?: ScreenMode; 271 | } 272 | 273 | // Provides information about the current dialog state. 274 | export interface DialogStateIn { 275 | // *Required* This field must always be set to the 276 | // [DialogStateOut.conversationState][google.assistant.embedded.v1alpha2.DialogStateOut.conversationState] 277 | // value that was returned in the prior `Assist` RPC. It should only be omitted (field not set) 278 | // if there was no prior `Assist` RPC because this is the first `Assist` RPC made by this 279 | // device after it was first setup and/or a factory-default reset. 280 | conversationState?: Buffer; 281 | 282 | // *Required* Language of the request in 283 | // [IETF BCP 47 syntax](https://tools.ietf.org/html/bcp47) (for example, 284 | // "en-US"). See [Language Support](https://developers.google.com/assistant/sdk/reference/rpc/languages) 285 | // for more information. If you have selected a language for this `deviceId` 286 | // using the [Settings](https://developers.google.com/assistant/sdk/reference/assistant-app/assistant-settings) 287 | // menu in your phone's Google Assistant app, that selection will override 288 | // this value. 289 | languageCode: string; 290 | 291 | // *Optional* Location of the device where the query originated. 292 | deviceLocation?: DeviceLocation; 293 | 294 | // *Optional* If true, the server will treat the request as a new conversation 295 | // and not use state from the prior request. Set this field to true when the 296 | // conversation should be restarted, such as after a device reboot, or after a 297 | // significant lapse of time since the prior query. 298 | isNewConversation?: boolean; 299 | } 300 | 301 | // *Required* Fields that identify the device to the Assistant. 302 | // 303 | // See also: 304 | // 305 | // * [Register a Device - REST 306 | // API](https://developers.google.com/assistant/sdk/reference/device-registration/register-device-manual) 307 | // * [Device Model and Instance 308 | // Schemas](https://developers.google.com/assistant/sdk/reference/device-registration/model-and-instance-schemas) 309 | // * [Device 310 | // Proto](https://developers.google.com/assistant/sdk/reference/rpc/google.assistant.devices.v1alpha2#device) 311 | export interface DeviceConfig { 312 | // *Required* Unique identifier for the device. The id length must be 128 313 | // characters or less. Example: DBCDW098234. This MUST match the deviceId 314 | // returned from device registration. This deviceId is used to match against 315 | // the user's registered devices to lookup the supported traits and 316 | // capabilities of this device. This information should not change across 317 | // device reboots. However, it should not be saved across 318 | // factory-default resets. 319 | deviceId: string; 320 | 321 | // *Required* Unique identifier for the device model. The combination of 322 | // deviceModelId and deviceId must have been previously associated through 323 | // device registration. 324 | deviceModelId: string; 325 | } 326 | 327 | // The audio containing the Assistant's response to the query. Sequential chunks 328 | // of audio data are received in sequential `AssistResponse` messages. 329 | export interface AudioOut { 330 | // *Output-only* The audio data containing the Assistant's response to the 331 | // query. Sequential chunks of audio data are received in sequential 332 | // `AssistResponse` messages. 333 | audioData: Buffer; 334 | } 335 | 336 | // Possible formats of the screen data. 337 | export enum ScreenOutFormat { 338 | // No format specified. 339 | FORMAT_UNSPECIFIED = 0, 340 | 341 | // Data will contain a fully-formed HTML5 layout encoded in UTF-8, e.g. 342 | // `
...
`. It is intended to be rendered 343 | // along with the audio response. Note that HTML5 doctype should be included 344 | // in the actual HTML data. 345 | HTML = 1, 346 | } 347 | 348 | // The Assistant's visual output response to query. Enabled by 349 | // `screenOutConfig`. 350 | export interface ScreenOut { 351 | // *Output-only* The format of the provided screen data. 352 | format: ScreenOutFormat; 353 | 354 | // *Output-only* The raw screen data to be displayed as the result of the 355 | // Assistant query. 356 | data: Buffer; 357 | } 358 | 359 | // The response returned to the device if the user has triggered a Device 360 | // Action. For example, a device which supports the query *Turn on the light* 361 | // would receive a `DeviceAction` with a JSON payload containing the semantics 362 | // of the request. 363 | export interface DeviceAction { 364 | // JSON containing the device command response generated from the triggered 365 | // Device Action grammar. The format is given by the 366 | // `action.devices.EXECUTE` intent for a given 367 | // [trait](https://developers.google.com/assistant/sdk/reference/traits/). 368 | deviceRequestJson: string; 369 | } 370 | 371 | // The estimated transcription of a phrase the user has spoken. This could be 372 | // a single segment or the full guess of the user's spoken query. 373 | export interface SpeechRecognitionResult { 374 | // *Output-only* Transcript text representing the words that the user spoke. 375 | transcript: string; 376 | 377 | // *Output-only* An estimate of the likelihood that the Assistant will not 378 | // change its guess about this result. Values range from 0.0 (completely 379 | // unstable) to 1.0 (completely stable and final). The default of 0.0 is a 380 | // sentinel value indicating `stability` was not set. 381 | stability: number; 382 | } 383 | 384 | // Possible states of the microphone after a `Assist` RPC completes. 385 | export enum MicrophoneMode { 386 | // No mode specified. 387 | MICROPHONE_MODE_UNSPECIFIED = 0, 388 | 389 | // The service is not expecting a follow-on question from the user. 390 | // The microphone should remain off until the user re-activates it. 391 | CLOSE_MICROPHONE = 1, 392 | 393 | // The service is expecting a follow-on question from the user. The 394 | // microphone should be re-opened when the `AudioOut` playback completes 395 | // (by starting a new `Assist` RPC call to send the new audio). 396 | DIALOG_FOLLOW_ON = 2, 397 | } 398 | 399 | // The dialog state resulting from the user's query. Multiple of these messages 400 | // may be received. 401 | export interface DialogStateOut { 402 | // *Output-only* Supplemental display text from the Assistant. This could be 403 | // the same as the speech spoken in `AssistResponse.audioOut` or it could 404 | // be some additional information which aids the user's understanding. 405 | supplementalDisplayText: string; 406 | 407 | // *Output-only* State information for the subsequent `Assist` RPC. This 408 | // value should be saved in the client and returned in the 409 | // [`DialogStateIn.conversationState`](#dialogstatein) field with the next 410 | // `Assist` RPC. (The client does not need to interpret or otherwise use this 411 | // value.) This information should be saved across device reboots. However, 412 | // this value should be cleared (not saved in the client) during a 413 | // factory-default reset. 414 | conversationState: Buffer; 415 | 416 | // *Output-only* Specifies the mode of the microphone after this `Assist` 417 | // RPC is processed. 418 | microphoneMode: MicrophoneMode; 419 | 420 | // *Output-only* Updated volume level. The value will be 0 or omitted 421 | // (indicating no change) unless a voice command such as *Increase the volume* 422 | // or *Set volume level 4* was recognized, in which case the value will be 423 | // between 1 and 100 (corresponding to the new volume level of 1% to 100%). 424 | // Typically, a client should use this volume level when playing the 425 | // `audioOut` data, and retain this value as the current volume level and 426 | // supply it in the `AudioOutConfig` of the next `AssistRequest`. (Some 427 | // clients may also implement other ways to allow the current volume level to 428 | // be changed, for example, by providing a knob that the user can turn.) 429 | volumePercentage: number; 430 | } 431 | 432 | // Debugging parameters for the current request. 433 | export interface DebugConfig { 434 | // When this field is set to true, the `debugInfo` field in `AssistResponse` 435 | // may be populated. However it will significantly increase latency of 436 | // responses. Do not set this field true in production code. 437 | returnDebugInfo: boolean; 438 | } 439 | 440 | // An object representing a latitude/longitude pair. This is expressed as a pair 441 | // of doubles representing degrees latitude and degrees longitude. Unless 442 | // specified otherwise, this must conform to the 443 | // WGS84 444 | // standard. Values must be within normalized ranges. 445 | // 446 | // Example of normalization code in Python: 447 | // 448 | // def NormalizeLongitude(longitude): 449 | // """Wraps decimal degrees longitude to [-180.0, 180.0].""" 450 | // q, r = divmod(longitude, 360.0) 451 | // if r > 180.0 or (r == 180.0 and q <= -1.0): 452 | // return r - 360.0 453 | // return r 454 | // 455 | // def NormalizeLatLng(latitude, longitude): 456 | // """Wraps decimal degrees latitude and longitude to 457 | // [-90.0, 90.0] and [-180.0, 180.0], respectively.""" 458 | // r = latitude % 360.0 459 | // if r <= 90.0: 460 | // return r, NormalizeLongitude(longitude) 461 | // elif r >= 270.0: 462 | // return r - 360, NormalizeLongitude(longitude) 463 | // else: 464 | // return 180 - r, NormalizeLongitude(longitude + 180.0) 465 | // 466 | // assert 180.0 == NormalizeLongitude(180.0) 467 | // assert -180.0 == NormalizeLongitude(-180.0) 468 | // assert -179.0 == NormalizeLongitude(181.0) 469 | // assert (0.0, 0.0) == NormalizeLatLng(360.0, 0.0) 470 | // assert (0.0, 0.0) == NormalizeLatLng(-360.0, 0.0) 471 | // assert (85.0, 180.0) == NormalizeLatLng(95.0, 0.0) 472 | // assert (-85.0, -170.0) == NormalizeLatLng(-95.0, 10.0) 473 | // assert (90.0, 10.0) == NormalizeLatLng(90.0, 10.0) 474 | // assert (-90.0, -10.0) == NormalizeLatLng(-90.0, -10.0) 475 | // assert (0.0, -170.0) == NormalizeLatLng(-180.0, 10.0) 476 | // assert (0.0, -170.0) == NormalizeLatLng(180.0, 10.0) 477 | // assert (-90.0, 10.0) == NormalizeLatLng(270.0, 10.0) 478 | // assert (90.0, 10.0) == NormalizeLatLng(-270.0, 10.0) 479 | export interface LatLng { 480 | // The latitude in degrees. It must be in the range [-90.0, +90.0]. 481 | latitude: number; 482 | 483 | // The longitude in degrees. It must be in the range [-180.0, +180.0]. 484 | longitude: number; 485 | } 486 | 487 | // There are three sources of locations. They are used with this precedence: 488 | // 489 | // 1. This `DeviceLocation`, which is primarily used for mobile devices with 490 | // GPS . 491 | // 2. Location specified by the user during device setup; this is per-user, per 492 | // device. This location is used if `DeviceLocation` is not specified. 493 | // 3. Inferred location based on IP address. This is used only if neither of the 494 | // above are specified. 495 | export interface DeviceLocation { 496 | coordinates: LatLng; 497 | } 498 | 499 | const PROTO_ROOT_DIR = getProtoPath('..'); 500 | 501 | const proto: any = loadSync( 502 | 'google/assistant/embedded/v1alpha2/embedded_assistant.proto', 503 | { 504 | includeDirs: [PROTO_ROOT_DIR], 505 | }, 506 | ); 507 | 508 | const { google }: any = loadPackageDefinition(proto); 509 | 510 | export const embeddedAssistantPb: typeof EmbeddedAssistant = 511 | google.assistant.embedded.v1alpha2.EmbeddedAssistant; 512 | -------------------------------------------------------------------------------- /src/text-conversation.ts: -------------------------------------------------------------------------------- 1 | import { ClientDuplexStream } from 'grpc'; 2 | import { AssistantLanguage } from './common'; 3 | import { Conversation } from './conversation'; 4 | import { 5 | AssistRequest, 6 | AssistResponse, 7 | AudioOutConfig, 8 | AudioOutEncoding, 9 | } from './proto'; 10 | 11 | /** 12 | * Represents a text conversation with the Assistant. 13 | * @author Giorgio Garasto 14 | * @license MIT 15 | * @class 16 | */ 17 | export class TextConversation extends Conversation { 18 | /** 19 | * Creates a new audio conversation. 20 | * @param _stream - The duplex stream to use to communicate with the Assistant SDK. 21 | * @param _deviceId - The device ID to use during this conversation. 22 | * @param _deviceModelId - The device model ID to use during this conversation. 23 | * @param locale - The locale to use during this conversation. 24 | * @param _audioOutConfig - The audio output configuration. 25 | * @constructor 26 | */ 27 | constructor( 28 | _stream: ClientDuplexStream, 29 | _deviceId: string, 30 | _deviceModelId: string, 31 | locale: AssistantLanguage, 32 | private _audioOutConfig: AudioOutConfig = { 33 | encoding: AudioOutEncoding.LINEAR16, 34 | sampleRateHertz: 16000, 35 | volumePercentage: 100, 36 | }, 37 | ) { 38 | super(_stream, _deviceId, _deviceModelId, locale); 39 | } 40 | 41 | /** 42 | * Sends a text query to the Assistant. 43 | * @param text - The text query to send to the Assistant. 44 | * @returns A boolean that tells whether the text query was successfully sent or not. 45 | */ 46 | public send(text: string): boolean { 47 | return this.sendRequest({ 48 | audioOutConfig: this._audioOutConfig, 49 | deviceId: this._deviceId, 50 | deviceModelId: this._deviceModelId, 51 | locale: this.locale, 52 | text, 53 | }); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /test/assistant.spec.ts: -------------------------------------------------------------------------------- 1 | describe('Assistant', () => { 2 | it('tests', () => { 3 | expect(true).toBe(true); 4 | }); 5 | }); 6 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "strictNullChecks": false, 5 | "target": "es2017", 6 | "rootDir": "src", 7 | "lib": [ 8 | "esnext" 9 | ], 10 | "baseUrl": ".", 11 | "outDir": "lib", 12 | "declaration": true, 13 | "module": "esnext", 14 | "moduleResolution": "node" 15 | }, 16 | "include": [ 17 | "src/**/*.ts" 18 | ] 19 | } 20 | --------------------------------------------------------------------------------