-
Notifications
You must be signed in to change notification settings - Fork 332
Add pack+sideload Teams app script to activity-protocol postdeploy (#9172) #9188
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
v1212
wants to merge
36
commits into
Azure:main
Choose a base branch
from
v1212:users/wujia/activity-teams-sideload-9172
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
36 commits
Select commit
Hold shift + click to select a range
4f6a5e9
Add pack+sideload Teams app script to activity-protocol postdeploy (#…
Copilot e236622
Suppress local invoke hint for websocket agents
Copilot e7a4c35
Revert "Suppress local invoke hint for websocket agents"
Copilot b70acb6
Fix golangci-lint lll violation in teams_sideload_script.go
Copilot 41006fd
Silence cspell for base64 icon constants in teams_sideload_script.go
Copilot 451f2bf
Address Copilot review: build-only mode, Teams field limits, safe hints
Copilot 1408d80
Avoid gosec G101 false positive in sideload run-command test
Copilot 24d5454
Rename test var to clear gosec G101 (pw substring match)
Copilot 2757e1d
Address 2nd Copilot review: manifest version, scopes, exit codes, quo…
Copilot 1c0f3ae
Bound Teams manifest version components to the 65535 limit
Copilot 1b677f5
Fix PS7.4+ native-error abort and qualify the no-admin claims
Copilot dc89ae6
Enforce script mode on rewrite and show build-only opt-out per shell
Copilot f5b4726
Re-chunk embedded base64 icons under the 125-char Go line limit
Copilot 6b2de0a
Do not clobber user-owned files sharing the sideload script name
Copilot ba95231
Reject non-regular files, unique pwsh temp dir, guide fast-path condi…
Copilot f85eb4e
Enable the fast path only when all sideload scripts were written
Copilot cd21bb5
Always give a next action; scope the build-only flag in PowerShell
Copilot 9291d75
fix(agents): guarantee monotonic Teams version and drop unrunnable sc…
Copilot d445022
fix(agents): harden version state file and use full atk login command
Copilot c1c9eb5
fix(agents): detect ATK 'Cannot get token' unauthenticated error
Copilot b54f5cc
fix(agents): scope generated Teams artifacts per agent
Copilot e64b0c9
fix(agents): reword comment to satisfy cspell
Copilot 4d647d7
Key Teams sideload ownership on the stable bot name; add execution tests
Copilot 5400a6e
Make the run hint shell-agnostic and stop the ps1 auto-opening a browser
Copilot f0ee571
Harden generated-file writes and make the run hint portable to Window…
Copilot b0997f5
fix(activity): key Teams script ownership on agent name; lock version…
Copilot 6b72194
test(activity): use a unique sentinel in the refresh assertion
Copilot 188a1a9
fix(activity): expansion-safe run hint, safer version lock, cd hint i…
Copilot 445345f
fix(activity): Windows execution-policy bypass, shell-safe cd, faithf…
Copilot 695c969
fix(agents): harden Teams sideload guide/scripts (round 21 review)
Copilot f122be1
fix(agents): shell-independent run hint + verifiable version-lock (ro…
Copilot 1a51292
fix(agents): transactional pair write, agent-scoped legacy guide, loc…
Copilot a58ff27
fix(agents): consistent guide cd + tolerate npm install failure (roun…
Copilot 2292c80
Slim down Teams pack+sideload script generation
Copilot a27b1ac
Correct stale comments/docs after slim-down; drop dead field
Copilot 718c633
Guard atk auth login in the PowerShell sideload script
Copilot File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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
190 changes: 190 additions & 0 deletions
190
cli/azd/extensions/azure.ai.agents/internal/cmd/assets/teams_pack_sideload.ps1
This file contains hidden or 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,190 @@ | ||
| #!/usr/bin/env pwsh | ||
| # Pack and sideload the Microsoft Teams app for {{.AgentName}} -- one command. | ||
| # | ||
| # Generated by 'azd deploy' (activity protocol) for agent {{.AgentName}}. Every id below was filled in by | ||
| # azd from the deployed agent and its auto-created Azure Bot, so this script does | ||
| # NOT call Azure -- it just packages and installs the Teams app. | ||
| # | ||
| # What it does (idempotent -- safe to re-run): | ||
| # 1. builds a Teams app package (manifest.json + icons) in a temp dir, | ||
| # 2. ensures the Microsoft 365 Agents Toolkit CLI (atk) is installed, | ||
| # 3. installs the app FOR THE CURRENT USER (atk install --scope Personal -- | ||
| # no org-catalog admin approval needed; your tenant must still allow custom | ||
| # app upload/sideloading, which a Teams admin can enable if it is off), and | ||
| # 4. prints an "Open in Teams" chat deep link. | ||
| # | ||
| # Prerequisites: Node.js (npm) for the atk CLI, and a one-time 'atk auth login m365' | ||
| # with your M365 account (this script launches it for you if you are not signed | ||
| # in). Set SKIP_TEAMS_INSTALL=1 to build the package only and skip the install. | ||
|
|
||
| $ErrorActionPreference = "Stop" | ||
|
|
||
| # ---- Ids baked in by azd deploy (do not edit) ------------------------------- | ||
| $AgentName = "{{.AgentName}}" | ||
| $BotId = "{{.MsaAppID}}" # Teams manifest bots[].botId = the Azure Bot msaAppId | ||
| $TeamsAppId = "{{.TeamsAppId}}" # stable per agent, so re-runs update the same app | ||
|
|
||
| # Teams manifest v1.19 caps name.short at 30 and description.short at 80 chars, | ||
| # but agent names may be longer, so bound the constrained fields. | ||
| $shortName = if ($AgentName.Length -gt 30) { $AgentName.Substring(0, 30) } else { $AgentName } | ||
| $shortDesc = "Chat with $shortName in Microsoft Teams." | ||
| if ($shortDesc.Length -gt 80) { $shortDesc = $shortDesc.Substring(0, 80) } | ||
|
|
||
| # Teams treats a re-uploaded package (same app id) as an update only when the | ||
| # manifest version is higher, so derive a strictly increasing version from the | ||
| # current time -- no state file, lock, or counter to leave behind. Encode epoch | ||
| # seconds into two Teams-bounded components (each capped at 65535): minor = N / | ||
| # 65536, patch = N % 65536 (minor stays < 65535 until ~2106). Two runs in the same | ||
| # second get the same version; if that ever rejects the second upload as "not | ||
| # newer", just re-run a moment later. | ||
| $nowEpoch = [DateTimeOffset]::UtcNow.ToUnixTimeSeconds() | ||
| $verMinor = [int]([math]::Floor($nowEpoch / 65536)) | ||
| $verPatch = [int]($nowEpoch % 65536) | ||
| $pkgVersion = "1.$verMinor.$verPatch" | ||
|
|
||
| Write-Host "Agent: $AgentName" | ||
| Write-Host "Bot ID: $BotId" | ||
| Write-Host "Teams app id: $TeamsAppId" | ||
|
|
||
| # ---- Build the Teams app package in a temp dir ------------------------------ | ||
| # Use a unique per-invocation directory (like the Bash script's mktemp -d) so | ||
| # concurrent runs -- including two different projects that share an agent name -- | ||
| # never share a manifest/zip or overwrite each other's package. | ||
| $buildDir = Join-Path ([System.IO.Path]::GetTempPath()) ("teams-app-" + [Guid]::NewGuid().ToString("N")) | ||
| New-Item -ItemType Directory -Force -Path $buildDir | Out-Null | ||
|
|
||
| $manifest = [ordered]@{ | ||
| '$schema' = "https://developer.microsoft.com/en-us/json-schemas/teams/v1.19/MicrosoftTeams.schema.json" | ||
| manifestVersion = "1.19" | ||
| version = $pkgVersion | ||
| id = $TeamsAppId | ||
| developer = [ordered]@{ | ||
| name = "Microsoft Foundry" | ||
| websiteUrl = "https://www.example.com" | ||
| privacyUrl = "https://www.example.com/privacy" | ||
| termsOfUseUrl = "https://www.example.com/termsofuse" | ||
| } | ||
| icons = [ordered]@{ color = "color.png"; outline = "outline.png" } | ||
| name = [ordered]@{ short = $shortName; full = "$AgentName (Activity, Teams)" } | ||
| description = [ordered]@{ | ||
| short = $shortDesc | ||
| full = "A Microsoft Foundry hosted agent on the Activity protocol. Send it a message in Teams." | ||
| } | ||
| accentColor = "#5B5FC7" | ||
| bots = @([ordered]@{ botId = $BotId; scopes = @("personal"); supportsFiles = $false; isNotificationOnly = $false }) | ||
| permissions = @("identity") | ||
| validDomains = @() | ||
| } | ||
| # Write manifest.json as UTF-8 WITHOUT a BOM. Windows PowerShell 5.1's | ||
| # Set-Content -Encoding UTF8 prepends a BOM, which some Teams/atk JSON parsers | ||
| # reject; WriteAllText with an explicit no-BOM encoding is correct on 5.1 and 7. | ||
| $noBomUtf8 = New-Object System.Text.UTF8Encoding($false) | ||
| [System.IO.File]::WriteAllText( | ||
| (Join-Path $buildDir "manifest.json"), ($manifest | ConvertTo-Json -Depth 10), $noBomUtf8) | ||
|
|
||
| # ---- Write the icons (embedded PNGs -- no image tooling needed) -------------- | ||
| # color.png is 192x192, outline.png is 32x32, per the Teams manifest icon rules. | ||
| $colorB64 = "{{.ColorPngB64}}" | ||
| $outlineB64 = "{{.OutlinePngB64}}" | ||
| [System.IO.File]::WriteAllBytes((Join-Path $buildDir "color.png"), [Convert]::FromBase64String($colorB64)) | ||
| [System.IO.File]::WriteAllBytes((Join-Path $buildDir "outline.png"), [Convert]::FromBase64String($outlineB64)) | ||
|
|
||
| # ---- Zip the package (files at the zip root, not in a subfolder) ------------ | ||
| $zipPath = Join-Path $buildDir "$AgentName-teams-app.zip" | ||
| if (Test-Path $zipPath) { Remove-Item $zipPath -Force } | ||
| Compress-Archive ` | ||
| -Path (Join-Path $buildDir "manifest.json"), (Join-Path $buildDir "color.png"), (Join-Path $buildDir "outline.png") ` | ||
| -DestinationPath $zipPath -Force | ||
| Write-Host "Teams app package: $zipPath" | ||
|
|
||
| # Build-only mode: the package (with icons) is ready; skip the atk install. | ||
| if ($env:SKIP_TEAMS_INSTALL -eq "1") { | ||
| Write-Host "SKIP_TEAMS_INSTALL=1 - package built; skipping the per-user Teams install." | ||
| Write-Host "Sideload it manually (requires custom app upload to be enabled for your tenant):" | ||
| Write-Host " $zipPath" | ||
| Write-Host " Teams -> Apps -> Manage your apps -> Upload an app -> Upload a custom app" | ||
| return | ||
| } | ||
|
|
||
| # ---- Ensure the atk CLI is available ---------------------------------------- | ||
| if (-not (Get-Command atk -ErrorAction SilentlyContinue)) { | ||
| if (-not (Get-Command npm -ErrorAction SilentlyContinue)) { | ||
| throw "Node.js/npm is required for the atk CLI (per-user Teams install). Install Node.js, then re-run this script." | ||
| } | ||
| Write-Host "Installing the Microsoft 365 Agents Toolkit CLI (atk)..." | ||
| # Tolerate a failed install: with the top-level $ErrorActionPreference = "Stop" | ||
| # and $PSNativeCommandUseErrorActionPreference (default on PowerShell 7.4+), a | ||
| # nonzero npm exit becomes a terminating error that would stop the script | ||
| # before the Get-Command check below emits the actionable manual-install | ||
| # message. Catch it and fall through, mirroring the bash script's `|| true`. | ||
| try { | ||
| npm install -g '@microsoft/m365agentstoolkit-cli' | Out-Null | ||
| } catch { | ||
| Write-Host "atk install via npm did not complete: $_" | ||
| } | ||
| if (-not (Get-Command atk -ErrorAction SilentlyContinue)) { | ||
| throw "Failed to install atk. Install it manually: npm i -g @microsoft/m365agentstoolkit-cli" | ||
| } | ||
| } | ||
|
|
||
| # atk is a native command; on PowerShell 7.4+ a nonzero exit combined with | ||
| # $ErrorActionPreference = "Stop" would terminate the script before we can inspect | ||
| # the output (e.g. the "not signed in" case). Run the probe installs with | ||
| # non-terminating error handling and return the combined output so the | ||
| # login-and-retry logic below can run. | ||
| function Invoke-AtkInstallProbe { | ||
| param([Parameter(Mandatory)][string]$Zip) | ||
| $ErrorActionPreference = "Continue" | ||
| atk install --file-path "$Zip" --scope Personal --interactive false 2>&1 | Out-String | ||
| } | ||
|
|
||
| # ---- Install the app for the current user (Personal scope) ------------------ | ||
| Write-Host "" | ||
| Write-Host "Installing the Teams app for the current user (atk, scope Personal)..." | ||
| $installOut = Invoke-AtkInstallProbe -Zip $zipPath | ||
| Write-Host $installOut | ||
|
|
||
| # If atk reports the user is not signed in, launch an interactive login and retry. | ||
| if ($installOut -match "(?i)(not\s+(logged|signed)\s+in|auth.*required|please\s+login|login\s+first|no\s+account|cannot\s+get\s+token|log\s+in\s+the\s+correct\s+account)") { | ||
| Write-Host "Not signed in - launching 'atk auth login m365' (complete the sign-in prompt)..." | ||
| # atk is a native command; on PowerShell 7.4+ a nonzero exit (e.g. the user | ||
| # cancels the sign-in) combined with $ErrorActionPreference = "Stop" would | ||
| # terminate the script here, before the retry and the graceful fallback below. | ||
| # Run it with non-terminating error handling so a cancelled/failed sign-in | ||
| # falls through, mirroring the bash script's tolerant set +e block. | ||
| try { | ||
| $ErrorActionPreference = "Continue" | ||
| atk auth login m365 | ||
| } catch { | ||
| Write-Host "atk auth login did not complete: $_" | ||
| } finally { | ||
| $ErrorActionPreference = "Stop" | ||
| } | ||
| $installOut = Invoke-AtkInstallProbe -Zip $zipPath | ||
| Write-Host $installOut | ||
| } | ||
|
|
||
| $titleId = $null | ||
| $m = [regex]::Match($installOut, 'TitleId:\s*(\S+)') | ||
| if ($m.Success) { $titleId = $m.Groups[1].Value.Trim() } | ||
|
|
||
| # Teams addresses a bot 1:1 as "28:<botId>". Once the app is installed for the | ||
| # user, this opens the conversation with the agent directly. | ||
| $chatLink = "https://teams.microsoft.com/l/chat/0/0?users=28:$BotId" | ||
|
|
||
| if ([string]::IsNullOrWhiteSpace($titleId)) { | ||
| Write-Host "" | ||
| Write-Host "Could not confirm the per-user install." | ||
| Write-Host "If you were prompted to sign in, run 'atk auth login m365' then re-run this script." | ||
| Write-Host "Or sideload the package manually (requires custom app upload to be enabled for your tenant):" | ||
| Write-Host " $zipPath" | ||
| Write-Host " Teams -> Apps -> Manage your apps -> Upload an app -> Upload a custom app" | ||
| # The install did not complete, so report failure (build-only mode already | ||
| # returned earlier). The package + manual steps above remain available. | ||
| exit 1 | ||
| } | ||
|
|
||
| Write-Host "" | ||
| Write-Host "Installed for the current user (titleId: $titleId)." | ||
| Write-Host "Chat with the agent in Teams:" | ||
| Write-Host " $chatLink" | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.