teams-cli 0.1.13__tar.gz → 0.2.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. {teams_cli-0.1.13 → teams_cli-0.2.1}/CHANGELOG.md +35 -0
  2. {teams_cli-0.1.13 → teams_cli-0.2.1}/Cargo.lock +40 -6
  3. {teams_cli-0.1.13 → teams_cli-0.2.1}/Cargo.toml +6 -1
  4. {teams_cli-0.1.13 → teams_cli-0.2.1}/PKG-INFO +178 -12
  5. {teams_cli-0.1.13 → teams_cli-0.2.1}/README.md +177 -11
  6. {teams_cli-0.1.13 → teams_cli-0.2.1}/docs/releases.md +108 -0
  7. {teams_cli-0.1.13 → teams_cli-0.2.1}/src/auth.rs +12 -2
  8. {teams_cli-0.1.13 → teams_cli-0.2.1}/src/backend.rs +15 -3
  9. {teams_cli-0.1.13 → teams_cli-0.2.1}/src/cli.rs +40 -4
  10. {teams_cli-0.1.13 → teams_cli-0.2.1}/src/config.rs +49 -1
  11. teams_cli-0.2.1/src/desktop/avatars.rs +589 -0
  12. teams_cli-0.2.1/src/desktop/browser.rs +1434 -0
  13. teams_cli-0.2.1/src/desktop/browser_core.rs +227 -0
  14. teams_cli-0.2.1/src/desktop/browser_install.rs +554 -0
  15. {teams_cli-0.1.13 → teams_cli-0.2.1}/src/desktop/cdp.rs +109 -8
  16. {teams_cli-0.1.13 → teams_cli-0.2.1}/src/desktop/connection.ps1 +34 -13
  17. {teams_cli-0.1.13 → teams_cli-0.2.1}/src/desktop/connection.rs +44 -7
  18. {teams_cli-0.1.13 → teams_cli-0.2.1}/src/desktop/helper.rs +3 -1
  19. {teams_cli-0.1.13 → teams_cli-0.2.1}/src/desktop/owner.rs +98 -5
  20. {teams_cli-0.1.13 → teams_cli-0.2.1}/src/desktop/session.rs +116 -16
  21. {teams_cli-0.1.13 → teams_cli-0.2.1}/src/desktop/windows.rs +57 -1
  22. {teams_cli-0.1.13 → teams_cli-0.2.1}/src/desktop.rs +445 -21
  23. teams_cli-0.2.1/src/desktop_core.js +25 -0
  24. teams_cli-0.2.1/src/desktop_people.js +362 -0
  25. {teams_cli-0.1.13 → teams_cli-0.2.1}/src/desktop_send.js +8 -3
  26. {teams_cli-0.1.13 → teams_cli-0.2.1}/src/error.rs +1 -1
  27. teams_cli-0.2.1/src/graph.rs +777 -0
  28. {teams_cli-0.1.13 → teams_cli-0.2.1}/src/main.rs +293 -48
  29. {teams_cli-0.1.13 → teams_cli-0.2.1}/src/schema.rs +113 -24
  30. {teams_cli-0.1.13 → teams_cli-0.2.1}/src/tui.rs +211 -0
  31. {teams_cli-0.1.13 → teams_cli-0.2.1}/tests/cli.rs +76 -0
  32. {teams_cli-0.1.13 → teams_cli-0.2.1}/tests/connection.test.ps1 +50 -14
  33. {teams_cli-0.1.13 → teams_cli-0.2.1}/tests/desktop.rs +193 -0
  34. {teams_cli-0.1.13 → teams_cli-0.2.1}/tests/desktop_scripts.test.cjs +59 -3
  35. teams_cli-0.2.1/tests/js/people.test.mjs +37 -0
  36. teams_cli-0.1.13/src/graph.rs +0 -376
  37. {teams_cli-0.1.13 → teams_cli-0.2.1}/.github/workflows/ci.yml +0 -0
  38. {teams_cli-0.1.13 → teams_cli-0.2.1}/.github/workflows/dependencies.yml +0 -0
  39. {teams_cli-0.1.13 → teams_cli-0.2.1}/.github/workflows/release.yml +0 -0
  40. {teams_cli-0.1.13 → teams_cli-0.2.1}/.gitignore +0 -0
  41. {teams_cli-0.1.13 → teams_cli-0.2.1}/.mise.toml +0 -0
  42. {teams_cli-0.1.13 → teams_cli-0.2.1}/LICENSE +0 -0
  43. {teams_cli-0.1.13 → teams_cli-0.2.1}/Makefile +0 -0
  44. {teams_cli-0.1.13 → teams_cli-0.2.1}/build.rs +0 -0
  45. {teams_cli-0.1.13 → teams_cli-0.2.1}/examples/validate_schema.rs +0 -0
  46. {teams_cli-0.1.13 → teams_cli-0.2.1}/pyproject.toml +0 -0
  47. {teams_cli-0.1.13 → teams_cli-0.2.1}/scripts/smoke-release.py +0 -0
  48. {teams_cli-0.1.13 → teams_cli-0.2.1}/src/delivery.rs +0 -0
  49. {teams_cli-0.1.13 → teams_cli-0.2.1}/src/desktop/install.rs +0 -0
  50. {teams_cli-0.1.13 → teams_cli-0.2.1}/src/desktop/macos.rs +0 -0
  51. {teams_cli-0.1.13 → teams_cli-0.2.1}/src/desktop.js +0 -0
  52. {teams_cli-0.1.13 → teams_cli-0.2.1}/src/desktop_cache.js +0 -0
  53. {teams_cli-0.1.13 → teams_cli-0.2.1}/src/lib.rs +0 -0
  54. {teams_cli-0.1.13 → teams_cli-0.2.1}/src/output.rs +0 -0
  55. {teams_cli-0.1.13 → teams_cli-0.2.1}/src/render.rs +0 -0
  56. {teams_cli-0.1.13 → teams_cli-0.2.1}/src/teams_cli_alias.rs +0 -0
  57. {teams_cli-0.1.13 → teams_cli-0.2.1}/tests/connection.rs +0 -0
  58. {teams_cli-0.1.13 → teams_cli-0.2.1}/tests/desktop_scripts.rs +0 -0
  59. {teams_cli-0.1.13 → teams_cli-0.2.1}/tests/fixtures/clispec-v0.3.json +0 -0
  60. {teams_cli-0.1.13 → teams_cli-0.2.1}/tests/package_alias.rs +0 -0
  61. {teams_cli-0.1.13 → teams_cli-0.2.1}/tests/release_smoke.rs +0 -0
  62. {teams_cli-0.1.13 → teams_cli-0.2.1}/vership.toml +0 -0
