teams-cli 0.2.0__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.2.0 → teams_cli-0.2.1}/CHANGELOG.md +24 -0
  2. {teams_cli-0.2.0 → teams_cli-0.2.1}/Cargo.lock +1 -1
  3. {teams_cli-0.2.0 → teams_cli-0.2.1}/Cargo.toml +3 -2
  4. {teams_cli-0.2.0 → teams_cli-0.2.1}/PKG-INFO +44 -11
  5. {teams_cli-0.2.0 → teams_cli-0.2.1}/README.md +43 -10
  6. {teams_cli-0.2.0 → teams_cli-0.2.1}/docs/releases.md +39 -0
  7. {teams_cli-0.2.0 → teams_cli-0.2.1}/src/auth.rs +12 -2
  8. {teams_cli-0.2.0 → teams_cli-0.2.1}/src/backend.rs +13 -0
  9. {teams_cli-0.2.0 → teams_cli-0.2.1}/src/cli.rs +23 -0
  10. {teams_cli-0.2.0 → teams_cli-0.2.1}/src/desktop/browser.rs +251 -34
  11. {teams_cli-0.2.0 → teams_cli-0.2.1}/src/desktop/connection.ps1 +34 -13
  12. {teams_cli-0.2.0 → teams_cli-0.2.1}/src/desktop/connection.rs +23 -7
  13. {teams_cli-0.2.0 → teams_cli-0.2.1}/src/desktop/helper.rs +3 -1
  14. {teams_cli-0.2.0 → teams_cli-0.2.1}/src/desktop/owner.rs +98 -5
  15. {teams_cli-0.2.0 → teams_cli-0.2.1}/src/desktop/session.rs +56 -8
  16. {teams_cli-0.2.0 → teams_cli-0.2.1}/src/desktop/windows.rs +57 -1
  17. {teams_cli-0.2.0 → teams_cli-0.2.1}/src/desktop.rs +278 -14
  18. teams_cli-0.2.1/src/desktop_people.js +362 -0
  19. {teams_cli-0.2.0 → teams_cli-0.2.1}/src/error.rs +1 -1
  20. teams_cli-0.2.1/src/graph.rs +777 -0
  21. {teams_cli-0.2.0 → teams_cli-0.2.1}/src/main.rs +150 -14
  22. {teams_cli-0.2.0 → teams_cli-0.2.1}/src/schema.rs +65 -5
  23. {teams_cli-0.2.0 → teams_cli-0.2.1}/src/tui.rs +211 -0
  24. {teams_cli-0.2.0 → teams_cli-0.2.1}/tests/cli.rs +76 -0
  25. {teams_cli-0.2.0 → teams_cli-0.2.1}/tests/connection.test.ps1 +50 -14
  26. teams_cli-0.2.1/tests/js/people.test.mjs +37 -0
  27. teams_cli-0.2.0/src/graph.rs +0 -376
  28. {teams_cli-0.2.0 → teams_cli-0.2.1}/.github/workflows/ci.yml +0 -0
  29. {teams_cli-0.2.0 → teams_cli-0.2.1}/.github/workflows/dependencies.yml +0 -0
  30. {teams_cli-0.2.0 → teams_cli-0.2.1}/.github/workflows/release.yml +0 -0
  31. {teams_cli-0.2.0 → teams_cli-0.2.1}/.gitignore +0 -0
  32. {teams_cli-0.2.0 → teams_cli-0.2.1}/.mise.toml +0 -0
  33. {teams_cli-0.2.0 → teams_cli-0.2.1}/LICENSE +0 -0
  34. {teams_cli-0.2.0 → teams_cli-0.2.1}/Makefile +0 -0
  35. {teams_cli-0.2.0 → teams_cli-0.2.1}/build.rs +0 -0
  36. {teams_cli-0.2.0 → teams_cli-0.2.1}/examples/validate_schema.rs +0 -0
  37. {teams_cli-0.2.0 → teams_cli-0.2.1}/pyproject.toml +0 -0
  38. {teams_cli-0.2.0 → teams_cli-0.2.1}/scripts/smoke-release.py +0 -0
  39. {teams_cli-0.2.0 → teams_cli-0.2.1}/src/config.rs +0 -0
  40. {teams_cli-0.2.0 → teams_cli-0.2.1}/src/delivery.rs +0 -0
  41. {teams_cli-0.2.0 → teams_cli-0.2.1}/src/desktop/avatars.rs +0 -0
  42. {teams_cli-0.2.0 → teams_cli-0.2.1}/src/desktop/browser_core.rs +0 -0
  43. {teams_cli-0.2.0 → teams_cli-0.2.1}/src/desktop/browser_install.rs +0 -0
  44. {teams_cli-0.2.0 → teams_cli-0.2.1}/src/desktop/cdp.rs +0 -0
  45. {teams_cli-0.2.0 → teams_cli-0.2.1}/src/desktop/install.rs +0 -0
  46. {teams_cli-0.2.0 → teams_cli-0.2.1}/src/desktop/macos.rs +0 -0
  47. {teams_cli-0.2.0 → teams_cli-0.2.1}/src/desktop.js +0 -0
  48. {teams_cli-0.2.0 → teams_cli-0.2.1}/src/desktop_cache.js +0 -0
  49. {teams_cli-0.2.0 → teams_cli-0.2.1}/src/desktop_core.js +0 -0
  50. {teams_cli-0.2.0 → teams_cli-0.2.1}/src/desktop_send.js +0 -0
  51. {teams_cli-0.2.0 → teams_cli-0.2.1}/src/lib.rs +0 -0
  52. {teams_cli-0.2.0 → teams_cli-0.2.1}/src/output.rs +0 -0
  53. {teams_cli-0.2.0 → teams_cli-0.2.1}/src/render.rs +0 -0
  54. {teams_cli-0.2.0 → teams_cli-0.2.1}/src/teams_cli_alias.rs +0 -0
  55. {teams_cli-0.2.0 → teams_cli-0.2.1}/tests/connection.rs +0 -0
  56. {teams_cli-0.2.0 → teams_cli-0.2.1}/tests/desktop.rs +0 -0
  57. {teams_cli-0.2.0 → teams_cli-0.2.1}/tests/desktop_scripts.rs +0 -0
  58. {teams_cli-0.2.0 → teams_cli-0.2.1}/tests/desktop_scripts.test.cjs +0 -0
  59. {teams_cli-0.2.0 → teams_cli-0.2.1}/tests/fixtures/clispec-v0.3.json +0 -0
  60. {teams_cli-0.2.0 → teams_cli-0.2.1}/tests/package_alias.rs +0 -0
  61. {teams_cli-0.2.0 → teams_cli-0.2.1}/tests/release_smoke.rs +0 -0
  62. {teams_cli-0.2.0 → teams_cli-0.2.1}/vership.toml +0 -0
