### Local Debugger Setup and Execution (Bash) Source: https://context7.com/alexa/alexa-skills-kit-sdk-for-nodejs/llms.txt These bash commands demonstrate how to install the 'ask-sdk-local-debug' package and then run the local debugger. You need to provide your access token, skill ID, and specify the skill entry file and handler name. ```bash # Install local debug package npm install ask-sdk-local-debug # Run local debugger node ./node_modules/ask-sdk-local-debug/dist/LocalDebuggerInvoker.js \ --accessToken "Atza|your-access-token-here" \ --skillId "amzn1.ask.skill.your-skill-id" \ --skillEntryFile "./index.js" \ --handlerName "handler" \ --region "NA" ``` ```bash # Then run with environment variables ACCESS_TOKEN="your-token" SKILL_ID="your-skill-id" npm run debug # The debugger creates a WebSocket connection to Alexa # All skill invocations are routed to your local machine # Connection remains active for 1 hour # Only works with development stage skills ``` -------------------------------- ### Install ASK SMAPI SDK Source: https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs/blob/2.0.x/ask-smapi-sdk/README.md Installs the ASK SMAPI SDK package from NPM. This is the first step to using the library in a Node.js project. ```shell $ npm install ask-smapi-sdk ``` -------------------------------- ### Install ASK SDK Model and Local Debug (Node.js) Source: https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs/blob/2.0.x/ask-sdk-local-debug/README.md Installs the ASK SDK model and the ASK SDK local debug package as development dependencies using npm. Ensure you are using version 1.29.0 or higher for the ASK SDK model. ```bash npm install --save ask-sdk-model@^1.29.0 npm install --save-dev ask-sdk-local-debug ``` -------------------------------- ### DynamoDB Persistence Adapter Setup and Usage (Node.js) Source: https://context7.com/alexa/alexa-skills-kit-sdk-for-nodejs/llms.txt This code demonstrates how to set up and use the DynamoDB Persistence Adapter for an Alexa skill. It includes creating the adapter instance, defining handlers for saving, retrieving, and deleting user preferences, and configuring the Alexa skill builder. ```javascript const { DynamoDbPersistenceAdapter } = require('ask-sdk-dynamodb-persistence-adapter'); const Alexa = require('ask-sdk-core'); // Create custom persistence adapter const dynamoDbPersistenceAdapter = new DynamoDbPersistenceAdapter({ tableName: 'MyCustomSkillTable', partitionKeyName: 'id', attributesName: 'attributes', createTable: true, partitionKeyGenerator: (requestEnvelope) => { // Generate partition key from userId const userId = requestEnvelope.context.System.user.userId; return userId; } }); const SaveUserPreferencesHandler = { canHandle(handlerInput) { return handlerInput.requestEnvelope.request.type === 'IntentRequest' && handlerInput.requestEnvelope.request.intent.name === 'SavePreferencesIntent'; }, async handle(handlerInput) { const { attributesManager, responseBuilder } = handlerInput; const slots = handlerInput.requestEnvelope.request.intent.slots; // Get existing preferences const persistentAttributes = await attributesManager.getPersistentAttributes() || {}; // Update preferences persistentAttributes.preferences = persistentAttributes.preferences || {}; persistentAttributes.preferences.favoriteColor = slots.color.value; persistentAttributes.preferences.favoriteFood = slots.food.value; persistentAttributes.updatedAt = new Date().toISOString(); // Save to DynamoDB attributesManager.setPersistentAttributes(persistentAttributes); await attributesManager.savePersistentAttributes(); return responseBuilder .speak('Your preferences have been saved!') .getResponse(); } }; const GetUserPreferencesHandler = { canHandle(handlerInput) { return handlerInput.requestEnvelope.request.type === 'IntentRequest' && handlerInput.requestEnvelope.request.intent.name === 'GetPreferencesIntent'; }, async handle(handlerInput) { const { attributesManager, responseBuilder } = handlerInput; const persistentAttributes = await attributesManager.getPersistentAttributes() || {}; const preferences = persistentAttributes.preferences; if (!preferences) { return responseBuilder .speak('You haven't saved any preferences yet.') .getResponse(); } const speechText = `Your favorite color is ${preferences.favoriteColor} and your favorite food is ${preferences.favoriteFood}.`; return responseBuilder .speak(speechText) .getResponse(); } }; const DeleteUserDataHandler = { canHandle(handlerInput) { return handlerInput.requestEnvelope.request.type === 'IntentRequest' && handlerInput.requestEnvelope.request.intent.name === 'DeleteDataIntent'; }, async handle(handlerInput) { const { attributesManager, responseBuilder } = handlerInput; await attributesManager.deletePersistentAttributes(); return responseBuilder .speak('All your data has been deleted.') .getResponse(); } }; exports.handler = Alexa.SkillBuilders.custom() .withPersistenceAdapter(dynamoDbPersistenceAdapter) .addRequestHandlers( SaveUserPreferencesHandler, GetUserPreferencesHandler, DeleteUserDataHandler ) .lambda(); ``` -------------------------------- ### List Skills for Vendor Source: https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs/blob/2.0.x/ask-smapi-sdk/README.md Retrieves a list of skills associated with a given vendor ID. This example shows how to get only the response body or the full response including headers and status code. It handles success and error cases. ```javascript // To only retrieve response body smapiClient.listSkillsForVendorV1(vendorId) .then((response) => { console.log(JSON.stringify(response)); }) .catch((err) => { console.log(err.message); console.log(JSON.stringify(err.response)); }); // To include response header and status code smapiClient.callListSkillsForVendorV1(vendorId) .then((response) => { console.log(response.header); }) .catch((err) => { console.log(err.message); console.log(JSON.stringify(err.response)); }); ``` -------------------------------- ### Alexa Skill Builder Lambda Function Entry Point in Node.js Source: https://context7.com/alexa/alexa-skills-kit-sdk-for-nodejs/llms.txt This code defines the entry point for an Alexa Skill Lambda function using the ASK SDK for Node.js. It initializes the SDK with a default API client and adds the previously defined request handlers (GetAddressHandler, SendProgressiveResponseHandler, GetUserProfileHandler) to the skill builder. This is the standard setup for a custom Alexa skill. ```javascript exports.handler = Alexa.SkillBuilders.custom() .withApiClient(new Alexa.DefaultApiClient()) .addRequestHandlers( GetAddressHandler, SendProgressiveResponseHandler, GetUserProfileHandler ) .lambda(); ``` -------------------------------- ### Start Local Debugger Invoker (Node.js) Source: https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs/blob/2.0.x/ask-sdk-local-debug/README.md Executes the local debugger invoker script from the skill's lambda directory. This command requires parameters such as access token, skill ID, skill entry file, handler name, and optionally, the region. The access token can be generated using the ASK CLI. ```bash node ./node_modules/ask-sdk-local-debug/dist/LocalDebuggerInvoker.js \ --accessToken \ --skillId \ --skillEntryFile \ --handlerName \ --region # Optional argument. Region defaults to NA. ``` -------------------------------- ### Get Skill Manifest Source: https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs/blob/2.0.x/ask-smapi-sdk/README.md Retrieves the manifest for a specific skill and stage (e.g., 'development', 'live'). This function returns the skill's manifest details and includes error handling. ```javascript smapiClient.getSkillManifestV1(skillId, stage) .then((response) => { console.log(JSON.stringify(response)); }) .catch((err) => { console.log(err.message); console.log(JSON.stringify(err.response)); }); ``` -------------------------------- ### Get Viewport Information with ViewportUtils Source: https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs/blob/2.0.x/docs/ja/ASK-SDK-Utilities.md ViewportUtils provides functions to determine device characteristics like viewport orientation, size group, DPI group, and the overall viewport profile from the RequestEnvelope. ```typescript import { RequestEnvelope, ViewportUtils, ViewportOrientation, ViewportSizeGroup, ViewportDpiGroup, ViewportProfile } from 'ask-sdk-core'; // Example usage: const orientation: ViewportOrientation = ViewportUtils.getViewportOrientation(width, height); const sizeGroup: ViewportSizeGroup = ViewportUtils.getViewportSizeGroup(size); const dpiGroup: ViewportDpiGroup = ViewportUtils.getViewportDpiGroup(dpi); const profile: ViewportProfile = ViewportUtils.getViewportProfile(requestEnvelope); ``` -------------------------------- ### Node.js SMAPI SDK: Manage Alexa Skills Source: https://context7.com/alexa/alexa-skills-kit-sdk-for-nodejs/llms.txt This Node.js code snippet demonstrates how to use the Alexa Skills Management API (SMAPI) SDK to programmatically manage Alexa skills. It covers authentication, listing skills, getting and updating skill manifests, retrieving interaction models, creating new skills, checking skill status, and submitting skills for certification. Ensure environment variables for LWA credentials and VENDOR_ID are set. ```javascript const Alexa = require('ask-smapi-sdk'); // Configure authentication const refreshTokenConfig = { clientId: process.env.LWA_CLIENT_ID, clientSecret: process.env.LWA_CLIENT_SECRET, refreshToken: process.env.LWA_REFRESH_TOKEN }; // Create SMAPI client const smapiClient = new Alexa.StandardSmapiClientBuilder() .withRefreshTokenConfig(refreshTokenConfig) .client(); // List all skills for a vendor async function listSkills() { try { const vendorId = process.env.VENDOR_ID; const response = await smapiClient.listSkillsForVendorV1(vendorId); console.log('Skills:'); response.skills.forEach(skill => { console.log(`- ${skill.nameByLocale['en-US']}: ${skill.skillId}`); }); return response.skills; } catch (error) { console.error('Error listing skills:', error); throw error; } } // Get skill manifest async function getSkillManifest(skillId, stage = 'development') { try { const response = await smapiClient.getSkillManifestV1(skillId, stage); console.log('Skill Manifest:', JSON.stringify(response.manifest, null, 2)); return response.manifest; } catch (error) { console.error('Error getting manifest:', error); throw error; } } // Update skill manifest async function updateSkillManifest(skillId, manifest, stage = 'development') { try { const updateRequest = { manifest }; await smapiClient.updateSkillManifestV1(skillId, stage, updateRequest); console.log('Manifest updated successfully'); } catch (error) { console.error('Error updating manifest:', error); throw error; } } // Get interaction model async function getInteractionModel(skillId, locale = 'en-US', stage = 'development') { try { const response = await smapiClient.getInteractionModelV1(skillId, stage, locale); console.log('Interaction Model:', JSON.stringify(response.interactionModel, null, 2)); return response.interactionModel; } catch (error) { console.error('Error getting interaction model:', error); throw error; } } // Create new skill async function createSkill(vendorId, manifest) { try { const createRequest = { vendorId, manifest }; const response = await smapiClient.createSkillForVendorV1(createRequest); console.log('Skill created:', response.skillId); return response.skillId; } catch (error) { console.error('Error creating skill:', error); throw error; } } // Get skill status async function getSkillStatus(skillId) { try { const response = await smapiClient.getSkillStatusV1(skillId); console.log('Skill Status:', JSON.stringify(response, null, 2)); return response; } catch (error) { console.error('Error getting skill status:', error); throw error; } } // Submit skill for certification async function submitSkill(skillId) { try { await smapiClient.submitSkillForCertificationV1(skillId); console.log('Skill submitted for certification'); } catch (error) { console.error('Error submitting skill:', error); throw error; } } // Example usage async function main() { try { // List all skills await listSkills(); // Get specific skill details const skillId = 'amzn1.ask.skill.12345678-1234-1234-1234-123456789012'; await getSkillManifest(skillId); await getInteractionModel(skillId); await getSkillStatus(skillId); } catch (error) { console.error('SMAPI operation failed:', error); } } module.exports = { listSkills, getSkillManifest, updateSkillManifest, getInteractionModel, createSkill, getSkillStatus, submitSkill }; ``` -------------------------------- ### Get Request Envelope Attributes with RequestEnvelopeUtils Source: https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs/blob/2.0.x/docs/ja/ASK-SDK-Utilities.md The RequestEnvelopeUtils provides functions to easily retrieve common attributes from the RequestEnvelope, such as locale, request type, intent name, and device ID, with built-in error handling. ```typescript import { RequestEnvelope, RequestEnvelopeUtils, Slot, SupportedInterfaces } from 'ask-sdk-core'; // Example usage: const locale: string = RequestEnvelopeUtils.getLocale(requestEnvelope); const intentName: string = RequestEnvelopeUtils.getIntentName(requestEnvelope); const slotValue: string = RequestEnvelopeUtils.getSlotValue(requestEnvelope, 'mySlotName'); const isNewSession: boolean = RequestEnvelopeUtils.isNewSession(requestEnvelope); ``` -------------------------------- ### Get User Profile Information (Name, Email) using UPS Service Client in Node.js Source: https://context7.com/alexa/alexa-skills-kit-sdk-for-nodejs/llms.txt This handler fetches the user's name and email address using the User Profile Service (UPS) Client. It requires the user to grant specific profile permissions ('alexa::profile:name:read', 'alexa::profile:email:read') via a consent card if not already provided. Handles potential errors during retrieval. ```javascript const GetUserProfileHandler = { canHandle(handlerInput) { return handlerInput.requestEnvelope.request.type === 'IntentRequest' && handlerInput.requestEnvelope.request.intent.name === 'GetProfileIntent'; }, async handle(handlerInput) { const { requestEnvelope, serviceClientFactory, responseBuilder } = handlerInput; const consentToken = requestEnvelope.context.System.user.permissions?.consentToken; if (!consentToken) { return responseBuilder .speak('Please grant profile permissions in the Alexa app.') .withAskForPermissionsConsentCard([ 'alexa::profile:name:read', 'alexa::profile:email:read' ]) .getResponse(); } try { const upsServiceClient = serviceClientFactory.getUpsServiceClient(); const name = await upsServiceClient.getProfileName(); const email = await upsServiceClient.getProfileEmail(); return responseBuilder .speak(`Hello ${name}, your email is ${email}.`) .getResponse(); } catch (error) { console.error('Error getting profile:', error); return responseBuilder .speak('There was an error retrieving your profile.') .getResponse(); } } }; ``` -------------------------------- ### Get Device Address using Alexa Service Client Factory in Node.js Source: https://context7.com/alexa/alexa-skills-kit-sdk-for-nodejs/llms.txt This handler retrieves the user's full address using the Device Address Service Client. It first checks for the necessary consent token and then calls `getFullAddress` with the device ID. It handles cases where the address might not be set or errors during retrieval. Requires the 'read::alexa:device:all:address' permission. ```javascript const GetAddressHandler = { canHandle(handlerInput) { return handlerInput.requestEnvelope.request.type === 'IntentRequest' && handlerInput.requestEnvelope.request.intent.name === 'GetAddressIntent'; }, async handle(handlerInput) { const { requestEnvelope, serviceClientFactory, responseBuilder } = handlerInput; const consentToken = requestEnvelope.context.System.user.permissions?.consentToken; if (!consentToken) { return responseBuilder .speak('Please enable Location permissions in the Amazon Alexa app.') .withAskForPermissionsConsentCard(['read::alexa:device:all:address']) .getResponse(); } try { const { deviceId } = requestEnvelope.context.System.device; const deviceAddressServiceClient = serviceClientFactory.getDeviceAddressServiceClient(); const address = await deviceAddressServiceClient.getFullAddress(deviceId); if (!address.addressLine1 && !address.stateOrRegion) { return responseBuilder .speak('It looks like you don't have an address set.') .getResponse(); } const message = `Your address is ${address.addressLine1}, ${address.stateOrRegion}, ${address.postalCode}`; return responseBuilder.speak(message).getResponse(); } catch (error) { console.error('Error getting address:', error); return responseBuilder .speak('There was an error retrieving your address.') .getResponse(); } } }; ``` -------------------------------- ### Handle Multi-Turn Dialogs with Slot Elicitation and Confirmation in Node.js Source: https://context7.com/alexa/alexa-skills-kit-sdk-for-nodejs/llms.txt This code snippet shows how to manage complex multi-turn dialogs in an Alexa skill. It handles slot elicitation, custom prompts, and slot confirmation using the ASK SDK for Node.js. The 'PlanTripIntentHandler' guides the user through collecting destination and travel date, and confirms the destination. The 'InProgressPlanTripHandler' adds custom logic during dialog progression, like preventing travel to Mars. ```javascript const Alexa = require('ask-sdk-core'); const PlanTripIntentHandler = { canHandle(handlerInput) { return handlerInput.requestEnvelope.request.type === 'IntentRequest' && handlerInput.requestEnvelope.request.intent.name === 'PlanTripIntent'; }, handle(handlerInput) { const { request } = handlerInput.requestEnvelope; const currentIntent = request.intent; const dialogState = request.dialogState; // Check if dialog is complete if (dialogState !== 'COMPLETED') { // Check specific slots and provide custom prompts if (!currentIntent.slots.destination.value) { return handlerInput.responseBuilder .speak('Where would you like to travel to?') .addElicitSlotDirective('destination', currentIntent) .getResponse(); } if (!currentIntent.slots.travelDate.value) { return handlerInput.responseBuilder .speak('When would you like to travel?') .addElicitSlotDirective('travelDate', currentIntent) .getResponse(); } // Confirm the slot value if (currentIntent.slots.destination.confirmationStatus !== 'CONFIRMED') { return handlerInput.responseBuilder .speak(`So you want to go to ${currentIntent.slots.destination.value}. Is that correct?`) .addConfirmSlotDirective('destination', currentIntent) .getResponse(); } // Let Alexa handle the rest of the dialog return handlerInput.responseBuilder .addDelegateDirective(currentIntent) .getResponse(); } // Dialog complete - process the request const destination = currentIntent.slots.destination.value; const travelDate = currentIntent.slots.travelDate.value; const numTravelers = currentIntent.slots.numTravelers.value || 1; const speechText = `Great! I'll help you plan a trip to ${destination} on ${travelDate} for ${numTravelers} traveler${numTravelers > 1 ? 's' : ''}.`; return handlerInput.responseBuilder .speak(speechText) .withSimpleCard('Trip Planned', speechText) .getResponse(); } }; const InProgressPlanTripHandler = { canHandle(handlerInput) { const { request } = handlerInput.requestEnvelope; return request.type === 'IntentRequest' && request.intent.name === 'PlanTripIntent' && request.dialogState === 'IN_PROGRESS'; }, handle(handlerInput) { const { request } = handlerInput.requestEnvelope; const currentIntent = request.intent; // Custom logic during dialog progression if (currentIntent.slots.destination.value === 'Mars') { return handlerInput.responseBuilder .speak('I\'m sorry, but interplanetary travel is not available yet. Please choose an Earth destination.') .addElicitSlotDirective('destination') .getResponse(); } return handlerInput.responseBuilder .addDelegateDirective(currentIntent) .getResponse(); } }; exports.handler = Alexa.SkillBuilders.custom() .addRequestHandlers( InProgressPlanTripHandler, PlanTripIntentHandler ) .lambda(); ``` -------------------------------- ### VS Code Launch Configuration for Skill Debug Source: https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs/blob/2.0.x/ask-sdk-local-debug/README.md A sample launch configuration for Visual Studio Code to attach to the local skill debugging process. It specifies the program to run and provides the necessary arguments for the debugger, mirroring the command-line invocation. ```json { "type": "node", "request": "launch", "name": "Skill Debug", "program": "<5. Program>", "args": [ "--accessToken", "", "--skillId", "", "--skillEntryFile", "", "--handlerName" , "", "--region", "" ] } ``` -------------------------------- ### Build Custom Alexa Skill with Request Handlers (Node.js) Source: https://context7.com/alexa/alexa-skills-kit-sdk-for-nodejs/llms.txt Demonstrates how to build a custom Alexa skill using the ASK SDK for Node.js. It includes handlers for LaunchRequest, HelloWorldIntent, and AMAZON.HelpIntent, along with a general ErrorHandler. This code requires the 'ask-sdk-core' package. ```javascript const Alexa = require('ask-sdk-core'); const LaunchRequestHandler = { canHandle(handlerInput) { return handlerInput.requestEnvelope.request.type === 'LaunchRequest'; }, handle(handlerInput) { const speechText = 'Welcome to the Alexa Skills Kit, you can say hello!'; return handlerInput.responseBuilder .speak(speechText) .reprompt(speechText) .withSimpleCard('Hello World', speechText) .getResponse(); } }; const HelloWorldIntentHandler = { canHandle(handlerInput) { return handlerInput.requestEnvelope.request.type === 'IntentRequest' && handlerInput.requestEnvelope.request.intent.name === 'HelloWorldIntent'; }, handle(handlerInput) { const speechText = 'Hello World!'; return handlerInput.responseBuilder .speak(speechText) .withSimpleCard('Hello World', speechText) .getResponse(); } }; const HelpIntentHandler = { canHandle(handlerInput) { return handlerInput.requestEnvelope.request.type === 'IntentRequest' && handlerInput.requestEnvelope.request.intent.name === 'AMAZON.HelpIntent'; }, handle(handlerInput) { const speechText = 'You can say hello to me! How can I help?'; return handlerInput.responseBuilder .speak(speechText) .reprompt(speechText) .getResponse(); } }; const ErrorHandler = { canHandle() { return true; }, handle(handlerInput, error) { console.log(`Error handled: ${error.message}`); return handlerInput.responseBuilder .speak('Sorry, I can\'t understand the command. Please say again.') .reprompt('Sorry, I can\'t understand the command. Please say again.') .getResponse(); } }; exports.handler = Alexa.SkillBuilders.custom() .addRequestHandlers( LaunchRequestHandler, HelloWorldIntentHandler, HelpIntentHandler ) .addErrorHandlers(ErrorHandler) .lambda(); ``` -------------------------------- ### Local Debugger Configuration in package.json Source: https://context7.com/alexa/alexa-skills-kit-sdk-for-nodejs/llms.txt This JSON snippet shows how to add a 'debug' script to your package.json file for easily running the local debugger. It utilizes environment variables for sensitive information like access tokens and skill IDs. ```json { "scripts": { "debug": "node ./node_modules/ask-sdk-local-debug/dist/LocalDebuggerInvoker.js --accessToken $ACCESS_TOKEN --skillId $SKILL_ID --skillEntryFile ./index.js --handlerName handler --region NA" } } ``` -------------------------------- ### Express Adapter for Hosting Alexa Skills (Node.js) Source: https://context7.com/alexa/alexa-skills-kit-sdk-for-nodejs/llms.txt This Node.js code snippet sets up an Express.js web service to host an Alexa skill. It uses the ask-sdk-express-adapter for automatic request verification, defines handlers for common Alexa requests (LaunchRequest, IntentRequest, SessionEndedRequest), and includes error handling. The skill endpoint is mounted at '/alexa', and a health check is available at '/health'. ```javascript const express = require('express'); const Alexa = require('ask-sdk-core'); const { ExpressAdapter } = require('ask-sdk-express-adapter'); const app = express(); // Define skill handlers const LaunchRequestHandler = { canHandle(handlerInput) { return handlerInput.requestEnvelope.request.type === 'LaunchRequest'; }, handle(handlerInput) { return handlerInput.responseBuilder .speak('Welcome to my web service skill!') .reprompt('What would you like to do?') .getResponse(); } }; const HelloIntentHandler = { canHandle(handlerInput) { return handlerInput.requestEnvelope.request.type === 'IntentRequest' && handlerInput.requestEnvelope.request.intent.name === 'HelloIntent'; }, handle(handlerInput) { return handlerInput.responseBuilder .speak('Hello from the web service!') .getResponse(); } }; const SessionEndedRequestHandler = { canHandle(handlerInput) { return handlerInput.requestEnvelope.request.type === 'SessionEndedRequest'; }, handle(handlerInput) { console.log(`Session ended: ${JSON.stringify(handlerInput.requestEnvelope.request)}`); return handlerInput.responseBuilder.getResponse(); } }; const ErrorHandler = { canHandle() { return true; }, handle(handlerInput, error) { console.error(`Error: ${error.stack || error.message}`); return handlerInput.responseBuilder .speak('Sorry, I had trouble processing your request. Please try again.') .reprompt('Please try again.') .getResponse(); } }; // Build the skill const skill = Alexa.SkillBuilders.custom() .addRequestHandlers( LaunchRequestHandler, HelloIntentHandler, SessionEndedRequestHandler ) .addErrorHandlers(ErrorHandler) .create(); // Create Express adapter with verification enabled const adapter = new ExpressAdapter(skill, true, true); // Mount the skill endpoint app.post('/alexa', adapter.getRequestHandlers()); // Health check endpoint app.get('/health', (req, res) => { res.json({ status: 'healthy', timestamp: new Date().toISOString() }); }); // Start server const PORT = process.env.PORT || 3000; app.listen(PORT, () => { console.log(`Alexa skill server running on port ${PORT}`); console.log(`Skill endpoint: http://localhost:${PORT}/alexa`); }); ``` -------------------------------- ### Configure SMAPI Client for Node.js Source: https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs/blob/2.0.x/ask-smapi-sdk/README.md Configures a new SMAPI client instance using provided LWA credentials (Client ID, Client Secret, and Refresh Token). This client is then used to make API calls. ```javascript const Alexa = require('ask-smapi-sdk'); // specify the refreshTokenConfig with clientId, clientSecret and refreshToken generated in the previous step const refreshTokenConfig = { clientId, clientSecret, refreshToken } const smapiClient = new Alexa.StandardSmapiClientBuilder() .withRefreshTokenConfig(refreshTokenConfig) .client(); ``` -------------------------------- ### Alexa Skill Code for Local Debugging (Node.js) Source: https://context7.com/alexa/alexa-skills-kit-sdk-for-nodejs/llms.txt This JavaScript code defines a basic Alexa skill handler and exports it, along with the skill instance, for use with the local debugging tools. It requires the 'ask-sdk-core' package. ```javascript // index.js - Your skill file const Alexa = require('ask-sdk-core'); const LaunchRequestHandler = { canHandle(handlerInput) { return handlerInput.requestEnvelope.request.type === 'LaunchRequest'; }, handle(handlerInput) { return handlerInput.responseBuilder .speak('Local debugging is working!') .reprompt('What would you like to do?') .getResponse(); } }; const skill = Alexa.SkillBuilders.custom() .addRequestHandlers(LaunchRequestHandler) .create(); exports.handler = async (event, context) => { return skill.invoke(event, context); }; // Export for local debugging exports.skill = skill; ``` -------------------------------- ### Response Builder Fluent API for Alexa Responses (Node.js) Source: https://context7.com/alexa/alexa-skills-kit-sdk-for-nodejs/llms.txt Demonstrates the use of the Alexa SDK's Response Builder fluent API in Node.js for creating complex responses. This includes building rich responses with speech, reprompts, standard cards, hint directives, and managing session state. It also shows how to initiate audio playback and stop it. ```javascript const Alexa = require('ask-sdk-core'); const RichResponseHandler = { canHandle(handlerInput) { return handlerInput.requestEnvelope.request.type === 'IntentRequest' && handlerInput.requestEnvelope.request.intent.name === 'RichResponseIntent'; }, handle(handlerInput) { const { responseBuilder } = handlerInput; // Build a rich response with multiple elements return responseBuilder .speak('This is the main speech output with SSML support.') .reprompt('This is the reprompt if the user doesn't respond.') .withStandardCard( 'Card Title', 'Card content with detailed information.', 'https://example.com/small-image.png', 'https://example.com/large-image.png' ) .addHintDirective('try saying show me more') .withShouldEndSession(false) .getResponse(); } }; const AudioPlayerHandler = { canHandle(handlerInput) { return handlerInput.requestEnvelope.request.type === 'IntentRequest' && handlerInput.requestEnvelope.request.intent.name === 'PlayAudioIntent'; }, handle(handlerInput) { const audioUrl = 'https://example.com/audio-file.mp3'; return handlerInput.responseBuilder .speak('Starting playback now.') .addAudioPlayerPlayDirective( 'REPLACE_ALL', audioUrl, 'token-12345', 0, null, { title: 'Song Title', subtitle: 'Artist Name', art: { sources: [ { url: 'https://example.com/album-art.png' } ] } } ) .getResponse(); } }; const StopAudioHandler = { canHandle(handlerInput) { return handlerInput.requestEnvelope.request.type === 'IntentRequest' && handlerInput.requestEnvelope.request.intent.name === 'AMAZON.StopIntent'; }, handle(handlerInput) { return handlerInput.responseBuilder .addAudioPlayerStopDirective() .getResponse(); } }; exports.handler = Alexa.SkillBuilders.custom() .addRequestHandlers(RichResponseHandler, AudioPlayerHandler, StopAudioHandler) .lambda(); ``` -------------------------------- ### Implement S3 Persistence Adapter for Alexa Skills (Node.js) Source: https://context7.com/alexa/alexa-skills-kit-sdk-for-nodejs/llms.txt This code demonstrates how to configure and use the S3PersistenceAdapter for storing and retrieving Alexa skill attributes. It requires the 'ask-sdk-s3-persistence-adapter' and 'ask-sdk-core' packages. The adapter uses AWS S3 for eventual consistency and allows custom object key generation based on user ID. ```javascript const { S3PersistenceAdapter } = require('ask-sdk-s3-persistence-adapter'); const Alexa = require('ask-sdk-core'); const s3PersistenceAdapter = new S3PersistenceAdapter({ bucketName: 'my-alexa-skill-bucket', pathPrefix: 'user-data/', objectKeyGenerator: (requestEnvelope) => { const userId = requestEnvelope.context.System.user.userId; // Create a clean key from userId const cleanUserId = userId.replace(/[^a-zA-Z0-9]/g, '_'); return `${cleanUserId}.json`; } }); const SaveGameProgressHandler = { canHandle(handlerInput) { return handlerInput.requestEnvelope.request.type === 'IntentRequest' && handlerInput.requestEnvelope.request.intent.name === 'SaveGameIntent'; }, async handle(handlerInput) { const { attributesManager, responseBuilder } = handlerInput; const slots = handlerInput.requestEnvelope.request.intent.slots; const persistentAttributes = await attributesManager.getPersistentAttributes() || {}; persistentAttributes.gameProgress = { level: parseInt(slots.level.value), score: parseInt(slots.score.value), lastPlayed: new Date().toISOString(), achievements: persistentAttributes.gameProgress?.achievements || [] }; attributesManager.setPersistentAttributes(persistentAttributes); await attributesManager.savePersistentAttributes(); return responseBuilder .speak(`Game progress saved! You're on level ${slots.level.value} with ${slots.score.value} points.`) .getResponse(); } }; const LoadGameProgressHandler = { canHandle(handlerInput) { return handlerInput.requestEnvelope.request.type === 'IntentRequest' && handlerInput.requestEnvelope.request.intent.name === 'LoadGameIntent'; }, async handle(handlerInput) { const { attributesManager, responseBuilder } = handlerInput; const persistentAttributes = await attributesManager.getPersistentAttributes() || {}; const gameProgress = persistentAttributes.gameProgress; if (!gameProgress) { return responseBuilder .speak('No saved game found. Start a new game!') .getResponse(); } const speechText = `Welcome back! You're on level ${gameProgress.level} with ${gameProgress.score} points.`; return responseBuilder .speak(speechText) .getResponse(); } }; exports.handler = Alexa.SkillBuilders.custom() .withPersistenceAdapter(s3PersistenceAdapter) .addRequestHandlers( SaveGameProgressHandler, LoadGameProgressHandler ) .lambda(); ``` -------------------------------- ### Configure SMAPI Client for TypeScript Source: https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs/blob/2.0.x/ask-smapi-sdk/README.md Configures a new SMAPI client instance using provided LWA credentials (Client ID, Client Secret, and Refresh Token) in a TypeScript environment. This client is then used to make API calls. ```typescript import * as Alexa from 'ask-smapi-sdk'; // specify the refreshTokenConfig with clientId, clientSecret and refreshToken generated in the previous step const refreshTokenConfig : Alexa.RefreshTokenConfig = { clientId, clientSecret, refreshToken } const smapiClient = new Alexa.StandardSmapiClientBuilder() .withRefreshTokenConfig(refreshTokenConfig) .client(); ``` -------------------------------- ### Standard Skill Builder with DynamoDB Persistence (Node.js) Source: https://context7.com/alexa/alexa-skills-kit-sdk-for-nodejs/llms.txt Implements a standard Alexa skill that uses DynamoDB to store and retrieve persistent attributes, such as a counter. It includes handlers for incrementing the counter and resetting it. The skill is configured with a DynamoDB table name, auto-creation, and a partition key based on the user ID. ```javascript const Alexa = require('ask-sdk'); const CounterIntentHandler = { canHandle(handlerInput) { return handlerInput.requestEnvelope.request.type === 'IntentRequest' && handlerInput.requestEnvelope.request.intent.name === 'CounterIntent'; }, async handle(handlerInput) { const attributesManager = handlerInput.attributesManager; const persistentAttributes = await attributesManager.getPersistentAttributes() || {}; const counter = persistentAttributes.counter || 0; const newCounter = counter + 1; persistentAttributes.counter = newCounter; attributesManager.setPersistentAttributes(persistentAttributes); await attributesManager.savePersistentAttributes(); const speechText = `This is visit number ${newCounter}. Thanks for coming back!`; return handlerInput.responseBuilder .speak(speechText) .withSimpleCard('Counter Skill', speechText) .getResponse(); } }; const ResetCounterHandler = { canHandle(handlerInput) { return handlerInput.requestEnvelope.request.type === 'IntentRequest' && handlerInput.requestEnvelope.request.intent.name === 'ResetCounterIntent'; }, async handle(handlerInput) { const attributesManager = handlerInput.attributesManager; attributesManager.setPersistentAttributes({ counter: 0 }); await attributesManager.savePersistentAttributes(); return handlerInput.responseBuilder .speak('Counter has been reset to zero.') .getResponse(); } }; exports.handler = Alexa.SkillBuilders.standard() .withTableName('AlexaCounterSkillTable') .withAutoCreateTable(true) .withPartitionKeyGenerator((requestEnvelope) => { return requestEnvelope.context.System.user.userId; }) .addRequestHandlers( CounterIntentHandler, ResetCounterHandler ) .addErrorHandlers({ canHandle() { return true; }, handle(handlerInput, error) { console.error(`Error: ${error.message}`); return handlerInput.responseBuilder .speak('Something went wrong. Please try again.') .getResponse(); } }) .lambda(); ``` -------------------------------- ### Send Progressive Response using Directive Service Client in Node.js Source: https://context7.com/alexa/alexa-skills-kit-sdk-for-nodejs/llms.txt This handler demonstrates sending a progressive response to the user while a long-running task is being processed. It uses the Directive Service Client to enqueue a 'VoicePlayer.Speak' directive before simulating the task with a timeout. This provides immediate feedback to the user. Requires no specific permissions beyond standard interaction. ```javascript const SendProgressiveResponseHandler = { canHandle(handlerInput) { return handlerInput.requestEnvelope.request.type === 'IntentRequest' && handlerInput.requestEnvelope.request.intent.name === 'LongTaskIntent'; }, async handle(handlerInput) { const { requestEnvelope, serviceClientFactory, responseBuilder } = handlerInput; try { // Send progressive response while processing const directiveServiceClient = serviceClientFactory.getDirectiveServiceClient(); const directive = { header: { requestId: requestEnvelope.request.requestId }, directive: { type: 'VoicePlayer.Speak', speech: 'Please wait while I process your request...' } }; await directiveServiceClient.enqueue(directive); // Simulate long-running task await new Promise(resolve => setTimeout(resolve, 3000)); return responseBuilder .speak('Processing complete! Here are your results.') .getResponse(); } catch (error) { console.error('Error sending directive:', error); return responseBuilder .speak('Processing your request.') .getResponse(); } } }; ``` -------------------------------- ### Generate LWA Tokens using ASK CLI Source: https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs/blob/2.0.x/ask-smapi-sdk/README.md This command generates Login with Amazon (LWA) tokens, including an access token and refresh token, required for authenticating with SMAPI. It requires a Client ID and Client Secret obtained from the Amazon Developer account. ```shell ask util generate-lwa-tokens --client-id --client-confirmation ``` -------------------------------- ### Alexa Skill Interceptors for Request/Response Handling (JavaScript) Source: https://context7.com/alexa/alexa-skills-kit-sdk-for-nodejs/llms.txt This JavaScript code defines various interceptors for an Alexa skill. It includes interceptors for localization, loading and saving persistent attributes, and logging requests and responses. These interceptors are then added to a standard Alexa skill builder. ```javascript const Alexa = require('ask-sdk'); // Request interceptor for localization const LocalizationRequestInterceptor = { process(handlerInput) { const locale = handlerInput.requestEnvelope.request.locale; const strings = { 'en-US': { WELCOME: 'Welcome to the app!', HELP: 'You can ask me for help.', GOODBYE: 'Goodbye!' }, 'es-ES': { WELCOME: '¡Bienvenido a la aplicación!', HELP: 'Puedes pedirme ayuda.', GOODBYE: '¡Adiós!' }, 'de-DE': { WELCOME: 'Willkommen in der App!', HELP: 'Du kannst mich um Hilfe bitten.', GOODBYE: 'Auf Wiedersehen!' } }; const requestAttributes = handlerInput.attributesManager.getRequestAttributes(); requestAttributes.strings = strings[locale] || strings['en-US']; } }; // Request interceptor for loading persistent attributes const LoadPersistenceRequestInterceptor = { async process(handlerInput) { const persistentAttributes = await handlerInput.attributesManager.getPersistentAttributes() || {}; // Merge with session attributes const sessionAttributes = handlerInput.attributesManager.getSessionAttributes(); Object.assign(sessionAttributes, persistentAttributes); handlerin const sessionAttributes = handlerInput.attributesManager.getSessionAttributes(); const persistentAttributes = { lastAccessed: new Date().toISOString(), ...sessionAttributes }; handlerInput.attributesManager.setPersistentAttributes(persistentAttributes); await handlerInput.attributesManager.savePersistentAttributes(); } }; // Response interceptor for logging const ResponseLogInterceptor = { process(handlerInput, response) { console.log(`RESPONSE: ${JSON.stringify(response, null, 2)}`); } }; const LaunchRequestHandler = { canHandle(handlerInput) { return handlerInput.requestEnvelope.request.type === 'LaunchRequest'; }, handle(handlerInput) { const requestAttributes = handlerInput.attributesManager.getRequestAttributes(); const speechText = requestAttributes.strings.WELCOME; return handlerInput.responseBuilder .speak(speechText) .reprompt(speechText) .getResponse(); } }; exports.handler = Alexa.SkillBuilders.standard() .withTableName('InterceptorDemoTable') .withAutoCreateTable(true) .addRequestHandlers(LaunchRequestHandler) .addRequestInterceptors( LocalizationRequestInterceptor, LoadPersistenceRequestInterceptor, RequestLogInterceptor ) .addResponseInterceptors( SavePersistenceResponseInterceptor, ResponseLogInterceptor ) .lambda(); ``` -------------------------------- ### Extract Request Information with ASK SDK for Node.js Source: https://context7.com/alexa/alexa-skills-kit-sdk-for-nodejs/llms.txt Demonstrates how to extract various details from an Alexa request envelope using utility functions provided by the ASK SDK for Node.js. This includes locale, request type, intent name, user ID, device ID, API access token, session status, slot values, dialog state, and supported interfaces. Useful for debugging and understanding request context. ```javascript const Alexa = require('ask-sdk-core'); const UtilityExampleHandler = { canHandle(handlerInput) { return handlerInput.requestEnvelope.request.type === 'IntentRequest' && handlerInput.requestEnvelope.request.intent.name === 'UtilityExampleIntent'; }, handle(handlerInput) { const { requestEnvelope, responseBuilder } = handlerInput; // Get locale const locale = Alexa.getLocale(requestEnvelope); console.log('Locale:', locale); // Get request type const requestType = Alexa.getRequestType(requestEnvelope); console.log('Request Type:', requestType); // Get intent name const intentName = Alexa.getIntentName(requestEnvelope); console.log('Intent Name:', intentName); // Get user ID const userId = Alexa.getUserId(requestEnvelope); console.log('User ID:', userId); // Get device ID const deviceId = Alexa.getDeviceId(requestEnvelope); console.log('Device ID:', deviceId); // Get API access token const apiAccessToken = Alexa.getApiAccessToken(requestEnvelope); console.log('API Access Token:', apiAccessToken ? 'Present' : 'Not present'); // Check if new session const isNewSession = Alexa.isNewSession(requestEnvelope); console.log('Is New Session:', isNewSession); // Get slot value const slotValue = Alexa.getSlotValue(requestEnvelope, 'cityName'); console.log('Slot Value:', slotValue); // Get slot object const slot = Alexa.getSlot(requestEnvelope, 'cityName'); if (slot) { console.log('Slot Status:', slot.confirmationStatus); console.log('Slot Resolutions:', slot.resolutions); } // Get dialog state const dialogState = Alexa.getDialogState(requestEnvelope); console.log('Dialog State:', dialogState); // Get supported interfaces const supportedInterfaces = Alexa.getSupportedInterfaces(requestEnvelope); console.log('Supported Interfaces:', supportedInterfaces); console.log('Has Display:', supportedInterfaces.hasOwnProperty('Display')); console.log('Has AudioPlayer:', supportedInterfaces.hasOwnProperty('AudioPlayer')); // Get viewport profile (for devices with screens) if (supportedInterfaces.hasOwnProperty('Alexa.Presentation.APL')) { const viewportProfile = Alexa.getViewportProfile(requestEnvelope); const viewportOrientation = Alexa.getViewportOrientation(requestEnvelope); const viewportSizeGroup = Alexa.getViewportSizeGroup(requestEnvelope); console.log('Viewport Profile:', viewportProfile); console.log('Viewport Orientation:', viewportOrientation); console.log('Viewport Size Group:', viewportSizeGroup); } return responseBuilder .speak('Utility functions executed. Check the logs for details.') .getResponse(); } }; exports.handler = Alexa.SkillBuilders.custom() .addRequestHandlers( UtilityExampleHandler ) .lambda(); ``` -------------------------------- ### Handle Slot Resolutions with ASK SDK for Node.js Source: https://context7.com/alexa/alexa-skills-kit-sdk-for-nodejs/llms.txt Illustrates how to retrieve and process slot values with entity resolution using the ASK SDK for Node.js. It checks if a city name slot has a value and attempts to resolve it to a known entity, extracting the spoken value, resolved name, and ID. This is crucial for understanding user input accurately. ```javascript const Alexa = require('ask-sdk-core'); const SlotResolutionHandler = { canHandle(handlerInput) { return handlerInput.requestEnvelope.request.type === 'IntentRequest' && handlerInput.requestEnvelope.request.intent.name === 'CityInfoIntent'; }, handle(handlerInput) { const { requestEnvelope, responseBuilder } = handlerInput; // Get slot with entity resolution const citySlot = Alexa.getSlot(requestEnvelope, 'cityName'); if (!citySlot || !citySlot.value) { return responseBuilder .speak('I didn't catch the city name. Please try again.') .reprompt('Which city would you like information about?') .getResponse(); } const spokenValue = citySlot.value; let resolvedValue = spokenValue; let cityId = null; // Check entity resolution if (citySlot.resolutions && citySlot.resolutions.resolutionsPerAuthority && citySlot.resolutions.resolutionsPerAuthority.length > 0) { const resolution = citySlot.resolutions.resolutionsPerAuthority[0]; if (resolution.status.code === 'ER_SUCCESS_MATCH') { resolvedValue = resolution.values[0].value.name; cityId = resolution.values[0].value.id; const speechText = `You asked about ${spokenValue}. I resolved that to ${resolvedValue} with ID ${cityId}.`; return responseBuilder .speak(speechText) .getResponse(); } } return responseBuilder .speak(`I heard ${spokenValue}, but couldn't resolve it to a known city.`) .getResponse(); } }; exports.handler = Alexa.SkillBuilders.custom() .addRequestHandlers( SlotResolutionHandler ) .lambda(); ```