add-teams: sign out of the Teams CLI once the bot exists

The M365 session is only needed to create the bot — the adapter runs on the
.env app credentials. On a headless box the session is a plaintext token
file, so setup no longer leaves it on disk. teams logout is idempotent;
later CLI maintenance just needs a fresh teams login (device code).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Koshkoshinsk
2026-07-06 13:52:05 +03:00
parent 0bcc3a74b1
commit b45dc2dc42
2 changed files with 20 additions and 0 deletions
+14
View File
@@ -215,6 +215,20 @@ Install the bot into Teams:
Once the app shows up in your Teams sidebar (or app list), continue.
```
### Sign out of the Teams CLI
The Microsoft 365 session was only needed to create the bot — the running
adapter authenticates with the app credentials in `.env`, never with your
account. On a headless box that session is a plaintext token file, so it
doesn't stay on disk once setup is done. Idempotent (already signed out is a
no-op). Any `teams …` command you run later — the Troubleshooting recovery
commands, `teams app rsc add`, an endpoint update — just needs a fresh
`teams login` first (a ~30-second device code).
```nc:run effect:external when:have_creds=no
"$(npm prefix -g 2>/dev/null)/bin/teams" logout
```
## Restart
Restart the service so it loads the Teams adapter and the credentials you just
+6
View File
@@ -126,6 +126,9 @@ describe('runChannelSkill adapter (Option A)', () => {
expect(log.some((c) => c.includes('pnpm add @chat-adapter/teams'))).toBe(true);
expect(log.some((c) => c.includes('app create'))).toBe(false);
expect(log.some((c) => c.includes('login'))).toBe(false);
// …no logout either — the drop-through path never signed in, and must not
// sign out a session the operator may be using for something else…
expect(log.some((c) => c.includes('logout'))).toBe(false);
// …the Teams CLI install is also skipped (nothing to create)…
expect(log.some((c) => c.includes('npm install -g @microsoft/teams.cli'))).toBe(false);
// …the service still restarts (adapter + existing credentials load)…
@@ -229,6 +232,9 @@ describe('runChannelSkill adapter (Option A)', () => {
// the prompted name, and the unconditional single-tenant default…
expect(log.some((c) => c.includes('--endpoint "https://acme.example/webhook/teams"'))).toBe(true);
expect(log.some((c) => c.includes('--name "NanoClaw"') && c.includes('--sign-in-audience myOrg'))).toBe(true);
// …the M365 session was signed out once the bot existed (the adapter runs
// on the .env app credentials; the plaintext token cache must not linger)…
expect(log.some((c) => c.includes('/bin/teams" logout'))).toBe(true);
// …the captured credentials landed in .env with the safe SingleTenant pairing…
const env = readFileSync(join(root, '.env'), 'utf8');
expect(env).toContain('TEAMS_APP_ID=12345678-1234-1234-1234-123456789abc');