@@ -7,6 +7,30 @@ 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
+
10
34
  ## [0.2.0](https://github.com/rvben/teams-cli/compare/v0.1.13...v0.2.0) - 2026-10-14
11
35
 
12
36
  ### Added
@@ -3187,7 +3187,7 @@ dependencies = [
3187
3187
 
3188
3188
  [[package]]
3189
3189
  name = "teams-cli"
3190
- version = "0.2.0"
3190
+ version = "0.2.1"
3191
3191
  dependencies = [
3192
3192
  "assert_cmd",
3193
3193
  "base64 0.23.1",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "teams-cli"
3
- version = "0.2.0"
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"
@@ -56,10 +56,11 @@ windows-sys = { version = "0.61", features = [
56
56
  "Win32_Security",
57
57
  "Win32_Storage_Packaging_Appx",
58
58
  "Win32_System_Diagnostics_ToolHelp",
59
+ "Win32_System_Registry",
59
60
  "Win32_System_Threading",
60
61
  ] }
61
62
 
62
- [target.'cfg(target_os = "macos")'.dependencies]
63
+ [target.'cfg(unix)'.dependencies]
63
64
  libc = "0.2"
64
65
 
65
66
  [dev-dependencies]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: teams-cli
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Classifier: Programming Language :: Rust
5
5
  Classifier: Programming Language :: Python :: Implementation :: CPython
6
6
  License-File: LICENSE
@@ -81,8 +81,8 @@ for sending, reconnecting, and experimental compatibility limits.
81
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:
82
82
 
83
83
  ```text
84
- openid profile offline_access User.Read Team.ReadBasic.All
85
- 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
86
86
  ChannelMessage.Send
87
87
  ```
88
88
 
@@ -163,6 +163,8 @@ For short-lived automation, `TEAMS_ACCESS_TOKEN` overrides the stored credential
163
163
  teams list
164
164
  teams channels list TEAM_ID
165
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
166
168
  teams messages list --chat CHAT_ID
167
169
  teams auth login --channel-history # once, with administrator approval
168
170
  teams messages list --team TEAM_ID --channel CHANNEL_ID
@@ -178,7 +180,21 @@ teams config path
178
180
  teams doctor --offline
179
181
  ```
180
182
 
181
- 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.
182
198
 
183
199
  Profiles can block every remote write while keeping reads, sign-in, diagnostics, and the TUI available:
184
200
 
@@ -273,7 +289,9 @@ paste; `r` checks delivery, `s` offers an explicit second-copy confirmation, and
273
289
  `esc` keeps the draft and uncertainty together in memory. Reopening it does not
274
290
  unlock ordinary sending. Queued results are labelled queued rather than sent.
275
291
 
276
- 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,
277
295
  replies, mentions, attachments and edits return `unsupported` on desktop. The TUI
278
296
  reads chats from the same cache and sends through the same path as `messages send`.
279
297
  Existing profiles without a backend field
@@ -295,15 +313,22 @@ Added auxiliary processes are left alone. If Teams was
295
313
  restarted independently, quit and relaunch it normally, then disconnect again to
296
314
  clear the old record. No command automatically restarts Teams on exit.
297
315
 
298
- On macOS, flags apply only to the managed launch. On Windows/WSL, setup saves the
299
- previous user environment value, applies flags temporarily while Teams starts, then
300
- 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.
301
326
  Windows and WSL share a recovery record and lock. WSL finds PowerShell in its normal
302
327
  Windows system directory even when Windows PATH entries are disabled; Windows
303
328
  executable interop must still be enabled.
304
329
 
305
- If setup is interrupted, run `teams --profile local desktop recover`. It restores
306
- 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
307
332
  not claim ownership of an unrecorded Teams process. Use `desktop status` to inspect
308
333
  the endpoint and saved session. Recovery cannot close an already-running debugging
309
334
  endpoint: quit Teams and relaunch it normally if its ownership was not recorded.
@@ -403,6 +428,13 @@ A sign-in timeout leaves the visible window open; finish signing in and retry
403
428
  `desktop login`. Interrupting login preserves the browser and its saved profile.
404
429
  On WSL2, the visible step needs WSLg or another graphical display.
405
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
+
406
438
  Use `--desktop-account personal` for a personal Microsoft account. Sending still
407
439
  requires an explicit `init --allow-send` and the existing send confirmation.
408
440
  `init --no-login` only saves configuration. `desktop connect --visible` remains
@@ -413,7 +445,8 @@ The browser uses its own profile under the local data directory at
413
445
  profile. Different ports use different browser profiles. Disconnect closes this
414
446
  browser and keeps its sign-in data. To switch between visible and headless operation,
415
447
  disconnect first. `desktop recover` closes a verified managed browser or clears a
416
- stale record when no listener remains. Unverifiable processes are left untouched.
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.
417
450
 
418
451
  The CLI checks that every listener is loopback-only and belongs to the recorded
419
452
  browser process, running as you with the expected executable, debugging port and
@@ -65,8 +65,8 @@ for sending, reconnecting, and experimental compatibility limits.
65
65
  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:
66
66
 
67
67
  ```text
68
- openid profile offline_access User.Read Team.ReadBasic.All
69
- Channel.ReadBasic.All Chat.Read Chat.Create ChatMessage.Send
68
+ openid profile offline_access User.Read User.ReadBasic.All
69
+ Team.ReadBasic.All Channel.ReadBasic.All Chat.Read Chat.Create ChatMessage.Send
70
70
  ChannelMessage.Send
71
71
  ```
72
72
 
@@ -147,6 +147,8 @@ For short-lived automation, `TEAMS_ACCESS_TOKEN` overrides the stored credential
147
147
  teams list
148
148
  teams channels list TEAM_ID
149
149
  teams chats list --limit 25
150
+ teams people search "Alex" --limit 20
151
+ teams chats create --recipient PERSON_ID --yes # no message is sent
150
152
  teams messages list --chat CHAT_ID
151
153
  teams auth login --channel-history # once, with administrator approval
152
154
  teams messages list --team TEAM_ID --channel CHANNEL_ID
@@ -162,7 +164,21 @@ teams config path
162
164
  teams doctor --offline
163
165
  ```
164
166
 
165
- 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`.
167
+ 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`.
168
+
169
+ To message someone for the first time, use `people search` to find their verified
170
+ identity in the active profile. Search accepts a name or email prefix of 2–256
171
+ characters and returns at most 50 people; refine the query if `truncated` is true.
172
+ Pass the exact returned ID to `chats create`, then pass the resulting chat ID to
173
+ `messages send`. Creating a conversation reuses an existing one-to-one chat when
174
+ available and sends no message. It requires a writable profile and confirmation
175
+ (`--yes` for automation). Recipient IDs belong to their selected account/backend.
176
+ If creation is unconfirmed, inspect your chats before trying again.
177
+
178
+ Graph recipient search needs delegated `User.ReadBasic.All` consent. Existing
179
+ sign-ins can keep reading chats; run `teams auth login` again to enable directory
180
+ search. Your organization may restrict consent, directory guest access, external
181
+ recipients or chat creation. Personal accounts use the desktop backend.
166
182
 
167
183
  Profiles can block every remote write while keeping reads, sign-in, diagnostics, and the TUI available:
168
184
 
@@ -257,7 +273,9 @@ paste; `r` checks delivery, `s` offers an explicit second-copy confirmation, and
257
273
  `esc` keeps the draft and uncertainty together in memory. Reopening it does not
258
274
  unlock ordinary sending. Queued results are labelled queued rather than sent.
259
275
 
260
- Desktop cannot create new chats. Teams, channels, identity commands, subscriptions,
276
+ Desktop supports people search and one-to-one chat creation through the selected
277
+ Teams client's own session. Creation requires a profile with `--allow-send` and
278
+ sends no message. Teams, channels, identity commands, subscriptions,
261
279
  replies, mentions, attachments and edits return `unsupported` on desktop. The TUI
262
280
  reads chats from the same cache and sends through the same path as `messages send`.
263
281
  Existing profiles without a backend field
@@ -279,15 +297,22 @@ Added auxiliary processes are left alone. If Teams was
279
297
  restarted independently, quit and relaunch it normally, then disconnect again to
280
298
  clear the old record. No command automatically restarts Teams on exit.
281
299
 
282
- On macOS, flags apply only to the managed launch. On Windows/WSL, setup saves the
283
- previous user environment value, applies flags temporarily while Teams starts, then
284
- restores that value. Unrelated changes to the value are preserved and reported.
300
+ Debugging flags apply only to the managed Teams launch; other apps and the user
301
+ environment are left unchanged. On Windows/WSL, Teams starts through its
302
+ `ms-teams.exe` app execution alias, which must be enabled (Settings > Apps >
303
+ Advanced app settings > App execution aliases). Connect refuses a user-wide
304
+ `WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS` that already has debugging flags: such a
305
+ setting opens the port in every WebView2 app, for example Outlook, and the
306
+ listener is then refused because Teams does not own it. Errors name those apps.
307
+ `teams desktop clear-flags` removes only the debugging flags from that setting
308
+ after confirmation (`--yes` when scripted); quit and reopen the named apps
309
+ afterwards. The TUI offers the same cleanup.
285
310
  Windows and WSL share a recovery record and lock. WSL finds PowerShell in its normal
286
311
  Windows system directory even when Windows PATH entries are disabled; Windows
287
312
  executable interop must still be enabled.
288
313
 
289
- If setup is interrupted, run `teams --profile local desktop recover`. It restores
290
- pending settings without restarting Teams or requiring the Windows helper; it does
314
+ If setup is interrupted, run `teams --profile local desktop recover`. It clears the
315
+ interrupted record (and restores settings left by older versions) without restarting Teams or requiring the Windows helper; it does
291
316
  not claim ownership of an unrecorded Teams process. Use `desktop status` to inspect
292
317
  the endpoint and saved session. Recovery cannot close an already-running debugging
293
318
  endpoint: quit Teams and relaunch it normally if its ownership was not recorded.
@@ -387,6 +412,13 @@ A sign-in timeout leaves the visible window open; finish signing in and retry
387
412
  `desktop login`. Interrupting login preserves the browser and its saved profile.
388
413
  On WSL2, the visible step needs WSLg or another graphical display.
389
414
 
415
+ Some organisations sign in through their own identity provider (for example AD FS
416
+ with Windows sign-in) and do not let Teams renew its sign-in in a headless browser.
417
+ For work/school accounts, login therefore watches the headless browser for up to a
418
+ minute; when Teams is sent back to sign-in, the visible browser is reopened and stays
419
+ open instead. Minimise that window, but keep it open. When a headless session is sent
420
+ back to sign-in later, reads report it; run `desktop login` again.
421
+
390
422
  Use `--desktop-account personal` for a personal Microsoft account. Sending still
391
423
  requires an explicit `init --allow-send` and the existing send confirmation.
392
424
  `init --no-login` only saves configuration. `desktop connect --visible` remains
@@ -397,7 +429,8 @@ The browser uses its own profile under the local data directory at
397
429
  profile. Different ports use different browser profiles. Disconnect closes this
398
430
  browser and keeps its sign-in data. To switch between visible and headless operation,
399
431
  disconnect first. `desktop recover` closes a verified managed browser or clears a
400
- stale record when no listener remains. Unverifiable processes are left untouched.
432
+ stale record when no listener remains. A verified browser that does not exit after
433
+ closing its debugging port is stopped; unverifiable processes are left untouched.
401
434
 
402
435
  The CLI checks that every listener is loopback-only and belongs to the recorded
403
436
  browser process, running as you with the expected executable, debugging port and
@@ -23,6 +23,45 @@ do not bump again. Its tag starts the release workflow.
23
23
 
24
24
  Cargo and Python package versions are kept synchronized as one release unit.
25
25
 
26
+ ## Release 0.2.1
27
+
28
+ The version is prepared. Do not bump it again. This release adds `people search`
29
+ and `chats create`; opening a conversation does not send a message. Personal
30
+ desktop search separates exact email lookup from name search and excludes
31
+ off-network invitations. A recipient must resolve to a Teams identity before
32
+ chat creation. Graph directory search requires renewed consent to
33
+ `User.ReadBasic.All`.
34
+
35
+ The Windows helper uses protocol 4 and must match the CLI version and build.
36
+ Validate the Windows helper and WSL client together. Conversation requests and
37
+ sends with an uncertain outcome must never be automatically retried.
38
+
39
+ Teams Companion 0.1.1 downloads CLI 0.2.1. Publish and verify the CLI archives and
40
+ checksums before distributing the extension so its built-in download works.
41
+ Older stable CLIs from 0.1.11 retain reading and replies; New Message offers an
42
+ update when the required commands are missing. Vership prepares the extension's
43
+ version, while its VSIX package check validates the installable artifact.
44
+ Vership does not publish or verify a VS Code Marketplace target.
45
+
46
+ Personal-account search, existing-chat reuse and one-shot send/readback passed
47
+ on Windows and WSL; macOS reads and recipient search also passed. First-contact
48
+ creation and work/school New Message runtime coverage remain unverified. Keep
49
+ these limits separate from the earlier work-account read/send reports.
50
+
51
+ Distinguish existing-chat reuse from first-contact coverage.
52
+ For an approved, discoverable recipient with no existing conversation, resolve
53
+ their exact identity, create the chat once, send one labelled message and verify
54
+ its exact ID and body. A second successful create must return the same chat ID.
55
+ An invite-only search result does not establish a supported recipient and must
56
+ not trigger a message or invitation. Record account/backend/platform limitations
57
+ separately from successful runtime tests.
58
+
59
+ After approved publication workflows finish, verify every CLI target:
60
+
61
+ ```sh
62
+ vership verify 0.2.1 --targets tag,release,crates,pypi
63
+ ```
64
+
26
65
  ## Release 0.2.0
27
66
 
28
67
  The managed lightweight browser is an experimental personal-account option on
@@ -12,9 +12,9 @@ use crate::config::Profile;
12
12
  use crate::error::AppError;
13
13
  use crate::output::Output;
14
14
 
15
- pub const BASE_SCOPES: &str = "openid profile offline_access User.Read Team.ReadBasic.All Channel.ReadBasic.All Chat.Read Chat.Create ChatMessage.Send ChannelMessage.Send";
15
+ pub const BASE_SCOPES: &str = "openid profile offline_access User.Read User.ReadBasic.All Team.ReadBasic.All Channel.ReadBasic.All Chat.Read Chat.Create ChatMessage.Send ChannelMessage.Send";
16
16
  pub const CHANNEL_HISTORY_SCOPE: &str = "ChannelMessage.Read.All";
17
- pub const CHANNEL_HISTORY_SCOPES: &str = "openid profile offline_access User.Read Team.ReadBasic.All Channel.ReadBasic.All Chat.Read Chat.Create ChatMessage.Send ChannelMessage.Send ChannelMessage.Read.All";
17
+ pub const CHANNEL_HISTORY_SCOPES: &str = "openid profile offline_access User.Read User.ReadBasic.All Team.ReadBasic.All Channel.ReadBasic.All Chat.Read Chat.Create ChatMessage.Send ChannelMessage.Send ChannelMessage.Read.All";
18
18
  const SERVICE: &str = "teams-cli";
19
19
 
20
20
  #[derive(Debug, Clone, Serialize, Deserialize)]
@@ -397,6 +397,16 @@ mod tests {
397
397
  #[test]
398
398
  fn default_login_is_least_privilege() {
399
399
  assert!(!BASE_SCOPES.contains(CHANNEL_HISTORY_SCOPE));
400
+ assert!(
401
+ BASE_SCOPES
402
+ .split_whitespace()
403
+ .any(|scope| scope == "User.ReadBasic.All")
404
+ );
405
+ assert!(
406
+ !BASE_SCOPES
407
+ .split_whitespace()
408
+ .any(|scope| scope == "User.Read.All")
409
+ );
400
410
  assert_eq!(scopes(false), BASE_SCOPES);
401
411
  }
402
412
 
@@ -51,6 +51,19 @@ impl TeamsBackend {
51
51
  Self::Desktop(client) => client.chats(limit, cursor).await,
52
52
  }
53
53
  }
54
+ pub async fn search_people(&self, query: &str, limit: u16) -> Result<Page, AppError> {
55
+ crate::graph::validate_people_query(query, usize::from(limit))?;
56
+ match self {
57
+ Self::Graph(client) => client.search_people(query, usize::from(limit)).await,
58
+ Self::Desktop(client) => client.search_people(query, limit).await,
59
+ }
60
+ }
61
+ pub async fn create_chat(&self, recipient: &str) -> Result<Value, AppError> {
62
+ match self {
63
+ Self::Graph(client) => client.create_chat(recipient).await,
64
+ Self::Desktop(client) => client.create_chat(recipient).await,
65
+ }
66
+ }
54
67
  pub async fn messages(
55
68
  &self,
56
69
  chat: Option<&str>,
@@ -66,6 +66,11 @@ pub enum Command {
66
66
  #[command(subcommand)]
67
67
  command: ChatsCommand,
68
68
  },
69
+ /// Find people to start a conversation with
70
+ People {
71
+ #[command(subcommand)]
72
+ command: PeopleCommand,
73
+ },
69
74
  /// Read and send messages
70
75
  Messages {
71
76
  #[command(subcommand)]
@@ -187,6 +192,22 @@ pub enum ChannelsCommand {
187
192
  #[derive(Debug, Subcommand)]
188
193
  pub enum ChatsCommand {
189
194
  List(PageArgs),
195
+ /// Create or reuse a one-to-one conversation; does not send a message
196
+ Create {
197
+ /// Exact person ID returned by `people search`
198
+ #[arg(long)]
199
+ recipient: String,
200
+ },
201
+ }
202
+
203
+ #[derive(Debug, Subcommand)]
204
+ pub enum PeopleCommand {
205
+ /// Search people by name or email, without sending anything
206
+ Search {
207
+ query: String,
208
+ #[arg(long, default_value_t = 20, value_parser = clap::value_parser!(u16).range(1..=50))]
209
+ limit: u16,
210
+ },
190
211
  }
191
212
 
192
213
  #[derive(Debug, Args, Clone)]
@@ -275,6 +296,8 @@ pub enum DesktopCommand {
275
296
  Disconnect(DesktopArgs),
276
297
  /// Recover interrupted native settings or close a managed browser session
277
298
  Recover(DesktopArgs),
299
+ /// Remove remote-debugging flags from the user-wide WebView2 setting (Windows/WSL)
300
+ ClearFlags(DesktopArgs),
278
301
  }
279
302
 
280
303
  #[derive(Debug, Args)]