@@ -7,6 +7,41 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.2.1](https://github.com/rvben/teams-cli/compare/v0.2.0...v0.2.1) - 2026-10-15
11
+
12
+ ### Added
13
+
14
+ - **desktop**: add `teams desktop clear-flags` and a confirmed TUI cleanup that remove remote-debugging flags from the user-wide WebView2 setting while keeping its other arguments.
15
+ - Search people by name or email and create or reuse a one-to-one conversation with `people search` and `chats create --recipient ID --yes`. Opening a conversation never sends a message.
16
+ - Support these operations through the selected personal or work/school desktop account and through Microsoft Graph. Graph search requires a new sign-in to consent to `User.ReadBasic.All`.
17
+
18
+ ### Changed
19
+
20
+ - Windows desktop helpers use protocol 4. WSL requires the helper from the same CLI release.
21
+ - Ambiguous conversation-creation outcomes remain unconfirmed and are never automatically retried.
22
+ - Personal-account searches put verified live matches first and filter unrelated recent-contact suggestions before applying the result limit.
23
+
24
+ ### Fixed
25
+
26
+ - **desktop**: pass the debugging flags only to the Teams launch on Windows and WSL, so other WebView2 apps such as Outlook no longer open the debugging port.
27
+ - **desktop**: name the application that holds a refused port and report a user-wide debugging setting as the cause.
28
+ - **desktop**: recognise the dedicated browser on Linux and WSL when Chrome rewrites its process title, so setup no longer stops with "did not open its debugging endpoint"; recovery hints now name the browser mode and port in use.
29
+ - **desktop**: keep the work/school browser visible when the organisation's sign-in (such as AD FS with Windows sign-in) does not let Teams stay signed in headless, instead of reporting success and then losing the session; reads now say when Teams was sent back to sign-in, and setup reports whether the browser runs headless or visible.
30
+ - **desktop**: stop a verified dedicated browser that hangs after closing its debugging port, so disconnect and recover no longer leave it running.
31
+ - **desktop**: refuse sign-in success when the browser closes or loses verified ownership during the sign-in check, and verify reads again after observing work-account sign-in.
32
+ - **desktop**: separate email lookup from name search ([b0647f1](https://github.com/rvben/teams-cli/commit/b0647f177c3f4cb5f73e74f42aed11078832e632))
33
+
34
+ ## [0.2.0](https://github.com/rvben/teams-cli/compare/v0.1.13...v0.2.0) - 2026-10-14
35
+
36
+ ### Added
37
+
38
+ - **desktop**: add one-command lightweight browser setup with automatic matching browser downloads, visible sign-in followed by verified hidden operation, personal-account defaults, and safe reconnect/recovery.
39
+ - **desktop**: add managed browser clients and cached avatars ([319c494](https://github.com/rvben/teams-cli/commit/319c494b18c9dff98d26b2d069d4347a39d02e6b))
40
+
41
+ ### Fixed
42
+
43
+ - **security**: update rustls to 0.23.46 to address the newly published TLS advisories.
44
+
10
45
  ## [0.1.13](https://github.com/rvben/teams-cli/compare/v0.1.12...v0.1.13) - 2026-10-14
11
46
 
12
47
  ### Fixed
@@ -1776,7 +1776,7 @@ version = "1.1.8"
1776
1776
  source = "registry+https://github.com/rust-lang/crates.io-index"
1777
1777
  checksum = "c0aeb26bf5e836cc1c341c8106051b573f1766dfa05aa87f0b98be5e51b02303"
1778
1778
  dependencies = [
1779
- "nix",
1779
+ "nix 0.29.0",
1780
1780
  "winapi",
1781
1781
  ]
1782
1782
 
@@ -1848,6 +1848,18 @@ dependencies = [
1848
1848
  "memoffset",
1849
1849
  ]
1850
1850
 
1851
+ [[package]]
1852
+ name = "nix"
1853
+ version = "0.31.3"
1854
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1855
+ checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d"
1856
+ dependencies = [
1857
+ "bitflags 2.13.1",
1858
+ "cfg-if",
1859
+ "cfg_aliases",
1860
+ "libc",
1861
+ ]
1862
+
1851
1863
  [[package]]
1852
1864
  name = "nom"
1853
1865
  version = "7.1.3"
@@ -2740,9 +2752,9 @@ dependencies = [
2740
2752
 
2741
2753
  [[package]]
2742
2754
  name = "rustls"
2743
- version = "0.23.45"
2755
+ version = "0.23.46"
2744
2756
  source = "registry+https://github.com/rust-lang/crates.io-index"
2745
- checksum = "0d41d731c7d2f962d1ccc364cec258de3c0e93b38c2fb3ba97ac74513048d634"
2757
+ checksum = "48e13bd8c0e9365c43cfa5c9e8f9ad49d3c8444926c9aac819e0e4dc503c8fdf"
2746
2758
  dependencies = [
2747
2759
  "aws-lc-rs",
2748
2760
  "once_cell",
@@ -3175,7 +3187,7 @@ dependencies = [
3175
3187
 
3176
3188
  [[package]]
3177
3189
  name = "teams-cli"
3178
- version = "0.1.13"
3190
+ version = "0.2.1"
3179
3191
  dependencies = [
3180
3192
  "assert_cmd",
3181
3193
  "base64 0.23.1",
@@ -3188,6 +3200,7 @@ dependencies = [
3188
3200
  "jiff",
3189
3201
  "jsonschema",
3190
3202
  "keyring",
3203
+ "libc",
3191
3204
  "open",
3192
3205
  "predicates",
3193
3206
  "rand 0.9.5",
@@ -3206,6 +3219,7 @@ dependencies = [
3206
3219
  "url",
3207
3220
  "windows-sys 0.61.2",
3208
3221
  "wiremock",
3222
+ "zip",
3209
3223
  ]
3210
3224
 
3211
3225
  [[package]]
@@ -3215,7 +3229,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
3215
3229
  checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
3216
3230
  dependencies = [
3217
3231
  "fastrand",
3218
- "getrandom 0.4.3",
3232
+ "getrandom 0.3.4",
3219
3233
  "once_cell",
3220
3234
  "rustix",
3221
3235
  "windows-sys 0.61.2",
@@ -3279,7 +3293,7 @@ dependencies = [
3279
3293
  "libc",
3280
3294
  "log",
3281
3295
  "memmem",
3282
- "nix",
3296
+ "nix 0.29.0",
3283
3297
  "num-derive",
3284
3298
  "num-traits",
3285
3299
  "ordered-float",
@@ -3577,6 +3591,12 @@ dependencies = [
3577
3591
  "thiserror 2.0.20",
3578
3592
  ]
3579
3593
 
3594
+ [[package]]
3595
+ name = "typed-path"
3596
+ version = "0.12.3"
3597
+ source = "registry+https://github.com/rust-lang/crates.io-index"
3598
+ checksum = "8e28f89b80c87b8fb0cf04ab448d5dd0dd0ade2f8891bae878de66a75a28600e"
3599
+
3580
3600
  [[package]]
3581
3601
  name = "typenum"
3582
3602
  version = "1.20.1"
@@ -4263,6 +4283,20 @@ dependencies = [
4263
4283
  "syn 3.0.4",
4264
4284
  ]
4265
4285
 
4286
+ [[package]]
4287
+ name = "zip"
4288
+ version = "9.0.3"
4289
+ source = "registry+https://github.com/rust-lang/crates.io-index"
4290
+ checksum = "c2d98561b612a84ae525331a7a094429ef8a5772514ed507ff6db5f54badc971"
4291
+ dependencies = [
4292
+ "crc32fast",
4293
+ "flate2",
4294
+ "indexmap",
4295
+ "memchr",
4296
+ "nix 0.31.3",
4297
+ "typed-path",
4298
+ ]
4299
+
4266
4300
  [[package]]
4267
4301
  name = "zmij"
4268
4302
  version = "1.0.23"
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "teams-cli"
3
- version = "0.1.13"
3
+ version = "0.2.1"
4
4
  edition = "2024"
5
5
  rust-version = "1.90"
6
6
  description = "A Microsoft Teams CLI and TUI designed equally for humans and agents"
@@ -46,6 +46,7 @@ toml = "1.1"
46
46
  url = "2"
47
47
  jiff = "0.2"
48
48
  unicode-width = "0.2"
49
+ zip = { version = "9.0.3", default-features = false, features = ["deflate-flate2"] }
49
50
 
50
51
  [target.'cfg(windows)'.dependencies]
51
52
  windows-sys = { version = "0.61", features = [
@@ -55,9 +56,13 @@ windows-sys = { version = "0.61", features = [
55
56
  "Win32_Security",
56
57
  "Win32_Storage_Packaging_Appx",
57
58
  "Win32_System_Diagnostics_ToolHelp",
59
+ "Win32_System_Registry",
58
60
  "Win32_System_Threading",
59
61
  ] }
60
62
 
63
+ [target.'cfg(unix)'.dependencies]
64
+ libc = "0.2"
65
+
61
66
  [dev-dependencies]
62
67
  assert_cmd = "2"
63
68
  jsonschema = "0.49.8"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: teams-cli
3
- Version: 0.1.13
3
+ Version: 0.2.1
4
4
  Classifier: Programming Language :: Rust
5
5
  Classifier: Programming Language :: Python :: Implementation :: CPython
6
6
  License-File: LICENSE
@@ -18,7 +18,7 @@ Project-URL: Repository, https://github.com/rvben/teams-cli
18
18
 
19
19
  Microsoft Teams from your terminal—human-first and agent-first.
20
20
 
21
- `teams-cli` is a Rust CLI and TUI for everyday Microsoft Teams work through supported Microsoft Graph APIs, with an optional experimental desktop backend that reads chats from, and can send chat messages through, a running Teams client on macOS, Windows or WSL. It is designed as two equally first-class experiences over one typed core: a fast, calm terminal interface for people and a deterministic, introspectable command contract for agents. The default Graph backend signs in as you with delegated OAuth and protects refresh credentials in your operating system's credential store. Output is readable text on a TTY and structured JSON when piped.
21
+ `teams-cli` is a Rust CLI and TUI for everyday Microsoft Teams work through supported Microsoft Graph APIs, with an optional experimental desktop backend that reads chats from, and can send chat messages through, a running Teams client on macOS, Windows or WSL. A dedicated browser client also provides an experimental route on native Linux; see [browser setup](#dedicated-browser-client-experimental). It is designed as two equally first-class experiences over one typed core: a fast, calm terminal interface for people and a deterministic, introspectable command contract for agents. The default Graph backend signs in as you with delegated OAuth and protects refresh credentials in your operating system's credential store. Output is readable text on a TTY and structured JSON when piped.
22
22
 
23
23
  For humans, no arguments open guided onboarding on first use and the TUI afterward. For agents, no command ever waits for an undeclared prompt: `teams schema` works offline, collection sizes are caller-bounded, writes declare their effects, errors have stable kinds and exit codes, and stdout contains data only.
24
24
 
@@ -68,11 +68,21 @@ teams init
68
68
  teams init
69
69
  ```
70
70
 
71
+ For a personal Microsoft account with the lightweight hidden browser:
72
+
73
+ ```console
74
+ teams init --profile personal --backend desktop --desktop-client browser-core
75
+ ```
76
+
77
+ This downloads its browsers automatically, opens visible sign-in once, and then
78
+ runs hidden. See [lightweight browser setup](#low-memory-browser-client-experimental)
79
+ for sending, reconnecting, and experimental compatibility limits.
80
+
71
81
  Browser PKCE is the human-friendly default. The browser talks directly to Microsoft, and the CLI never receives or stores your password. Normal sign-in requests these delegated scopes:
72
82
 
73
83
  ```text
74
- openid profile offline_access User.Read Team.ReadBasic.All
75
- Channel.ReadBasic.All Chat.Read Chat.Create ChatMessage.Send
84
+ openid profile offline_access User.Read User.ReadBasic.All
85
+ Team.ReadBasic.All Channel.ReadBasic.All Chat.Read Chat.Create ChatMessage.Send
76
86
  ChannelMessage.Send
77
87
  ```
78
88
 
@@ -153,6 +163,8 @@ For short-lived automation, `TEAMS_ACCESS_TOKEN` overrides the stored credential
153
163
  teams list
154
164
  teams channels list TEAM_ID
155
165
  teams chats list --limit 25
166
+ teams people search "Alex" --limit 20
167
+ teams chats create --recipient PERSON_ID --yes # no message is sent
156
168
  teams messages list --chat CHAT_ID
157
169
  teams auth login --channel-history # once, with administrator approval
158
170
  teams messages list --team TEAM_ID --channel CHANNEL_ID
@@ -168,7 +180,21 @@ teams config path
168
180
  teams doctor --offline
169
181
  ```
170
182
 
171
- Collection commands expose `--limit` and `--fields`; Graph-native collections use `--cursor`, while joined teams and channels use `--offset` because those endpoints do not accept `$top`. With a terminal they render concise text; when stdout is piped they emit JSON envelopes with continuation metadata and `truncated`.
183
+ Chat, message, team and channel collections expose `--limit` and `--fields`; Graph-native collections use `--cursor`, while joined teams and channels use `--offset` because those endpoints do not accept `$top`. With a terminal they render concise text; when stdout is piped they emit JSON envelopes with continuation metadata and `truncated`.
184
+
185
+ To message someone for the first time, use `people search` to find their verified
186
+ identity in the active profile. Search accepts a name or email prefix of 2–256
187
+ characters and returns at most 50 people; refine the query if `truncated` is true.
188
+ Pass the exact returned ID to `chats create`, then pass the resulting chat ID to
189
+ `messages send`. Creating a conversation reuses an existing one-to-one chat when
190
+ available and sends no message. It requires a writable profile and confirmation
191
+ (`--yes` for automation). Recipient IDs belong to their selected account/backend.
192
+ If creation is unconfirmed, inspect your chats before trying again.
193
+
194
+ Graph recipient search needs delegated `User.ReadBasic.All` consent. Existing
195
+ sign-ins can keep reading chats; run `teams auth login` again to enable directory
196
+ search. Your organization may restrict consent, directory guest access, external
197
+ recipients or chat creation. Personal accounts use the desktop backend.
172
198
 
173
199
  Profiles can block every remote write while keeping reads, sign-in, diagnostics, and the TUI available:
174
200
 
@@ -223,6 +249,15 @@ ids returned by `chats list`; personal chats are accessed through the desktop ba
223
249
  The chat with yourself is listed too, with the id `48:notes`, so
224
250
  you can try sending there first.
225
251
 
252
+ Desktop chat results may also include optional `avatarData` raster data URIs on
253
+ members and one-to-one chats. Photos come only from image resources already loaded
254
+ by that account's Teams page; the CLI never fetches a photo URL. Missing, stale,
255
+ ambiguous, or unsupported photos leave the fields absent, so clients should show
256
+ initials as a fallback. Reads allow at most 16 photos, 256 KiB per photo, 1 MiB of
257
+ avatar data in total, and a one-second budget. A photo failure never fails a chat
258
+ read. Personal photo reads have been checked on macOS; work/school and other
259
+ platforms still need photo-specific runtime validation.
260
+
226
261
  Desktop profiles are read-only unless created with `init --allow-send`;
227
262
  `TEAMS_READ_ONLY=true` still blocks sending, but `TEAMS_READ_ONLY=false` cannot unlock a
228
263
  desktop profile. With the opt-in, `messages send --chat` sends one plain-text message
@@ -254,7 +289,9 @@ paste; `r` checks delivery, `s` offers an explicit second-copy confirmation, and
254
289
  `esc` keeps the draft and uncertainty together in memory. Reopening it does not
255
290
  unlock ordinary sending. Queued results are labelled queued rather than sent.
256
291
 
257
- Desktop cannot create new chats. Teams, channels, identity commands, subscriptions,
292
+ Desktop supports people search and one-to-one chat creation through the selected
293
+ Teams client's own session. Creation requires a profile with `--allow-send` and
294
+ sends no message. Teams, channels, identity commands, subscriptions,
258
295
  replies, mentions, attachments and edits return `unsupported` on desktop. The TUI
259
296
  reads chats from the same cache and sends through the same path as `messages send`.
260
297
  Existing profiles without a backend field
@@ -276,15 +313,22 @@ Added auxiliary processes are left alone. If Teams was
276
313
  restarted independently, quit and relaunch it normally, then disconnect again to
277
314
  clear the old record. No command automatically restarts Teams on exit.
278
315
 
279
- On macOS, flags apply only to the managed launch. On Windows/WSL, setup saves the
280
- previous user environment value, applies flags temporarily while Teams starts, then
281
- restores that value. Unrelated changes to the value are preserved and reported.
316
+ Debugging flags apply only to the managed Teams launch; other apps and the user
317
+ environment are left unchanged. On Windows/WSL, Teams starts through its
318
+ `ms-teams.exe` app execution alias, which must be enabled (Settings > Apps >
319
+ Advanced app settings > App execution aliases). Connect refuses a user-wide
320
+ `WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS` that already has debugging flags: such a
321
+ setting opens the port in every WebView2 app, for example Outlook, and the
322
+ listener is then refused because Teams does not own it. Errors name those apps.
323
+ `teams desktop clear-flags` removes only the debugging flags from that setting
324
+ after confirmation (`--yes` when scripted); quit and reopen the named apps
325
+ afterwards. The TUI offers the same cleanup.
282
326
  Windows and WSL share a recovery record and lock. WSL finds PowerShell in its normal
283
327
  Windows system directory even when Windows PATH entries are disabled; Windows
284
328
  executable interop must still be enabled.
285
329
 
286
- If setup is interrupted, run `teams --profile local desktop recover`. It restores
287
- pending settings without restarting Teams or requiring the Windows helper; it does
330
+ If setup is interrupted, run `teams --profile local desktop recover`. It clears the
331
+ interrupted record (and restores settings left by older versions) without restarting Teams or requiring the Windows helper; it does
288
332
  not claim ownership of an unrecorded Teams process. Use `desktop status` to inspect
289
333
  the endpoint and saved session. Recovery cannot close an already-running debugging
290
334
  endpoint: quit Teams and relaunch it normally if its ownership was not recorded.
@@ -360,12 +404,134 @@ This backend is experimental: chat reading depends on the Teams client's interna
360
404
  cache layout and sending on its internal send interface, and an unrecognised layout
361
405
  fails with `desktop_error` rather than guessing.
362
406
 
407
+ ### Dedicated browser client (experimental)
408
+
409
+ On native Linux, macOS, or WSL2, the desktop backend can use an installed Chrome,
410
+ Chromium, or Edge browser instead of the native Teams app. WSL2 uses a Linux browser;
411
+ visible sign-in requires WSLg or another graphical display. The existing Windows
412
+ helper route remains the default for native desktop profiles.
413
+
414
+ Create a separate read-only profile and sign in in the window that opens:
415
+
416
+ ```console
417
+ teams init --profile browser --backend desktop --desktop-client browser --desktop-account work-school --yes
418
+ # The window closes automatically after a successful chat read.
419
+ # Teams then runs headless with the same saved sign-in:
420
+ teams --profile browser
421
+ # To renew sign-in or complete MFA later:
422
+ teams --profile browser desktop login --yes
423
+ ```
424
+
425
+ Login waits up to five minutes for a readable chat cache, then restarts headless
426
+ and verifies another read before reporting success. An empty chat list is valid.
427
+ A sign-in timeout leaves the visible window open; finish signing in and retry
428
+ `desktop login`. Interrupting login preserves the browser and its saved profile.
429
+ On WSL2, the visible step needs WSLg or another graphical display.
430
+
431
+ Some organisations sign in through their own identity provider (for example AD FS
432
+ with Windows sign-in) and do not let Teams renew its sign-in in a headless browser.
433
+ For work/school accounts, login therefore watches the headless browser for up to a
434
+ minute; when Teams is sent back to sign-in, the visible browser is reopened and stays
435
+ open instead. Minimise that window, but keep it open. When a headless session is sent
436
+ back to sign-in later, reads report it; run `desktop login` again.
437
+
438
+ Use `--desktop-account personal` for a personal Microsoft account. Sending still
439
+ requires an explicit `init --allow-send` and the existing send confirmation.
440
+ `init --no-login` only saves configuration. `desktop connect --visible` remains
441
+ available for manually managed sign-in; `desktop connect` starts headless.
442
+
443
+ The browser uses its own profile under the local data directory at
444
+ `teams-cli/browser/<port>/profile`; it never copies or opens your normal browser
445
+ profile. Different ports use different browser profiles. Disconnect closes this
446
+ browser and keeps its sign-in data. To switch between visible and headless operation,
447
+ disconnect first. `desktop recover` closes a verified managed browser or clears a
448
+ stale record when no listener remains. A verified browser that does not exit after
449
+ closing its debugging port is stopped; unverifiable processes are left untouched.
450
+
451
+ The CLI checks that every listener is loopback-only and belongs to the recorded
452
+ browser process, running as you with the expected executable, debugging port and
453
+ dedicated profile. Only HTTPS Teams pages are used for reads or sends. While the
454
+ port is open, other programs running as you can access that browser session.
455
+
456
+ Browser discovery supports common system installations. For another installation,
457
+ set `TEAMS_DESKTOP_BROWSER` to the absolute path of the actual browser executable,
458
+ not a shell launcher. Snap/Flatpak installations are not automatically supported.
459
+ The optional `TEAMS_DESKTOP_BROWSER_DATA_DIR` sets an absolute browser data directory
460
+ (each port gets a separate subdirectory). Keep the same browser executable and data
461
+ directory settings while managing a session. Headless mode removes the visible window; Teams still consumes browser CPU and RAM.
462
+
463
+ This route is experimental. Browser lifecycle and ownership checks can be tested
464
+ without signing in, but live Teams Web reads, sends, session persistence and MFA
465
+ recovery still require validation on a signed-in machine. Existing native Teams
466
+ validation does not establish browser-client compatibility.
467
+
468
+ ### Low-memory browser client (experimental)
469
+
470
+ `--desktop-client browser-core` keeps the authenticated Teams data runtime without
471
+ starting the main UI. It supports personal accounts only and requires the
472
+ exact Teams Personal bootstrap build validated by this CLI. Unknown builds fail
473
+ with `desktop_error`; the CLI does not fall back to old cache data or the full UI.
474
+ Use `--desktop-client browser` on a separate port for the full web client.
475
+
476
+ Start with one command:
477
+
478
+ ```console
479
+ teams init --profile core --backend desktop --desktop-client browser-core
480
+ ```
481
+
482
+ The CLI downloads a matching **Chrome for Testing / chrome-headless-shell** pair
483
+ from Google's official Stable channel on first setup, opens a separate sign-in
484
+ window, and hides it once chats are readable. It verifies the hidden session before
485
+ saving your CLI profile. Core mode defaults to a personal account, port **9223**,
486
+ and read-only access; add `--allow-send` to enable sending. Your normal browser,
487
+ its profile, and the native Teams connection on port 9222 are left alone.
488
+
489
+ Browser binaries and sign-in state live in a private `teams-cli/browser` data
490
+ directory. The installed pair is reused offline and is not silently updated while
491
+ sessions are running. Setup needs roughly 1 GiB of disk space for the browsers and
492
+ extraction; the ZIP downloads are removed after installation. Linux/WSL2 need a
493
+ desktop display (WSLg on WSL2) for initial sign-in.
494
+
495
+ ```console
496
+ teams --profile core desktop login # Sign in again; automatically hide afterward
497
+ teams --profile core desktop disconnect # Stop the browser; retain sign-in
498
+ teams --profile core desktop connect # Start hidden using the saved session
499
+ teams --profile core desktop status # Inspect process and recovery state
500
+ ```
501
+
502
+ An interrupted sign-in retains the dedicated window and profile. Rerun `desktop
503
+ login` to finish; an expired hidden session can use the same command. A stale
504
+ process record is recovered automatically only when its port has no listener;
505
+ unrelated or changed processes are refused. `desktop recover` preserves sign-in
506
+ and can close only a verified managed browser. The status command does not prove
507
+ that you are signed in; `chats list` or `doctor` verifies message access.
508
+
509
+ Advanced setups can override both `TEAMS_DESKTOP_BROWSER` and
510
+ `TEAMS_DESKTOP_HEADLESS_BROWSER` with absolute paths to matching executables.
511
+ Set both or neither; a partial override is refused. `TEAMS_DESKTOP_BROWSER_DATA_DIR`
512
+ changes the private data location. Keep these settings stable while a session is
513
+ running. Unknown Teams builds are refused; use the full `browser` client on its
514
+ own port if your Teams build is not yet supported.
515
+
516
+ On macOS, both dedicated test browsers use mock-keychain encryption because the
517
+ headless shell uses it. This profile contains sensitive login state and remains in
518
+ a private directory; it never opens your ordinary browser profile.
519
+
520
+ The integrated CLI measured about **307 MiB after collection and 322 MiB idle**
521
+ of summed browser process physical footprint on macOS with a personal account.
522
+ Signed-in reads, visible-to-headless login, sending/readback and incoming updates
523
+ from a second verified client passed. This is approximate process accounting, not a guaranteed RAM cap or a
524
+ Linux measurement. Startup peaks, large accounts, overnight use, reconnect/MFA and
525
+ Linux/WSL2 signed-in operation still need validation. Size-oriented V8 settings may
526
+ trade CPU for memory. Avatar fields may be absent because UI photos are not loaded.
527
+ Sending still requires `init --allow-send` and the existing send confirmation.
528
+
363
529
  ## Graph boundaries
364
530
 
365
531
  - The Graph backend supports Microsoft 365 work or school accounts only. Use the experimental desktop backend on macOS, Windows or WSL for personal Microsoft accounts; the relevant Teams Graph endpoints do not support them.
366
532
  - A work account can authenticate successfully before Microsoft Teams is licensed or provisioned; `teams doctor` distinguishes identity access from Teams availability.
367
533
  - Calling and screen sharing are not available as a delegated user CLI; Graph calling is built around bot/application scenarios.
368
- - No extracted desktop tokens, browser interception, or direct calls to private Teams services; the experimental desktop backend reads the local client cache and sends only through the Teams window's own client.
534
+ - No extracted desktop tokens or direct calls to private Teams services; the experimental desktop backend reads the local client cache and sends only through the Teams window's own client.
369
535
  - Tenant policy and admin consent can restrict delegated capabilities.
370
536
 
371
537
  ## CLI Spec