├── .commitlintrc.json ├── .eslintignore ├── .eslintrc.js ├── .github ├── ISSUE_TEMPLATE.md ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ └── guidance_request.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── extensionTests.yml │ ├── release.yml │ └── stale.yml ├── .gitignore ├── .prettierignore ├── .prettierrc ├── .versionrc.json ├── .vscode ├── launch.json ├── settings.json └── tasks.json ├── .vscodeignore ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── NOTICE ├── README.md ├── THIRD_PARTY.txt ├── extensionMain.js ├── languages └── acdl │ ├── language-configuration.json │ └── syntaxes │ └── acdl.tmLanguage.json ├── media ├── aplResourceSync │ ├── aplResourceSync.html │ └── aplResourceSync.js ├── createSkill │ ├── authenticateAccount.html │ ├── createComplete.html │ ├── createInProgress.html │ ├── createSkill.css │ ├── createSkill.html │ └── createSkill.js ├── deployHostedSkill │ ├── deployHostedSkill.html │ ├── deployHostedSkill.js │ └── deployInProgress.html ├── deployNonHostedSkill │ ├── deployInProgress.html │ ├── deployNonHostedSkill.html │ └── deployNonHostedSkill.js ├── deploySkill │ ├── deployInProgress.html │ ├── deploySkill.html │ └── deploySkill.js ├── deviceRegistry │ ├── deviceRegistry.html │ ├── deviceRegistry.js │ ├── deviceRegistryDone.html │ ├── deviceRegistryDone.js │ ├── deviceRegistryInProgress.html │ └── deviceRegistryInProgress.js ├── docs │ ├── create_apl_doc.gif │ ├── create_self_hosted_skill.gif │ ├── create_skill.gif │ ├── deploy_hosted_skill.gif │ ├── deploy_self_hosted_skill.gif │ ├── deploy_skill.gif │ ├── download_self_hosted_skill.gif │ ├── local_debugging.gif │ └── sign_in_flow.gif ├── images │ ├── LICENSE.txt │ └── ask_icon.png ├── initialLogin │ └── initialLogin.html ├── interactionModelSync │ ├── interactionModelSync.html │ └── interactionModelSync.js ├── manifestSync │ ├── manifestSync.html │ └── manifestSync.js ├── previewApl │ ├── aplRenderUtils.js │ ├── previewApl.html │ └── previewApl.js ├── profileManager │ ├── authFlowResult.html │ ├── initiateAuthFlow.html │ ├── profileManager.html │ └── profileManager.js ├── simulate.css ├── simulateSkill │ ├── simulateSkill.html │ └── simulateSkill.js ├── skillDeploy.css ├── skillInfo │ ├── skillInformation.html │ └── skillInformation.js ├── toolkit.css ├── toolkitUpdate │ └── toolkitUpdate.html └── welcomeScreen │ └── welcomeScreen.html ├── package-lock.json ├── package.json ├── snippets └── aplSnippets.json ├── src ├── @types │ └── git.d.ts ├── acdlServer │ ├── README.md │ ├── acdlServer.ts │ ├── completions.ts │ ├── definition.ts │ ├── hover.ts │ ├── index.ts │ ├── keywordsProvider.ts │ └── utils.ts ├── aplContainer │ ├── commands │ │ ├── changeViewportProfile.ts │ │ ├── createAplDocumentFromSample.ts │ │ ├── previewApl.ts │ │ └── syncAplResource.ts │ ├── config │ │ └── configuration.ts │ ├── constants │ │ └── messages.ts │ ├── models │ │ └── index.ts │ ├── utils │ │ ├── aplDiagnositicsHelper.ts │ │ └── viewportProfileHelper.ts │ └── webViews │ │ ├── aplPreviewWebView.ts │ │ └── aplResourceSyncWebview.ts ├── askContainer │ ├── commands │ │ ├── accessToken.ts │ │ ├── changeProfile.ts │ │ ├── changeSimulatorViewport.ts │ │ ├── cloneSkill.ts │ │ ├── cloneSkill │ │ │ ├── abstractCloneSkillManager.ts │ │ │ ├── cloneHostedSkillManager.ts │ │ │ ├── cloneNonHostedSkillManager.ts │ │ │ └── cloneSkill.ts │ │ ├── cloneSkillFromConsole.ts │ │ ├── createSkill.ts │ │ ├── deployHostedSkill.ts │ │ ├── deployNonHostedSkill.ts │ │ ├── deploySkill.ts │ │ ├── deviceDeletionCommand.ts │ │ ├── deviceRegistryCommand.ts │ │ ├── exportSkillPackage.ts │ │ ├── init.ts │ │ ├── listSkills.ts │ │ ├── local-debug │ │ │ └── debugAdapterPath.ts │ │ ├── login.ts │ │ ├── refreshSkillActions.ts │ │ ├── showToolkitUpdates.ts │ │ ├── simulateReplay.ts │ │ ├── simulateSkill.ts │ │ ├── skillIdFromWorkspaceCommand.ts │ │ ├── syncInteractionModel.ts │ │ ├── syncManifest.ts │ │ ├── viewAllSkills.ts │ │ └── welcome.ts │ ├── events.ts │ ├── fileSystem │ │ └── skillPackageWatcher.ts │ ├── treeViews │ │ ├── helpView.ts │ │ ├── skillActionsView.ts │ │ ├── skillConsoleView.ts │ │ └── treeViewProviders │ │ │ ├── helpViewProvider.ts │ │ │ ├── skillActionsViewProvider.ts │ │ │ └── skillConsoleViewProvider.ts │ └── webViews │ │ ├── createSkillWebview.ts │ │ ├── createSkillWebview │ │ ├── abstractCreateSkillManager.ts │ │ ├── createHostedSkillManager.ts │ │ ├── createNonHostedSkillManager.ts │ │ └── createSkillWebview.ts │ │ ├── deploySkillWebview.ts │ │ ├── deploySkillWebview │ │ ├── abstractDeploySkillManager.ts │ │ ├── deployHostedSkillManager.ts │ │ ├── deployHostedSkillWebview.ts │ │ ├── deployNonHostedSkillManager.ts │ │ └── deployNonHostedSkillWebview.ts │ │ ├── deviceRegistryWebview.ts │ │ ├── initialLogin.ts │ │ ├── interactionModelSync.ts │ │ ├── manifestSync.ts │ │ ├── profileManagerWebview.ts │ │ ├── simulateSkillWebview.ts │ │ ├── toolkitUpdateWebview.ts │ │ └── welcomeScreenWebview.ts ├── constants.ts ├── exceptions.ts ├── extension.ts ├── extensionGlobals.ts ├── logger.ts ├── models │ ├── manifest.ts │ ├── resourcesConfig │ │ ├── abstractResourcesConfig.ts │ │ ├── askResource.ts │ │ └── askStates.ts │ └── types.ts ├── runtime │ ├── index.ts │ └── lib │ │ ├── API.ts │ │ ├── credentialsManager.ts │ │ ├── index.ts │ │ ├── smapiClientFactory.ts │ │ ├── telemetry │ │ ├── constants.ts │ │ ├── index.ts │ │ └── metricAction.ts │ │ ├── types.ts │ │ └── utils │ │ ├── configuration.ts │ │ ├── constants.ts │ │ ├── index.ts │ │ ├── jsonRead.ts │ │ ├── jsonUtility.ts │ │ ├── lwa.ts │ │ ├── oauthWrapper.ts │ │ ├── profileHelper.ts │ │ ├── serverFactory.ts │ │ └── stringUtils.ts └── utils │ ├── avs │ ├── avsClient.ts │ ├── avsClientUtil.ts │ ├── avsInterface.ts │ ├── avsPayload.ts │ ├── deviceToken.ts │ ├── deviceTokenUtil.ts │ ├── downChannelClient.ts │ └── simulateAVSHelper.ts │ ├── captchaValidator.ts │ ├── cloneSkillHelper.ts │ ├── commands │ ├── contactToolkitTeam.ts │ ├── getToolkitInfo.ts │ ├── openUrl.ts │ └── openWorkspace.ts │ ├── createSkillHelper.ts │ ├── dateHelper.ts │ ├── deploySkillHelper.ts │ ├── fileHelper.ts │ ├── gitHelper.ts │ ├── hashHelper.ts │ ├── httpHelper.ts │ ├── mediaHelper.ts │ ├── retry.ts │ ├── s3ScriptChecker.ts │ ├── schemaHelper.ts │ ├── simulateMessageHelper.ts │ ├── simulateReplayHelper.ts │ ├── simulateSkillHelper.ts │ ├── skillHelper.ts │ ├── skillPackageHelper.ts │ ├── statusBarHelper.ts │ ├── urlHandlers.ts │ ├── webViews │ ├── authHelper.ts │ ├── viewLoader.ts │ └── viewManager.ts │ ├── workspaceHelper.ts │ └── zipHelper.ts ├── test ├── acdlServer │ ├── acdlTestUtils.ts │ ├── completion.test.ts │ ├── completionUtils.ts │ ├── definition.test.ts │ ├── hover.test.ts │ └── mockACDL │ │ ├── ask-resources.json │ │ └── skill-package │ │ ├── conversations │ │ └── Weather.acdl │ │ ├── interactionModels │ │ └── custom │ │ │ └── en-US.json │ │ ├── response │ │ └── prompts │ │ │ ├── AlexaConversationsBye │ │ │ └── document.json │ │ │ ├── AlexaConversationsNotifyFailure │ │ │ └── document.json │ │ │ ├── AlexaConversationsOutOfDomain │ │ │ └── document.json │ │ │ ├── AlexaConversationsProvideHelp │ │ │ └── document.json │ │ │ ├── AlexaConversationsRequestMore │ │ │ └── document.json │ │ │ ├── AlexaConversationsThankYou │ │ │ └── document.json │ │ │ ├── AlexaConversationsWelcome │ │ │ └── document.json │ │ │ ├── AlexaConversationsYouAreWelcome │ │ │ └── document.json │ │ │ ├── request_city_apla │ │ │ └── document.json │ │ │ ├── request_city_date_apla │ │ │ └── document.json │ │ │ ├── request_date_apla │ │ │ └── document.json │ │ │ └── weather_apla │ │ │ └── document.json │ │ └── skill.json ├── acdlSyntaxHighlighterGrammar.test.ts ├── askContainer │ ├── commands │ │ ├── deviceRegistry.test.ts │ │ └── listSkills.test.ts │ ├── fileSystem │ │ └── skillPackageWatcher.test.ts │ ├── treeViews │ │ └── treeViewProviders │ │ │ └── helpViewProvider.test.ts │ └── webview │ │ ├── deploySkillWebview.test.ts │ │ └── profileManagerWebview.test.ts ├── definitionUtils.ts ├── extension.test.ts ├── index.ts ├── mockSkill │ ├── .ask │ │ └── schema │ │ │ └── skillPackageSchema.json │ ├── skill-package-NHS │ │ └── skill.txt │ └── skill-package │ │ ├── alexaSkill.json │ │ ├── interaction │ │ └── .gitkeep │ │ ├── interactionModels │ │ └── custom │ │ │ └── en-US.json │ │ └── skill.json ├── runTest.ts ├── runtime │ └── utils │ │ └── profileHelper.test.ts ├── testUtilities.ts └── utils │ ├── commands │ ├── debugAdapterPath.test.ts │ ├── openUrl.test.ts │ └── openWorkspace.test.ts │ ├── gitHelper.test.ts │ └── schemaHelper.test.ts ├── third-party ├── README.md └── resources │ └── from-vscode-icons │ ├── dark │ ├── open-preview.svg │ └── refresh.svg │ └── light │ ├── open-preview.svg │ └── refresh.svg ├── tsconfig.eslint.json ├── tsconfig.json └── webpack.config.js /.commitlintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "body-leading-blank": [1, "always"], 4 | "footer-leading-blank": [1, "always"], 5 | "subject-empty": [2, "never"], 6 | "subject-full-stop": [2, "never", "."], 7 | "subject-case": [2, "never", ["sentence-case", "pascal-case", "start-case", "upper-case"]], 8 | "scope-case": [2, "always", "lower-case"], 9 | "type-case": [2, "always", "lower-case"], 10 | "type-empty": [2, "never"], 11 | "type-enum": [2, "always", ["chore", "docs", "feat", "fix", "perf", "refactor", "revert", "style", "test"]] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/** 2 | third-party 3 | **/*.json 4 | dist 5 | out 6 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parser: "@typescript-eslint/parser", 4 | parserOptions: { 5 | project: ["./tsconfig.json", "./tsconfig.eslint.json"], 6 | tsconfigRootDir: __dirname, 7 | }, 8 | plugins: ["@typescript-eslint"], 9 | extends: ["airbnb-base", "airbnb-typescript/base", "prettier"], 10 | rules: { 11 | "import/prefer-default-export": "off", 12 | "no-restricted-syntax": "off", 13 | "class-methods-use-this": "off", // https://github.com/airbnb/javascript#classes--methods-use-this 14 | "no-underscore-dangle": "off", // we allow use `_`: this._privateMethod = ...; 15 | }, 16 | }; 17 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 8 | 9 | **Description:** 10 | 11 | 12 | 13 | **Steps to reproduce the issue:** 14 | 1. 15 | 2. 16 | 3. 17 | 18 | **Observed result:** 19 | 20 | **Expected result:** 21 | 22 | **Additional environment details (Ex: Windows, Mac, Amazon Linux etc)** 23 | 24 | **VS Code version**: 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve the Alexa Skill Toolkit for Visual Studio Code 4 | labels: bug 5 | --- 6 | 7 | **Describe the bug** 8 | 9 | 10 | 11 | **To Reproduce** 12 | 13 | 22 | 23 | **Extension Logs** 24 | 25 | 29 | 30 | **Expected behavior** 31 | 32 | 33 | 34 | **Screenshots** 35 | 36 | 37 | 38 | **Desktop (please complete the following information):** 39 | 40 | 41 | 42 | - OS: 43 | - Visual Studio Code Version: 44 | - Alexa Skill Toolkit Version: 45 | 46 | **Additional context** 47 | 48 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for the Alexa Skill Toolkit for Visual Studio Code 4 | labels: feature-request 5 | --- 6 | 7 | **Is your feature request related to a problem? Please describe.** 8 | 9 | 10 | 11 | **Describe the solution you'd like** 12 | 13 | 14 | 15 | **Describe alternatives you've considered** 16 | 17 | 18 | 19 | **Additional context** 20 | 21 | 22 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/guidance_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Ask a question 3 | about: Ask for guidance, "how to", or other questions 4 | labels: guidance 5 | --- 6 | 7 | **Desktop (please complete the following information):** 8 | 9 | 10 | 11 | - OS: 12 | - VS Code version: 13 | - Alexa Skill Toolkit extension version: 14 | 15 | **Question** 16 | 17 | 18 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Description 4 | 5 | 6 | ## Motivation and Context 7 | 8 | 9 | 10 | ## Testing 11 | 12 | 13 | 14 | 15 | ## Screenshots (if appropriate) 16 | 17 | ## Types of changes 18 | 19 | - [ ] Bug fix (non-breaking change which fixes an issue) 20 | - [ ] New feature (non-breaking change which adds functionality) 21 | - [ ] Breaking change (fix or feature that would cause existing functionality to change) 22 | 23 | ## Checklist 24 | 25 | 26 | - [ ] My code follows the code style of this project 27 | - [ ] My change requires a change to the documentation 28 | - [ ] I have updated the documentation accordingly 29 | - [ ] I have read the **README** document 30 | - [ ] I have added tests to cover my changes 31 | - [ ] All new and existing tests passed 32 | - [ ] My commit message follows [Conventional Commit Guideline](https://conventionalcommits.org/) 33 | 34 | ## License 35 | 36 | - [ ] By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice. 37 | -------------------------------------------------------------------------------- /.github/workflows/extensionTests.yml: -------------------------------------------------------------------------------- 1 | # This is a workflow to run extension tests 2 | name: Extension tests 3 | 4 | # Controls when the action will run. 5 | on: 6 | push: 7 | branches: 8 | - development 9 | - master 10 | pull_request: 11 | branches: 12 | - development 13 | 14 | jobs: 15 | build: 16 | strategy: 17 | matrix: 18 | os: [macos-latest, ubuntu-latest, windows-latest] 19 | runs-on: ${{ matrix.os }} 20 | steps: 21 | - name: Checkout 22 | uses: actions/checkout@v2 23 | - name: Install Node.js 24 | uses: actions/setup-node@v1 25 | with: 26 | node-version: 12.x 27 | - name: Install dependencies 28 | run: npm install 29 | - name: Run extension tests in linux 30 | run: xvfb-run -a npm test 31 | if: runner.os == 'Linux' 32 | - name: Run extension tests 33 | run: npm test 34 | if: runner.os != 'Linux' 35 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | # This is a workflow to publish master branch changes to VSCE 2 | name: "Release to Marketplace" 3 | 4 | # This action will only run when a new GitHub release is published. 5 | on: 6 | release: 7 | types: [published] 8 | 9 | jobs: 10 | release: 11 | runs-on: "ubuntu-latest" 12 | steps: 13 | - name: "Checkout" 14 | uses: "actions/checkout@v2" 15 | 16 | - name: "Setup NodeJS" 17 | uses: actions/setup-node@v1 18 | 19 | - name: "Install Dependencies" 20 | run: "npm install" 21 | 22 | - name: "Run Tests" 23 | run: "xvfb-run -a npm test" 24 | 25 | - name: "Publish to Marketplace" 26 | uses: "sigma/vsce-publish-action@v0.0.2" 27 | with: 28 | vsce_token: ${{ secrets.VSCE_TOKEN }} 29 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | name: Mark stale issues and pull requests 2 | 3 | on: 4 | schedule: 5 | - cron: "14 1 * * *" 6 | 7 | jobs: 8 | stale: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/stale@v3 12 | with: 13 | repo-token: ${{ secrets.GITHUB_TOKEN }} 14 | stale-issue-message: "This issue is stale since it has been open for 60 days with no activity. Please respond or this issue will be closed soon." 15 | close-issue-message: "This stale issue has been closed now. Please reopen the issue if you are still having problems with the toolkit." 16 | stale-pr-message: "This pr is stale since it has been open for 60 days with no activity. Please respond or this pr will be closed soon." 17 | stale-issue-label: "closing soon if no response" 18 | stale-pr-label: "no-pr-activity" 19 | exempt-issue-labels: "feature-request,waiting: external dependency,investigating,in progress,enhancement" 20 | days-before-stale: 60 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | node_modules 3 | .vscode-test/ 4 | *.vsix 5 | npm-debug.log 6 | .idea/ 7 | dist/ 8 | coverage 9 | .DS_Store -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Ignore Vscode artifacts 2 | .vscode* 3 | *.md 4 | node_modules 5 | out 6 | coverage 7 | dist 8 | media/**/*.html 9 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "always", 3 | "bracketSpacing": false, 4 | "embeddedLanguageFormatting": "auto", 5 | "printWidth": 140, 6 | "proseWrap": "never", 7 | "semi": true, 8 | "singleQuote": false, 9 | "tabWidth": 2, 10 | "trailingComma": "all", 11 | "useTabs": false, 12 | "endOfLine": "auto" 13 | } 14 | -------------------------------------------------------------------------------- /.versionrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "types": [ 3 | {"type": "feat", "section": "Features"}, 4 | {"type": "fix", "section": "Bug Fixes"}, 5 | {"type": "chore", "hidden": true}, 6 | {"type": "docs", "hidden": true}, 7 | {"type": "style", "hidden": true}, 8 | {"type": "refactor", "hidden": true}, 9 | {"type": "perf", "hidden": true}, 10 | {"type": "test", "hidden": true} 11 | ], 12 | "commitUrlFormat": "https://github.com/alexa/ask-toolkit-for-vscode/commits/{{hash}}", 13 | "compareUrlFormat": "https://github.com/alexa/ask-toolkit-for-vscode/compare/{{previousTag}}...{{currentTag}}" 14 | } 15 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 2 | { 3 | "version": "0.2.0", 4 | "configurations": [ 5 | { 6 | "name": "Extension", // needed for debugging and manual testing 7 | "type": "extensionHost", 8 | "request": "launch", 9 | "runtimeExecutable": "${execPath}", 10 | "args": [ 11 | // "--disable-extensions", // uncomment this for performance testing 12 | "--extensionDevelopmentPath=${workspaceFolder}" 13 | ], 14 | "outFiles": [ 15 | "${workspaceFolder}/dist/*.js" 16 | ], 17 | "preLaunchTask": "npm: webpack" 18 | }, 19 | { 20 | "name": "Extension Tests", // needed for compiling and running unit test for ts files 21 | "type": "extensionHost", 22 | "request": "launch", 23 | "runtimeExecutable": "${execPath}", 24 | "args": [ 25 | "--disable-extensions", 26 | "--extensionDevelopmentPath=${workspaceFolder}", 27 | "--extensionTestsPath=${workspaceFolder}/out/test" 28 | ], 29 | "env": { 30 | "ASK_TOOLKIT_IGNORE_WEBPACK_BUNDLE": "true", 31 | "ASK_TOOLKIT_NO_COVERAGE": "true" 32 | }, 33 | "outFiles": [ 34 | "${workspaceFolder}/out/test/**/*.js" 35 | ], 36 | "preLaunchTask": "npm: pretest" 37 | }, 38 | { 39 | "name": "Extension Tests (Coverage)", // needed for compiling and running unit test for ts files 40 | "type": "extensionHost", 41 | "request": "launch", 42 | "runtimeExecutable": "${execPath}", 43 | "args": [ 44 | "--disable-extensions", 45 | "--extensionDevelopmentPath=${workspaceFolder}", 46 | "--extensionTestsPath=${workspaceFolder}/out/test" 47 | ], 48 | "env": { 49 | "ASK_TOOLKIT_IGNORE_WEBPACK_BUNDLE": "true", 50 | "ASK_TOOLKIT_NO_COVERAGE": "false" 51 | }, 52 | "outFiles": [ 53 | "${workspaceFolder}/out/test/**/*.js" 54 | ], 55 | "preLaunchTask": "npm: pretest" 56 | } 57 | ] 58 | } 59 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.exclude": { 3 | "out": false // set this to true to hide the "out" folder with the compiled JS files 4 | }, 5 | "search.exclude": { 6 | "out": true // set this to false to include "out" folder in search results 7 | }, 8 | "editor.autoIndent": "full", 9 | "editor.tabSize": 4 // unify development format 10 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // See https://go.microsoft.com/fwlink/?LinkId=733558 2 | // for the documentation about the tasks.json format 3 | 4 | // The task is needed for launch.json -> "Extension" command for debug mode 5 | { 6 | "version": "2.0.0", 7 | "tasks": [ 8 | { 9 | "type": "npm", 10 | "script": "watch", 11 | "problemMatcher": "$tsc-watch", 12 | "isBackground": true, 13 | "presentation": { 14 | "reveal": "never" 15 | }, 16 | "group": { 17 | "kind": "build", 18 | "isDefault": true 19 | } 20 | } 21 | ] 22 | } -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .vscode-test/** 3 | out/test/** 4 | out/**/*.map 5 | src/** 6 | .gitignore 7 | tsconfig.json 8 | tslint.json 9 | .idea/** 10 | 11 | # Extra webpack files 12 | .vscode 13 | node_modules 14 | out/ 15 | src/ 16 | tsconfig.json 17 | webpack.config.js 18 | 19 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Alexa Skills Toolkit for Visual Studio Code 2 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -------------------------------------------------------------------------------- /THIRD_PARTY.txt: -------------------------------------------------------------------------------- 1 | The scripts provided herein will retrieve the Alexa Presentation Language (APL) Viewhost Web at install-time or 2 | build-time. By installing Alexa Presentation Language (APL) Viewhost Web you agree to abide by the terms and 3 | conditions available at https://github.com/alexa/apl-viewhost-web. 4 | 5 | ** vscode-icons -- https://github.com/microsoft/vscode-icons 6 | Copyright (c) Microsoft Corporation. 7 | 8 | MIT License 9 | 10 | Copyright (c) Microsoft Corporation. 11 | 12 | Permission is hereby granted, free of charge, to any person obtaining a copy 13 | of this software and associated documentation files (the "Software"), to deal 14 | in the Software without restriction, including without limitation the rights 15 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 16 | copies of the Software, and to permit persons to whom the Software is 17 | furnished to do so, subject to the following conditions: 18 | 19 | The above copyright notice and this permission notice shall be included in all 20 | copies or substantial portions of the Software. 21 | 22 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 27 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 28 | SOFTWARE -------------------------------------------------------------------------------- /extensionMain.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line eslint-comments/disable-enable-pair 2 | /* eslint-disable @typescript-eslint/no-unsafe-call */ 3 | 4 | "use strict"; 5 | 6 | // eslint-disable-next-line @typescript-eslint/no-var-requires 7 | const join = require("path").join; 8 | 9 | /** 10 | * This file serves as the extension's entryPoint. 11 | * It loads the actual entryPoint from a webpack bundle or from 12 | * tsc compiled source based on the ASK_TOOLKIT_IGNORE_WEBPACK_BUNDLE environment variable. 13 | * 14 | * This allows us to activate the extension from tests. 15 | */ 16 | 17 | Object.defineProperty(exports, "__esModule", {value: true}); 18 | 19 | const extensionEntryPath = 20 | useBundledEntryPoint() === true ? join(__dirname, "dist", "extension") : join(__dirname, "out", "src", "extension"); 21 | 22 | // eslint-disable-next-line @typescript-eslint/no-var-requires 23 | const extension = require(extensionEntryPath); 24 | 25 | async function activate(context) { 26 | await extension.activate(context); 27 | } 28 | 29 | async function deactivate() { 30 | await extension.deactivate(); 31 | } 32 | 33 | function useBundledEntryPoint() { 34 | // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions 35 | return (process.env.ASK_TOOLKIT_IGNORE_WEBPACK_BUNDLE || "false").toLowerCase() !== "true"; 36 | } 37 | 38 | exports.activate = activate; 39 | exports.deactivate = deactivate; 40 | -------------------------------------------------------------------------------- /languages/acdl/language-configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | // symbols used as brackets 3 | "brackets": [ 4 | ["{", "}"], 5 | ["[", "]"], 6 | ["(", ")"] 7 | ], 8 | // symbols that are auto closed when typing 9 | "autoClosingPairs": [ 10 | ["{", "}"], 11 | ["[", "]"], 12 | ["(", ")"], 13 | ["\"", "\""], 14 | ["'", "'"] 15 | ], 16 | // symbols that can be used to surround a selection 17 | "surroundingPairs": [ 18 | ["{", "}"], 19 | ["[", "]"], 20 | ["(", ")"], 21 | ["\"", "\""], 22 | ["'", "'"] 23 | ], 24 | // Indentation Rules 25 | "indentationRules": { 26 | "increaseIndentPattern": "^((?!\\/\\/).)*(\\{[^}\"'`]*|\\([^)\"'`]*|\\[[^\\]\"'`]*)$", 27 | "decreaseIndentPattern": "^((?!.*?\\/\\*).*\\*/)?\\s*[\\)\\}\\]].*$" 28 | }, 29 | //for adding comments using vscode shortcuts 30 | "comments": { 31 | "lineComment": "//", 32 | "blockComment": ["/*", "*/"] 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /media/aplResourceSync/aplResourceSync.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 12 | Download APL document 13 | 14 | 15 | 16 | 17 |
18 | 19 |

Download APL document

20 |
21 | 22 |
23 |

24 | This tool allows you to retrieve your skill's APL documents from the Developer Console and sync it to your local workspace. 25 |

26 |
27 |
28 |
29 |
30 | 32 | 33 |
34 |
35 |
36 |
37 | 38 | -------------------------------------------------------------------------------- /media/aplResourceSync/aplResourceSync.js: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | /* eslint-disable @typescript-eslint/explicit-function-return-type */ 7 | /* eslint-disable no-undef */ 8 | const vscode = acquireVsCodeApi(); 9 | 10 | window.onload = function () { 11 | retrieve(); 12 | window.addEventListener("message", (event) => { 13 | const message = event.data; 14 | if (message.names) { 15 | const selectList = document.getElementById("name"); 16 | selectList.innerHTML = ""; 17 | message.names.forEach((name) => { 18 | var option = document.createElement("option"); 19 | option.value = name; 20 | option.text = name; 21 | selectList.appendChild(option); 22 | }); 23 | } 24 | }); 25 | 26 | document.getElementById("downloadApl").onsubmit = function downloadApl() { 27 | const name = document.getElementById("name").value; 28 | vscode.postMessage({ 29 | action: "sync", 30 | name: name, 31 | }); 32 | return false; 33 | }; 34 | }; 35 | 36 | function retrieve() { 37 | vscode.postMessage({ 38 | action: "retrieve", 39 | }); 40 | } 41 | -------------------------------------------------------------------------------- /media/createSkill/authenticateAccount.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 12 | Authenticate Account 13 | 14 | 15 | 16 |

17 | 18 |

Authenticate your account.

19 | Use the page opened in your browser to authenticate your account to create Hosted Skills. 20 |

21 | 22 | -------------------------------------------------------------------------------- /media/createSkill/createComplete.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 12 | Skill created 13 | 14 | 15 | 16 |

17 | 18 |

Skill created.

19 | Your skill has been created successfully. 20 |

21 | 22 | -------------------------------------------------------------------------------- /media/createSkill/createInProgress.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 12 | Skill creation in progress 13 | 14 | 15 | 16 |

17 | 18 |

Skill creation in progress.

19 | Your skill is being created. Once it's ready it'll be cloned to your workspace. 20 |

21 | 22 | -------------------------------------------------------------------------------- /media/createSkill/createSkill.css: -------------------------------------------------------------------------------- 1 | @import "../toolkit.css"; 2 | 3 | .provision-cards-container { 4 | display: flex; 5 | } 6 | 7 | .card-container { 8 | width: 300px; 9 | height: 200px; 10 | border: 0.5px solid #ffffff; 11 | margin-top: 10px; 12 | margin-right: 30px; 13 | } 14 | 15 | .skill-card { 16 | min-height: 185px; 17 | max-width: 280px; 18 | min-width: 280px; 19 | flex-direction: column; 20 | align-items: flex-start; 21 | justify-content: space-between; 22 | margin-top: 10px; 23 | margin-right: 30px; 24 | border: 0.5px solid #ccc; 25 | position: relative; 26 | overflow: hidden; 27 | padding: 10px 20px; 28 | } 29 | 30 | .skill-card:hover { 31 | border: 0.5px solid #00b0e6; 32 | box-shadow: 0 0.1rem 0.5rem #00b0e6; 33 | } 34 | 35 | .skill-card .skill-card-sticker { 36 | position: absolute; 37 | margin-right: 0; 38 | width: 110px; 39 | height: 110px; 40 | font-size: 0.85rem; 41 | vertical-align: middle; 42 | background-color: #00b0e6; 43 | color: #fff; 44 | display: flex; 45 | align-items: flex-end; 46 | justify-content: center; 47 | text-align: center; 48 | right: -60px; 49 | top: -60px; 50 | transform: rotate(45deg); 51 | overflow: hidden; 52 | padding-bottom: 5px; 53 | text-transform: uppercase; 54 | } 55 | 56 | .skill-card .title { 57 | font-family: "Amazon Ember Regular", "Helvetica Neue", Arial, Helvetica, Roboto, sans-serif; 58 | font-size: 1.7em; 59 | margin: 20px 0px; 60 | } 61 | 62 | .skill-card .details { 63 | font-family: "Amazon Ember Regular", "Helvetica Neue", Arial, Helvetica, Roboto, sans-serif; 64 | font-size: 1em; 65 | } 66 | 67 | .create-btn-text { 68 | float: right; 69 | margin-right: 15px; 70 | } 71 | -------------------------------------------------------------------------------- /media/deployHostedSkill/deployInProgress.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 12 | Skill deployment in progress 13 | 14 | 15 | 16 |

17 | 18 |

Skill deployment in progress.

19 | Your skill is being deployed. Check the notification area for status updates. 20 |

21 | 22 | -------------------------------------------------------------------------------- /media/deployNonHostedSkill/deployInProgress.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 12 | Skill deployment in progress 13 | 14 | 15 | 16 |

17 | 18 |

Skill deployment in progress.

19 | Your skill is being deployed. Check the notification area for status updates. 20 |

21 | 22 | -------------------------------------------------------------------------------- /media/deploySkill/deployInProgress.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 12 | Skill deployment in progress 13 | 14 | 15 | 16 |

17 | 18 |

Skill deployment in progress.

19 | Your skill is being deployed. Check the notification area for status updates. 20 |

21 | 22 | -------------------------------------------------------------------------------- /media/deploySkill/deploySkill.js: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | const vscode = acquireVsCodeApi(); 7 | 8 | window.onload = function () { 9 | const deployBtn = document.getElementById("deployBtn"); 10 | const noteDeployBtnActive = document.getElementById("noteBtnActive"); 11 | const noteDeployBtnInactive = document.getElementById("noteBtnInactive"); 12 | 13 | // Handle the message inside the webview 14 | window.addEventListener("message", (event) => { 15 | const message = event.data; // The JSON data our extension sent 16 | if (message.deploymentStatus !== undefined && message.deploymentStatus === "done") { 17 | deployBtn.disabled = false; 18 | noteDeployBtnActive.style.display = "inline"; 19 | noteDeployBtnInactive.style.display = "none"; 20 | } else if (message.changesExist !== undefined) { 21 | if (message.changesExist) { 22 | deployBtn.disabled = false; 23 | noteDeployBtnActive.style.display = "inline"; 24 | noteDeployBtnInactive.style.display = "none"; 25 | } else { 26 | deployBtn.disabled = true; 27 | noteDeployBtnActive.style.display = "none"; 28 | noteDeployBtnInactive.style.display = "inline"; 29 | } 30 | } 31 | }); 32 | 33 | document.getElementById("deploySkill").onsubmit = function deploySkill() { 34 | const deployBtn = document.getElementById("deployBtn"); 35 | deployBtn.disabled = true; 36 | 37 | vscode.postMessage("deploySkill"); 38 | return false; 39 | }; 40 | 41 | document.getElementById("refresh").onclick = function refresh() { 42 | vscode.postMessage("refresh"); 43 | }; 44 | }; 45 | -------------------------------------------------------------------------------- /media/deviceRegistry/deviceRegistryDone.js: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | 7 | const vscode = acquireVsCodeApi(); 8 | const DEVICE_MESSAGE_TYPE = { 9 | START_AVS_MODE: "startAvsMode", 10 | CANCEL_AVS_MODE: "cancelAvsMode", 11 | TO_FIRST_PAGE: "toFirstPage", 12 | }; 13 | const CONSTANTS = { 14 | REMEMBER_DEVICE: "rememberDevice", 15 | CHECKED_TEXT: "checkedText", 16 | NO_CHECKED_TEXT: "notCheckedText", 17 | }; 18 | 19 | window.onload = function () { 20 | yesBtn.onclick = openAvsMode; 21 | cancelBtn.onclick = cancelAvsMode; 22 | firstPage.onclick = backToFirstPage; 23 | rememberDevice.onclick = showPromptInfo; 24 | const previousState = vscode.getState(); 25 | productId.innerText = previousState.productId; 26 | window.addEventListener("message", (event) => {}); 27 | }; 28 | 29 | function openAvsMode() { 30 | const isRememberDeviceChecked = document.getElementById(CONSTANTS.REMEMBER_DEVICE).checked; 31 | vscode.postMessage({ 32 | type: DEVICE_MESSAGE_TYPE.START_AVS_MODE, 33 | isRememberDeviceChecked, 34 | }); 35 | } 36 | 37 | function cancelAvsMode() { 38 | vscode.postMessage({ 39 | type: DEVICE_MESSAGE_TYPE.CANCEL_AVS_MODE, 40 | }); 41 | } 42 | 43 | function backToFirstPage() { 44 | vscode.postMessage({ 45 | type: DEVICE_MESSAGE_TYPE.TO_FIRST_PAGE, 46 | }); 47 | } 48 | 49 | function showPromptInfo() { 50 | if (document.getElementById(CONSTANTS.REMEMBER_DEVICE).checked) { 51 | document.getElementById(CONSTANTS.CHECKED_TEXT).style.display = "block"; 52 | document.getElementById(CONSTANTS.NO_CHECKED_TEXT).style.display = "none"; 53 | } else { 54 | document.getElementById(CONSTANTS.CHECKED_TEXT).style.display = "none"; 55 | document.getElementById(CONSTANTS.NO_CHECKED_TEXT).style.display = "block"; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /media/deviceRegistry/deviceRegistryInProgress.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 12 | Device registration: in progress 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 |

Device registry

21 |
22 |
23 |
24 |

25 | Create device

26 |
27 |
28 |
29 |

Register device

30 |
31 |
32 |
33 |

Confirm device

34 |
35 |
36 |
37 | 38 |
39 |
40 |
41 |

Register your device

42 | 43 | To register the device with your Amazon account, open Amazon account linking in your browser. 44 | After you link your account, come back to this page to complete device registration. 45 | 46 |

47 | If an error occurs during the account linking process, return to Create device to verify your product information. 49 |

50 |

51 |
52 |

Go to Amazon account linking and 53 | input the code: 54 |

55 |

56 |
57 |

58 |
59 |
60 |
61 |
62 | 63 | 64 | -------------------------------------------------------------------------------- /media/deviceRegistry/deviceRegistryInProgress.js: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | 7 | const vscode = acquireVsCodeApi(); 8 | const DEVICE_MESSAGE_TYPE = { 9 | USER_CODE: "userCode", 10 | RETRY: "retry", 11 | RETRY_USER_CODE: "retryForUserCode", 12 | TO_FIRST_PAGE: "toFirstPage", 13 | }; 14 | 15 | window.onload = function () { 16 | //Read user_code 17 | const label = document.getElementById("userCode"); 18 | const previousState = vscode.getState(); 19 | label.innerHTML = previousState.userCode; 20 | validTime.innerText = "Valid until " + previousState.expiredTime; 21 | document.getElementById("externalLink").href = previousState.externalLink; 22 | 23 | refresh.onclick = retryForUserCode; 24 | firstPage.onclick = backToFirstPage; 25 | firstPageLink.onclick = backToFirstPage; 26 | // eslint-disable-next-line @typescript-eslint/no-misused-promises 27 | window.addEventListener("message", async (event) => { 28 | const message = event.data; 29 | if (message.type === DEVICE_MESSAGE_TYPE.USER_CODE) { 30 | const expiredTime = getExpiredTime(message.response.expires_in); 31 | const state = vscode.getState() || {}; 32 | state["userCode"] = message.response.user_code; 33 | state["expiredTime"] = expiredTime; 34 | state["externalLink"] = message.response.verification_uri; 35 | await new Promise(() => { 36 | vscode.setState(state); 37 | const label = document.getElementById("userCode"); 38 | label.innerHTML = message.response.user_code; 39 | validTime.innerText = "Valid until " + expiredTime; 40 | document.getElementById("externalLink").href = message.response.verification_uri; 41 | }); 42 | } else if (message.type === DEVICE_MESSAGE_TYPE.RETRY) { 43 | validTime.innerText = message.information.toString(); 44 | } 45 | }); 46 | }; 47 | 48 | function retryForUserCode() { 49 | validTime.innerText = ""; 50 | vscode.postMessage({ 51 | type: DEVICE_MESSAGE_TYPE.RETRY_USER_CODE, 52 | }); 53 | } 54 | 55 | function backToFirstPage() { 56 | vscode.postMessage({ 57 | type: DEVICE_MESSAGE_TYPE.TO_FIRST_PAGE, 58 | }); 59 | } 60 | 61 | function getExpiredTime(expiresTime) { 62 | const datetime = new Date(); 63 | datetime.setSeconds(datetime.getSeconds() + expiresTime); 64 | const hour = datetime.getHours(); 65 | const minute = datetime.getMinutes(); 66 | const expiredTime = (hour > 9 ? hour.toString() : `0${hour}`) + ":" + (minute > 9 ? minute.toString() : `0${minute}`); 67 | return expiredTime; 68 | } 69 | -------------------------------------------------------------------------------- /media/docs/create_apl_doc.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexa/ask-toolkit-for-vscode/2fd1033f594a3281e1612d51d0c216a14e5e344d/media/docs/create_apl_doc.gif -------------------------------------------------------------------------------- /media/docs/create_self_hosted_skill.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexa/ask-toolkit-for-vscode/2fd1033f594a3281e1612d51d0c216a14e5e344d/media/docs/create_self_hosted_skill.gif -------------------------------------------------------------------------------- /media/docs/create_skill.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexa/ask-toolkit-for-vscode/2fd1033f594a3281e1612d51d0c216a14e5e344d/media/docs/create_skill.gif -------------------------------------------------------------------------------- /media/docs/deploy_hosted_skill.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexa/ask-toolkit-for-vscode/2fd1033f594a3281e1612d51d0c216a14e5e344d/media/docs/deploy_hosted_skill.gif -------------------------------------------------------------------------------- /media/docs/deploy_self_hosted_skill.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexa/ask-toolkit-for-vscode/2fd1033f594a3281e1612d51d0c216a14e5e344d/media/docs/deploy_self_hosted_skill.gif -------------------------------------------------------------------------------- /media/docs/deploy_skill.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexa/ask-toolkit-for-vscode/2fd1033f594a3281e1612d51d0c216a14e5e344d/media/docs/deploy_skill.gif -------------------------------------------------------------------------------- /media/docs/download_self_hosted_skill.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexa/ask-toolkit-for-vscode/2fd1033f594a3281e1612d51d0c216a14e5e344d/media/docs/download_self_hosted_skill.gif -------------------------------------------------------------------------------- /media/docs/local_debugging.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexa/ask-toolkit-for-vscode/2fd1033f594a3281e1612d51d0c216a14e5e344d/media/docs/local_debugging.gif -------------------------------------------------------------------------------- /media/docs/sign_in_flow.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexa/ask-toolkit-for-vscode/2fd1033f594a3281e1612d51d0c216a14e5e344d/media/docs/sign_in_flow.gif -------------------------------------------------------------------------------- /media/images/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Amazon, Alexa, and all related logos (the “Marks”) are trademarks of Amazon.com, or its affiliates. 2 | Subject to your compliance with the terms of the Amazon Developer Services Agreement, 3 | available at https://developer.amazon.com/support/legal/da, and the Trademark Guidelines, 4 | available at https://developer.amazon.com/support/legal/tuabg#trademark, you may use the Marks solely 5 | for the purpose expressly authorized in the Alexa Skills Toolkit for VS Code. -------------------------------------------------------------------------------- /media/images/ask_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexa/ask-toolkit-for-vscode/2fd1033f594a3281e1612d51d0c216a14e5e344d/media/images/ask_icon.png -------------------------------------------------------------------------------- /media/initialLogin/initialLogin.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 12 | Sign in 13 | 52 | 61 | 62 | 63 |
64 | 65 |

Sign in

66 |
67 | 68 |
69 |

70 | To start using the Alexa Skills Kit (ASK) Toolkit you first need to sign in with your Amazon Developer Account 71 | and grant permission to access your skills. 72 |

73 |
74 | 75 |
76 |
77 | 78 | -------------------------------------------------------------------------------- /media/interactionModelSync/interactionModelSync.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 12 | Download interaction model 13 | 14 | 15 | 16 | 17 |
18 | 19 |

Download latest interaction model

20 |
21 | 22 |
23 |

24 | This tool allows you to download your skill's latest interaction model from the Developer Console to your local workspace. 25 |

26 | Only changes that have been built in the Console can be retrieved through this tool. 27 |

28 |
29 |
30 |
31 |

Locale

32 |
33 | 50 | 51 |
52 |
53 |
54 |
55 |
56 | 57 | -------------------------------------------------------------------------------- /media/interactionModelSync/interactionModelSync.js: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | /* eslint-disable @typescript-eslint/explicit-function-return-type */ 7 | /* eslint-disable no-undef */ 8 | const vscode = acquireVsCodeApi(); 9 | 10 | window.onload = function () { 11 | document.getElementById("syncIm").onsubmit = function syncIm() { 12 | const locale = document.getElementById("locale").value; 13 | vscode.postMessage({ 14 | locale: locale, 15 | }); 16 | return false; 17 | }; 18 | }; 19 | -------------------------------------------------------------------------------- /media/manifestSync/manifestSync.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 12 | Download manifest 13 | 14 | 15 | 16 | 17 |
18 | 19 |

Download latest skill manifest

20 |
21 | 22 |
23 |
24 |
25 | This tool allows you to download your skill's latest manifest from the Developer Console to your local workspace. 26 |
27 | 28 |
29 |
30 |
31 |
32 |
33 | 34 | -------------------------------------------------------------------------------- /media/manifestSync/manifestSync.js: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | /* eslint-disable @typescript-eslint/explicit-function-return-type */ 7 | /* eslint-disable no-undef */ 8 | const vscode = acquireVsCodeApi(); 9 | 10 | window.onload = function () { 11 | document.getElementById("syncManifest").onsubmit = function syncManifest() { 12 | vscode.postMessage({}); 13 | return false; 14 | }; 15 | }; 16 | -------------------------------------------------------------------------------- /media/previewApl/previewApl.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 11 | 12 | APL Preview 13 | 14 | 15 | 16 | 17 | 18 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /media/previewApl/previewApl.js: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | 7 | // load separate js file by vscode and renderer init sometimes has race condition 8 | let renderer; 9 | const vscode = acquireVsCodeApi(); 10 | const EMPTY_STRING = ""; 11 | 12 | window.onload = function () { 13 | initialize(); 14 | }; 15 | 16 | window.addEventListener("message", (event) => { 17 | const message = event.data; // The json data that the extension sent 18 | const sendCommandEvent = (commandEvent) => {}; 19 | loadAplDoc(renderer, message.document, message.datasources, JSON.parse(message.viewport), message?.mode,EMPTY_STRING, sendCommandEvent); 20 | }); 21 | 22 | function initialize() { 23 | // init renderer engine 24 | AplRenderer.initEngine().then(() => { 25 | vscode.postMessage({}); 26 | }); 27 | } 28 | -------------------------------------------------------------------------------- /media/profileManager/authFlowResult.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 12 | Sign in result 13 | 14 | 15 | 16 |

17 | 18 |

${title}

19 | ${message} 20 |

21 | 22 | -------------------------------------------------------------------------------- /media/profileManager/initiateAuthFlow.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 12 | Sign in using your browser 13 | 14 | 15 | 16 |

17 | 18 |

Sign in using your browser.

19 | Use the page opened in your browser to log in with your Amazon Developer Account and grant the toolkit 20 | permission to access your skill resources. 21 |

22 | 23 | -------------------------------------------------------------------------------- /media/profileManager/profileManager.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 12 | Profile Manager 13 | 14 | 15 | 16 | 17 |
18 | 19 |

Profile manager

20 |
21 | 22 |
23 |

24 | The profile manager guides you through logging in with your Amazon Developer Account and granting permission for 25 | the toolkit to access your Alexa Skills resources. The toolkit supports storing credentials under multiple profiles, 26 | useful if you are working across multiple developer accounts. The currently in-use profile is displayed on the toolkit 27 | status bar and can be changed at any time. 28 |

29 | Once you select a new or existing profile, your web browser will be opened to a Login with Amazon prompt and 30 | you will be guided through the process of signing in and granting permission to the toolkit. 31 |

32 |
33 |
34 |
35 |

Create new profile

36 |
37 | 38 |

39 | 40 |
41 |
42 |
43 |

Delete existing profile

44 |
45 | 46 |

47 | 48 |



49 | 50 |
51 |
52 |
53 |
54 | 55 | -------------------------------------------------------------------------------- /media/profileManager/profileManager.js: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | const vscode = acquireVsCodeApi(); 7 | 8 | window.onload = function () { 9 | document.getElementById("createForm").onsubmit = function createNewProfile() { 10 | document.getElementById("createButton").disabled = true; 11 | const profileName = document.getElementById("newProfileName").value; 12 | vscode.postMessage({profileName: profileName}); 13 | return false; 14 | }; 15 | 16 | document.getElementById("deleteForm").onsubmit = function deleteProfile() { 17 | if (window.deleteConfirmed) { 18 | const profileName = document.getElementById("deleteProfiles").value; 19 | vscode.postMessage({deleteProfile: profileName}); 20 | document.getElementById("submitDelete").value = "Delete"; 21 | document.getElementById("deleteMessage").innerHTML = ""; 22 | window.deleteConfirmed = false; 23 | } else { 24 | window.deleteConfirmed = true; 25 | document.getElementById("submitDelete").value = "Confirm delete"; 26 | } 27 | return false; 28 | }; 29 | 30 | document.getElementById("deleteProfiles").onchange = function clearDelete() { 31 | window.deleteConfirmed = false; 32 | document.getElementById("submitDelete").value = "Delete"; 33 | document.getElementById("deleteMessage").innerHTML = ""; 34 | }; 35 | }; 36 | 37 | window.addEventListener("message", (event) => { 38 | const message = event.data; 39 | 40 | if (message.reEnable) { 41 | document.getElementById("createButton").disabled = false; 42 | return; 43 | } 44 | 45 | var submits = document.getElementsByClassName("submit-existing"); 46 | for (x = 0; x < submits.length; x++) { 47 | submits[x].disabled = true; 48 | } 49 | var selects = document.getElementsByClassName("existing-profiles"); 50 | for (x = 0; x < selects.length; x++) { 51 | resetSelect(selects[x]); 52 | } 53 | if (message.profiles.length > 0) { 54 | for (i = 0; i < message.profiles.length; i++) { 55 | for (x = 0; x < selects.length; x++) { 56 | var option = document.createElement("option"); 57 | option.text = message.profiles[i]; 58 | option.value = message.profiles[i]; 59 | selects[x].appendChild(option); 60 | } 61 | } 62 | for (x = 0; x < selects.length; x++) { 63 | selects[x].disabled = false; 64 | } 65 | for (x = 0; x < submits.length; x++) { 66 | submits[x].disabled = false; 67 | } 68 | } 69 | }); 70 | 71 | function resetSelect(select) { 72 | select.disabled = true; 73 | select.innerHTML = ""; 74 | } 75 | 76 | function updateProfile() { 77 | const profileName = document.getElementById("updateProfiles").value; 78 | vscode.postMessage({profileName: profileName}); 79 | } 80 | -------------------------------------------------------------------------------- /media/skillInfo/skillInformation.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 12 | Skill Information 13 | 14 | 15 | 16 | 17 |
18 | 19 |

Skill Information

20 |
21 | 22 |
23 |

24 | The Skill Information page provides some basic information about your skill. You can check and confirm 25 | the skill metadata before going ahead with cloning the skill for local development. 26 |

27 |
28 |
29 |
30 |

${skillName}

31 |
32 | 33 | 34 |

35 |

36 | 37 | 38 | 39 |
40 |

41 | 42 |

43 | 44 |

45 | 46 |

47 |

48 | 49 |
50 |
51 |
52 |
53 |
54 | 55 | -------------------------------------------------------------------------------- /media/skillInfo/skillInformation.js: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | /* eslint-disable @typescript-eslint/explicit-function-return-type */ 7 | /* eslint-disable no-undef */ 8 | const vscode = acquireVsCodeApi(); 9 | 10 | window.onload = function () { 11 | document.getElementById("cloneSkill").onsubmit = function cloneSkill() { 12 | vscode.postMessage(); 13 | return false; 14 | }; 15 | 16 | const skillLocaleNameTable = document.getElementById("skillLocaleNames"); 17 | const tableBody = skillLocaleNameTable.tBodies[0]; 18 | 19 | let localesInfoArray = document.getElementById("localeNameMap").value; 20 | localesInfoArray = JSON.parse(localesInfoArray.replace(/'/g, '"')); 21 | 22 | localesInfoArray.forEach((localeInfo) => { 23 | const row = document.createElement("tr"); 24 | 25 | const column1 = document.createElement("td"); 26 | column1.appendChild(document.createTextNode(localeInfo[0])); 27 | row.appendChild(column1); 28 | 29 | const column2 = document.createElement("td"); 30 | column2.appendChild(document.createTextNode(localeInfo[1])); 31 | row.appendChild(column2); 32 | 33 | tableBody.appendChild(row); 34 | }); 35 | }; 36 | -------------------------------------------------------------------------------- /src/acdlServer/keywordsProvider.ts: -------------------------------------------------------------------------------- 1 | import {ACDLLexer} from "@alexa/acdl/dist/cjs/syntax/ACDLLexer"; 2 | 3 | // valid keywords tart with a-zA-Z, end with a-zA-Z, no space 4 | const validKeywordCharacters = /^[a-zA-Z]+$/; 5 | 6 | // keywords that are excluded from the list 7 | const excludedKeywords = ["multimodal"]; 8 | 9 | // extra keywords that are not part of the ACDLLexer 10 | const extraKeywords = ["confirmAction", "confirmArgs", "ensure", "expect", "response", "skill", "utterances"]; 11 | 12 | // TODO: How to get the builtin expressions? 13 | // eslint-disable-next-line @typescript-eslint/dot-notation 14 | export const KEYWORDS = ACDLLexer["_LITERAL_NAMES"].reduce((acc, literalName) => { 15 | // ACDLLexer['_LITERAL_NAMES'] contains items that are undefined, filter them out. 16 | if (typeof literalName === "string") { 17 | // string items are wrapped with single quote, change "'namespace'" to "namespace" 18 | const sanitizedLiteralName = literalName.replace(/[']/g, ""); 19 | // filter out the literaNames that are symbols like ">" 20 | if (validKeywordCharacters.test(sanitizedLiteralName) && !excludedKeywords.includes(sanitizedLiteralName)) { 21 | acc.push(sanitizedLiteralName); 22 | } 23 | } 24 | return acc; 25 | }, extraKeywords); 26 | -------------------------------------------------------------------------------- /src/aplContainer/commands/syncAplResource.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import {AbstractCommand, CommandContext} from "../../runtime"; 7 | import {AplResourceSyncWebview} from "../webViews/aplResourceSyncWebview"; 8 | import * as vscode from "vscode"; 9 | import {EXTENSION_COMMAND_CONFIG} from "../config/configuration"; 10 | import {Logger} from "../../logger"; 11 | 12 | export class SyncAplResourceCommand extends AbstractCommand { 13 | constructor() { 14 | super(EXTENSION_COMMAND_CONFIG.DOWNLOAD_APL_DOCUMENT.NAME); 15 | } 16 | 17 | // eslint-disable-next-line @typescript-eslint/require-await 18 | async execute(context: CommandContext, skillFolderWs: vscode.Uri): Promise { 19 | Logger.debug(`Calling method: ${this.commandName}`); 20 | const aplResourceSyncWebview = new AplResourceSyncWebview( 21 | "Sync APL resource", 22 | "syncAplResource", 23 | context.extensionContext, 24 | skillFolderWs, 25 | ); 26 | aplResourceSyncWebview.showView(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/aplContainer/constants/messages.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | export const PROMPT_MESSAGES = Object.freeze({ 7 | CREATE_SAMPLE_TEMPLATE: "Choose a template", 8 | ENTER_NAME_FOR_TEMPLATE: "Enter a name for the APL document", 9 | PREVIEW_APL_NO_APL_FOUND_IN_DIRECTORY: "No APL documents found. Create an APL document before selecting Preview", 10 | TEMPLATE_NAMING_HINTS: "Hint: Template name only accepts alphanumeric characters, periods, underscores and dashes", 11 | SYNC_APL_RESOURCE_RETRIEVING_INFORMATION: "Retrieving list of APL documents...", 12 | SYNC_APL_RESOURCE_NO_RESOURCE_FOUND_IN_CONSOLE: 13 | "No APL documents found in the Alexa Developer Console. Create an APL document locally by clicking on Alexa Presentation Language > Create.", 14 | }); 15 | 16 | export const ERROR_MESSAGES = Object.freeze({ 17 | CHANGE_VIEWPORT_PROFILE_NO_APL_PREVIEW: 18 | "There is no APL document selected. Choose an APL document and then choose Change viewport profile", 19 | CHANGE_VIEWPORT_PROFILE_NO_MATCHED_VIEWPORT: "The selected viewport profile cannot be loaded.", 20 | CREATE_APL_FROM_SAMPLE_TEMPLATE_NAME_EXISTS: "The APL document must have a unique name.", 21 | CREATE_APL_FROM_SAMPLE_NO_SAMPLE_TEMPLATE_FOUND: "This APL document sample is not available.", 22 | PREVIEW_APL_NO_APL_DOCUMENT_FOUND: "There is no document.json file in the APL document folder, so the APL cannot be previewed.", 23 | NO_SKILL_PACKAGE_FOUND: "There was a failure loading APL from the zip file", 24 | }); 25 | 26 | export const SUCCESS_MESSAGES = Object.freeze({ 27 | CREATE_APL_FROM_SAMPLE_SUCCESS: "Success: APL document created.", 28 | SYNC_APL_RESOURCE_SUCCESS: "Success: APL document downloaded.", 29 | }); 30 | -------------------------------------------------------------------------------- /src/aplContainer/models/index.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import * as vscode from "vscode"; 7 | 8 | /** 9 | * Interface of APL resource 10 | * 11 | * @export 12 | * @interface AplResource 13 | */ 14 | export interface AplResource { 15 | sources?: string; 16 | document: string; 17 | datasources?: string; 18 | } 19 | 20 | /** 21 | * Interface of APL Sample Template QuickPickItem 22 | * 23 | * @export 24 | * @interface SampleTemplateQuickPickItem 25 | */ 26 | export interface SampleTemplateQuickPickItem extends vscode.QuickPickItem { 27 | id: string; 28 | } 29 | -------------------------------------------------------------------------------- /src/aplContainer/utils/viewportProfileHelper.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | 7 | import {getDefaultViewport, IViewport, ViewportMode, ViewportShape} from "apl-suggester"; 8 | 9 | /** 10 | * DP -> Pixel convert 11 | * https://aplspec.aka.corp.amazon.com/release-1.3/html/viewport.html?highlight=viewport#dpi 12 | * @param {number} dpValue - dp value 13 | * @param {number} dpi - dpi 14 | */ 15 | export const getPixelValueFromDpValue = (dpValue: number, dpi: number): number => { 16 | return Math.round((dpi / 160) * dpValue); 17 | }; 18 | 19 | /** 20 | * IViewport -> IViewportCharacteristics (defined in APL webviewhost) convert 21 | * https://code.amazon.com/packages/APLViewhostWeb/blobs/6c9fe801a56e6b66213f930ba19a107e1c9462b7/--/js/apl-html/src/APLRenderer.ts#L36 22 | * @param {IViewport} activeViewport 23 | * @returns {any} - viewport characteristic used in APL webviewhost renderer config 24 | */ 25 | export function getViewportCharacteristicsFromViewPort(activeViewport: IViewport): any { 26 | if (activeViewport) { 27 | return { 28 | isRound: activeViewport.shape === ViewportShape.ROUND, 29 | height: getPixelValueFromDpValue(activeViewport.height, activeViewport.dpi), 30 | width: getPixelValueFromDpValue(activeViewport.width, activeViewport.dpi), 31 | dpi: activeViewport.dpi, 32 | }; 33 | } 34 | return { 35 | isRound: false, 36 | height: 600, 37 | width: 1024, 38 | dpi: 160, 39 | }; 40 | } 41 | 42 | export const DEFAULT_VIEWPORT_CHARACTERISTICS = getViewportCharacteristicsFromViewPort(getDefaultViewport()); 43 | export const DEFAULT_VIEWPORT_NAME = "Echo Show 1"; 44 | export const DEFAULT_VIEWPORT_MODE = ViewportMode.HUB; -------------------------------------------------------------------------------- /src/askContainer/commands/accessToken.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import {AbstractCommand, CommandContext, Utils, Resource} from "../../runtime"; 7 | 8 | export class AccessTokenCommand extends AbstractCommand { 9 | async execute(context: CommandContext): Promise { 10 | const profile = Utils.getCachedProfile(context.extensionContext); 11 | if (profile === undefined) { 12 | throw new Error("Profile not defined."); 13 | } 14 | const access_token = await Utils.refreshToken(profile); 15 | if (access_token === undefined) { 16 | throw new Error("Access token unavailable."); 17 | } 18 | return access_token; 19 | } 20 | constructor() { 21 | super("ask.accessToken"); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/askContainer/commands/cloneSkill.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import {SmapiResource, AbstractCommand, CommandContext} from "../../runtime"; 7 | 8 | import {SkillInfo} from "../../models/types"; 9 | import {executeClone} from "../../utils/cloneSkillHelper"; 10 | import {Logger} from "../../logger"; 11 | 12 | export class CloneSkillCommand extends AbstractCommand { 13 | constructor() { 14 | super("askContainer.skillsConsole.cloneSkill"); 15 | } 16 | 17 | async execute(context: CommandContext, skillInfo: SmapiResource): Promise { 18 | Logger.debug(`Calling method: ${this.commandName}, args: `, skillInfo); 19 | await executeClone(context, skillInfo); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/askContainer/commands/cloneSkill/cloneNonHostedSkillManager.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import * as fs from "fs"; 7 | import * as path from "path"; 8 | import * as vscode from "vscode"; 9 | 10 | import {AbstractCloneSkillManager} from "./abstractCloneSkillManager"; 11 | import {createSkillPackageFolder, syncSkillPackage} from "../../../utils/skillPackageHelper"; 12 | import {SKILL, SKILL_FOLDER} from "../../../constants"; 13 | import {Logger} from "../../../logger"; 14 | 15 | export class CloneNonHostedSkillManager extends AbstractCloneSkillManager { 16 | async cloneSkill(progressBar: vscode.Progress<{message: string; increment: number}>): Promise { 17 | Logger.verbose(`Calling method: cloneSkill, args: `, progressBar); 18 | 19 | const incrAmount = 25; 20 | fs.mkdirSync(path.join(this.fsPath, SKILL_FOLDER.HIDDEN_ASK_FOLDER)); 21 | this.createAskResourcesConfig(false); 22 | this.createAskStateConfig(); 23 | progressBar.report({ 24 | increment: incrAmount, 25 | message: "Skill metadata files created. Checking skill package...", 26 | }); 27 | 28 | createSkillPackageFolder(this.fsPath); 29 | progressBar.report({ 30 | increment: incrAmount, 31 | message: "Skill package created. Syncing from service...", 32 | }); 33 | 34 | const skillPkgPath = path.join(this.fsPath, SKILL_FOLDER.SKILL_PACKAGE.NAME); 35 | const skillPackageStatus = await syncSkillPackage( 36 | skillPkgPath, 37 | this.skillInfo.data.skillSummary.skillId!, 38 | this.context, 39 | SKILL.STAGE.DEVELOPMENT, 40 | ); 41 | void this.postCloneSkill(false, skillPackageStatus.skill?.eTag); 42 | progressBar.report({ 43 | increment: incrAmount, 44 | message: "Skill package synced.", 45 | }); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/askContainer/commands/cloneSkillFromConsole.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import {CloneSkillCommand} from "./cloneSkill/cloneSkill"; 7 | 8 | export class CloneSkillFromConsoleCommand extends CloneSkillCommand { 9 | constructor() { 10 | super("askContainer.skillsConsole.cloneSkillFromConsole"); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/askContainer/commands/createSkill.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import {AbstractCommand, CommandContext} from "../../runtime"; 7 | import {CreateSkillWebview} from "../webViews/createSkillWebview/createSkillWebview"; 8 | import {Logger} from "../../logger"; 9 | 10 | export class CreateSkillCommand extends AbstractCommand { 11 | private createSkillWebview: CreateSkillWebview; 12 | 13 | constructor(createSkillWebview: CreateSkillWebview) { 14 | super("ask.new"); 15 | this.createSkillWebview = createSkillWebview; 16 | } 17 | 18 | // eslint-disable-next-line @typescript-eslint/require-await 19 | async execute(context: CommandContext): Promise { 20 | Logger.debug(`Calling method: ${this.commandName}`); 21 | this.createSkillWebview.showView(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/askContainer/commands/deployHostedSkill.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import * as vscode from "vscode"; 7 | import {logAskError} from "../../exceptions"; 8 | import {Logger} from "../../logger"; 9 | import {AbstractCommand, CommandContext} from "../../runtime"; 10 | import {checkProfileSkillAccess} from "../../utils/skillHelper"; 11 | import {DeployHostedSkillWebview} from "../webViews/deploySkillWebview/deployHostedSkillWebview"; 12 | 13 | export class DeployHostedSkillCommand extends AbstractCommand { 14 | private deploySkillWebview: DeployHostedSkillWebview; 15 | 16 | constructor(webview: DeployHostedSkillWebview) { 17 | super("askContainer.skillsConsole.deployHostedSkill"); 18 | this.deploySkillWebview = webview; 19 | } 20 | 21 | // eslint-disable-next-line @typescript-eslint/require-await 22 | async execute(context: CommandContext, skillFolderWs: vscode.WorkspaceFolder): Promise { 23 | Logger.debug(`Calling method: ${this.commandName}`); 24 | try { 25 | checkProfileSkillAccess(context.extensionContext); 26 | 27 | this.deploySkillWebview.showView(); 28 | } catch (err) { 29 | throw logAskError(`Cannot open deploy skill view`, err, true); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/askContainer/commands/deployNonHostedSkill.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import * as vscode from "vscode"; 7 | import {logAskError} from "../../exceptions"; 8 | import {Logger} from "../../logger"; 9 | import {AbstractCommand, CommandContext} from "../../runtime"; 10 | import {checkProfileSkillAccess} from "../../utils/skillHelper"; 11 | import {DeployNonHostedSkillWebview} from "../webViews/deploySkillWebview/deployNonHostedSkillWebview"; 12 | 13 | export class DeployNonHostedSkillCommand extends AbstractCommand { 14 | private deployNonHostedSkillWebview: DeployNonHostedSkillWebview; 15 | 16 | constructor(webview: DeployNonHostedSkillWebview) { 17 | super("askContainer.skillsConsole.deploySelfHostedSkill"); 18 | this.deployNonHostedSkillWebview = webview; 19 | } 20 | 21 | // eslint-disable-next-line @typescript-eslint/require-await 22 | async execute(context: CommandContext, skillFolderWs: vscode.WorkspaceFolder): Promise { 23 | Logger.debug(`Calling method: ${this.commandName}`); 24 | try { 25 | checkProfileSkillAccess(context.extensionContext); 26 | 27 | this.deployNonHostedSkillWebview.showView(); 28 | } catch (err) { 29 | throw logAskError(`Cannot open deploy skill view`, err, true); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/askContainer/commands/deploySkill.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import * as vscode from "vscode"; 7 | import {logAskError} from "../../exceptions"; 8 | import {Logger} from "../../logger"; 9 | import {AbstractCommand, CommandContext} from "../../runtime"; 10 | import {checkProfileSkillAccess} from "../../utils/skillHelper"; 11 | import {DeploySkillWebview} from "../webViews/deploySkillWebview"; 12 | 13 | export class DeploySkillCommand extends AbstractCommand { 14 | private deploySkillWebview: DeploySkillWebview; 15 | 16 | constructor(webview: DeploySkillWebview) { 17 | super("askContainer.skillsConsole.deploySkill"); 18 | this.deploySkillWebview = webview; 19 | } 20 | 21 | async execute(context: CommandContext, skillFolderWs: vscode.WorkspaceFolder): Promise { 22 | Logger.debug(`Calling method: ${this.commandName}`); 23 | try { 24 | checkProfileSkillAccess(context.extensionContext); 25 | 26 | // eslint-disable-next-line @typescript-eslint/await-thenable 27 | await this.deploySkillWebview.showView(); 28 | } catch (err) { 29 | throw logAskError(`Cannot open deploy skill view`, err, true); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/askContainer/commands/deviceDeletionCommand.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | 7 | import * as vscode from "vscode"; 8 | import {Logger} from "../../logger"; 9 | import {AbstractCommand, CommandContext} from "../../runtime"; 10 | import {onDeviceDeletionEventEmitter} from "../events"; 11 | import {getRegisteredDeviceId, deleteRegisteredDevice} from "../../utils/avs/deviceTokenUtil"; 12 | import {ERRORS} from "../../constants"; 13 | 14 | export class DeviceDeletionCommand extends AbstractCommand { 15 | constructor() { 16 | super("askContainer.skillsConsole.deviceDeletion"); 17 | } 18 | 19 | async execute(context: CommandContext): Promise { 20 | Logger.debug(`Calling method: ${this.commandName}`); 21 | const registeredDeviceId = await getRegisteredDeviceId(context.extensionContext); 22 | if (registeredDeviceId && registeredDeviceId !== null) { 23 | const deleteConfirm = "Yes"; 24 | const deleteReject = "No"; 25 | const option = await vscode.window.showWarningMessage( 26 | ERRORS.DEVICE_DELETION_WARNING(registeredDeviceId), 27 | deleteConfirm, 28 | deleteReject, 29 | ); 30 | if (option === deleteConfirm) { 31 | Logger.verbose("Confirmed device deletion"); 32 | await deleteRegisteredDevice(registeredDeviceId, context.extensionContext); 33 | onDeviceDeletionEventEmitter.fire(registeredDeviceId); 34 | } else { 35 | Logger.verbose("Device deletion cancelled"); 36 | } 37 | } else { 38 | void vscode.window.showInformationMessage(ERRORS.NO_DEVICES_REGISTERED); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/askContainer/commands/deviceRegistryCommand.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | 7 | import {logAskError} from "../../exceptions"; 8 | import {Logger} from "../../logger"; 9 | import {AbstractCommand, CommandContext} from "../../runtime"; 10 | import {DeviceRegistryWebview} from "../webViews/deviceRegistryWebview"; 11 | 12 | export class DeviceRegistryCommand extends AbstractCommand { 13 | private deviceRegistryWebview: DeviceRegistryWebview; 14 | 15 | constructor(webview: DeviceRegistryWebview, commandOverride?: string) { 16 | super(commandOverride ?? "askContainer.skillsConsole.deviceRegistry"); 17 | this.deviceRegistryWebview = webview; 18 | } 19 | 20 | async execute(context: CommandContext): Promise { 21 | Logger.debug(`Calling method: ${this.commandName}`); 22 | try { 23 | this.deviceRegistryWebview.showView(); 24 | } catch (err) { 25 | throw logAskError(`Cannot open device registry webview`, err, true); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/askContainer/commands/init.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import {AbstractCommand, CommandContext} from "../../runtime"; 7 | import {ProfileManagerWebview} from "../webViews/profileManagerWebview"; 8 | import {Logger} from "../../logger"; 9 | 10 | export class InitCommand extends AbstractCommand { 11 | private profileManager: ProfileManagerWebview; 12 | 13 | constructor(profileManager: ProfileManagerWebview) { 14 | super("ask.init"); 15 | this.profileManager = profileManager; 16 | } 17 | 18 | async execute(context: CommandContext): Promise { 19 | Logger.debug(`Calling method: ${this.commandName}`); 20 | this.profileManager.showView(); 21 | this.profileManager.populateProfilesList(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/askContainer/commands/listSkills.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import * as model from "ask-smapi-model"; 7 | import {DEFAULT_PROFILE, EN_US_LOCALE} from "../../constants"; 8 | import {logAskError} from "../../exceptions"; 9 | import {Logger} from "../../logger"; 10 | import {AbstractCommand, CommandContext, SmapiClientFactory, SmapiResource, Utils} from "../../runtime"; 11 | 12 | import skillSummary = model.v1.skill.SkillSummary; 13 | import skillManagementServiceClient = model.services.skillManagement.SkillManagementServiceClient; 14 | 15 | export class ListSkillsCommand extends AbstractCommand>> { 16 | constructor() { 17 | super("ask.container.listSkills"); 18 | } 19 | 20 | private async getSkillsList( 21 | smapiClientInstance: skillManagementServiceClient, 22 | vendorId: string, 23 | nextToken: undefined | string, 24 | skillsList: Array>, 25 | ): Promise>> { 26 | const listSkills: model.v1.skill.ListSkillResponse = await smapiClientInstance.listSkillsForVendorV1(vendorId, nextToken); 27 | listSkills.skills?.forEach((listSkill) => { 28 | let skillName = "someSkill"; 29 | if (listSkill.nameByLocale !== undefined && Object.values(listSkill.nameByLocale).length > 0) { 30 | if (listSkill.nameByLocale[EN_US_LOCALE]) { 31 | skillName = listSkill.nameByLocale[EN_US_LOCALE]; 32 | } else { 33 | skillName = Object.values(listSkill.nameByLocale)[0]; 34 | } 35 | } 36 | skillsList.push(new SmapiResource(listSkill, skillName)); 37 | }); 38 | 39 | if (listSkills.isTruncated ?? true) { 40 | await this.getSkillsList(smapiClientInstance, vendorId, listSkills.nextToken, skillsList); 41 | } 42 | 43 | return skillsList; 44 | } 45 | 46 | async execute(context: CommandContext): Promise>> { 47 | Logger.debug(`Calling method: ${this.commandName}`); 48 | const skills: Array> = []; 49 | let profile = Utils.getCachedProfile(context.extensionContext); 50 | profile = profile ?? DEFAULT_PROFILE; 51 | let vendorId: string; 52 | try { 53 | vendorId = Utils.resolveVendorId(profile); 54 | } catch (err) { 55 | throw logAskError(`Failed to retrieve vendorID for profile ${profile}`, err, true); 56 | } 57 | 58 | await this.getSkillsList(SmapiClientFactory.getInstance(profile, context.extensionContext), vendorId, undefined, skills); 59 | return skills; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/askContainer/commands/local-debug/debugAdapterPath.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import * as child_process from "child_process"; 7 | import * as fs from "fs"; 8 | import * as path from "path"; 9 | import * as vscode from "vscode"; 10 | import {DEFAULT_ENCODING, LOCAL_DEBUG} from "../../../constants"; 11 | import {logAskError} from "../../../exceptions"; 12 | import {Logger} from "../../../logger"; 13 | import {AbstractCommand, CommandContext} from "../../../runtime"; 14 | 15 | export class DebugAdapterPathCommand extends AbstractCommand { 16 | async execute(context: CommandContext, debugArgs: any): Promise { 17 | Logger.debug(`Calling method: ${this.commandName}, args:`, debugArgs); 18 | const languageType: string = debugArgs.type; 19 | if (languageType === LOCAL_DEBUG.NODE_DEPENDENCIES.LANG_TYPE) { 20 | const URIs = await vscode.workspace.findFiles(LOCAL_DEBUG.NODE_DEPENDENCIES.DEP_REGEX); 21 | if (URIs.length === 0) { 22 | throw logAskError(LOCAL_DEBUG.NODE_DEPENDENCIES.DEP_INSTALL_MSG); 23 | } 24 | return URIs[0].fsPath; 25 | } else if (languageType === LOCAL_DEBUG.PYTHON_DEPENDENCIES.LANG_TYPE) { 26 | const pythonInterpreterPath: string = debugArgs.pythonPath; 27 | const sitePkgUint8Array: Uint8Array = child_process.execSync(LOCAL_DEBUG.PYTHON_DEPENDENCIES.SITE_PKGS_CHECK(pythonInterpreterPath)); 28 | let sitePkgLocationsStr = new TextDecoder(DEFAULT_ENCODING).decode(sitePkgUint8Array); 29 | 30 | // Preprocessing the string to get site locations for searching 31 | // https://docs.python.org/3/library/site.html#site.getsitepackages gives an array of strings 32 | // eg: "['sitePkg-A', 'sitePkg-B']", and we need to preprocess to get each location 33 | sitePkgLocationsStr = sitePkgLocationsStr.replace("[", "").replace("]", "").trim(); 34 | const sitePkgLocations = sitePkgLocationsStr.split(","); 35 | 36 | for (let sitePkgLocation of sitePkgLocations) { 37 | // Remove extra quotes and white spaces 38 | sitePkgLocation = sitePkgLocation.replace(/['"]+/g, "").trim(); 39 | const localDebuggerPath = path.join(sitePkgLocation, LOCAL_DEBUG.PYTHON_DEPENDENCIES.DEP_PATH); 40 | if (fs.existsSync(localDebuggerPath)) { 41 | return localDebuggerPath; 42 | } 43 | } 44 | throw logAskError(LOCAL_DEBUG.PYTHON_DEPENDENCIES.DEP_INSTALL_MSG); 45 | } 46 | throw logAskError(LOCAL_DEBUG.DEBUG_PATH_ERROR(languageType)); 47 | } 48 | 49 | constructor() { 50 | super("ask.debugAdapterPath"); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/askContainer/commands/login.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import {AbstractCommand, CommandContext} from "../../runtime"; 7 | import {InitialLoginWebview} from "../webViews/initialLogin"; 8 | import * as vscode from "vscode"; 9 | import {registerWebviews} from "../../utils/webViews/viewManager"; 10 | import {Logger} from "../../logger"; 11 | 12 | export class LoginCommand extends AbstractCommand { 13 | private loginView: InitialLoginWebview; 14 | 15 | constructor(context: vscode.ExtensionContext) { 16 | super("ask.login"); 17 | this.loginView = new InitialLoginWebview("Sign in", "initialLogin", context); 18 | registerWebviews(this.loginView); 19 | } 20 | 21 | async execute(context: CommandContext, dispose?: boolean): Promise { 22 | Logger.debug(`Calling method: ${this.commandName}`); 23 | if (dispose) { 24 | this.loginView.dispose(); 25 | } else { 26 | this.loginView.showView(); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/askContainer/commands/refreshSkillActions.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import {AbstractCommand, CommandContext} from "../../runtime"; 7 | import {Logger} from "../../logger"; 8 | import {onWorkspaceOpenEventEmitter} from "../events"; 9 | 10 | export class RefreshSkillActionsCommand extends AbstractCommand { 11 | constructor() { 12 | super("askContainer.skill-actions.refresh"); 13 | } 14 | 15 | async execute(context: CommandContext): Promise { 16 | Logger.debug(`Calling method: ${this.commandName}`); 17 | onWorkspaceOpenEventEmitter.fire(undefined); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/askContainer/commands/showToolkitUpdates.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import {AbstractCommand, CommandContext} from "../../runtime"; 7 | import {ToolkitUpdateWebview} from "../webViews/toolkitUpdateWebview"; 8 | import {Logger} from "../../logger"; 9 | 10 | export class ShowToolkitUpdatesCommand extends AbstractCommand { 11 | private toolkitUpdate: ToolkitUpdateWebview; 12 | 13 | constructor(toolkitUpdate: ToolkitUpdateWebview) { 14 | super("ask.showToolkitUpdates"); 15 | this.toolkitUpdate = toolkitUpdate; 16 | } 17 | 18 | async execute(context: CommandContext): Promise { 19 | Logger.debug(`Calling method: ${this.commandName}`); 20 | this.toolkitUpdate.showView(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/askContainer/commands/simulateReplay.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import {logAskError} from "../../exceptions"; 7 | import {Logger} from "../../logger"; 8 | import {AbstractCommand, CommandContext} from "../../runtime"; 9 | import {SimulateSkillWebview} from "../webViews/simulateSkillWebview"; 10 | 11 | export class SimulateReplayCommand extends AbstractCommand { 12 | private simulateSkillWebview: SimulateSkillWebview; 13 | 14 | constructor(webview: SimulateSkillWebview) { 15 | super("askContainer.skillsConsole.simulateReplay"); 16 | this.simulateSkillWebview = webview; 17 | } 18 | 19 | async execute(context: CommandContext): Promise { 20 | Logger.debug(`Calling method: ${this.commandName}`); 21 | try { 22 | this.simulateSkillWebview.showView(); 23 | void this.simulateSkillWebview.replaySessionInSimulator(); 24 | } catch (err) { 25 | throw logAskError(`Cannot open test skill view`, err, true); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/askContainer/commands/simulateSkill.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import {logAskError} from "../../exceptions"; 7 | import {Logger} from "../../logger"; 8 | import {AbstractCommand, CommandContext} from "../../runtime"; 9 | import {SimulateSkillWebview} from "../webViews/simulateSkillWebview"; 10 | 11 | export class SimulateSkillCommand extends AbstractCommand { 12 | private simulateSkillWebview: SimulateSkillWebview; 13 | 14 | constructor(webview: SimulateSkillWebview) { 15 | super("askContainer.skillsConsole.simulateSkill"); 16 | this.simulateSkillWebview = webview; 17 | } 18 | 19 | async execute(context: CommandContext): Promise { 20 | Logger.debug(`Calling method: ${this.commandName}`); 21 | try { 22 | this.simulateSkillWebview.showView(); 23 | } catch (err) { 24 | throw logAskError(`Cannot open test skill view`, err, true); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/askContainer/commands/skillIdFromWorkspaceCommand.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import {ERRORS} from "../../constants"; 7 | import {logAskError} from "../../exceptions"; 8 | import {AbstractCommand, CommandContext, Utils} from "../../runtime"; 9 | import {checkProfileSkillAccess, getSkillDetailsFromWorkspace} from "../../utils/skillHelper"; 10 | 11 | export class GetSkillIdFromWorkspaceCommand extends AbstractCommand { 12 | // eslint-disable-next-line @typescript-eslint/require-await 13 | async execute(context: CommandContext): Promise { 14 | checkProfileSkillAccess(context.extensionContext); 15 | const skillId = getSkillDetailsFromWorkspace(context.extensionContext).skillId; 16 | if (!Utils.isNonBlankString(skillId)) { 17 | throw logAskError(`${ERRORS.MISSING_INFO_LOCAL_DEBUG("SkillId")}`); 18 | } 19 | return skillId; 20 | } 21 | constructor() { 22 | super("ask.skillIdFromWorkspace"); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/askContainer/commands/syncInteractionModel.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import * as vscode from "vscode"; 7 | import {logAskError} from "../../exceptions"; 8 | import {Logger} from "../../logger"; 9 | import {AbstractCommand, CommandContext} from "../../runtime"; 10 | import {checkProfileSkillAccess} from "../../utils/skillHelper"; 11 | import {InteractionModelSyncWebview} from "../webViews/interactionModelSync"; 12 | 13 | export class SyncInteractionModelCommand extends AbstractCommand { 14 | private syncInteractionModelView: InteractionModelSyncWebview; 15 | 16 | constructor(syncInteractionModelView: InteractionModelSyncWebview) { 17 | super("ask.container.syncInteractionModel"); 18 | this.syncInteractionModelView = syncInteractionModelView; 19 | } 20 | 21 | // eslint-disable-next-line @typescript-eslint/require-await 22 | async execute(context: CommandContext, skillFolderWs: vscode.Uri): Promise { 23 | Logger.debug(`Calling method: ${this.commandName}`); 24 | try { 25 | checkProfileSkillAccess(context.extensionContext); 26 | this.syncInteractionModelView.showView(skillFolderWs); 27 | } catch (err) { 28 | throw logAskError(`View failed to load; try again.`, err, true); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/askContainer/commands/syncManifest.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import * as vscode from "vscode"; 7 | import {logAskError} from "../../exceptions"; 8 | import {Logger} from "../../logger"; 9 | import {AbstractCommand, CommandContext} from "../../runtime"; 10 | import {checkProfileSkillAccess} from "../../utils/skillHelper"; 11 | import {ManifestSyncWebview} from "../webViews/manifestSync"; 12 | 13 | export class SyncManifestCommand extends AbstractCommand { 14 | private syncManifestView: ManifestSyncWebview; 15 | 16 | constructor(syncManifestView: ManifestSyncWebview) { 17 | super("ask.container.syncManifest"); 18 | this.syncManifestView = syncManifestView; 19 | } 20 | 21 | // eslint-disable-next-line @typescript-eslint/require-await 22 | async execute(context: CommandContext, skillFolderWs: vscode.Uri): Promise { 23 | Logger.debug(`Calling method: ${this.commandName}`); 24 | try { 25 | checkProfileSkillAccess(context.extensionContext); 26 | this.syncManifestView.showView(skillFolderWs); 27 | } catch (err) { 28 | throw logAskError(`Cannot open sync manifest view`, err, true); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/askContainer/commands/welcome.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import {AbstractCommand, CommandContext} from "../../runtime"; 7 | import {WelcomeScreenWebview} from "../webViews/welcomeScreenWebview"; 8 | import {Logger} from "../../logger"; 9 | import {registerWebviews} from "../../utils/webViews/viewManager"; 10 | import * as vscode from "vscode"; 11 | 12 | export class WelcomeCommand extends AbstractCommand { 13 | private welcomeScreen: WelcomeScreenWebview; 14 | 15 | constructor(context: vscode.ExtensionContext) { 16 | super("ask.welcome"); 17 | this.welcomeScreen = new WelcomeScreenWebview("Alexa Skills Kit", "welcomeScreen", context); 18 | registerWebviews(this.welcomeScreen); 19 | } 20 | 21 | async execute(context: CommandContext): Promise { 22 | Logger.debug(`Calling method: ${this.commandName}`); 23 | this.welcomeScreen.showView(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/askContainer/events.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | 7 | import {EventEmitter} from "vscode"; 8 | import {PluginTreeItem, Resource} from "../runtime"; 9 | 10 | export const onSkillConsoleViewChangeEventEmitter: EventEmitter | undefined> = new EventEmitter< 11 | PluginTreeItem | undefined 12 | >(); 13 | 14 | export const onWorkspaceOpenEventEmitter: EventEmitter | undefined> = new EventEmitter< 15 | PluginTreeItem | undefined 16 | >(); 17 | 18 | export const onDeviceRegistrationEventEmitter: EventEmitter = new EventEmitter(); 19 | 20 | export const onDeviceDeletionEventEmitter: EventEmitter = new EventEmitter(); 21 | -------------------------------------------------------------------------------- /src/askContainer/treeViews/helpView.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import * as vscode from "vscode"; 7 | import {PluginTreeItem, Resource, AbstractTreeView} from "../../runtime"; 8 | 9 | import {HelpViewProvider} from "./treeViewProviders/helpViewProvider"; 10 | import {TREE_VIEW_IDS} from "../../constants"; 11 | 12 | export class HelpView extends AbstractTreeView { 13 | protected view: vscode.TreeView>; 14 | 15 | constructor(context: vscode.ExtensionContext) { 16 | super(context); 17 | this.view = vscode.window.createTreeView(TREE_VIEW_IDS.HELP, { 18 | treeDataProvider: new HelpViewProvider(this), 19 | showCollapseAll: true, 20 | }); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/askContainer/treeViews/skillActionsView.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import * as vscode from "vscode"; 7 | import {PluginTreeItem, Resource, AbstractTreeView} from "../../runtime"; 8 | 9 | import {SkillActionsViewProvider} from "./treeViewProviders/skillActionsViewProvider"; 10 | import {TREE_VIEW_IDS} from "../../constants"; 11 | 12 | export class SkillActionsView extends AbstractTreeView { 13 | protected view: vscode.TreeView>; 14 | 15 | constructor(context: vscode.ExtensionContext) { 16 | super(context); 17 | this.view = vscode.window.createTreeView(TREE_VIEW_IDS.SKILL_ACTIONS, { 18 | treeDataProvider: new SkillActionsViewProvider(this), 19 | showCollapseAll: true, 20 | }); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/askContainer/treeViews/skillConsoleView.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import * as vscode from "vscode"; 7 | import {PluginTreeItem, Resource, AbstractTreeView} from "../../runtime"; 8 | 9 | import {SkillsConsoleViewProvider} from "./treeViewProviders/skillConsoleViewProvider"; 10 | import {TREE_VIEW_IDS} from "../../constants"; 11 | 12 | export class SkillsConsoleView extends AbstractTreeView { 13 | protected view: vscode.TreeView>; 14 | 15 | constructor(context: vscode.ExtensionContext) { 16 | super(context); 17 | this.view = vscode.window.createTreeView>(TREE_VIEW_IDS.SKILLS_CONSOLE, { 18 | treeDataProvider: new SkillsConsoleViewProvider(this), 19 | showCollapseAll: true, 20 | }); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/askContainer/webViews/createSkillWebview/abstractCreateSkillManager.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import {ExtensionContext, Progress} from "vscode"; 7 | 8 | import {createSkillWebViewType} from "./createSkillWebview"; 9 | import {DEFAULT_PROFILE} from "../../../constants"; 10 | import {Utils} from "../../../runtime"; 11 | 12 | export abstract class AbstractCreateSkillManager { 13 | protected context: ExtensionContext; 14 | protected skillFolder: string; 15 | protected profile: string; 16 | 17 | constructor(context: ExtensionContext, skillFolder: string) { 18 | this.context = context; 19 | this.skillFolder = skillFolder; 20 | this.profile = Utils.getCachedProfile(this.context) ?? DEFAULT_PROFILE; 21 | } 22 | 23 | abstract createSkill(userInput: createSkillWebViewType, progress?: Progress): Promise; 24 | } 25 | -------------------------------------------------------------------------------- /src/askContainer/webViews/deploySkillWebview/deployNonHostedSkillManager.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import * as vscode from "vscode"; 7 | 8 | import {Logger} from "../../../logger"; 9 | import {AbstractWebView} from "../../../runtime"; 10 | 11 | import {AbstractDeploySkillManager} from "./abstractDeploySkillManager"; 12 | 13 | export class DeployNonHostedSkillManager extends AbstractDeploySkillManager { 14 | private currentHash: string; 15 | 16 | constructor(context: vscode.ExtensionContext, skillFolderWs: vscode.Uri, currentHash: string) { 17 | super(context, skillFolderWs); 18 | this.currentHash = currentHash; 19 | } 20 | 21 | async deploySkill(view: AbstractWebView, isForce?: boolean, eTag?: string): Promise { 22 | Logger.verbose("Calling method: deploySkill"); 23 | 24 | await this.deploySkillPackage(view, isForce, eTag, this.currentHash); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/askContainer/webViews/initialLogin.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import * as vscode from "vscode"; 7 | import {DEFAULT_PROFILE, WEB_VIEW_NAME} from "../../constants"; 8 | import {AskParameterAbsenceError, logAskError} from "../../exceptions"; 9 | import {Logger} from "../../logger"; 10 | import {AbstractWebView} from "../../runtime"; 11 | import {authenticate} from "../../utils/webViews/authHelper"; 12 | import {ViewLoader} from "../../utils/webViews/viewLoader"; 13 | import {AUTH_FLOW_RESULT} from "./profileManagerWebview"; 14 | 15 | type SignInType = { 16 | profileName: string; 17 | }; 18 | 19 | export class InitialLoginWebview extends AbstractWebView { 20 | private loader: ViewLoader; 21 | 22 | constructor(viewTitle: string, viewId: string, context: vscode.ExtensionContext) { 23 | super(viewTitle, viewId, context); 24 | this.loader = new ViewLoader(this.extensionContext, WEB_VIEW_NAME.INITIAL_LOGIN, this); 25 | } 26 | 27 | onViewChangeListener(event: vscode.WebviewPanelOnDidChangeViewStateEvent): void { 28 | Logger.debug(`Calling method: ${this.viewId}.getHtmlForView`); 29 | 30 | return; 31 | } 32 | 33 | onReceiveMessageListener(message: SignInType): void { 34 | Logger.debug(`Calling method: ${this.viewId}.onReceiveMessageListener, args:`, message); 35 | this.doAuthenticate(); 36 | } 37 | 38 | getHtmlForView(...args: unknown[]): string { 39 | Logger.debug(`Calling method: ${this.viewId}.getHtmlForView`); 40 | return this.loader.renderView({ 41 | name: WEB_VIEW_NAME.INITIAL_LOGIN, 42 | js: true, 43 | }); 44 | } 45 | 46 | async doAuthenticate(): Promise { 47 | Logger.debug(`Calling method: ${this.viewId}.doAuthenticate`); 48 | try { 49 | await authenticate(this.extensionContext, this, DEFAULT_PROFILE); 50 | void vscode.commands.executeCommand("workbench.action.reloadWindow"); 51 | } catch (error) { 52 | Logger.error(error); 53 | let viewArgs; 54 | if (error instanceof AskParameterAbsenceError) { 55 | viewArgs = AUTH_FLOW_RESULT.VENDOR_ID_ABSENCE; 56 | } else { 57 | viewArgs = AUTH_FLOW_RESULT.FAILED; 58 | viewArgs.message = error instanceof Error ? error.message : error; 59 | } 60 | this.loader = new ViewLoader(this.extensionContext, "profileManager", this); 61 | if (!this.isDisposed()) { 62 | this.getPanel().webview.html = this.loader.renderView({ 63 | name: "authFlowResult", 64 | args: viewArgs, 65 | }); 66 | } 67 | } 68 | } 69 | 70 | reviveView(): void { 71 | Logger.debug(`Calling method: ${this.viewId}.reviveView`); 72 | throw logAskError("Method not implemented."); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/askContainer/webViews/toolkitUpdateWebview.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import {AbstractWebView} from "../../runtime"; 7 | import {ExtensionContext, WebviewPanelOnDidChangeViewStateEvent} from "vscode"; 8 | import {ViewLoader} from "../../utils/webViews/viewLoader"; 9 | import {Logger} from "../../logger"; 10 | import {WEB_VIEW_NAME} from "../../constants"; 11 | 12 | export class ToolkitUpdateWebview extends AbstractWebView { 13 | private loader: ViewLoader; 14 | 15 | constructor(viewTitle: string, viewId: string, context: ExtensionContext) { 16 | super(viewTitle, viewId, context); 17 | this.isGlobal = true; 18 | this.loader = new ViewLoader(this.extensionContext, WEB_VIEW_NAME.TOOLKIT_UPDATE, this); 19 | } 20 | 21 | onViewChangeListener(event: WebviewPanelOnDidChangeViewStateEvent): void { 22 | Logger.debug(`Calling method: ${this.viewId}.onViewChangeListener`); 23 | return; 24 | } 25 | 26 | onReceiveMessageListener(message: any): void { 27 | Logger.debug(`Calling method: ${this.viewId}.onReceiveMessageListener, args: `, message); 28 | return; 29 | } 30 | 31 | getHtmlForView(...args: unknown[]): string { 32 | Logger.debug(`Calling method: ${this.viewId}.getHtmlForView`); 33 | return this.loader.renderView({ 34 | name: WEB_VIEW_NAME.TOOLKIT_UPDATE, 35 | js: false, 36 | }); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/exceptions.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import * as vscode from "vscode"; 7 | import {Logger} from "./logger"; 8 | 9 | export class AskError extends Error { 10 | constructor(message: string) { 11 | super(message); 12 | this.name = "AskError"; 13 | } 14 | } 15 | 16 | export class AskParameterAbsenceError extends AskError { 17 | constructor(message: string) { 18 | super(message); 19 | this.name = "AskParameterAbsenceError"; 20 | } 21 | } 22 | 23 | export function logAskError(message: string, error: any | undefined = undefined, exposeToUser = false): Error { 24 | let errMsg: string; 25 | if (error !== undefined) { 26 | let msg: string; 27 | if (error instanceof Error) { 28 | msg = error.message; 29 | } else { 30 | msg = error; 31 | } 32 | errMsg = `${message}. Reason: ${msg}`; 33 | } else { 34 | errMsg = `${message}.`; 35 | } 36 | 37 | Logger.error(errMsg); 38 | 39 | if (exposeToUser) { 40 | void vscode.window.showErrorMessage(errMsg); 41 | } 42 | 43 | return new AskError(errMsg); 44 | } 45 | -------------------------------------------------------------------------------- /src/extensionGlobals.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import {ExtensionContext} from "vscode"; 7 | 8 | import {GenericCommand, AbstractWebView, AbstractTreeView} from "../src/runtime/lib/API"; 9 | import {SkillPackageWatcher} from "./askContainer/fileSystem/skillPackageWatcher"; 10 | /** 11 | * Namespace for common variables used globally in the extension. 12 | * All variables here must be initialized in the activate() method of extension.ts 13 | */ 14 | export namespace ext { 15 | export let context: ExtensionContext; 16 | /** 17 | * Commands which will be available when extension is activated 18 | */ 19 | export let askGeneralCommands: GenericCommand[]; 20 | 21 | /** 22 | * Commands which will be available when extension is activated 23 | * And when a skill is detected 24 | */ 25 | export let askSkillCommands: GenericCommand[]; 26 | 27 | /** 28 | * Webviews which are registered in this extension 29 | */ 30 | export const webViews: AbstractWebView[] = []; 31 | 32 | /** 33 | * TreeViews which are registered in this extension 34 | */ 35 | export const treeViews: AbstractTreeView[] = []; 36 | 37 | export let skillPackageWatcher: SkillPackageWatcher; 38 | } 39 | -------------------------------------------------------------------------------- /src/models/resourcesConfig/abstractResourcesConfig.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import * as R from "ramda"; 7 | import {write, read} from "../../runtime/lib/utils/jsonUtility"; 8 | 9 | /** 10 | * A utility class to define common methods for getting and setting data in a config JSON file 11 | */ 12 | export class AbstractResourcesConfig { 13 | protected filePath: string; 14 | content: any; 15 | 16 | /** 17 | * Constructor for an ask resources config class 18 | * @param filePath The path of the config file 19 | * @param content The JSON object of the config file, the given content will replace an existing one 20 | */ 21 | constructor(filePath: string, content?: any) { 22 | this.filePath = filePath; 23 | if (content !== undefined) { 24 | write(filePath, content); 25 | } 26 | this.read(); 27 | } 28 | 29 | /** 30 | * To get the value with the given lens path from the JSON object 31 | * @param pathArray 32 | */ 33 | getProperty(pathArray: string[]): string { 34 | return R.view(R.lensPath(pathArray), this.content); 35 | } 36 | 37 | /** 38 | * To assign a value to the given lens path in the JSON object 39 | * @param pathArray 40 | * @param value 41 | */ 42 | setProperty(pathArray: string[], value: string | undefined): void { 43 | this.content = R.set(R.lensPath(pathArray), value, this.content); 44 | } 45 | 46 | /** 47 | * To write a JSON file 48 | */ 49 | write(): void { 50 | write(this.filePath, this.content); 51 | } 52 | 53 | /** 54 | * To read the JSON file 55 | */ 56 | read(): any { 57 | this.content = read(this.filePath); 58 | // eslint-disable-next-line @typescript-eslint/no-unsafe-return 59 | return this.content; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/models/resourcesConfig/askResource.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import * as fs from "fs-extra"; 7 | import * as path from "path"; 8 | 9 | import {ASK_RESOURCES_JSON, BASE_RESOURCES_CONFIG, SKILL_FOLDER} from "../../constants"; 10 | import {AbstractResourcesConfig} from "./abstractResourcesConfig"; 11 | 12 | /** 13 | * A utility class to create, get and set data in ask-resources.json file 14 | */ 15 | export class AskResources extends AbstractResourcesConfig { 16 | /** 17 | * The constructor for AskResources class 18 | * @param skillFolderPath 19 | * @param content the given content to initialize or overwrite the file 20 | */ 21 | constructor(skillFolderPath: string, content?: any) { 22 | const filePath = path.join(skillFolderPath, SKILL_FOLDER.ASK_RESOURCES_JSON_CONFIG); 23 | if (!fs.existsSync(filePath)) { 24 | if (content === undefined) { 25 | content = BASE_RESOURCES_CONFIG; 26 | } 27 | fs.ensureDirSync(path.dirname(filePath)); 28 | } 29 | super(filePath, content); 30 | } 31 | 32 | // getter and setter 33 | 34 | getSkillId(profile: string): string { 35 | return this.getProperty([ASK_RESOURCES_JSON.PROFILES, profile, ASK_RESOURCES_JSON.SKILL_ID]); 36 | } 37 | 38 | setSkillId(profile: string, skillId: string): void { 39 | this.setProperty([ASK_RESOURCES_JSON.PROFILES, profile, ASK_RESOURCES_JSON.SKILL_ID], skillId); 40 | } 41 | 42 | getProfile(profile: string): string { 43 | return this.getProperty([ASK_RESOURCES_JSON.PROFILES, profile]); 44 | } 45 | 46 | setProfile(profile: string, profileVale: string | undefined): void { 47 | this.setProperty([ASK_RESOURCES_JSON.PROFILES, profile], profileVale); 48 | } 49 | 50 | getSkillMetaSrc(profile: string): string { 51 | return this.getProperty([ 52 | ASK_RESOURCES_JSON.PROFILES, 53 | profile, 54 | ASK_RESOURCES_JSON.SKILL_METADATA.NAME, 55 | ASK_RESOURCES_JSON.SKILL_METADATA.SRC, 56 | ]); 57 | } 58 | 59 | setSkillMetaSrc(profile: string, skillMetaSrc: string): void { 60 | this.setProperty( 61 | [ASK_RESOURCES_JSON.PROFILES, profile, ASK_RESOURCES_JSON.SKILL_METADATA.NAME, ASK_RESOURCES_JSON.SKILL_METADATA.SRC], 62 | skillMetaSrc, 63 | ); 64 | } 65 | 66 | getSkillInfraType(profile: string): string { 67 | return this.getProperty([ 68 | ASK_RESOURCES_JSON.PROFILES, 69 | profile, 70 | ASK_RESOURCES_JSON.SKILL_INFRASTRUCTURE.NAME, 71 | ASK_RESOURCES_JSON.SKILL_INFRASTRUCTURE.TYPE, 72 | ]); 73 | } 74 | 75 | setSkillInfraType(profile: string, type: string): void { 76 | this.setProperty( 77 | [ASK_RESOURCES_JSON.PROFILES, profile, ASK_RESOURCES_JSON.SKILL_INFRASTRUCTURE.NAME, ASK_RESOURCES_JSON.SKILL_INFRASTRUCTURE.TYPE], 78 | type, 79 | ); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/models/resourcesConfig/askStates.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import * as fs from "fs-extra"; 7 | import * as path from "path"; 8 | 9 | import {ASK_STATES_JSON, BASE_STATES_CONFIG, SKILL_FOLDER} from "../../constants"; 10 | import {AbstractResourcesConfig} from "./abstractResourcesConfig"; 11 | 12 | /** 13 | * A utility class to create, get and set data in ask-states.json file 14 | */ 15 | export class AskStates extends AbstractResourcesConfig { 16 | /** 17 | * The constructor for AskStates class 18 | * @param skillFolderPath 19 | * @param content the given content to initialize or overwrite the file 20 | */ 21 | constructor(skillFolderPath: string, content?: any) { 22 | const filePath = path.join(skillFolderPath, SKILL_FOLDER.HIDDEN_ASK_FOLDER, SKILL_FOLDER.ASK_STATES_JSON_CONFIG); 23 | if (!fs.existsSync(filePath)) { 24 | if (content === undefined) { 25 | content = BASE_STATES_CONFIG; 26 | } 27 | } 28 | fs.ensureDirSync(path.dirname(filePath)); 29 | super(filePath, content); 30 | } 31 | 32 | // getter and setter 33 | 34 | getSkillId(profile: string): string { 35 | return this.getProperty([ASK_STATES_JSON.PROFILES, profile, ASK_STATES_JSON.SKILL_ID]); 36 | } 37 | 38 | setSkillId(profile: string, skillId: string): void { 39 | this.setProperty([ASK_STATES_JSON.PROFILES, profile, ASK_STATES_JSON.SKILL_ID], skillId); 40 | } 41 | 42 | getSkillMetaLastDeployHash(profile: string): string { 43 | return this.getProperty([ 44 | ASK_STATES_JSON.PROFILES, 45 | profile, 46 | ASK_STATES_JSON.SKILL_METADATA.NAME, 47 | ASK_STATES_JSON.SKILL_METADATA.LAST_DEPLOY_HASH, 48 | ]); 49 | } 50 | 51 | setSkillMetaLastDeployHash(profile: string, lastDeployHash: string): void { 52 | this.setProperty( 53 | [ASK_STATES_JSON.PROFILES, profile, ASK_STATES_JSON.SKILL_METADATA.NAME, ASK_STATES_JSON.SKILL_METADATA.LAST_DEPLOY_HASH], 54 | lastDeployHash, 55 | ); 56 | } 57 | 58 | getSkillMetaETag(profile: string): string { 59 | return this.getProperty([ASK_STATES_JSON.PROFILES, profile, ASK_STATES_JSON.SKILL_METADATA.NAME, ASK_STATES_JSON.SKILL_METADATA.ETAG]); 60 | } 61 | 62 | setSkillMetaETag(profile: string, eTag: string): void { 63 | this.setProperty([ASK_STATES_JSON.PROFILES, profile, ASK_STATES_JSON.SKILL_METADATA.NAME, ASK_STATES_JSON.SKILL_METADATA.ETAG], eTag); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/models/types.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import {SmapiClientFactory} from "../runtime"; 7 | import * as model from "ask-smapi-model"; 8 | import skillSummary = model.v1.skill.SkillSummary; 9 | import hostedSkillMetadata = model.v1.skill.AlexaHosted.HostedSkillMetadata; 10 | 11 | export class SkillInfo { 12 | skillSummary: skillSummary; 13 | isHosted: boolean | undefined; 14 | hostedSkillMetadata: hostedSkillMetadata | undefined; 15 | 16 | constructor( 17 | skillSummary: skillSummary, 18 | isHosted: boolean | undefined = undefined, 19 | hostedSkillMetadata: hostedSkillMetadata | undefined = undefined, 20 | ) { 21 | this.skillSummary = skillSummary; 22 | this.hostedSkillMetadata = hostedSkillMetadata; 23 | this.isHosted = isHosted; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/runtime/index.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import * as Utils from "./lib/utils/index"; 7 | 8 | export * from "./lib/API"; 9 | export * from "./lib/credentialsManager"; 10 | export * from "./lib/types"; 11 | export * from "./lib/smapiClientFactory"; 12 | export {Utils}; 13 | -------------------------------------------------------------------------------- /src/runtime/lib/credentialsManager.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import {logAskError} from "../../exceptions"; 7 | import {resolver} from "./utils/configuration"; 8 | import {AUTH} from "./utils/constants"; 9 | import {readToken, StoredToken} from "./utils/oauthWrapper"; 10 | import {resolveVendorId} from "./utils/profileHelper"; 11 | 12 | export class Credentials { 13 | readonly clientId: string; 14 | readonly clientSecret: string; 15 | readonly refreshToken: string; 16 | readonly vendorId: string; 17 | 18 | constructor(clientId: string, clientSecret: string, refreshToken: string, vendorId: string) { 19 | this.clientId = clientId; 20 | this.clientSecret = clientSecret; 21 | this.refreshToken = refreshToken; 22 | this.vendorId = vendorId; 23 | } 24 | } 25 | 26 | export class CredentialsManager { 27 | public static getCredentials(profile: string, clientId?: string, clientSecret?: string): Credentials { 28 | const token: StoredToken | undefined = readToken(profile); 29 | let vendorId: string; 30 | try { 31 | vendorId = resolveVendorId(profile); 32 | } catch (err) { 33 | throw logAskError(`Failed to retrieve vendorID for profile ${profile}`, err, true); 34 | } 35 | return new Credentials( 36 | resolver([clientId, process.env.ASK_LWA_CLIENT_ID, AUTH.DEFAULT_CLIENT_ID]), 37 | resolver([clientSecret, process.env.ASK_LWA_CLIENT_CONFIRMATION, AUTH.DEFAULT_CLIENT_CONFIRMATION]), 38 | token!.refresh_token, 39 | vendorId, 40 | ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/runtime/lib/index.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import "./API"; 7 | import "./types"; 8 | import "./smapiClientFactory"; 9 | import "./credentialsManager"; 10 | -------------------------------------------------------------------------------- /src/runtime/lib/smapiClientFactory.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import * as vscode from "vscode"; 7 | import {RefreshTokenConfig, CustomSmapiClientBuilder} from "ask-smapi-sdk"; 8 | import * as smapiModel from "ask-smapi-model"; 9 | import {CredentialsManager, Credentials} from "./credentialsManager"; 10 | import {resolver} from "./utils/configuration"; 11 | import {AUTH} from "./utils/constants"; 12 | import {EXTENSION_ID} from "../../constants"; 13 | 14 | export class SmapiClientFactory { 15 | private static readonly profileInstanceMap: Map = new Map(); 16 | 17 | private constructor() {} 18 | 19 | public static getInstance( 20 | profile: string, 21 | context: vscode.ExtensionContext, 22 | ): smapiModel.services.skillManagement.SkillManagementServiceClient { 23 | let smapiClient = this.profileInstanceMap.get(profile); 24 | if (!smapiClient) { 25 | const authConfig: Credentials = CredentialsManager.getCredentials(profile); 26 | const refreshTokenConfig: RefreshTokenConfig = { 27 | clientId: authConfig.clientId, 28 | clientSecret: authConfig.clientSecret, 29 | refreshToken: authConfig.refreshToken, 30 | }; 31 | const pluginVersion: string = vscode.extensions.getExtension(EXTENSION_ID)?.packageJSON.version; 32 | smapiClient = new CustomSmapiClientBuilder() 33 | .withApiEndpoint(resolver([process.env.ASK_SMAPI_SERVER_BASE_URL, undefined])) 34 | .withAuthEndpoint(resolver([process.env.ASK_LWA_TOKEN_HOST, AUTH.DEFAULT_ASK_LWA_TOKEN_HOST])) 35 | .withRefreshTokenConfig(refreshTokenConfig) 36 | .withCustomUserAgent(`alexa-skills-kit-toolkit/${pluginVersion}`) 37 | .client(); 38 | this.profileInstanceMap.set(profile, smapiClient); 39 | } 40 | return smapiClient; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/runtime/lib/telemetry/constants.ts: -------------------------------------------------------------------------------- 1 | export enum MetricActionResult { 2 | SUCCESS = "Success", 3 | FAILURE = "Failure", 4 | } 5 | 6 | export enum TelemetryEnabled { 7 | ENABLED = "Enabled", 8 | DISABLED = "Disabled", 9 | USE_IDE_SETTINGS = "Use IDE settings", 10 | } 11 | 12 | export enum ActionType { 13 | COMMAND = "command", 14 | EVENT = "event", 15 | TOOLS_DOCS_VSCODE = "TOOLS_DOCS_VSCODE", 16 | TOOLS_DOCS_CLI = "TOOLS_DOCS_CLI", 17 | TOOLS_DOCS_ASK_SDK = "TOOLS_DOCS_ASK_SDK", 18 | TOOLS_DOCS_SMAPI_SDK = "TOOLS_DOCS_SMAPI_SDK", 19 | IM_EDITOR = "IM_EDITOR", 20 | ACDL_SERVER = "ACDL_SERVER", 21 | } 22 | -------------------------------------------------------------------------------- /src/runtime/lib/telemetry/metricAction.ts: -------------------------------------------------------------------------------- 1 | import uuid from "uuid"; 2 | import { ActionType, MetricActionResult } from "./constants"; 3 | 4 | export class MetricAction { 5 | private result?: MetricActionResult; 6 | 7 | private name: string; 8 | 9 | private startTime: Date; 10 | 11 | private type: ActionType; 12 | 13 | public id: string; 14 | 15 | private endTime?: Date; 16 | 17 | private failureMessage: string; 18 | 19 | private ended: boolean; 20 | 21 | /** 22 | * @constructor 23 | * @param {string} name - The action name. 24 | * @param {string} type - The action type. 25 | */ 26 | constructor(name: string, type: ActionType) { 27 | this.failureMessage = ""; 28 | this.name = name; 29 | this.startTime = new Date(); 30 | this.type = type; 31 | this.id = uuid(); 32 | this.ended = false; 33 | } 34 | 35 | /** 36 | * Closes action 37 | * @param {Error} [error] - Error object indicating an error. 38 | */ 39 | public end(error?: Error) { 40 | if (this.ended) { 41 | return; 42 | } 43 | 44 | const hasError = error instanceof Error; 45 | this.result = hasError ? MetricActionResult.FAILURE : MetricActionResult.SUCCESS; 46 | this.failureMessage = hasError ? (error?.message as string) : ""; 47 | this.endTime = new Date(); 48 | this.ended = true; 49 | } 50 | 51 | /** 52 | * Implementation of custom toJSON method to modify serialization with JSON.stringify 53 | */ 54 | protected toJSON() { 55 | return { 56 | end_time: this.endTime, 57 | failure_message: this.failureMessage, 58 | name: this.name, 59 | result: this.result, 60 | start_time: this.startTime, 61 | type: this.type, 62 | id: this.id, 63 | }; 64 | } 65 | } -------------------------------------------------------------------------------- /src/runtime/lib/types.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | export interface Resource { 7 | label?: string; 8 | } 9 | 10 | export class SmapiResource { 11 | label?: string; 12 | data: T; 13 | 14 | constructor(data: T, label?: string) { 15 | this.data = data; 16 | this.label = label; 17 | } 18 | } 19 | export interface CustomResource extends Resource { 20 | description: string; 21 | hasChildren?: boolean; 22 | } 23 | -------------------------------------------------------------------------------- /src/runtime/lib/utils/configuration.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import {isNonBlankString} from "./stringUtils"; 7 | 8 | //TODO: Extend support for Promises. 9 | export function resolver(chain: Array): string { 10 | for (const item of chain) { 11 | if (item !== undefined && isNonBlankString(item)) { 12 | return item; 13 | } 14 | } 15 | return ""; 16 | } 17 | -------------------------------------------------------------------------------- /src/runtime/lib/utils/constants.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | export const AUTH = { 7 | PLACEHOLDER_ENVIRONMENT_VAR_PROFILE_NAME: "__ENVIRONMENT_ASK_PROFILE__", 8 | DEFAULT_CLIENT_ID: "amzn1.application-oa2-client.aad322b5faab44b980c8f87f94fbac56", 9 | DEFAULT_CLIENT_CONFIRMATION: "1642d8869b829dda3311d6c6539f3ead55192e3fc767b9071c888e60ef151cf9", 10 | SIGNIN_URL: "https://www.amazon.com/ap/signin", 11 | SIGNIN_PATH: "/ap/signin", 12 | DEFAULT_LWA_AUTHORIZE_HOST: "https://www.amazon.com", 13 | DEFAULT_ASK_LWA_TOKEN_HOST: "https://api.amazon.com", 14 | }; 15 | 16 | export const EXTENSION_STATE_KEY = { 17 | CACHED_SKILLS: "AskContainer.HostedSkillsCache", 18 | LWA_PROFILE: "LwaProfile", 19 | CLIENT_ID: "CLIENT_ID", 20 | CLIENT_SECRET: "CLIENT_SECRET", 21 | ASK_LWA_AUTHORIZE_HOST: "ASK_LWA_AUTHORIZE_HOST", 22 | ASK_LWA_TOKEN_HOST: "ASK_LWA_TOKEN_HOST", 23 | ASK_SMAPI_SERVER_BASE_URL: "ASK_SMAPI_SERVER_BASE_URL", 24 | }; 25 | 26 | export const FILE_PATH = { 27 | ASK: { 28 | HIDDEN_FOLDER: ".ask", 29 | PROFILE_FILE: "cli_config", 30 | }, 31 | }; 32 | 33 | export const CONFIGURATION = { 34 | FILE_PERMISSION: { 35 | USER_READ_WRITE: "0600", 36 | }, 37 | JSON_DISPLAY_INTENT: 2, 38 | }; 39 | -------------------------------------------------------------------------------- /src/runtime/lib/utils/index.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | export * from "./jsonUtility"; 7 | export * from "./lwa"; 8 | export * from "./oauthWrapper"; 9 | export * from "./profileHelper"; 10 | export * from "./stringUtils"; 11 | export * from "./configuration"; 12 | -------------------------------------------------------------------------------- /src/runtime/lib/utils/jsonRead.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import {readFileSync} from "jsonfile"; 7 | 8 | export function readFile(filePath: string): any { 9 | let file: any; 10 | try { 11 | file = readFileSync(filePath); 12 | } catch (e) { 13 | console.error("Invalid json: " + filePath); 14 | return null; 15 | } 16 | return file; 17 | } 18 | 19 | export function readString(str: string): any { 20 | let jsonObj: any; 21 | try { 22 | jsonObj = JSON.parse(str); 23 | } catch (e) { 24 | console.error("Invalid json string: " + str); 25 | return null; 26 | } 27 | return jsonObj; 28 | } 29 | 30 | export function getProperty(jsonObject: any, track: string): any { 31 | const trackArray: string[] = track.split(".").slice(1); 32 | let property: any = jsonObject; 33 | for (let i = 0; i < trackArray.length; i++) { 34 | if (property.hasOwnProperty(trackArray[i])) { 35 | property = property[trackArray[i]]; 36 | } else { 37 | console.log('The property "' + trackArray[i] + '" does not exist.'); 38 | return null; 39 | } 40 | } 41 | return property; 42 | } 43 | -------------------------------------------------------------------------------- /src/runtime/lib/utils/serverFactory.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import {createServer, Server} from "http"; 7 | import {checkPortStatus} from "portscanner"; 8 | 9 | /** 10 | * Factory for localhost server instances used for LWA and CAPTCHA validation 11 | * responses. 12 | */ 13 | export class ServerFactory { 14 | private static server: Server; 15 | 16 | /** 17 | * Gets an instance of an HTTP server. 18 | * Currently locked to port 9090. 19 | */ 20 | public static async getInstance(): Promise { 21 | if (this.server) { 22 | this.server.close(); 23 | } 24 | const SERVER_PORT = 9090; 25 | const portStatus = await checkPortStatus(SERVER_PORT); 26 | if (portStatus === "open") { 27 | throw new Error("LWA listening port already in use."); 28 | } 29 | this.server = createServer(); 30 | return this.server; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/runtime/lib/utils/stringUtils.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import {is, isEmpty} from "ramda"; 7 | 8 | export function isNonEmptyString(str: string | undefined): boolean { 9 | return str !== undefined && is(String, str) && !isEmpty(str); 10 | } 11 | 12 | export function isNonBlankString(str: string | undefined): boolean { 13 | return isNonEmptyString(str) && str !== undefined && !!str.trim(); 14 | } 15 | 16 | /** 17 | * Check if input string is a valid lambda function name 18 | * @param {string} str 19 | */ 20 | export function isLambdaFunctionName(str: string): boolean { 21 | if (!isNonBlankString(str)) { 22 | return false; 23 | } 24 | 25 | // This regex can be used to check if the str 26 | // could be a valid lambda function name 27 | const lambdaFunctionNameRegex = /^([a-zA-Z0-9-_]+)$/; 28 | return lambdaFunctionNameRegex.test(str); 29 | } 30 | 31 | /** 32 | * Check if the input string is a meaningful diff result or only contains header message 33 | */ 34 | export function hasDiffContent(str: string): boolean { 35 | if (!isNonBlankString(str)) { 36 | return false; 37 | } 38 | 39 | const lines = str.split(/\r\n|\n/); 40 | 41 | // Exclude the case when string only contains three lines of headers (diff example shown as below): 42 | // =================================================================== 43 | // --- local {filename} 44 | // +++ remote {filename} 45 | // (new line) 46 | if (lines.length <= 4) { 47 | return false; 48 | } 49 | 50 | // Exclude when there is no line starting with "-" or "+" 51 | for (const line of lines) { 52 | if ((line.startsWith("-") && !line.startsWith("---")) || (line.startsWith("+") && !line.startsWith("+++"))) { 53 | return true; 54 | } 55 | } 56 | return false; 57 | } 58 | 59 | /** 60 | * Filter non-alphanumeric in a string and remove the character 61 | * @param {string} str 62 | */ 63 | export function filterNonAlphanumeric(str: string): string { 64 | if (!str) { 65 | return str; 66 | } 67 | return str.replace(/[^a-zA-Z0-9-]+/g, ""); 68 | } 69 | -------------------------------------------------------------------------------- /src/utils/avs/avsInterface.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | 7 | export interface IDirective { 8 | header: IHeader; 9 | payload: any; 10 | } 11 | 12 | export interface IHeader { 13 | namespace: string; 14 | name: string; 15 | messageId?: string; 16 | dialogRequestId?: string; 17 | } 18 | 19 | export interface IPayload {} 20 | 21 | /** 22 | * This payload is for the directive of SkillDebugger.CaptureDebuggingInfo. 23 | */ 24 | export interface ISkillDebuggerPayload extends IPayload { 25 | skillId: string | null; 26 | type: string; // "ConsideredIntents" or "SkillExecutionInfo". 27 | content: any; 28 | } 29 | 30 | export interface ISkillDebuggerContent { 31 | invocationRequest: any; 32 | invocationResponse: any; 33 | } 34 | 35 | /** 36 | * This payload is for the directive of SpeechSynthesizer.speak. 37 | */ 38 | export interface ISpeakPayload extends IPayload { 39 | caption: any; 40 | format: string; 41 | token: string; 42 | } 43 | 44 | /** 45 | * This payload is for the directive of Alexa.Presentation.APL.RenderDocument. 46 | */ 47 | export interface IAplDocumentPayload extends IPayload { 48 | presentationToken: string; 49 | document: any; 50 | datasources?: any; 51 | } 52 | 53 | /** 54 | * This payload is for the directive of Alexa.Presentation.APL.ExecuteCommands. 55 | */ 56 | export interface IExecuteCommandsPayload extends IPayload { 57 | presentationToken: string; 58 | commands: any; 59 | } 60 | 61 | export interface IDeviceCodeResponse { 62 | user_code: string; 63 | device_code: string; 64 | verification_uri: string; 65 | expires_in: number; 66 | interval: number; 67 | } 68 | 69 | export interface IEvent { 70 | header: IHeader; 71 | payload: any; 72 | } 73 | 74 | export interface IEventPack { 75 | event: IEvent; 76 | context: IEvent[]; 77 | } 78 | -------------------------------------------------------------------------------- /src/utils/avs/simulateAVSHelper.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | 7 | import * as vscode from "vscode"; 8 | import * as simulateReplayHelper from "../../utils/simulateReplayHelper"; 9 | import {EXTENSION_STATE_KEY, SIMULATOR_MESSAGE_TYPE} from "../../constants"; 10 | import {Logger} from "../../logger"; 11 | import {readDeviceToken} from "./deviceTokenUtil"; 12 | import {AVSClient} from "./avsClient"; 13 | 14 | /** 15 | * Generate apeechCall AVS for the text utterances. 16 | */ 17 | export async function callAvsForRecognizeEvent( 18 | userInput: string, 19 | isNewSession: boolean, 20 | context: vscode.ExtensionContext, 21 | ): Promise> { 22 | Logger.verbose(`Calling method: simulateAVSHelper.callAvsForRecognizeEvent`); 23 | const token = await readDeviceToken(context); 24 | const region = await context.secrets.get(EXTENSION_STATE_KEY.REGISTERED_DEVICE.REGION); 25 | if (isNewSession) { 26 | await AVSClient.getInstance(token, context, region).sendNewSessionEvent(); 27 | } 28 | const response = await AVSClient.getInstance(token, context).sendAudioEvent(userInput, isNewSession, context); 29 | return { 30 | invocationRequests: response.debugging.request, 31 | invocationResponses: response.debugging.response, 32 | alexaExecutionInfo: response.debugging.intent, 33 | alexaResponse: response.alexaResponse, 34 | documents: response.documents, 35 | dataSources: response.dataSources, 36 | viewport: JSON.stringify(simulateReplayHelper.aplViewport), 37 | viewportName: JSON.stringify(simulateReplayHelper.viewportName), 38 | type: SIMULATOR_MESSAGE_TYPE.UTTERANCE, 39 | aplCommands: response.aplCommands, 40 | }; 41 | } 42 | 43 | /** 44 | * Call AVS with the user event callback when user click on the APL preview. 45 | * @param userEvent 46 | */ 47 | export async function callAvsForAplUserEvent( 48 | userEvent: Record, 49 | context: vscode.ExtensionContext, 50 | ): Promise> { 51 | Logger.verbose(`Calling method: simulateAVSHelper.callAvsForAplUserEvent`); 52 | const token = await readDeviceToken(context); 53 | const region = await context.secrets.get(EXTENSION_STATE_KEY.REGISTERED_DEVICE.REGION); 54 | const response = await AVSClient.getInstance(token, context, region).sendUserEvent(userEvent); 55 | return { 56 | invocationRequests: "", 57 | invocationResponses: "", 58 | alexaExecutionInfo: "", 59 | alexaResponse: response.alexaResponse, 60 | documents: response.documents, 61 | dataSources: response.dataSources, 62 | viewport: JSON.stringify(simulateReplayHelper.aplViewport), 63 | viewportName: JSON.stringify(simulateReplayHelper.viewportName), 64 | type: SIMULATOR_MESSAGE_TYPE.UTTERANCE, 65 | aplCommands: response.aplCommands, 66 | }; 67 | } 68 | -------------------------------------------------------------------------------- /src/utils/commands/contactToolkitTeam.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | "use strict"; 7 | import * as vscode from "vscode"; 8 | 9 | import {EXTERNAL_LINKS} from "../../constants"; 10 | import {AbstractCommand, CommandContext} from "../../runtime"; 11 | import {Logger} from "../../logger"; 12 | 13 | export class ContactToolkitTeamCommand extends AbstractCommand { 14 | protected feedbackOptions = new Map(); 15 | 16 | constructor() { 17 | super("ask.contactToolkitTeam"); 18 | 19 | for (const option of Object.values(EXTERNAL_LINKS.CONTACT_ALEXA_TEAM)) { 20 | this.feedbackOptions.set(option.TITLE, option.URL); 21 | } 22 | } 23 | 24 | async execute(context: CommandContext): Promise { 25 | Logger.debug(`Calling method: ${this.commandName}`); 26 | 27 | const pickedContactMethod = await vscode.window.showQuickPick(Array.from(this.feedbackOptions.keys())); 28 | if (pickedContactMethod) { 29 | await vscode.env.openExternal(vscode.Uri.parse(this.feedbackOptions.get(pickedContactMethod) as string)); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/utils/commands/getToolkitInfo.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import * as vscode from "vscode"; 7 | import * as os from "os"; 8 | import * as child_process from "child_process"; 9 | 10 | import {AbstractCommand} from "../../runtime"; 11 | import {Logger} from "../../logger"; 12 | import {DEFAULT_ENCODING, EXTENSION_ID} from "../../constants"; 13 | 14 | export class GetToolkitInfoCommand extends AbstractCommand { 15 | constructor() { 16 | super("ask.aboutToolkit"); 17 | } 18 | 19 | private getToolkitDetails(): string { 20 | const osType = os.type(); 21 | const osArch = os.arch(); 22 | const osRelease = os.release(); 23 | const vsCodeVersion = vscode.version; 24 | const gitVersionUint8Array: Uint8Array = child_process.execSync("git --version"); 25 | const gitVersion = new TextDecoder(DEFAULT_ENCODING).decode(gitVersionUint8Array); 26 | const pluginVersion: string = vscode.extensions.getExtension(EXTENSION_ID)?.packageJSON.version; 27 | 28 | const toolkitDetails = `OS: ${osType} ${osArch} ${osRelease}\nVisual Studio Code Version: ${vsCodeVersion}\nAlexa Skills Toolkit Version: ${pluginVersion}\nGit Version: ${gitVersion}`; 29 | 30 | return toolkitDetails; 31 | } 32 | 33 | async execute(): Promise { 34 | Logger.debug(`Calling method: ${this.commandName}`); 35 | 36 | const toolkitEnvDetails = this.getToolkitDetails(); 37 | const result = await vscode.window.showInformationMessage(toolkitEnvDetails, {modal: true}, "Copy"); 38 | if (result === "Copy") { 39 | void vscode.env.clipboard.writeText(toolkitEnvDetails); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/utils/commands/openUrl.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import * as vscode from "vscode"; 7 | import {logAskError} from "../../exceptions"; 8 | import {Logger} from "../../logger"; 9 | import {AbstractCommand, CommandContext} from "../../runtime"; 10 | import {checkProfileSkillAccess} from "../skillHelper"; 11 | 12 | export class OpenUrlCommand extends AbstractCommand { 13 | constructor() { 14 | super("ask.container.openUrl"); 15 | } 16 | 17 | async execute(context: CommandContext, url: string, skipSkillAccessCheck = false): Promise { 18 | Logger.debug(`Calling method: ${this.commandName}, args: `, url, skipSkillAccessCheck); 19 | 20 | try { 21 | if (!skipSkillAccessCheck) { 22 | checkProfileSkillAccess(context.extensionContext); 23 | } 24 | await vscode.env.openExternal(vscode.Uri.parse(url)); 25 | } catch (err) { 26 | throw logAskError(`Open URL failed`, err, true); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/utils/commands/openWorkspace.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import * as vscode from "vscode"; 7 | import {logAskError} from "../../exceptions"; 8 | import {Logger} from "../../logger"; 9 | import {AbstractCommand, CommandContext} from "../../runtime"; 10 | import {openWorkspaceFolder} from "../../utils/workspaceHelper"; 11 | 12 | export class OpenWorkspaceCommand extends AbstractCommand { 13 | constructor() { 14 | super("ask.container.openWorkspace"); 15 | } 16 | 17 | async execute(context: CommandContext): Promise { 18 | Logger.debug(`Calling method: ${this.commandName}`); 19 | const userChoseWorkSpace = await vscode.window.showOpenDialog({ 20 | canSelectFiles: false, 21 | canSelectFolders: true, 22 | canSelectMany: false, 23 | }); 24 | if (!userChoseWorkSpace) { 25 | throw logAskError("Cannot find a workspace to create the skill project"); 26 | } 27 | 28 | await openWorkspaceFolder(userChoseWorkSpace[0]); 29 | // await vscode.commands.executeCommand('vscode.openFolder', userChoseWorkSpace[0]); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/utils/dateHelper.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import {Logger} from "../logger"; 7 | 8 | export function getCurrentDate(): string { 9 | Logger.verbose(`Calling method: getCurrentDate`); 10 | const date = new Date(); 11 | let month: string | number = date.getMonth() + 1; 12 | let strDate: string | number = date.getDate(); 13 | 14 | if (month <= 9) { 15 | month = "0" + month; 16 | } 17 | if (strDate <= 9) { 18 | strDate = "0" + strDate; 19 | } 20 | const currentDate: string = date.getFullYear().toString() + month + strDate + date.getHours() + date.getMinutes() + date.getSeconds(); 21 | return currentDate; 22 | } 23 | -------------------------------------------------------------------------------- /src/utils/hashHelper.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import * as folderHash from "folder-hash"; 7 | 8 | export async function getHash(sourcePath: string): Promise { 9 | const option = {algo: "sha1"}; 10 | return new Promise((resolve, reject) => { 11 | // eslint-disable-next-line @typescript-eslint/no-unsafe-call 12 | folderHash.hashElement(sourcePath, "", option, (error, result) => { 13 | if (error !== null && error !== undefined) { 14 | reject(error); 15 | } 16 | // @ts-ignore 17 | resolve(result); 18 | }); 19 | }); 20 | } 21 | -------------------------------------------------------------------------------- /src/utils/httpHelper.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import * as vscode from "vscode"; 7 | import * as child_process from "child_process"; 8 | import * as os from "os"; 9 | import {EXTENSION_ID} from "../constants"; 10 | import {Logger} from "../logger"; 11 | 12 | export function resolveUserAgent(): string { 13 | Logger.verbose(`Calling method: resolveUserAgent`); 14 | const pluginVersion: string = vscode.extensions.getExtension(EXTENSION_ID)?.packageJSON.version; 15 | const vsCodeVersion = vscode.version; 16 | const osType = os.type(); 17 | const osArch = os.arch(); 18 | const gitVersionUint8Array: Buffer = child_process.execSync("git --version"); 19 | const gitVersion = gitVersionUint8Array.toString().replace("\n", ""); 20 | return `askToolkit/${pluginVersion} VSCode/${vsCodeVersion} ${osType}/${osArch} Git/${gitVersion}`; 21 | } 22 | -------------------------------------------------------------------------------- /src/utils/mediaHelper.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import * as vscode from "vscode"; 7 | import * as path from "path"; 8 | 9 | export function getImagesFolder(context: vscode.ExtensionContext): string { 10 | return context.asAbsolutePath(path.join("third-party", "resources", "from-vscode-icons")); 11 | } 12 | -------------------------------------------------------------------------------- /src/utils/retry.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | export async function promiseRetry(retries: number, fn: () => Promise): Promise { 7 | return fn().catch((err: Error) => { 8 | if (retries > 1) { 9 | return promiseRetry(retries - 1, fn); 10 | } else { 11 | return Promise.reject(err); 12 | } 13 | }); 14 | } 15 | -------------------------------------------------------------------------------- /src/utils/statusBarHelper.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | 7 | "use strict"; 8 | import * as vscode from "vscode"; 9 | 10 | /** 11 | * Create a status bar item. 12 | * @param {number} priority The higher the priority, the more left the icon will be. 13 | * @param {string} commandName The function that will be triggered when the user click the button. 14 | * @param {string} iconText The shape and text of the button. 15 | * @param {string} tooltip The description when hovering the button. 16 | * @return {vscode.StatusBarItem} 17 | */ 18 | export function createStatusBarItem( 19 | priority: number, 20 | commandName: string | undefined, 21 | iconText: string, 22 | tooltip: string, 23 | aligment: vscode.StatusBarAlignment = vscode.StatusBarAlignment.Left, 24 | ): vscode.StatusBarItem { 25 | const item = vscode.window.createStatusBarItem(aligment, priority); 26 | item.command = commandName; 27 | item.text = iconText; 28 | item.tooltip = tooltip; 29 | item.show(); 30 | return item; 31 | } 32 | -------------------------------------------------------------------------------- /src/utils/webViews/authHelper.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import {Token} from "simple-oauth2"; 7 | import {EventEmitter, ExtensionContext} from "vscode"; 8 | import {AbstractWebView, PluginTreeItem, Resource, Utils} from "../../runtime"; 9 | import {onSkillConsoleViewChangeEventEmitter} from "../../askContainer/events"; 10 | import {Logger} from "../../logger"; 11 | import {DEFAULT_PROFILE, EXTENSION_STATE_KEY} from "../../constants"; 12 | import {AskParameterAbsenceError} from "../../exceptions"; 13 | import {ViewLoader} from "./viewLoader"; 14 | import {clearCachedSkills} from "../skillHelper"; 15 | 16 | export async function authenticate( 17 | context: ExtensionContext, 18 | webview?: AbstractWebView, 19 | profileName?: string, 20 | eventEmitter?: EventEmitter>, 21 | ): Promise { 22 | Logger.debug(`Calling method: authenticate, args: `, profileName); 23 | const profile = profileName ?? DEFAULT_PROFILE; 24 | 25 | if (webview && !webview.isDisposed()) { 26 | const loader = new ViewLoader(context, "profileManager", webview); 27 | webview.getPanel().webview.html = loader.renderView({ 28 | name: "initiateAuthFlow", 29 | js: false, 30 | }); 31 | } 32 | const token: Token = await Utils.accessTokenGenerator({}); 33 | Utils.deleteProfile(profile); 34 | Utils.writeToken(token, profile); 35 | const hasVendorId = await Utils.setVendorId(profile, context); 36 | if (!hasVendorId) { 37 | Utils.deleteProfile(profile); 38 | throw new AskParameterAbsenceError("No Vendor Id."); 39 | } 40 | context.globalState.update("LwaProfile", profile); 41 | context.globalState.update("didFirstTimeLogin", true); 42 | clearCachedSkills(context); 43 | onSkillConsoleViewChangeEventEmitter.fire(undefined); 44 | } 45 | -------------------------------------------------------------------------------- /src/utils/webViews/viewManager.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import {AbstractWebView} from "../../runtime"; 7 | import {ext} from "../../extensionGlobals"; 8 | 9 | export function registerWebviews(...views: AbstractWebView[]): void { 10 | views.forEach((view) => { 11 | ext.webViews.push(view); 12 | }); 13 | } 14 | 15 | export function disposeWebviews(shouldRemoveWebViews?: boolean, shouldCloseGlobalViews?: boolean): void { 16 | ext.webViews.forEach((view) => { 17 | if (view !== undefined && view.getPanel() !== undefined) { 18 | // If global dispose is true, dispose all else dispose only the non global views. 19 | if (shouldCloseGlobalViews !== undefined && shouldCloseGlobalViews) { 20 | view.dispose(); 21 | } else if (!view.getIsGlobal()) { 22 | view.dispose(); 23 | } 24 | } 25 | }); 26 | // Remove existing web views when resetting the workspace. 27 | if (shouldRemoveWebViews !== undefined && shouldRemoveWebViews) { 28 | removeWebViews(); 29 | } 30 | } 31 | 32 | function removeWebViews(): void { 33 | ext.webViews.splice(0, ext.webViews.length); 34 | } 35 | -------------------------------------------------------------------------------- /src/utils/zipHelper.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import AdmZip from "adm-zip"; 7 | import * as fs from "fs"; 8 | import * as path from "path"; 9 | import {AskError, logAskError} from "../exceptions"; 10 | import {Logger} from "../logger"; 11 | 12 | export function unzipFile(zipLocation: string, destPath: string, overWriteFiles = true, deleteZipAfter = true): void { 13 | Logger.verbose(`Calling method: unzipFile, args: `, zipLocation, destPath, overWriteFiles, deleteZipAfter); 14 | const zip = new AdmZip(zipLocation); 15 | try { 16 | zip.extractAllTo(destPath, overWriteFiles); 17 | 18 | if (deleteZipAfter) { 19 | fs.unlinkSync(zipLocation); 20 | } 21 | } catch (unzipErr) { 22 | throw logAskError(`Unzip failed: ${unzipErr}`); 23 | } 24 | } 25 | 26 | export function createZipFile(sourceDir: string, zipFileDir: string): string { 27 | Logger.verbose(`Calling method: createZipFile, args: `, sourceDir, zipFileDir); 28 | try { 29 | fs.accessSync(sourceDir, fs.constants.W_OK); 30 | if (path.extname(zipFileDir) === ".zip") { 31 | throw new AskError(`The source file ${zipFileDir} has already been compressed. Skip the zipping`); 32 | } 33 | const zip = new AdmZip(); 34 | zip.addLocalFolder(sourceDir); 35 | const zipFilePath = path.join(zipFileDir, "ask_tmp.zip"); 36 | zip.writeZip(zipFilePath); 37 | return zipFilePath; 38 | } catch (error) { 39 | throw logAskError(`Create temp zip failed: ${error}`); 40 | } 41 | } 42 | 43 | export function zipDirectory(sourceDir: string, outDir: string): void { 44 | Logger.verbose(`Calling method: zipDirectory, args: `, sourceDir, outDir); 45 | try { 46 | const zip = new AdmZip(); 47 | zip.addLocalFolder(sourceDir); 48 | const zipFilePath = path.join(outDir); 49 | zip.writeZip(zipFilePath); 50 | } catch (error) { 51 | throw logAskError(`Zip Directory failed: ${error}`); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /test/acdlServer/completionUtils.ts: -------------------------------------------------------------------------------- 1 | import * as acdl from "@alexa/acdl"; 2 | import {Apply} from "@alexa/acdl/dist/cjs/apply"; 3 | 4 | export function getMockedTypeChecker(): acdl.TypeChecker { 5 | return { 6 | getVisibleNames: () => {}, 7 | isNameReference: () => {}, 8 | resolveNameReferenceToDecl: () => {}, 9 | getType: () => {}, 10 | getApply: () => {}, 11 | } as unknown as acdl.TypeChecker; 12 | } 13 | 14 | export function getMockedApplyWithArgs() { 15 | const mockedArgumentDecl1 = {name: {name: "decl1"}} as acdl.ArgumentDeclaration; 16 | const mockedArgumentDecl2 = {name: {name: "decl2"}} as acdl.ArgumentDeclaration; 17 | const mockedArgumentDecl3 = {name: {name: "decl3"}} as acdl.ArgumentDeclaration; 18 | const argArray = [mockedArgumentDecl1, mockedArgumentDecl2, mockedArgumentDecl3]; 19 | return { 20 | decl: { 21 | kind: "ActionDeclaration", 22 | arguments: argArray, 23 | }, 24 | argumentDeclarations: argArray, 25 | }; 26 | } 27 | 28 | export function getMockedApplyWithoutArgs(): Apply { 29 | return { 30 | decl: { 31 | kind: "ActionDeclaration", 32 | arguments: undefined, 33 | }, 34 | } as Apply; 35 | } 36 | -------------------------------------------------------------------------------- /test/acdlServer/mockACDL/ask-resources.json: -------------------------------------------------------------------------------- 1 | { 2 | "askcliResourcesVersion": "2020-03-31", 3 | "profiles": { 4 | "default": { 5 | "skillMetadata": { 6 | "src": "./skill-package" 7 | } 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /test/acdlServer/mockACDL/skill-package/conversations/Weather.acdl: -------------------------------------------------------------------------------- 1 | namespace com.weatherbot 2 | 3 | import com.amazon.alexa.ask.conversations.* 4 | import com.amazon.ask.types.builtins.AMAZON.* 5 | import com.amazon.alexa.schema.Nothing 6 | import prompts.weather_apla 7 | import prompts.request_city_apla 8 | import prompts.request_date_apla 9 | import prompts.request_city_date_apla 10 | 11 | type CityAndDate { 12 | optional City cityName 13 | optional DATE date 14 | } 15 | 16 | getWeatherEvent = utterances( 17 | [ 18 | "What's the weather {date} in {cityName}", 19 | "what is the weather {date}", 20 | "How is the weather {date}", 21 | "How is weather in {cityName} {date}", 22 | "how is weather", 23 | "can you please give me weather report for {date}" 24 | ] 25 | ) 26 | 27 | type WeatherResult { 28 | City cityName 29 | NUMBER highTemp 30 | NUMBER lowTemp 31 | } 32 | 33 | type ResponsePayload { 34 | WeatherResult weatherResult 35 | } 36 | 37 | action WeatherResult getWeather(City cityName, DATE date) 38 | 39 | dialog Nothing Weather { 40 | sample { 41 | weatherRequest = expect(Invoke, getWeatherEvent) 42 | 43 | ensure( 44 | RequestArguments {arguments = [getWeather.arguments.cityName], response = request_city_apla}, 45 | RequestArguments {arguments = [getWeather.arguments.date], response = request_date_apla}, 46 | RequestArguments {arguments = [getWeather.arguments.cityName, getWeather.arguments.date], response = request_city_date_apla} 47 | ) 48 | 49 | weatherResult = getWeather(weatherRequest.cityName, weatherRequest.date) 50 | 51 | response(weather_apla, Notify {actionName = getWeather}, payload = ResponsePayload {weatherResult = weatherResult}) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /test/acdlServer/mockACDL/skill-package/interactionModels/custom/en-US.json: -------------------------------------------------------------------------------- 1 | { 2 | "interactionModel": { 3 | "languageModel": { 4 | "invocationName": "my weather bot", 5 | "intents": [ 6 | { 7 | "name": "AMAZON.CancelIntent", 8 | "samples": [] 9 | }, 10 | { 11 | "name": "AMAZON.HelpIntent", 12 | "samples": [] 13 | }, 14 | { 15 | "name": "AMAZON.StopIntent", 16 | "samples": [] 17 | }, 18 | { 19 | "name": "GetWeatherIntent", 20 | "samples": [ 21 | "what is the weather", 22 | "how is the weather", 23 | "tell me the weather" 24 | ] 25 | }, 26 | { 27 | "name": "AMAZON.NavigateHomeIntent", 28 | "samples": [] 29 | }, 30 | { 31 | "name": "AMAZON.FallbackIntent", 32 | "samples": [] 33 | } 34 | ], 35 | "types": [] 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /test/acdlServer/mockACDL/skill-package/response/prompts/AlexaConversationsBye/document.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "APL-A", 3 | "version": "0.1", 4 | "mainTemplate": { 5 | "parameters": [ 6 | "payload" 7 | ], 8 | "item": { 9 | "type": "RandomSelector", 10 | "description": "Change 'type' above to try different Selector Component Types like Sequential", 11 | "items": [ 12 | { 13 | "type": "Speech", 14 | "contentType": "text", 15 | "content": "Bye.", 16 | "description": "Expand on 'items' array to add multiple prompts, use response template arguments by adding it to 'content' like this ${payload.input_argument_name} and add SSML by changing 'contentType' to 'SSML' and adding SSML to 'content' like that" 17 | } 18 | ] 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /test/acdlServer/mockACDL/skill-package/response/prompts/AlexaConversationsNotifyFailure/document.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "APL-A", 3 | "version": "0.1", 4 | "mainTemplate": { 5 | "parameters": [ 6 | "payload" 7 | ], 8 | "item": { 9 | "type": "RandomSelector", 10 | "description": "Change 'type' above to try different Selector Component Types like Sequential", 11 | "items": [ 12 | { 13 | "type": "Speech", 14 | "contentType": "text", 15 | "content": "Sorry, there was an error in the request.", 16 | "description": "Expand on 'items' array to add multiple prompts, use response template arguments by adding it to 'content' like this ${payload.input_argument_name} and add SSML by changing 'contentType' to 'SSML' and adding SSML to 'content' like that" 17 | } 18 | ] 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /test/acdlServer/mockACDL/skill-package/response/prompts/AlexaConversationsOutOfDomain/document.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "APL-A", 3 | "version": "0.1", 4 | "mainTemplate": { 5 | "parameters": [ 6 | "payload" 7 | ], 8 | "item": { 9 | "type": "RandomSelector", 10 | "description": "Change 'type' above to try different Selector Component Types like Sequential", 11 | "items": [ 12 | { 13 | "type": "Speech", 14 | "contentType": "text", 15 | "content": "Sorry, I don't understand.", 16 | "description": "Expand on 'items' array to add multiple prompts, use response template arguments by adding it to 'content' like this ${payload.input_argument_name} and add SSML by changing 'contentType' to 'SSML' and adding SSML to 'content' like that" 17 | } 18 | ] 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /test/acdlServer/mockACDL/skill-package/response/prompts/AlexaConversationsProvideHelp/document.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "APL-A", 3 | "version": "0.1", 4 | "mainTemplate": { 5 | "parameters": [ 6 | "payload" 7 | ], 8 | "item": { 9 | "type": "RandomSelector", 10 | "description": "Change 'type' above to try different Selector Component Types like Sequential", 11 | "items": [ 12 | { 13 | "type": "Speech", 14 | "contentType": "text", 15 | "content": "I can give you the weather report. Just say \"what's the weather\" to start.", 16 | "description": "Expand on 'items' array to add multiple prompts, use response template arguments by adding it to 'content' like this ${payload.input_argument_name} and add SSML by changing 'contentType' to 'SSML' and adding SSML to 'content' like that" 17 | } 18 | ] 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /test/acdlServer/mockACDL/skill-package/response/prompts/AlexaConversationsRequestMore/document.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "APL-A", 3 | "version": "0.1", 4 | "mainTemplate": { 5 | "parameters": [ 6 | "payload" 7 | ], 8 | "item": { 9 | "type": "RandomSelector", 10 | "description": "Change 'type' above to try different Selector Component Types like Sequential", 11 | "items": [ 12 | { 13 | "type": "Speech", 14 | "contentType": "text", 15 | "content": "What do you want to do.", 16 | "description": "Expand on 'items' array to add multiple prompts, use response template arguments by adding it to 'content' like this ${payload.input_argument_name} and add SSML by changing 'contentType' to 'SSML' and adding SSML to 'content' like that" 17 | } 18 | ] 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /test/acdlServer/mockACDL/skill-package/response/prompts/AlexaConversationsThankYou/document.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "APL-A", 3 | "version": "0.1", 4 | "mainTemplate": { 5 | "parameters": [ 6 | "payload" 7 | ], 8 | "item": { 9 | "type": "RandomSelector", 10 | "description": "Change 'type' above to try different Selector Component Types like Sequential", 11 | "items": [ 12 | { 13 | "type": "Speech", 14 | "contentType": "text", 15 | "content": "Thank you.", 16 | "description": "Expand on 'items' array to add multiple prompts, use response template arguments by adding it to 'content' like this ${payload.input_argument_name} and add SSML by changing 'contentType' to 'SSML' and adding SSML to 'content' like that" 17 | } 18 | ] 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /test/acdlServer/mockACDL/skill-package/response/prompts/AlexaConversationsWelcome/document.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "APL-A", 3 | "version": "0.1", 4 | "mainTemplate": { 5 | "parameters": [ 6 | "payload" 7 | ], 8 | "item": { 9 | "type": "RandomSelector", 10 | "description": "Change 'type' above to try different Selector Component Types like Sequential", 11 | "items": [ 12 | { 13 | "type": "Speech", 14 | "contentType": "text", 15 | "content": "Welcome to the weather bot, I can give you the weather report. Just say \"what's the weather\" to start.", 16 | "description": "Expand on 'items' array to add multiple prompts, use response template arguments by adding it to 'content' like this ${payload.input_argument_name} and add SSML by changing 'contentType' to 'SSML' and adding SSML to 'content' like that" 17 | } 18 | ] 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /test/acdlServer/mockACDL/skill-package/response/prompts/AlexaConversationsYouAreWelcome/document.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "APL-A", 3 | "version": "0.1", 4 | "mainTemplate": { 5 | "parameters": [ 6 | "payload" 7 | ], 8 | "item": { 9 | "type": "RandomSelector", 10 | "description": "Change 'type' above to try different Selector Component Types like Sequential", 11 | "items": [ 12 | { 13 | "type": "Speech", 14 | "contentType": "text", 15 | "content": "You are welcome.", 16 | "description": "Expand on 'items' array to add multiple prompts, use response template arguments by adding it to 'content' like this ${payload.input_argument_name} and add SSML by changing 'contentType' to 'SSML' and adding SSML to 'content' like that" 17 | } 18 | ] 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /test/acdlServer/mockACDL/skill-package/response/prompts/request_city_apla/document.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "APL-A", 3 | "version": "0.1", 4 | "mainTemplate": { 5 | "parameters": [ 6 | "payload" 7 | ], 8 | "item": { 9 | "type": "RandomSelector", 10 | "description": "Change 'type' above to try different Selector Component Types like Sequential", 11 | "items": [ 12 | { 13 | "type": "Speech", 14 | "contentType": "text", 15 | "content": "Which city?", 16 | "description": "Expand on 'items' array to add multiple prompts, use response template arguments by adding it to 'content' like this ${payload.input_argument_name} and add SSML by changing 'contentType' to 'SSML' and adding SSML to 'content' like that" 17 | } 18 | ] 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /test/acdlServer/mockACDL/skill-package/response/prompts/request_city_date_apla/document.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "APL-A", 3 | "version": "0.1", 4 | "mainTemplate": { 5 | "parameters": [ 6 | "payload" 7 | ], 8 | "item": { 9 | "type": "RandomSelector", 10 | "description": "Change 'type' above to try different Selector Component Types like Sequential", 11 | "items": [ 12 | { 13 | "type": "Speech", 14 | "contentType": "text", 15 | "content": "Which city and for which date?", 16 | "description": "Expand on 'items' array to add multiple prompts, use response template arguments by adding it to 'content' like this ${payload.input_argument_name} and add SSML by changing 'contentType' to 'SSML' and adding SSML to 'content' like that" 17 | } 18 | ] 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /test/acdlServer/mockACDL/skill-package/response/prompts/request_date_apla/document.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "APL-A", 3 | "version": "0.1", 4 | "mainTemplate": { 5 | "parameters": [ 6 | "payload" 7 | ], 8 | "item": { 9 | "type": "RandomSelector", 10 | "description": "Change 'type' above to try different Selector Component Types like Sequential", 11 | "items": [ 12 | { 13 | "type": "Speech", 14 | "contentType": "text", 15 | "content": "Which date?", 16 | "description": "Expand on 'items' array to add multiple prompts, use response template arguments by adding it to 'content' like this ${payload.input_argument_name} and add SSML by changing 'contentType' to 'SSML' and adding SSML to 'content' like that" 17 | } 18 | ] 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /test/acdlServer/mockACDL/skill-package/response/prompts/weather_apla/document.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "APL-A", 3 | "version": "0.1", 4 | "mainTemplate": { 5 | "parameters": [ 6 | "payload" 7 | ], 8 | "item": { 9 | "type": "RandomSelector", 10 | "description": "Change 'type' above to try different Selector Component Types like Sequential", 11 | "items": [ 12 | { 13 | "when": "${payload.weatherResult != null && payload.weatherResult.cityName != null && payload.weatherResult.highTemp != null && payload.weatherResult.lowTemp != null}", 14 | "type": "Speech", 15 | "contentType": "text", 16 | "content": "In ${payload.weatherResult.cityName}, it's a high of ${payload.weatherResult.highTemp} degrees and a low of ${payload.weatherResult.lowTemp} degrees.", 17 | "description": "Expand on 'items' array to add multiple prompts, use response template arguments by adding it to 'content' like this ${payload.input_argument_name} and add SSML by changing 'contentType' to 'SSML' and adding SSML to 'content' like that" 18 | } 19 | ] 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /test/acdlServer/mockACDL/skill-package/skill.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest": { 3 | "publishingInformation": { 4 | "locales": { 5 | "en-US": { 6 | "summary": "Sample Short Description", 7 | "examplePhrases": [ 8 | "Alexa open my weather bot", 9 | "hello", 10 | "help" 11 | ], 12 | "name": "template-weather-bot-blank", 13 | "description": "Sample Full Description" 14 | } 15 | }, 16 | "isAvailableWorldwide": true, 17 | "testingInstructions": "Sample Testing Instructions.", 18 | "category": "KNOWLEDGE_AND_TRIVIA", 19 | "distributionCountries": [] 20 | }, 21 | "apis": { 22 | "custom": { 23 | "dialogManagement": { 24 | "sessionStartDelegationStrategy": { 25 | "target": "AMAZON.Conversations" 26 | }, 27 | "dialogManagers": [ 28 | { 29 | "type": "AMAZON.Conversations" 30 | } 31 | ] 32 | } 33 | } 34 | }, 35 | "manifestVersion": "1.0" 36 | } 37 | } -------------------------------------------------------------------------------- /test/askContainer/commands/deviceRegistry.test.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import * as vscode from "vscode"; 7 | import * as assert from "assert"; 8 | import * as sinon from "sinon"; 9 | 10 | import {DeviceRegistryCommand} from "../../../src/askContainer/commands/deviceRegistryCommand"; 11 | import {stubTelemetryClient} from "../../../test/testUtilities"; 12 | 13 | describe("Command askContainer.skillsConsole.deviceRegistry", () => { 14 | let command: DeviceRegistryCommand; 15 | let sandbox: sinon.SinonSandbox; 16 | 17 | after(() => { 18 | command.dispose(); 19 | }); 20 | beforeEach(() => { 21 | sandbox = sinon.createSandbox(); 22 | stubTelemetryClient(sandbox); 23 | }); 24 | 25 | afterEach(() => { 26 | sandbox.restore(); 27 | }); 28 | 29 | it("Should show the view when executed", async () => { 30 | const showViewSpy = sinon.spy(); 31 | 32 | command = new DeviceRegistryCommand({showView: showViewSpy} as any, "askContainer.skillsConsole.deviceRegistryTest"); 33 | /* 34 | undefined argument due to 35 | 36 | https://code.visualstudio.com/api/references/contribution-points#contributes.menus 37 | Note: When a command is invoked from a (context) menu, VS Code tries to infer the currently selected resource 38 | and passes that as a parameter when invoking the command. 39 | 40 | */ 41 | await vscode.commands.executeCommand("askContainer.skillsConsole.deviceRegistryTest", undefined); 42 | 43 | assert.ok(showViewSpy.called); 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /test/askContainer/webview/profileManagerWebview.test.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import * as assert from "assert"; 7 | import * as sinon from "sinon"; 8 | import * as vscode from "vscode"; 9 | import {ProfileManagerWebview} from "../../../src/askContainer/webViews/profileManagerWebview"; 10 | import {FakeExtensionContext, FakeWebviewPanelOnDidChangeViewStateEvent} from "../../testUtilities"; 11 | import {EXTENSION_STATE_KEY} from "../../../src/constants"; 12 | 13 | describe("Webview_deploySkill tests", () => { 14 | let webView: ProfileManagerWebview; 15 | let sandbox: sinon.SinonSandbox; 16 | 17 | beforeEach(() => { 18 | sandbox = sinon.createSandbox(); 19 | }); 20 | 21 | afterEach(() => { 22 | sandbox.restore(); 23 | }); 24 | 25 | it("When message is deleteProfile, should display error message if profileName equals current profile", async () => { 26 | const fakeContext = FakeExtensionContext.getFakeExtContext(); 27 | fakeContext.globalState.update(EXTENSION_STATE_KEY.LWA_PROFILE, "fakeProfileName"); 28 | webView = new ProfileManagerWebview("fakeTitle", "fakeID", fakeContext); 29 | 30 | const message = {deleteProfile: "fakeProfileName"}; 31 | const spy = sinon.spy(vscode.window, "showErrorMessage"); 32 | 33 | await webView.onReceiveMessageListener(message); 34 | assert.deepStrictEqual(true, spy.called); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /test/definitionUtils.ts: -------------------------------------------------------------------------------- 1 | import * as acdl from "@alexa/acdl"; 2 | import {uinteger} from "vscode-languageclient"; 3 | import {SourceLocation} from "@alexa/acdl"; 4 | import {Position} from "vscode-languageserver/node"; 5 | 6 | export function createMockNode(typeStr: string, name: string, uri: string, loc: acdl.SourceLocation | undefined): T { 7 | return { 8 | loc, 9 | kind: typeStr, 10 | name: {name, uri, loc} as acdl.Name, 11 | } as unknown as T; 12 | } 13 | 14 | export function createSrcLocation( 15 | startLine: uinteger, 16 | startCharacter: uinteger, 17 | endLine: uinteger, 18 | endCharacter: uinteger, 19 | ): SourceLocation { 20 | return { 21 | begin: {line: startLine, character: startCharacter}, 22 | end: {line: endLine, character: endCharacter}, 23 | }; 24 | } 25 | 26 | export function createMockTypeChecker(): acdl.TypeChecker { 27 | return { 28 | lookupName: () => {}, 29 | } as unknown as acdl.TypeChecker; 30 | } 31 | 32 | export function convertACDLToVSCodeSrcPosition(line: number, char: number): Position { 33 | return {line: line - 1, character: char}; 34 | } 35 | 36 | export function convertVSCodeToACDLSrcPosition(line: number, char: number): Position { 37 | return {line: line + 1, character: char}; 38 | } 39 | -------------------------------------------------------------------------------- /test/extension.test.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | "use stirct"; 7 | 8 | import * as assert from "assert"; 9 | import * as vscode from "vscode"; 10 | import {EXTENSION_PUBLISHER, EXTENSION_FULL_NAME} from "../src/constants"; 11 | import {ext} from "../src/extensionGlobals"; 12 | import * as sinon from "sinon"; 13 | import * as s3ScriptChecker from "../src/utils/s3ScriptChecker"; 14 | 15 | const extensionId = `${EXTENSION_PUBLISHER}.${EXTENSION_FULL_NAME}`; 16 | 17 | describe("Alexa Skill Kit Extension", () => { 18 | let extension: vscode.Extension | undefined; 19 | 20 | it("Extension should be present", () => { 21 | extension = vscode.extensions.getExtension(extensionId); 22 | assert.ok(extension !== undefined); 23 | }); 24 | 25 | it("should activate", async () => { 26 | extension = vscode.extensions.getExtension(extensionId); 27 | if (extension !== undefined) { 28 | sinon.stub(s3ScriptChecker, "checkAllSkillS3Scripts"); 29 | await extension.activate(); 30 | assert.ok(extension.isActive); 31 | } else { 32 | assert.fail("Extension is not available"); 33 | } 34 | }); 35 | 36 | after(() => { 37 | ext.askGeneralCommands.forEach((command) => { 38 | command.dispose(); 39 | }); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /test/index.ts: -------------------------------------------------------------------------------- 1 | // PLEASE DO NOT MODIFY / DELETE UNLESS YOU KNOW WHAT YOU ARE DOING 2 | // 3 | // This file is providing the test runner to use when running extension tests. 4 | // By default the test runner in use is Mocha based. 5 | // 6 | // You can provide your own test runner if you want to override it by exporting 7 | // a function run(testRoot: string, clb: (error:Error) => void) that the extension 8 | // host can call to run the tests. The test runner is expected to use console.log 9 | // to report the results back to the caller. When the tests are finished, return 10 | // a possible error to the callback or null if none. 11 | 12 | /* eslint-disable eslint-comments/disable-enable-pair */ 13 | /* eslint-disable @typescript-eslint/no-this-alias */ 14 | /* eslint-disable prefer-const */ 15 | /* eslint-disable @typescript-eslint/no-unsafe-call */ 16 | /* eslint-disable @typescript-eslint/no-var-requires */ 17 | 18 | "use strict"; 19 | 20 | import * as glob from "glob"; 21 | import Mocha from "mocha"; 22 | import * as paths from "path"; 23 | 24 | // The test coverage approach is inspired by https://github.com/microsoft/vscode-js-debug/blob/master/src/test/testRunner.ts 25 | function setupCoverage() { 26 | const NYC = require("nyc"); 27 | const nyc = new NYC({ 28 | // set the project root 29 | cwd: paths.join(__dirname, "..", ".."), 30 | exclude: ["**/test/**", ".vscode-test/**"], 31 | reporter: ["html"], 32 | tempDir: paths.join(__dirname, "..", "..", "coverage", ".nyc_output"), 33 | all: true, 34 | instrument: true, 35 | hookRequire: true, 36 | hookRunInContext: true, 37 | hookRunInThisContext: true, 38 | }); 39 | 40 | nyc.reset(); 41 | nyc.wrap(); 42 | 43 | // eslint-disable-next-line @typescript-eslint/no-unsafe-return 44 | return nyc; 45 | } 46 | 47 | const mocha = new Mocha({ 48 | ui: "bdd", 49 | color: true, 50 | timeout: 30 * 1000, // for windows extension activation test 51 | }); 52 | 53 | export async function run(): Promise { 54 | let nyc; 55 | if (shouldGenerateCoverage()) { 56 | nyc = setupCoverage(); 57 | } 58 | // only search test files under out/test 59 | const testsRoot = paths.resolve(__dirname, ".."); 60 | const options = {cwd: testsRoot}; 61 | const files = glob.sync("**/**.test.js", options); 62 | for (const file of files) { 63 | mocha.addFile(paths.resolve(testsRoot, file)); 64 | } 65 | try { 66 | await new Promise((resolve, reject) => 67 | mocha.run((failures) => (failures ? reject(new Error(`${failures} tests failed`)) : resolve())), 68 | ); 69 | } finally { 70 | if (nyc !== undefined) { 71 | nyc.writeCoverageFile(); 72 | nyc.report(); 73 | } 74 | } 75 | } 76 | 77 | function shouldGenerateCoverage(): boolean { 78 | // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions 79 | return (process.env.ASK_TOOLKIT_NO_COVERAGE || "false").toLowerCase() !== "true"; 80 | } 81 | -------------------------------------------------------------------------------- /test/mockSkill/skill-package-NHS/skill.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexa/ask-toolkit-for-vscode/2fd1033f594a3281e1612d51d0c216a14e5e344d/test/mockSkill/skill-package-NHS/skill.txt -------------------------------------------------------------------------------- /test/mockSkill/skill-package/alexaSkill.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexa/ask-toolkit-for-vscode/2fd1033f594a3281e1612d51d0c216a14e5e344d/test/mockSkill/skill-package/alexaSkill.json -------------------------------------------------------------------------------- /test/mockSkill/skill-package/interaction/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexa/ask-toolkit-for-vscode/2fd1033f594a3281e1612d51d0c216a14e5e344d/test/mockSkill/skill-package/interaction/.gitkeep -------------------------------------------------------------------------------- /test/mockSkill/skill-package/interactionModels/custom/en-US.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexa/ask-toolkit-for-vscode/2fd1033f594a3281e1612d51d0c216a14e5e344d/test/mockSkill/skill-package/interactionModels/custom/en-US.json -------------------------------------------------------------------------------- /test/mockSkill/skill-package/skill.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexa/ask-toolkit-for-vscode/2fd1033f594a3281e1612d51d0c216a14e5e344d/test/mockSkill/skill-package/skill.json -------------------------------------------------------------------------------- /test/runTest.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import * as path from "path"; 7 | 8 | import {runTests} from "vscode-test"; 9 | import {env} from "process"; 10 | 11 | async function main() { 12 | try { 13 | // The folder containing the Extension Manifest package.json 14 | // Passed to `--extensionDevelopmentPath` 15 | const extensionDevelopmentPath = path.resolve(__dirname, "../../"); 16 | 17 | // The path to the extension test runner script 18 | // Passed to --extensionTestsPath 19 | const extensionTestsPath = path.resolve(__dirname, "./"); 20 | 21 | env.ASK_TOOLKIT_IGNORE_WEBPACK_BUNDLE = "true"; 22 | 23 | // Download VS Code, unzip it and run the integration test 24 | await runTests({ 25 | extensionDevelopmentPath, 26 | extensionTestsPath, 27 | launchArgs: ["--disable-extensions"], 28 | }); 29 | } catch (err) { 30 | console.error(err); 31 | console.error("Failed to run tests"); 32 | process.exit(1); 33 | } 34 | } 35 | 36 | // eslint-disable-next-line no-void 37 | void main(); 38 | -------------------------------------------------------------------------------- /test/utils/commands/debugAdapterPath.test.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import * as assert from "assert"; 7 | import child_process from "child_process"; 8 | import fs from "fs"; 9 | import * as path from "path"; 10 | import * as sinon from "sinon"; 11 | import * as vscode from "vscode"; 12 | import {DebugAdapterPathCommand} from "../../../src/askContainer/commands/local-debug/debugAdapterPath"; 13 | import {LOCAL_DEBUG} from "../../../src/constants"; 14 | import {stubTelemetryClient} from "../../../test/testUtilities"; 15 | 16 | describe("Command ask.debugAdapterPath", () => { 17 | let command: DebugAdapterPathCommand; 18 | let sandbox: sinon.SinonSandbox; 19 | let commandId: string; 20 | before(() => { 21 | command = new DebugAdapterPathCommand(); 22 | commandId = command.commandName; 23 | }); 24 | after(() => { 25 | command.dispose(); 26 | }); 27 | beforeEach(() => { 28 | sandbox = sinon.createSandbox(); 29 | stubTelemetryClient(sandbox); 30 | }); 31 | 32 | afterEach(() => { 33 | sandbox.restore(); 34 | }); 35 | 36 | it("Constructor should work as expected", () => { 37 | assert.strictEqual(command.title, commandId); 38 | assert.strictEqual(command.command, commandId); 39 | }); 40 | 41 | it("Should be able to return node local debugger path from the installed node_modules", async () => { 42 | const mockedDebugArgs = { 43 | type: "pwa-node", 44 | }; 45 | const fakePath = [{fsPath: "test"}]; 46 | 47 | sandbox.stub(vscode.workspace, "findFiles").resolves(fakePath); 48 | 49 | const debuggerPath = await vscode.commands.executeCommand(commandId, mockedDebugArgs); 50 | assert.ok(debuggerPath === fakePath[0].fsPath); 51 | }); 52 | 53 | it("Should be able to return python local debugger path from the installed node_modules", async () => { 54 | const mockPythonPath = "test/site-packages"; 55 | const localDebuggerPath = path.join(mockPythonPath, LOCAL_DEBUG.PYTHON_DEPENDENCIES.DEP_PATH); 56 | const mockedDebugArgs = { 57 | type: "python", 58 | pythonPath: mockPythonPath, 59 | }; 60 | const testSitePkgLocation = "['test/site-packages']"; 61 | 62 | sandbox.stub(child_process, "execSync"); 63 | sandbox.stub(TextDecoder.prototype, "decode").returns(testSitePkgLocation); 64 | sandbox.stub(fs, "existsSync").withArgs(localDebuggerPath).returns(true); 65 | 66 | const debuggerPath = await vscode.commands.executeCommand(commandId, mockedDebugArgs); 67 | assert.ok(debuggerPath === localDebuggerPath); 68 | }); 69 | }); 70 | -------------------------------------------------------------------------------- /test/utils/commands/openUrl.test.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import * as vscode from "vscode"; 7 | import * as assert from "assert"; 8 | import * as sinon from "sinon"; 9 | 10 | import {OpenUrlCommand} from "../../../src/utils/commands/openUrl"; 11 | import * as skillHelper from "../../../src/utils/skillHelper"; 12 | import {stubTelemetryClient} from "../../../test/testUtilities"; 13 | describe("Command ask.container.openUrl", () => { 14 | let command: OpenUrlCommand; 15 | let sandbox: sinon.SinonSandbox; 16 | let commandId: string; 17 | before(() => { 18 | command = new OpenUrlCommand(); 19 | commandId = command.commandName; 20 | }); 21 | after(() => { 22 | command.dispose(); 23 | }); 24 | beforeEach(() => { 25 | sandbox = sinon.createSandbox(); 26 | stubTelemetryClient(sandbox); 27 | }); 28 | 29 | afterEach(() => { 30 | sandbox.restore(); 31 | }); 32 | it("Constructor should work as expected", () => { 33 | assert.strictEqual(command.title, commandId); 34 | assert.strictEqual(command.command, commandId); 35 | }); 36 | 37 | it("Should check skillProfileAccess by default", async () => { 38 | const testUrl = "https://test.com"; 39 | const skillAccessStub = sandbox.stub(skillHelper, "checkProfileSkillAccess"); 40 | const openExternalStub = sandbox.stub(vscode.env, "openExternal"); 41 | await vscode.commands.executeCommand(commandId, testUrl); 42 | assert.ok(skillAccessStub.calledOnce); 43 | assert.ok(openExternalStub.calledOnceWith(vscode.Uri.parse(testUrl))); 44 | }); 45 | 46 | it("Should be able to skip skillProfileAccess", async () => { 47 | const testUrl = "https://test.com"; 48 | const skillAccessStub = sandbox.stub(skillHelper, "checkProfileSkillAccess"); 49 | const openExternalStub = sandbox.stub(vscode.env, "openExternal"); 50 | await vscode.commands.executeCommand(commandId, testUrl, true); 51 | assert.ok(skillAccessStub.notCalled); 52 | assert.ok(openExternalStub.calledOnceWith(vscode.Uri.parse(testUrl))); 53 | }); 54 | 55 | it.skip("Should throw error when open url failed", async () => { 56 | const testUrl = "https://test.com"; 57 | sandbox.stub(skillHelper, "checkProfileSkillAccess"); 58 | sandbox.stub(vscode.env, "openExternal").throws(new Error("foo")); 59 | try { 60 | await vscode.commands.executeCommand(commandId, testUrl, true); 61 | } catch (e) { 62 | assert.strictEqual(e.message, `Running the contributed command: '${commandId}' failed.`); 63 | return; 64 | } 65 | assert.fail("Should throw an error"); 66 | }); 67 | }); 68 | -------------------------------------------------------------------------------- /test/utils/commands/openWorkspace.test.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Alexa Skills Toolkit for Visual Studio Code 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | *--------------------------------------------------------------------------------------------*/ 6 | import * as vscode from "vscode"; 7 | import * as assert from "assert"; 8 | import * as sinon from "sinon"; 9 | 10 | import {OpenWorkspaceCommand} from "../../../src/utils/commands/openWorkspace"; 11 | import * as workSpaceHelper from "../../../src/utils/workspaceHelper"; 12 | import {stubTelemetryClient} from "../../../test/testUtilities"; 13 | 14 | describe("Command ask.container.openWorkspace", () => { 15 | let command: OpenWorkspaceCommand; 16 | let sandbox: sinon.SinonSandbox; 17 | let commandId: string; 18 | before(() => { 19 | command = new OpenWorkspaceCommand(); 20 | commandId = command.commandName; 21 | }); 22 | after(() => { 23 | command.dispose(); 24 | }); 25 | beforeEach(() => { 26 | sandbox = sinon.createSandbox(); 27 | stubTelemetryClient(sandbox); 28 | }); 29 | 30 | afterEach(() => { 31 | sandbox.restore(); 32 | }); 33 | it("Constructor should work as expected", () => { 34 | assert.strictEqual(command.title, commandId); 35 | assert.strictEqual(command.command, commandId); 36 | }); 37 | 38 | it("Should be able to call showOpenDialog with fixed config, and openWorkSpace folder with user provided path", async () => { 39 | const fakePath = ["fakePath"]; 40 | const showOpenDialogStub = sandbox.stub(vscode.window, "showOpenDialog").resolves(fakePath); 41 | const openWorkspaceStub = sandbox.stub(workSpaceHelper, "openWorkspaceFolder"); 42 | const expectedConfig = { 43 | canSelectFiles: false, 44 | canSelectFolders: true, 45 | canSelectMany: false, 46 | }; 47 | await vscode.commands.executeCommand(commandId); 48 | assert.ok(showOpenDialogStub.calledOnceWith(expectedConfig)); 49 | assert.ok(openWorkspaceStub.calledOnceWith(fakePath[0])); 50 | }); 51 | 52 | it.skip("Should throw error when no workspace provided by user", async () => { 53 | sandbox.stub(vscode.window, "showOpenDialog").resolves(undefined); 54 | 55 | try { 56 | await vscode.commands.executeCommand(commandId); 57 | } catch (e) { 58 | assert.strictEqual(e.message, `Running the contributed command: '${commandId}' failed.`); 59 | 60 | return; 61 | } 62 | assert.fail("Should throw an error"); 63 | }); 64 | }); 65 | -------------------------------------------------------------------------------- /test/utils/gitHelper.test.ts: -------------------------------------------------------------------------------- 1 | import {existsSync, mkdirSync} from "fs"; 2 | import {removeSync} from "fs-extra"; 3 | import * as assert from "assert"; 4 | import os from "os"; 5 | import * as fs from "fs"; 6 | import sinon from "sinon"; 7 | import * as path from "path"; 8 | import {LogLevel} from "../../src/logger"; 9 | import {GitInTerminalHelper} from "../../src/utils/gitHelper"; 10 | import {SYSTEM_ASK_FOLDER} from "../../src/constants"; 11 | 12 | describe("GitInTerminalHelper tests", () => { 13 | let sandbox: sinon.SinonSandbox; 14 | 15 | beforeEach(() => { 16 | sandbox = sinon.createSandbox(); 17 | }); 18 | 19 | afterEach(() => { 20 | sandbox.restore(); 21 | }); 22 | 23 | describe("init", () => { 24 | const tmpDir = os.tmpdir(); 25 | const emptyDir = `${tmpDir}/gitHelperTest`; 26 | beforeEach(() => { 27 | if (!existsSync(emptyDir)) { 28 | mkdirSync(emptyDir); 29 | } 30 | }); 31 | 32 | afterEach(() => { 33 | if (existsSync(emptyDir)) { 34 | removeSync(emptyDir); 35 | } 36 | }); 37 | 38 | it("creates a .git subfolder when initializing a new repository", () => { 39 | const helper = new GitInTerminalHelper(emptyDir, LogLevel.off); 40 | helper.init(); 41 | assert.strictEqual(existsSync(`${emptyDir}/.git`), true); 42 | }); 43 | }); 44 | 45 | describe("getCurrentBranch", () => { 46 | it("returns the branch 'master'", () => { 47 | const helper = new GitInTerminalHelper("mockRepo", LogLevel.off); 48 | sandbox.stub(helper, "_execChildProcessSync").returns("master"); 49 | const currentBranch = helper.getCurrentBranch(); 50 | assert.strictEqual( 51 | currentBranch, 52 | "master", 53 | `Current branch not equal to master. Current branch: ${currentBranch}\nRepository location: ${helper.folderPath}`, 54 | ); 55 | }); 56 | }); 57 | }); 58 | -------------------------------------------------------------------------------- /third-party/README.md: -------------------------------------------------------------------------------- 1 | # Third Party components 2 | 3 | ## VS Code Icons 4 | 5 | Some UI icons are sourced from [Visual Studio Code Icons](https://github.com/microsoft/vscode-icons). These files are located within [third-party/resources/from-vscode-icons](resources/from-vscode-icons). 6 | -------------------------------------------------------------------------------- /third-party/resources/from-vscode-icons/dark/open-preview.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /third-party/resources/from-vscode-icons/dark/refresh.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /third-party/resources/from-vscode-icons/light/open-preview.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /third-party/resources/from-vscode-icons/light/refresh.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tsconfig.eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": [".eslintrc.js"] 4 | } 5 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "alwaysStrict": true, 4 | "declaration": true, 5 | "declarationMap": true, 6 | "downlevelIteration": true, 7 | "esModuleInterop": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "inlineSourceMap": false, 10 | "lib": ["DOM", "ES2019", "ES5", "ES6", "ES2020.Promise", "ESNext.AsyncIterable", "ES2019.Array"], 11 | "module": "CommonJS", 12 | "moduleResolution": "Node", 13 | "noEmitOnError": false, 14 | "noFallthroughCasesInSwitch": true, 15 | "noImplicitAny": false, 16 | "noImplicitReturns": true, 17 | "noImplicitThis": true, 18 | "noUnusedLocals": false, 19 | "noUnusedParameters": false, 20 | "resolveJsonModule": true, 21 | "skipLibCheck": true, 22 | "strict": false, // FIXME: turn off temporarily after bumping up the TS version 23 | "strictBindCallApply": true, 24 | "strictFunctionTypes": false, 25 | "strictNullChecks": true, 26 | "strictPropertyInitialization": false, 27 | "target": "ES2018", 28 | "useDefineForClassFields": true, 29 | "outDir": "out", 30 | "rootDir": ".", 31 | "sourceMap": true 32 | }, 33 | "exclude": ["node_modules", ".vscode-test", "out"] 34 | } 35 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | // @ts-nocheck 2 | 3 | "use strict"; 4 | 5 | const path = require("path"); 6 | const CopyPlugin = require("copy-webpack-plugin"); 7 | 8 | /**@type {import('webpack').Configuration}*/ 9 | const config = { 10 | target: "node", // vscode extensions run in a Node.js-context 📖 -> https://webpack.js.org/configuration/node/ 11 | 12 | entry: { 13 | extension: "./src/extension.ts", 14 | "server/acdlServer": "./src/acdlServer/index.ts", 15 | }, // the entry point of this extension, 📖 -> https://webpack.js.org/configuration/entry-context/ 16 | output: { 17 | filename: "[name].js", 18 | libraryTarget: "commonjs2", 19 | devtoolModuleFilenameTemplate: "../[resource-path]", 20 | }, 21 | devtool: "source-map", 22 | externals: { 23 | vscode: "commonjs vscode", // the vscode-module is created on-the-fly and must be excluded. Add other modules that cannot be webpack'ed, 📖 -> https://webpack.js.org/configuration/externals/ 24 | }, 25 | resolve: { 26 | // support reading TypeScript and JavaScript files, 📖 -> https://github.com/TypeStrong/ts-loader 27 | extensions: [".ts", ".js"], 28 | }, 29 | module: { 30 | rules: [ 31 | { 32 | test: /\.ts$/, 33 | exclude: /node_modules/, 34 | use: [ 35 | { 36 | loader: "ts-loader", 37 | }, 38 | ], 39 | }, 40 | ], 41 | }, 42 | plugins: [ 43 | new CopyPlugin({ 44 | patterns: [ 45 | {from: "./node_modules/@alexa/acdl/dist/lib", to: "lib"}, 46 | {from: "./node_modules/@alexa/ask-expressions-spec/", to: "node_modules/@alexa/ask-expressions-spec"}, 47 | ], 48 | }), 49 | ], 50 | }; 51 | module.exports = config; 52 | --------------------------------------------------------------------------------