-
Notifications
You must be signed in to change notification settings - Fork 34
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' into samples/node-skill
- Loading branch information
Showing
9 changed files
with
305 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
// For format details, see https://aka.ms/devcontainer.json. For config options, see the | ||
// README at: https://github.com/devcontainers/templates/tree/main/src/dotnet | ||
{ | ||
"name": "universal", | ||
"image": "mcr.microsoft.com/devcontainers/universal:focal", | ||
"features": { | ||
"ghcr.io/devcontainers/features/azure-cli:1": {}, | ||
"ghcr.io/devcontainers/features/powershell:1": {}, | ||
"ghcr.io/devcontainers/features/docker-in-docker:2": {}, | ||
"ghcr.io/stuartleeks/dev-container-features/dev-tunnels:0": {} | ||
}, | ||
"customizations": { | ||
"vscode": { | ||
"extensions": [ | ||
"GitHub.copilot-chat", | ||
"TeamsDevApp.vscode-adaptive-cards" | ||
] | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -399,7 +399,7 @@ FodyWeavers.xsd | |
|
||
#Ignore env files | ||
*.env | ||
|
||
dist/ | ||
devTools/ | ||
node_modules/ | ||
node_modules/ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
registry=https://registry.npmjs.org/ | ||
@microsoft:registry=https://pkgs.dev.azure.com/ConversationalAI/BotFramework/_packaging/SDK/npm/registry/ | ||
package-lock=false |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
# Copilot Studio Client | ||
|
||
This is a sample to show how to use the `@microsoft/agents-copilotstudio-client` package to talk to an Agent hosted in CopilotStudio. | ||
|
||
|
||
## Prerequisite | ||
|
||
To set up this sample, you will need the following: | ||
|
||
1. [Node.js](https://nodejs.org) version 20 or higher | ||
|
||
```bash | ||
# determine node version | ||
node --version | ||
``` | ||
2. An Agent Created in Microsoft Copilot Studio or access to an existing Agent. | ||
3. Ability to Create an Application Identity in Azure for a Public Client/Native App Registration Or access to an existing Public Client/Native App registration with the CopilotStudio.Copilot.Invoke API Permission assigned. | ||
|
||
## Create an Agent in Copilot Studio | ||
|
||
1. Create an Agent in [Copilot Studio](https://copilotstudio.microsoft.com) | ||
1. Publish your newly created Copilot | ||
2. Goto Settings => Advanced => Metadata and copy the following values, You will need them later: | ||
1. Schema name | ||
2. Environment Id | ||
|
||
## Create an Application Registration in Entra ID | ||
|
||
This step will require permissions to create application identities in your Azure tenant. For this sample, you will create a Native Client Application Identity, which does not have secrets. | ||
|
||
1. Open https://portal.azure.com | ||
2. Navigate to Entra Id | ||
3. Create a new App Registration in Entra ID | ||
1. Provide a Name | ||
2. Choose "Accounts in this organization directory only" | ||
3. In the "Select a Platform" list, Choose "Public Client/native (mobile & desktop) | ||
4. In the Redirect URI url box, type in `http://localhost` (**note: use HTTP, not HTTPS**) | ||
5. Then click register. | ||
4. In your newly created application | ||
1. On the Overview page, Note down for use later when configuring the example application: | ||
1. The Application (client) ID | ||
2. The Directory (tenant) ID | ||
2. Go to API Permissions in `Manage` section | ||
3. Click Add Permission | ||
1. In the side panel that appears, Click the tab `API's my organization uses` | ||
2. Search for `Power Platform API`. | ||
1. *If you do not see `Power Platform API` see the note at the bottom of this section.* | ||
3. In the permissions list, choose `CopilotStudio` and Check `CopilotStudio.Copilots.Invoke` | ||
4. Click `Add Permissions` | ||
4. (Optional) Click `Grant Admin consent for copilotsdk` | ||
> [!TIP] | ||
> If you do not see `Power Platform API` in the list of API's your organization uses, you need to add the Power Platform API to your tenant. To do that, goto [Power Platform API Authentication](https://learn.microsoft.com/power-platform/admin/programmability-authentication-v2#step-2-configure-api-permissions) and follow the instructions on Step 2 to add the Power Platform Admin API to your Tenant | ||
## Instructions - Configure the Example Application | ||
With the above information, you can now run the client `CopilostStudioClient` sample. | ||
1. Open the `env.TEMPLATE` file and rename it to `.env`. | ||
2. Configure the values based on what was recorded during the setup phase. | ||
```bash | ||
environmentId="" # Environment ID of environment with the CopilotStudio App. | ||
botIdentifier="" # Schema Name of the Copilot to use | ||
tenantId="" # Tenant ID of the App Registration used to login, this should be in the same tenant as the Copilot. | ||
appClientId="" # App ID of the App Registration used to login, this should be in the same tenant as the Copilot. | ||
``` | ||
3. Run the CopilotStudioClient sample using `npm start`, which will install the packages, build the project and run it. | ||
This should challenge you to login and connect to the Copilot Studio Hosted bot, allowing you to communicate via a console interface. | ||
## Authentication | ||
The DirectToEngine Client requires a User Token to operate. For this sample, we are using a user interactive flow to get the user token for the application ID created above. | ||
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
{ | ||
"name": "copilotstudio-client", | ||
"version": "1.0.0", | ||
"private": true, | ||
"description": "Agents Copilot Studio Client sample", | ||
"author": "Microsoft", | ||
"license": "MIT", | ||
"main": "./dist/index.js", | ||
"type": "module", | ||
"scripts": { | ||
"build": "tsc --build", | ||
"prestart": "npm run build", | ||
"start": "node --env-file .env ./dist/index.js" | ||
}, | ||
"dependencies": { | ||
"@microsoft/agents-copilotstudio-client": "0.1.20", | ||
"@azure/msal-node": "^3.2.3", | ||
"open": "^10.1.0" | ||
}, | ||
"devDependencies": { | ||
"@types/debug": "^4.1.12", | ||
"@types/node": "^22.13.4", | ||
"typescript": "^5.7.3" | ||
}, | ||
"keywords": [] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
/** | ||
* Copyright (c) Microsoft Corporation. All rights reserved. | ||
* Licensed under the MIT License. | ||
*/ | ||
|
||
import * as msal from '@azure/msal-node' | ||
import { Activity, ActivityTypes, CardAction } from '@microsoft/agents-bot-activity' | ||
import { ConnectionSettings, loadCopilotStudioConnectionSettingsFromEnv, CopilotStudioClient } from '@microsoft/agents-copilotstudio-client' | ||
import pkg from '@microsoft/agents-copilotstudio-client/package.json' with { type: 'json' } | ||
import readline from 'readline' | ||
import open from 'open' | ||
import os from 'os' | ||
import path from 'path' | ||
|
||
import { MsalCachePlugin } from './msalCachePlugin.js' | ||
|
||
async function acquireToken(settings: ConnectionSettings): Promise<string> { | ||
const msalConfig = { | ||
auth: { | ||
clientId: settings.appClientId, | ||
authority: `https://login.microsoftonline.com/${settings.tenantId}`, | ||
}, | ||
cache: { | ||
cachePlugin: new MsalCachePlugin(path.join(os.tmpdir(), 'msal.usercache.json')) | ||
}, | ||
system: { | ||
loggerOptions: { | ||
loggerCallback(loglevel: msal.LogLevel, message: string, containsPii: boolean) { | ||
console.log(message) | ||
}, | ||
piiLoggingEnabled: false, | ||
logLevel: msal.LogLevel.Verbose, | ||
} | ||
} | ||
} | ||
const pca = new msal.PublicClientApplication(msalConfig) | ||
const tokenRequest = { | ||
scopes: ['https://api.powerplatform.com/.default'], | ||
redirectUri: 'http://localhost', | ||
openBrowser: async (url: string) => { | ||
await open(url) | ||
} | ||
} | ||
let token | ||
try { | ||
const accounts = await pca.getAllAccounts() | ||
if (accounts.length > 0) { | ||
const response2 = await pca.acquireTokenSilent({ account: accounts[0], scopes: tokenRequest.scopes }) | ||
token = response2.accessToken | ||
} else { | ||
const response = await pca.acquireTokenInteractive(tokenRequest) | ||
token = response.accessToken | ||
} | ||
} catch (error) { | ||
console.error('Error acquiring token interactively:', error) | ||
const response = await pca.acquireTokenInteractive(tokenRequest) | ||
token = response.accessToken | ||
} | ||
return token | ||
} | ||
|
||
const createClient = async (): Promise<CopilotStudioClient> => { | ||
const settings = loadCopilotStudioConnectionSettingsFromEnv() | ||
const token = await acquireToken(settings) | ||
const copilotClient = new CopilotStudioClient(settings, token) | ||
console.log(`Copilot Studio Client Version: ${pkg.version}, running with settings: ${JSON.stringify(settings, null, 2)}`) | ||
return copilotClient | ||
} | ||
|
||
const rl = readline.createInterface({ | ||
input: process.stdin, | ||
output: process.stdout | ||
}) | ||
|
||
const askQuestion = async (copilotClient: CopilotStudioClient, conversationId: string) => { | ||
rl.question('\n>>>: ', async (answer) => { | ||
if (answer.toLowerCase() === 'exit') { | ||
rl.close() | ||
} else { | ||
const replies = await copilotClient.askQuestionAsync(answer, conversationId) | ||
replies.forEach((act: Activity) => { | ||
if (act.type === ActivityTypes.Message) { | ||
console.log(`\n${act.text}`) | ||
act.suggestedActions?.actions.forEach((action: CardAction) => console.log(action.value)) | ||
} else if (act.type === ActivityTypes.EndOfConversation) { | ||
console.log(`\n${act.text}`) | ||
rl.close() | ||
} | ||
}) | ||
await askQuestion(copilotClient, conversationId) | ||
} | ||
}) | ||
} | ||
|
||
const main = async () => { | ||
const copilotClient = await createClient() | ||
const act: Activity = await copilotClient.startConversationAsync(true) | ||
console.log('\nSuggested Actions: ') | ||
act.suggestedActions?.actions.forEach((action: CardAction) => console.log(action.value)) | ||
await askQuestion(copilotClient, act.conversation?.id!) | ||
} | ||
|
||
main().catch(e => console.log(e)) |
49 changes: 49 additions & 0 deletions
49
samples/basic/copilotstudio-client/nodejs/src/msalCachePlugin.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
/* | ||
* Copyright (c) Microsoft Corporation. All rights reserved. | ||
* Licensed under the MIT License. | ||
*/ | ||
import fs from 'fs' | ||
import { ICachePlugin, TokenCacheContext } from '@azure/msal-node' | ||
|
||
export class MsalCachePlugin implements ICachePlugin { | ||
private cacheLocation: string = '' | ||
constructor(cacheLocation: string) { | ||
this.cacheLocation = cacheLocation | ||
} | ||
|
||
async beforeCacheAccess(tokenCacheContext: TokenCacheContext): Promise<void> { | ||
return new Promise((resolve, reject) => { | ||
if (fs.existsSync(this.cacheLocation)) { | ||
fs.readFile(this.cacheLocation, 'utf-8', (error, data) => { | ||
if (error) { | ||
reject(error) | ||
} else { | ||
tokenCacheContext.tokenCache.deserialize(data) | ||
resolve() | ||
} | ||
}) | ||
} else { | ||
fs.writeFile(this.cacheLocation, tokenCacheContext.tokenCache.serialize(), (error) => { | ||
if (error) { | ||
reject(error) | ||
} | ||
}) | ||
} | ||
}) | ||
} | ||
|
||
async afterCacheAccess(tokenCacheContext: TokenCacheContext): Promise<void> { | ||
return new Promise((resolve, reject) => { | ||
if (tokenCacheContext.cacheHasChanged) { | ||
fs.writeFile(this.cacheLocation, tokenCacheContext.tokenCache.serialize(), (error) => { | ||
if (error) { | ||
reject(error) | ||
} | ||
resolve() | ||
}) | ||
} else { | ||
resolve() | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
{ | ||
"compilerOptions": { | ||
"incremental": true, | ||
"lib": ["ES2021"], | ||
"target": "es2019", | ||
"declaration": true, | ||
"sourceMap": true, | ||
"composite": true, | ||
"strict": true, | ||
"moduleResolution": "node", | ||
"esModuleInterop": true, | ||
"skipLibCheck": true, | ||
"forceConsistentCasingInFileNames": true, | ||
"resolveJsonModule": true, | ||
"module": "ESNext", | ||
"rootDir": "src", | ||
"outDir": "dist", | ||
"tsBuildInfoFile": "dist/.tsbuildinfo" | ||
} | ||
} |