Skip to content

Document OAuth login methods and unattended agent patterns#242

Merged
markus-lassfolk merged 13 commits into
mainfrom
claude/agent-login-flows-dxfk01
Jul 18, 2026
Merged

Document OAuth login methods and unattended agent patterns#242
markus-lassfolk merged 13 commits into
mainfrom
claude/agent-login-flows-dxfk01

Conversation

@markus-lassfolk

Copy link
Copy Markdown
Owner

Summary

This PR adds comprehensive documentation about authentication methods for m365-agent-cli, with a focus on clarifying the distinction between interactive device-code flow (the only built-in method) and standard OAuth alternatives for fully unattended scenarios (app-only credentials and workload identity federation).

Key Changes

  • New "Login methods" section in docs/AUTHENTICATION.md:

    • Explains the built-in device-code flow: interactive once, then non-interactive refresh token reuse
    • Documents three OAuth approaches in a comparison table: device code (built-in), app-only client credentials, and workload identity federation
    • Strongly discourages automating the device-code browser UI to fake unattended login, citing security and reliability risks (Conditional Access scoring, markup changes, credential collapse)
    • Recommends using real app-only credentials when no-human auth is required
  • New "Driving login from a script or agent" section in docs/AUTHENTICATION.md:

    • Clarifies that login is a synchronous foreground poll that must stay alive until sign-in completes or device code expires (~15 min)
    • Warns that killing the process early discards the pending session even if the browser shows success
    • Advises verifying login success with whoami and verify-token --capabilities rather than relying on wrapper output
    • Documents the auto-healing behavior of .refresh-{identity}.lock (stale lock cleanup after 120s or dead PID)
  • Updated skills/m365-agent-cli/SKILL.md:

    • Added guidance on unattended agents, reiterating that device code is the only built-in flow and must run once interactively
    • Linked to the new authentication documentation for app-only and workload identity alternatives
    • Expanded lock file documentation to clarify auto-healing and warn against manual deletion
    • Added explicit guidance for wrapper scripts: keep login alive, verify with CLI commands, not wrapper success messages

Notable Details

  • The changes emphasize security best practices: avoiding brittle browser automation, preventing Conditional Access risk escalation, and using proper credentials for unattended scenarios
  • Lock file auto-healing is now documented as a feature that requires no manual intervention
  • All guidance is grounded in the actual behavior of the device-code flow and token refresh mechanism

https://claude.ai/code/session_01XDLvytPEefgPnLm2vk7FRQ

Device code is the CLI's only built-in sign-in flow. Clarify how it fits
unattended agents (one-time interactive login, then non-interactive refresh)
and document the standard no-human alternatives (app-only client credentials,
workload identity federation) with their trade-offs, noting they are not yet
implemented in the CLI.

Add operational guidance for driving `login` from a wrapper: it is a
synchronous foreground poll that must stay alive until sign-in completes,
verification should use whoami + verify-token, and the refresh lock
auto-heals (no manual lock cleanup). Advise against scripting the
device-code browser UI to fake unattended login.

Updates docs/AUTHENTICATION.md and the bundled skills/m365-agent-cli/SKILL.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XDLvytPEefgPnLm2vk7FRQ
@github-actions

Copy link
Copy Markdown

Coverage after merging claude/agent-login-flows-dxfk01 into main will be

44.00%

Coverage Report
FileStmtsBranchesFuncsLinesUncovered Lines
src/commands
   approvals.ts34.75%100%33.33%34.80%100–106, 109, 114–115, 121–135, 141–160, 171–200, 213–241, 245–254, 258–263, 42–43, 46–47, 60–61, 66–81, 90–91, 96–99
   auto-reply.ts18.90%100%100%17.90%100–104, 106–116, 118–119, 122–125, 127–132, 134–135, 137–140, 142–143, 145–150, 152–153, 155–159, 161–181, 185–190, 192, 28, 32–40, 42–43, 46–51, 53–54, 57–66, 68–78, 80–82, 84–87, 90–95, 97–98
   bookings.ts38.87%100%9.52%40.17%100, 1000–1009, 101, 1010–1019, 102–103, 1033–1039, 104, 1040–1047, 105–106, 1062, 1076–1096, 116–128, 139–149, 160–175, 186–201, 212–227, 237–251, 262–270, 281–298, 309–323, 336–348, 360–374, 386–398, 412–432, 444–458, 471–483, 495–510, 523–537, 551–567, 581–595, 609–623, 638–649, 65, 650–653, 66, 667–669, 67, 670–679, 68, 680–687, 69–70, 701–709, 71, 710–719, 72, 720–721, 73, 736–739, 74, 740–749, 75, 750–759, 76, 760–761, 773–788, 802–816, 831, 845–865, 879–893, 908, 922–939, 94, 940–942, 956–970, 985, 99, 999
   calendar.ts27.07%100%50%26.34%100, 1006, 101, 1010–1014, 1016–1019, 1021–1033, 1035–1041, 1043–1051, 1060–1064, 1066, 1072–1077, 1079, 1093, 1106–1112, 151–170, 181–182, 184–185, 187–188, 204–212, 227–231, 234–236, 258, 260–264, 266–268, 270–274, 276–278, 280–291, 294–295, 297–301, 304–316, 321–327, 331–368, 379, 389, 39, 394–396, 399, 40, 400–401, 403–408, 41, 411–412, 414–416, 419, 42, 420–421, 424–427, 43, 430–444, 48–51, 516–519, 52, 520–521, 523, 526–529, 53, 533–539, 54, 540–549, 55, 550–559, 56, 560–569, 57, 570–579, 58, 580–589, 59, 590–591, 596–599, 60, 600–606, 608–609, 61, 610–616, 618–619, 62, 620–621, 623–628, 63, 630–631, 633, 635–639, 64, 640–649, 65, 650–659, 66, 660–662, 664, 666–667, 67, 671–679, 68, 680–689, 69, 690–699, 70, 700–709, 71, 710–719, 72, 720–729, 73, 730–739, 74, 740–749, 75, 750–759, 76, 760–769, 77, 770–782, 784–790, 792–800, 802–805, 807–809, 81, 810–812, 814–815, 817–819, 82, 820–829, 83, 830–839, 84, 840–849, 85, 850–859, 86, 860–869, 87, 870–879, 88, 880–882, 884–889, 89, 890–899, 90, 900–901, 903, 91, 913, 92, 929, 93–96, 962, 964–967, 969, 97, 970–979, 98, 980–982, 984–989, 99, 990–995, 998–999
   contacts.ts36.68%100%9.09%37.54%1000–1025, 1041–1071, 1086–1100, 1115–1129, 1144–1163, 1178–1187, 1200–1216, 1228–1250, 134–144, 158–179, 194–211, 225–243, 257–270, 310, 323–329, 336–355, 363–369, 384–399, 415–438, 453–470, 484–502, 519–536, 557–604, 627–656, 674–707, 71–72, 726–729, 73, 730–739, 74, 740–749, 75, 750–757, 76–77, 774–779, 78, 780–803, 820–851, 86–87, 870–879, 88, 880, 89, 894–899, 90, 900–909, 91, 910, 92, 924–929, 93, 930–939, 94, 940–942, 95–96, 961–969, 97, 970–974, 98–99, 991–999
   copilot.ts49.61%100%0.83%52.28%1000–1005, 1017–1020, 1034–1040, 1053–1056, 1066–1069, 1080–1085, 1098–1101, 1112–1116, 1137–1167, 1186–1216, 1232–1235, 1250–1260, 1277–1295, 1310–1319, 132, 1320, 133, 1335–1339, 134, 1340–1343, 135–136, 1360–1369, 137, 1370–1386, 1402–1409, 141, 1410–1425, 1442–1453, 1466–1477, 1490–1501, 1514–1518, 1532–1539, 154–155, 1554–1560, 1578–1581, 1591–1596, 1606–1609, 1620–1626, 1636–1641, 1651–1656, 1667–1672, 1683–1688, 1700–1716, 1728–1734, 1752–1755, 176, 1769, 177, 1770–1779, 178, 1780–1785, 179, 1798–1799, 180, 1800–1803, 181, 1815–1818, 182–183, 1830–1833, 184, 1845–1849, 185, 1850, 186, 1864–1869, 187, 1870, 188, 1886–1889, 189, 1890–1899, 190, 1900–1903, 191, 1918–1919, 192, 1920–1927, 193–194, 1940–1943, 195, 1955–1958, 196–197, 1970–1975, 198, 1989,

Drop the alternative login-method documentation (app-only client
credentials, workload identity federation); device code stays the only
supported sign-in flow for now. The device-code operational notes
(foreground poll, verification, refresh-lock auto-heal) remain.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XDLvytPEefgPnLm2vk7FRQ
@github-actions

Copy link
Copy Markdown

Coverage after merging claude/agent-login-flows-dxfk01 into main will be

44.00%

Coverage Report
FileStmtsBranchesFuncsLinesUncovered Lines
src/commands
   approvals.ts34.75%100%33.33%34.80%100–106, 109, 114–115, 121–135, 141–160, 171–200, 213–241, 245–254, 258–263, 42–43, 46–47, 60–61, 66–81, 90–91, 96–99
   auto-reply.ts18.90%100%100%17.90%100–104, 106–116, 118–119, 122–125, 127–132, 134–135, 137–140, 142–143, 145–150, 152–153, 155–159, 161–181, 185–190, 192, 28, 32–40, 42–43, 46–51, 53–54, 57–66, 68–78, 80–82, 84–87, 90–95, 97–98
   bookings.ts38.87%100%9.52%40.17%100, 1000–1009, 101, 1010–1019, 102–103, 1033–1039, 104, 1040–1047, 105–106, 1062, 1076–1096, 116–128, 139–149, 160–175, 186–201, 212–227, 237–251, 262–270, 281–298, 309–323, 336–348, 360–374, 386–398, 412–432, 444–458, 471–483, 495–510, 523–537, 551–567, 581–595, 609–623, 638–649, 65, 650–653, 66, 667–669, 67, 670–679, 68, 680–687, 69–70, 701–709, 71, 710–719, 72, 720–721, 73, 736–739, 74, 740–749, 75, 750–759, 76, 760–761, 773–788, 802–816, 831, 845–865, 879–893, 908, 922–939, 94, 940–942, 956–970, 985, 99, 999
   calendar.ts27.07%100%50%26.34%100, 1006, 101, 1010–1014, 1016–1019, 1021–1033, 1035–1041, 1043–1051, 1060–1064, 1066, 1072–1077, 1079, 1093, 1106–1112, 151–170, 181–182, 184–185, 187–188, 204–212, 227–231, 234–236, 258, 260–264, 266–268, 270–274, 276–278, 280–291, 294–295, 297–301, 304–316, 321–327, 331–368, 379, 389, 39, 394–396, 399, 40, 400–401, 403–408, 41, 411–412, 414–416, 419, 42, 420–421, 424–427, 43, 430–444, 48–51, 516–519, 52, 520–521, 523, 526–529, 53, 533–539, 54, 540–549, 55, 550–559, 56, 560–569, 57, 570–579, 58, 580–589, 59, 590–591, 596–599, 60, 600–606, 608–609, 61, 610–616, 618–619, 62, 620–621, 623–628, 63, 630–631, 633, 635–639, 64, 640–649, 65, 650–659, 66, 660–662, 664, 666–667, 67, 671–679, 68, 680–689, 69, 690–699, 70, 700–709, 71, 710–719, 72, 720–729, 73, 730–739, 74, 740–749, 75, 750–759, 76, 760–769, 77, 770–782, 784–790, 792–800, 802–805, 807–809, 81, 810–812, 814–815, 817–819, 82, 820–829, 83, 830–839, 84, 840–849, 85, 850–859, 86, 860–869, 87, 870–879, 88, 880–882, 884–889, 89, 890–899, 90, 900–901, 903, 91, 913, 92, 929, 93–96, 962, 964–967, 969, 97, 970–979, 98, 980–982, 984–989, 99, 990–995, 998–999
   contacts.ts36.68%100%9.09%37.54%1000–1025, 1041–1071, 1086–1100, 1115–1129, 1144–1163, 1178–1187, 1200–1216, 1228–1250, 134–144, 158–179, 194–211, 225–243, 257–270, 310, 323–329, 336–355, 363–369, 384–399, 415–438, 453–470, 484–502, 519–536, 557–604, 627–656, 674–707, 71–72, 726–729, 73, 730–739, 74, 740–749, 75, 750–757, 76–77, 774–779, 78, 780–803, 820–851, 86–87, 870–879, 88, 880, 89, 894–899, 90, 900–909, 91, 910, 92, 924–929, 93, 930–939, 94, 940–942, 95–96, 961–969, 97, 970–974, 98–99, 991–999
   copilot.ts49.61%100%0.83%52.28%1000–1005, 1017–1020, 1034–1040, 1053–1056, 1066–1069, 1080–1085, 1098–1101, 1112–1116, 1137–1167, 1186–1216, 1232–1235, 1250–1260, 1277–1295, 1310–1319, 132, 1320, 133, 1335–1339, 134, 1340–1343, 135–136, 1360–1369, 137, 1370–1386, 1402–1409, 141, 1410–1425, 1442–1453, 1466–1477, 1490–1501, 1514–1518, 1532–1539, 154–155, 1554–1560, 1578–1581, 1591–1596, 1606–1609, 1620–1626, 1636–1641, 1651–1656, 1667–1672, 1683–1688, 1700–1716, 1728–1734, 1752–1755, 176, 1769, 177, 1770–1779, 178, 1780–1785, 179, 1798–1799, 180, 1800–1803, 181, 1815–1818, 182–183, 1830–1833, 184, 1845–1849, 185, 1850, 186, 1864–1869, 187, 1870, 188, 1886–1889, 189, 1890–1899, 190, 1900–1903, 191, 1918–1919, 192, 1920–1927, 193–194, 1940–1943, 195, 1955–1958, 196–197, 1970–1975, 198, 1989,

… example

Make it easier to automate the device-code sign-in for unattended agents,
while keeping device code as the only auth flow.

- login: add `--json` to emit newline-delimited JSON events (device_code,
  authenticated, complete, error) on stdout so a wrapper can capture the user
  code / verification URL instead of scraping log text; human-readable text
  moves to stderr. Non-interactive (errors instead of prompting when
  EWS_CLIENT_ID is missing). Default (non-json) output is unchanged.
- docs: add UNATTENDED_LOGIN.md — how to complete the interactive device-code
  step with a headless browser + TOTP, the security tradeoffs, the Microsoft
  page sequence, the pitfalls, and hygiene rules. Linked from AUTHENTICATION.md
  and the bundled skill.
- examples/unattended-login: adaptable, secret-free reference scripts
  (Playwright automation + an orchestration wrapper) to copy and adapt.
- biome: exclude examples/ (reference code users copy, not repo source).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XDLvytPEefgPnLm2vk7FRQ
@github-actions

Copy link
Copy Markdown

Coverage after merging claude/agent-login-flows-dxfk01 into main will be

43.96%

Coverage Report
FileStmtsBranchesFuncsLinesUncovered Lines
src/commands
   approvals.ts34.75%100%33.33%34.80%100–106, 109, 114–115, 121–135, 141–160, 171–200, 213–241, 245–254, 258–263, 42–43, 46–47, 60–61, 66–81, 90–91, 96–99
   auto-reply.ts18.90%100%100%17.90%100–104, 106–116, 118–119, 122–125, 127–132, 134–135, 137–140, 142–143, 145–150, 152–153, 155–159, 161–181, 185–190, 192, 28, 32–40, 42–43, 46–51, 53–54, 57–66, 68–78, 80–82, 84–87, 90–95, 97–98
   bookings.ts38.87%100%9.52%40.17%100, 1000–1009, 101, 1010–1019, 102–103, 1033–1039, 104, 1040–1047, 105–106, 1062, 1076–1096, 116–128, 139–149, 160–175, 186–201, 212–227, 237–251, 262–270, 281–298, 309–323, 336–348, 360–374, 386–398, 412–432, 444–458, 471–483, 495–510, 523–537, 551–567, 581–595, 609–623, 638–649, 65, 650–653, 66, 667–669, 67, 670–679, 68, 680–687, 69–70, 701–709, 71, 710–719, 72, 720–721, 73, 736–739, 74, 740–749, 75, 750–759, 76, 760–761, 773–788, 802–816, 831, 845–865, 879–893, 908, 922–939, 94, 940–942, 956–970, 985, 99, 999
   calendar.ts27.07%100%50%26.34%100, 1006, 101, 1010–1014, 1016–1019, 1021–1033, 1035–1041, 1043–1051, 1060–1064, 1066, 1072–1077, 1079, 1093, 1106–1112, 151–170, 181–182, 184–185, 187–188, 204–212, 227–231, 234–236, 258, 260–264, 266–268, 270–274, 276–278, 280–291, 294–295, 297–301, 304–316, 321–327, 331–368, 379, 389, 39, 394–396, 399, 40, 400–401, 403–408, 41, 411–412, 414–416, 419, 42, 420–421, 424–427, 43, 430–444, 48–51, 516–519, 52, 520–521, 523, 526–529, 53, 533–539, 54, 540–549, 55, 550–559, 56, 560–569, 57, 570–579, 58, 580–589, 59, 590–591, 596–599, 60, 600–606, 608–609, 61, 610–616, 618–619, 62, 620–621, 623–628, 63, 630–631, 633, 635–639, 64, 640–649, 65, 650–659, 66, 660–662, 664, 666–667, 67, 671–679, 68, 680–689, 69, 690–699, 70, 700–709, 71, 710–719, 72, 720–729, 73, 730–739, 74, 740–749, 75, 750–759, 76, 760–769, 77, 770–782, 784–790, 792–800, 802–805, 807–809, 81, 810–812, 814–815, 817–819, 82, 820–829, 83, 830–839, 84, 840–849, 85, 850–859, 86, 860–869, 87, 870–879, 88, 880–882, 884–889, 89, 890–899, 90, 900–901, 903, 91, 913, 92, 929, 93–96, 962, 964–967, 969, 97, 970–979, 98, 980–982, 984–989, 99, 990–995, 998–999
   contacts.ts36.68%100%9.09%37.54%1000–1025, 1041–1071, 1086–1100, 1115–1129, 1144–1163, 1178–1187, 1200–1216, 1228–1250, 134–144, 158–179, 194–211, 225–243, 257–270, 310, 323–329, 336–355, 363–369, 384–399, 415–438, 453–470, 484–502, 519–536, 557–604, 627–656, 674–707, 71–72, 726–729, 73, 730–739, 74, 740–749, 75, 750–757, 76–77, 774–779, 78, 780–803, 820–851, 86–87, 870–879, 88, 880, 89, 894–899, 90, 900–909, 91, 910, 92, 924–929, 93, 930–939, 94, 940–942, 95–96, 961–969, 97, 970–974, 98–99, 991–999
   copilot.ts49.61%100%0.83%52.28%1000–1005, 1017–1020, 1034–1040, 1053–1056, 1066–1069, 1080–1085, 1098–1101, 1112–1116, 1137–1167, 1186–1216, 1232–1235, 1250–1260, 1277–1295, 1310–1319, 132, 1320, 133, 1335–1339, 134, 1340–1343, 135–136, 1360–1369, 137, 1370–1386, 1402–1409, 141, 1410–1425, 1442–1453, 1466–1477, 1490–1501, 1514–1518, 1532–1539, 154–155, 1554–1560, 1578–1581, 1591–1596, 1606–1609, 1620–1626, 1636–1641, 1651–1656, 1667–1672, 1683–1688, 1700–1716, 1728–1734, 1752–1755, 176, 1769, 177, 1770–1779, 178, 1780–1785, 179, 1798–1799, 180, 1800–1803, 181, 1815–1818, 182–183, 1830–1833, 184, 1845–1849, 185, 1850, 186, 1864–1869, 187, 1870, 188, 1886–1889, 189, 1890–1899, 190, 1900–1903, 191, 1918–1919, 192, 1920–1927, 193–194, 1940–1943, 195, 1955–1958, 196–197, 1970–1975, 198, 1989,

Comment thread examples/unattended-login/device-login.mjs Fixed
CodeQL flagged logging the password / TOTP-seed lengths in the unattended-login
example as clear-text logging of sensitive data. Log a generic status line
instead — never reference a secret (or its length) in a log sink. Align the
shell wrapper and the guide's hygiene note to match.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XDLvytPEefgPnLm2vk7FRQ
@github-actions

Copy link
Copy Markdown

Coverage after merging claude/agent-login-flows-dxfk01 into main will be

43.96%

Coverage Report
FileStmtsBranchesFuncsLinesUncovered Lines
src/commands
   approvals.ts34.75%100%33.33%34.80%100–106, 109, 114–115, 121–135, 141–160, 171–200, 213–241, 245–254, 258–263, 42–43, 46–47, 60–61, 66–81, 90–91, 96–99
   auto-reply.ts18.90%100%100%17.90%100–104, 106–116, 118–119, 122–125, 127–132, 134–135, 137–140, 142–143, 145–150, 152–153, 155–159, 161–181, 185–190, 192, 28, 32–40, 42–43, 46–51, 53–54, 57–66, 68–78, 80–82, 84–87, 90–95, 97–98
   bookings.ts38.87%100%9.52%40.17%100, 1000–1009, 101, 1010–1019, 102–103, 1033–1039, 104, 1040–1047, 105–106, 1062, 1076–1096, 116–128, 139–149, 160–175, 186–201, 212–227, 237–251, 262–270, 281–298, 309–323, 336–348, 360–374, 386–398, 412–432, 444–458, 471–483, 495–510, 523–537, 551–567, 581–595, 609–623, 638–649, 65, 650–653, 66, 667–669, 67, 670–679, 68, 680–687, 69–70, 701–709, 71, 710–719, 72, 720–721, 73, 736–739, 74, 740–749, 75, 750–759, 76, 760–761, 773–788, 802–816, 831, 845–865, 879–893, 908, 922–939, 94, 940–942, 956–970, 985, 99, 999
   calendar.ts27.07%100%50%26.34%100, 1006, 101, 1010–1014, 1016–1019, 1021–1033, 1035–1041, 1043–1051, 1060–1064, 1066, 1072–1077, 1079, 1093, 1106–1112, 151–170, 181–182, 184–185, 187–188, 204–212, 227–231, 234–236, 258, 260–264, 266–268, 270–274, 276–278, 280–291, 294–295, 297–301, 304–316, 321–327, 331–368, 379, 389, 39, 394–396, 399, 40, 400–401, 403–408, 41, 411–412, 414–416, 419, 42, 420–421, 424–427, 43, 430–444, 48–51, 516–519, 52, 520–521, 523, 526–529, 53, 533–539, 54, 540–549, 55, 550–559, 56, 560–569, 57, 570–579, 58, 580–589, 59, 590–591, 596–599, 60, 600–606, 608–609, 61, 610–616, 618–619, 62, 620–621, 623–628, 63, 630–631, 633, 635–639, 64, 640–649, 65, 650–659, 66, 660–662, 664, 666–667, 67, 671–679, 68, 680–689, 69, 690–699, 70, 700–709, 71, 710–719, 72, 720–729, 73, 730–739, 74, 740–749, 75, 750–759, 76, 760–769, 77, 770–782, 784–790, 792–800, 802–805, 807–809, 81, 810–812, 814–815, 817–819, 82, 820–829, 83, 830–839, 84, 840–849, 85, 850–859, 86, 860–869, 87, 870–879, 88, 880–882, 884–889, 89, 890–899, 90, 900–901, 903, 91, 913, 92, 929, 93–96, 962, 964–967, 969, 97, 970–979, 98, 980–982, 984–989, 99, 990–995, 998–999
   contacts.ts36.68%100%9.09%37.54%1000–1025, 1041–1071, 1086–1100, 1115–1129, 1144–1163, 1178–1187, 1200–1216, 1228–1250, 134–144, 158–179, 194–211, 225–243, 257–270, 310, 323–329, 336–355, 363–369, 384–399, 415–438, 453–470, 484–502, 519–536, 557–604, 627–656, 674–707, 71–72, 726–729, 73, 730–739, 74, 740–749, 75, 750–757, 76–77, 774–779, 78, 780–803, 820–851, 86–87, 870–879, 88, 880, 89, 894–899, 90, 900–909, 91, 910, 92, 924–929, 93, 930–939, 94, 940–942, 95–96, 961–969, 97, 970–974, 98–99, 991–999
   copilot.ts49.61%100%0.83%52.28%1000–1005, 1017–1020, 1034–1040, 1053–1056, 1066–1069, 1080–1085, 1098–1101, 1112–1116, 1137–1167, 1186–1216, 1232–1235, 1250–1260, 1277–1295, 1310–1319, 132, 1320, 133, 1335–1339, 134, 1340–1343, 135–136, 1360–1369, 137, 1370–1386, 1402–1409, 141, 1410–1425, 1442–1453, 1466–1477, 1490–1501, 1514–1518, 1532–1539, 154–155, 1554–1560, 1578–1581, 1591–1596, 1606–1609, 1620–1626, 1636–1641, 1651–1656, 1667–1672, 1683–1688, 1700–1716, 1728–1734, 1752–1755, 176, 1769, 177, 1770–1779, 178, 1780–1785, 179, 1798–1799, 180, 1800–1803, 181, 1815–1818, 182–183, 1830–1833, 184, 1845–1849, 185, 1850, 186, 1864–1869, 187, 1870, 188, 1886–1889, 189, 1890–1899, 190, 1900–1903, 191, 1918–1919, 192, 1920–1927, 193–194, 1940–1943, 195, 1955–1958, 196–197, 1970–1975, 198, 1989,

Explain how to obtain the raw base32 TOTP seed the automation needs, which a
normal phone (QR) setup hides: reveal it during self-service authenticator
registration ("Can't scan image?"), or provision your own as an admin OATH
token. Add the tenant authentication-methods / Conditional Access
prerequisites and how to generate and verify codes (SHA-1 / 6 digits / 30s).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XDLvytPEefgPnLm2vk7FRQ
@github-actions

Copy link
Copy Markdown

Coverage after merging claude/agent-login-flows-dxfk01 into main will be

43.96%

Coverage Report
FileStmtsBranchesFuncsLinesUncovered Lines
src/commands
   approvals.ts34.75%100%33.33%34.80%100–106, 109, 114–115, 121–135, 141–160, 171–200, 213–241, 245–254, 258–263, 42–43, 46–47, 60–61, 66–81, 90–91, 96–99
   auto-reply.ts18.90%100%100%17.90%100–104, 106–116, 118–119, 122–125, 127–132, 134–135, 137–140, 142–143, 145–150, 152–153, 155–159, 161–181, 185–190, 192, 28, 32–40, 42–43, 46–51, 53–54, 57–66, 68–78, 80–82, 84–87, 90–95, 97–98
   bookings.ts38.87%100%9.52%40.17%100, 1000–1009, 101, 1010–1019, 102–103, 1033–1039, 104, 1040–1047, 105–106, 1062, 1076–1096, 116–128, 139–149, 160–175, 186–201, 212–227, 237–251, 262–270, 281–298, 309–323, 336–348, 360–374, 386–398, 412–432, 444–458, 471–483, 495–510, 523–537, 551–567, 581–595, 609–623, 638–649, 65, 650–653, 66, 667–669, 67, 670–679, 68, 680–687, 69–70, 701–709, 71, 710–719, 72, 720–721, 73, 736–739, 74, 740–749, 75, 750–759, 76, 760–761, 773–788, 802–816, 831, 845–865, 879–893, 908, 922–939, 94, 940–942, 956–970, 985, 99, 999
   calendar.ts27.07%100%50%26.34%100, 1006, 101, 1010–1014, 1016–1019, 1021–1033, 1035–1041, 1043–1051, 1060–1064, 1066, 1072–1077, 1079, 1093, 1106–1112, 151–170, 181–182, 184–185, 187–188, 204–212, 227–231, 234–236, 258, 260–264, 266–268, 270–274, 276–278, 280–291, 294–295, 297–301, 304–316, 321–327, 331–368, 379, 389, 39, 394–396, 399, 40, 400–401, 403–408, 41, 411–412, 414–416, 419, 42, 420–421, 424–427, 43, 430–444, 48–51, 516–519, 52, 520–521, 523, 526–529, 53, 533–539, 54, 540–549, 55, 550–559, 56, 560–569, 57, 570–579, 58, 580–589, 59, 590–591, 596–599, 60, 600–606, 608–609, 61, 610–616, 618–619, 62, 620–621, 623–628, 63, 630–631, 633, 635–639, 64, 640–649, 65, 650–659, 66, 660–662, 664, 666–667, 67, 671–679, 68, 680–689, 69, 690–699, 70, 700–709, 71, 710–719, 72, 720–729, 73, 730–739, 74, 740–749, 75, 750–759, 76, 760–769, 77, 770–782, 784–790, 792–800, 802–805, 807–809, 81, 810–812, 814–815, 817–819, 82, 820–829, 83, 830–839, 84, 840–849, 85, 850–859, 86, 860–869, 87, 870–879, 88, 880–882, 884–889, 89, 890–899, 90, 900–901, 903, 91, 913, 92, 929, 93–96, 962, 964–967, 969, 97, 970–979, 98, 980–982, 984–989, 99, 990–995, 998–999
   contacts.ts36.68%100%9.09%37.54%1000–1025, 1041–1071, 1086–1100, 1115–1129, 1144–1163, 1178–1187, 1200–1216, 1228–1250, 134–144, 158–179, 194–211, 225–243, 257–270, 310, 323–329, 336–355, 363–369, 384–399, 415–438, 453–470, 484–502, 519–536, 557–604, 627–656, 674–707, 71–72, 726–729, 73, 730–739, 74, 740–749, 75, 750–757, 76–77, 774–779, 78, 780–803, 820–851, 86–87, 870–879, 88, 880, 89, 894–899, 90, 900–909, 91, 910, 92, 924–929, 93, 930–939, 94, 940–942, 95–96, 961–969, 97, 970–974, 98–99, 991–999
   copilot.ts49.61%100%0.83%52.28%1000–1005, 1017–1020, 1034–1040, 1053–1056, 1066–1069, 1080–1085, 1098–1101, 1112–1116, 1137–1167, 1186–1216, 1232–1235, 1250–1260, 1277–1295, 1310–1319, 132, 1320, 133, 1335–1339, 134, 1340–1343, 135–136, 1360–1369, 137, 1370–1386, 1402–1409, 141, 1410–1425, 1442–1453, 1466–1477, 1490–1501, 1514–1518, 1532–1539, 154–155, 1554–1560, 1578–1581, 1591–1596, 1606–1609, 1620–1626, 1636–1641, 1651–1656, 1667–1672, 1683–1688, 1700–1716, 1728–1734, 1752–1755, 176, 1769, 177, 1770–1779, 178, 1780–1785, 179, 1798–1799, 180, 1800–1803, 181, 1815–1818, 182–183, 1830–1833, 184, 1845–1849, 185, 1850, 186, 1864–1869, 187, 1870, 188, 1886–1889, 189, 1890–1899, 190, 1900–1903, 191, 1918–1919, 192, 1920–1927, 193–194, 1940–1943, 195, 1955–1958, 196–197, 1970–1975, 198, 1989,

enroll-totp.mjs signs in with the account password, drives the Microsoft
Security info wizard down the "I want to use a different authenticator app"
branch, scrapes the base32 seed off the "Can't scan image?" screen, activates
the method with a generated code, and prints {"totp_secret","account_name"} on
stdout for the caller to store in a vault. Diagnostics stay on stderr and the
secret is never logged.

Document it in UNATTENDED_LOGIN.md ("Automated first-time TOTP enrollment"):
the two gates that must be open (password sign-in works interactively but not
via ROPC, which security defaults block; and no "require MFA to register
security info" Conditional Access policy, which would force a TAP), that this
captures Microsoft's generated seed rather than injecting one you choose, and
that the SPA selectors are a starting point that will drift.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XDLvytPEefgPnLm2vk7FRQ
@github-actions

Copy link
Copy Markdown

Coverage after merging claude/agent-login-flows-dxfk01 into main will be

43.96%

Coverage Report
FileStmtsBranchesFuncsLinesUncovered Lines
src/commands
   approvals.ts34.75%100%33.33%34.80%100–106, 109, 114–115, 121–135, 141–160, 171–200, 213–241, 245–254, 258–263, 42–43, 46–47, 60–61, 66–81, 90–91, 96–99
   auto-reply.ts18.90%100%100%17.90%100–104, 106–116, 118–119, 122–125, 127–132, 134–135, 137–140, 142–143, 145–150, 152–153, 155–159, 161–181, 185–190, 192, 28, 32–40, 42–43, 46–51, 53–54, 57–66, 68–78, 80–82, 84–87, 90–95, 97–98
   bookings.ts38.87%100%9.52%40.17%100, 1000–1009, 101, 1010–1019, 102–103, 1033–1039, 104, 1040–1047, 105–106, 1062, 1076–1096, 116–128, 139–149, 160–175, 186–201, 212–227, 237–251, 262–270, 281–298, 309–323, 336–348, 360–374, 386–398, 412–432, 444–458, 471–483, 495–510, 523–537, 551–567, 581–595, 609–623, 638–649, 65, 650–653, 66, 667–669, 67, 670–679, 68, 680–687, 69–70, 701–709, 71, 710–719, 72, 720–721, 73, 736–739, 74, 740–749, 75, 750–759, 76, 760–761, 773–788, 802–816, 831, 845–865, 879–893, 908, 922–939, 94, 940–942, 956–970, 985, 99, 999
   calendar.ts27.07%100%50%26.34%100, 1006, 101, 1010–1014, 1016–1019, 1021–1033, 1035–1041, 1043–1051, 1060–1064, 1066, 1072–1077, 1079, 1093, 1106–1112, 151–170, 181–182, 184–185, 187–188, 204–212, 227–231, 234–236, 258, 260–264, 266–268, 270–274, 276–278, 280–291, 294–295, 297–301, 304–316, 321–327, 331–368, 379, 389, 39, 394–396, 399, 40, 400–401, 403–408, 41, 411–412, 414–416, 419, 42, 420–421, 424–427, 43, 430–444, 48–51, 516–519, 52, 520–521, 523, 526–529, 53, 533–539, 54, 540–549, 55, 550–559, 56, 560–569, 57, 570–579, 58, 580–589, 59, 590–591, 596–599, 60, 600–606, 608–609, 61, 610–616, 618–619, 62, 620–621, 623–628, 63, 630–631, 633, 635–639, 64, 640–649, 65, 650–659, 66, 660–662, 664, 666–667, 67, 671–679, 68, 680–689, 69, 690–699, 70, 700–709, 71, 710–719, 72, 720–729, 73, 730–739, 74, 740–749, 75, 750–759, 76, 760–769, 77, 770–782, 784–790, 792–800, 802–805, 807–809, 81, 810–812, 814–815, 817–819, 82, 820–829, 83, 830–839, 84, 840–849, 85, 850–859, 86, 860–869, 87, 870–879, 88, 880–882, 884–889, 89, 890–899, 90, 900–901, 903, 91, 913, 92, 929, 93–96, 962, 964–967, 969, 97, 970–979, 98, 980–982, 984–989, 99, 990–995, 998–999
   contacts.ts36.68%100%9.09%37.54%1000–1025, 1041–1071, 1086–1100, 1115–1129, 1144–1163, 1178–1187, 1200–1216, 1228–1250, 134–144, 158–179, 194–211, 225–243, 257–270, 310, 323–329, 336–355, 363–369, 384–399, 415–438, 453–470, 484–502, 519–536, 557–604, 627–656, 674–707, 71–72, 726–729, 73, 730–739, 74, 740–749, 75, 750–757, 76–77, 774–779, 78, 780–803, 820–851, 86–87, 870–879, 88, 880, 89, 894–899, 90, 900–909, 91, 910, 92, 924–929, 93, 930–939, 94, 940–942, 95–96, 961–969, 97, 970–974, 98–99, 991–999
   copilot.ts49.61%100%0.83%52.28%1000–1005, 1017–1020, 1034–1040, 1053–1056, 1066–1069, 1080–1085, 1098–1101, 1112–1116, 1137–1167, 1186–1216, 1232–1235, 1250–1260, 1277–1295, 1310–1319, 132, 1320, 133, 1335–1339, 134, 1340–1343, 135–136, 1360–1369, 137, 1370–1386, 1402–1409, 141, 1410–1425, 1442–1453, 1466–1477, 1490–1501, 1514–1518, 1532–1539, 154–155, 1554–1560, 1578–1581, 1591–1596, 1606–1609, 1620–1626, 1636–1641, 1651–1656, 1667–1672, 1683–1688, 1700–1716, 1728–1734, 1752–1755, 176, 1769, 177, 1770–1779, 178, 1780–1785, 179, 1798–1799, 180, 1800–1803, 181, 1815–1818, 182–183, 1830–1833, 184, 1845–1849, 185, 1850, 186, 1864–1869, 187, 1870, 188, 1886–1889, 189, 1890–1899, 190, 1900–1903, 191, 1918–1919, 192, 1920–1927, 193–194, 1940–1943, 195, 1955–1958, 196–197, 1970–1975, 198, 1989,

enroll.sh wraps enroll-totp.mjs into a hands-off first-time bootstrap: fetch
email + password from your secret store, run the enrollment, parse and store
the returned base32 seed (piped on stdin so it never hits argv), then verify
end-to-end by running refresh-token.sh — which re-reads the stored seed and
completes a real device-code sign-in, proving both that MFA works and that the
seed round-tripped the store. Verification is skipped when the CLI isn't on the
host or ENROLL_VERIFY=0. Mirrors refresh-token.sh's fetch_secret placeholder and
no-jq node-inline parsing; document it in the README and UNATTENDED_LOGIN.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XDLvytPEefgPnLm2vk7FRQ
@github-actions

Copy link
Copy Markdown

Coverage after merging claude/agent-login-flows-dxfk01 into main will be

43.96%

Coverage Report
FileStmtsBranchesFuncsLinesUncovered Lines
src/commands
   approvals.ts34.75%100%33.33%34.80%100–106, 109, 114–115, 121–135, 141–160, 171–200, 213–241, 245–254, 258–263, 42–43, 46–47, 60–61, 66–81, 90–91, 96–99
   auto-reply.ts18.90%100%100%17.90%100–104, 106–116, 118–119, 122–125, 127–132, 134–135, 137–140, 142–143, 145–150, 152–153, 155–159, 161–181, 185–190, 192, 28, 32–40, 42–43, 46–51, 53–54, 57–66, 68–78, 80–82, 84–87, 90–95, 97–98
   bookings.ts38.87%100%9.52%40.17%100, 1000–1009, 101, 1010–1019, 102–103, 1033–1039, 104, 1040–1047, 105–106, 1062, 1076–1096, 116–128, 139–149, 160–175, 186–201, 212–227, 237–251, 262–270, 281–298, 309–323, 336–348, 360–374, 386–398, 412–432, 444–458, 471–483, 495–510, 523–537, 551–567, 581–595, 609–623, 638–649, 65, 650–653, 66, 667–669, 67, 670–679, 68, 680–687, 69–70, 701–709, 71, 710–719, 72, 720–721, 73, 736–739, 74, 740–749, 75, 750–759, 76, 760–761, 773–788, 802–816, 831, 845–865, 879–893, 908, 922–939, 94, 940–942, 956–970, 985, 99, 999
   calendar.ts27.07%100%50%26.34%100, 1006, 101, 1010–1014, 1016–1019, 1021–1033, 1035–1041, 1043–1051, 1060–1064, 1066, 1072–1077, 1079, 1093, 1106–1112, 151–170, 181–182, 184–185, 187–188, 204–212, 227–231, 234–236, 258, 260–264, 266–268, 270–274, 276–278, 280–291, 294–295, 297–301, 304–316, 321–327, 331–368, 379, 389, 39, 394–396, 399, 40, 400–401, 403–408, 41, 411–412, 414–416, 419, 42, 420–421, 424–427, 43, 430–444, 48–51, 516–519, 52, 520–521, 523, 526–529, 53, 533–539, 54, 540–549, 55, 550–559, 56, 560–569, 57, 570–579, 58, 580–589, 59, 590–591, 596–599, 60, 600–606, 608–609, 61, 610–616, 618–619, 62, 620–621, 623–628, 63, 630–631, 633, 635–639, 64, 640–649, 65, 650–659, 66, 660–662, 664, 666–667, 67, 671–679, 68, 680–689, 69, 690–699, 70, 700–709, 71, 710–719, 72, 720–729, 73, 730–739, 74, 740–749, 75, 750–759, 76, 760–769, 77, 770–782, 784–790, 792–800, 802–805, 807–809, 81, 810–812, 814–815, 817–819, 82, 820–829, 83, 830–839, 84, 840–849, 85, 850–859, 86, 860–869, 87, 870–879, 88, 880–882, 884–889, 89, 890–899, 90, 900–901, 903, 91, 913, 92, 929, 93–96, 962, 964–967, 969, 97, 970–979, 98, 980–982, 984–989, 99, 990–995, 998–999
   contacts.ts36.68%100%9.09%37.54%1000–1025, 1041–1071, 1086–1100, 1115–1129, 1144–1163, 1178–1187, 1200–1216, 1228–1250, 134–144, 158–179, 194–211, 225–243, 257–270, 310, 323–329, 336–355, 363–369, 384–399, 415–438, 453–470, 484–502, 519–536, 557–604, 627–656, 674–707, 71–72, 726–729, 73, 730–739, 74, 740–749, 75, 750–757, 76–77, 774–779, 78, 780–803, 820–851, 86–87, 870–879, 88, 880, 89, 894–899, 90, 900–909, 91, 910, 92, 924–929, 93, 930–939, 94, 940–942, 95–96, 961–969, 97, 970–974, 98–99, 991–999
   copilot.ts49.61%100%0.83%52.28%1000–1005, 1017–1020, 1034–1040, 1053–1056, 1066–1069, 1080–1085, 1098–1101, 1112–1116, 1137–1167, 1186–1216, 1232–1235, 1250–1260, 1277–1295, 1310–1319, 132, 1320, 133, 1335–1339, 134, 1340–1343, 135–136, 1360–1369, 137, 1370–1386, 1402–1409, 141, 1410–1425, 1442–1453, 1466–1477, 1490–1501, 1514–1518, 1532–1539, 154–155, 1554–1560, 1578–1581, 1591–1596, 1606–1609, 1620–1626, 1636–1641, 1651–1656, 1667–1672, 1683–1688, 1700–1716, 1728–1734, 1752–1755, 176, 1769, 177, 1770–1779, 178, 1780–1785, 179, 1798–1799, 180, 1800–1803, 181, 1815–1818, 182–183, 1830–1833, 184, 1845–1849, 185, 1850, 186, 1864–1869, 187, 1870, 188, 1886–1889, 189, 1890–1899, 190, 1900–1903, 191, 1918–1919, 192, 1920–1927, 193–194, 1940–1943, 195, 1955–1958, 196–197, 1970–1975, 198, 1989,

…rences

enroll-totp.mjs now accepts a Temporary Access Pass (M365_TAP) as an
alternative to a password (exactly one required), redeeming it in the TAP
field, and adds an explicit "confirm signed in" gate that fails fast on an
expired/invalid credential before touching the wizard. A TAP satisfies the
"require MFA to register" policy, so this covers hardened tenants and accounts
that already have MFA.

enroll.sh prefers a TAP (m365-tap) over a password for the enrollment sign-in.
The verify step still needs a first-factor password from the vault, since TOTP
is only a second factor and a TAP can't set a password; it's skipped with a
clear note when no password is present.

Reference the MFA/TOTP enrollment docs from SKILL.md and AUTHENTICATION.md, and
document the TAP path plus the password-for-refreshes caveat across
UNATTENDED_LOGIN.md and the example README.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XDLvytPEefgPnLm2vk7FRQ
@github-actions

Copy link
Copy Markdown

Coverage after merging claude/agent-login-flows-dxfk01 into main will be

43.96%

Coverage Report
FileStmtsBranchesFuncsLinesUncovered Lines
src/commands
   approvals.ts34.75%100%33.33%34.80%100–106, 109, 114–115, 121–135, 141–160, 171–200, 213–241, 245–254, 258–263, 42–43, 46–47, 60–61, 66–81, 90–91, 96–99
   auto-reply.ts18.90%100%100%17.90%100–104, 106–116, 118–119, 122–125, 127–132, 134–135, 137–140, 142–143, 145–150, 152–153, 155–159, 161–181, 185–190, 192, 28, 32–40, 42–43, 46–51, 53–54, 57–66, 68–78, 80–82, 84–87, 90–95, 97–98
   bookings.ts38.87%100%9.52%40.17%100, 1000–1009, 101, 1010–1019, 102–103, 1033–1039, 104, 1040–1047, 105–106, 1062, 1076–1096, 116–128, 139–149, 160–175, 186–201, 212–227, 237–251, 262–270, 281–298, 309–323, 336–348, 360–374, 386–398, 412–432, 444–458, 471–483, 495–510, 523–537, 551–567, 581–595, 609–623, 638–649, 65, 650–653, 66, 667–669, 67, 670–679, 68, 680–687, 69–70, 701–709, 71, 710–719, 72, 720–721, 73, 736–739, 74, 740–749, 75, 750–759, 76, 760–761, 773–788, 802–816, 831, 845–865, 879–893, 908, 922–939, 94, 940–942, 956–970, 985, 99, 999
   calendar.ts27.07%100%50%26.34%100, 1006, 101, 1010–1014, 1016–1019, 1021–1033, 1035–1041, 1043–1051, 1060–1064, 1066, 1072–1077, 1079, 1093, 1106–1112, 151–170, 181–182, 184–185, 187–188, 204–212, 227–231, 234–236, 258, 260–264, 266–268, 270–274, 276–278, 280–291, 294–295, 297–301, 304–316, 321–327, 331–368, 379, 389, 39, 394–396, 399, 40, 400–401, 403–408, 41, 411–412, 414–416, 419, 42, 420–421, 424–427, 43, 430–444, 48–51, 516–519, 52, 520–521, 523, 526–529, 53, 533–539, 54, 540–549, 55, 550–559, 56, 560–569, 57, 570–579, 58, 580–589, 59, 590–591, 596–599, 60, 600–606, 608–609, 61, 610–616, 618–619, 62, 620–621, 623–628, 63, 630–631, 633, 635–639, 64, 640–649, 65, 650–659, 66, 660–662, 664, 666–667, 67, 671–679, 68, 680–689, 69, 690–699, 70, 700–709, 71, 710–719, 72, 720–729, 73, 730–739, 74, 740–749, 75, 750–759, 76, 760–769, 77, 770–782, 784–790, 792–800, 802–805, 807–809, 81, 810–812, 814–815, 817–819, 82, 820–829, 83, 830–839, 84, 840–849, 85, 850–859, 86, 860–869, 87, 870–879, 88, 880–882, 884–889, 89, 890–899, 90, 900–901, 903, 91, 913, 92, 929, 93–96, 962, 964–967, 969, 97, 970–979, 98, 980–982, 984–989, 99, 990–995, 998–999
   contacts.ts36.68%100%9.09%37.54%1000–1025, 1041–1071, 1086–1100, 1115–1129, 1144–1163, 1178–1187, 1200–1216, 1228–1250, 134–144, 158–179, 194–211, 225–243, 257–270, 310, 323–329, 336–355, 363–369, 384–399, 415–438, 453–470, 484–502, 519–536, 557–604, 627–656, 674–707, 71–72, 726–729, 73, 730–739, 74, 740–749, 75, 750–757, 76–77, 774–779, 78, 780–803, 820–851, 86–87, 870–879, 88, 880, 89, 894–899, 90, 900–909, 91, 910, 92, 924–929, 93, 930–939, 94, 940–942, 95–96, 961–969, 97, 970–974, 98–99, 991–999
   copilot.ts49.61%100%0.83%52.28%1000–1005, 1017–1020, 1034–1040, 1053–1056, 1066–1069, 1080–1085, 1098–1101, 1112–1116, 1137–1167, 1186–1216, 1232–1235, 1250–1260, 1277–1295, 1310–1319, 132, 1320, 133, 1335–1339, 134, 1340–1343, 135–136, 1360–1369, 137, 1370–1386, 1402–1409, 141, 1410–1425, 1442–1453, 1466–1477, 1490–1501, 1514–1518, 1532–1539, 154–155, 1554–1560, 1578–1581, 1591–1596, 1606–1609, 1620–1626, 1636–1641, 1651–1656, 1667–1672, 1683–1688, 1700–1716, 1728–1734, 1752–1755, 176, 1769, 177, 1770–1779, 178, 1780–1785, 179, 1798–1799, 180, 1800–1803, 181, 1815–1818, 182–183, 1830–1833, 184, 1845–1849, 185, 1850, 186, 1864–1869, 187, 1870, 188, 1886–1889, 189, 1890–1899, 190, 1900–1903, 191, 1918–1919, 192, 1920–1927, 193–194, 1940–1943, 195, 1955–1958, 196–197, 1970–1975, 198, 1989,

…thentication section

The pointer to UNATTENDED_LOGIN.md and the enrollment examples previously lived
only in a long agent-tips bullet. Add a concise, discoverable pointer in the
top-level "Authentication and profiles" section — where an agent looks first —
covering headless device-code login and bootstrapping a software TOTP
(optionally via a Temporary Access Pass on tenants that require MFA to register),
with the reminder that a first-factor password must still exist for refreshes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XDLvytPEefgPnLm2vk7FRQ
@github-actions

Copy link
Copy Markdown

Coverage after merging claude/agent-login-flows-dxfk01 into main will be

43.96%

Coverage Report
FileStmtsBranchesFuncsLinesUncovered Lines
src/commands
   approvals.ts34.75%100%33.33%34.80%100–106, 109, 114–115, 121–135, 141–160, 171–200, 213–241, 245–254, 258–263, 42–43, 46–47, 60–61, 66–81, 90–91, 96–99
   auto-reply.ts18.90%100%100%17.90%100–104, 106–116, 118–119, 122–125, 127–132, 134–135, 137–140, 142–143, 145–150, 152–153, 155–159, 161–181, 185–190, 192, 28, 32–40, 42–43, 46–51, 53–54, 57–66, 68–78, 80–82, 84–87, 90–95, 97–98
   bookings.ts38.87%100%9.52%40.17%100, 1000–1009, 101, 1010–1019, 102–103, 1033–1039, 104, 1040–1047, 105–106, 1062, 1076–1096, 116–128, 139–149, 160–175, 186–201, 212–227, 237–251, 262–270, 281–298, 309–323, 336–348, 360–374, 386–398, 412–432, 444–458, 471–483, 495–510, 523–537, 551–567, 581–595, 609–623, 638–649, 65, 650–653, 66, 667–669, 67, 670–679, 68, 680–687, 69–70, 701–709, 71, 710–719, 72, 720–721, 73, 736–739, 74, 740–749, 75, 750–759, 76, 760–761, 773–788, 802–816, 831, 845–865, 879–893, 908, 922–939, 94, 940–942, 956–970, 985, 99, 999
   calendar.ts27.07%100%50%26.34%100, 1006, 101, 1010–1014, 1016–1019, 1021–1033, 1035–1041, 1043–1051, 1060–1064, 1066, 1072–1077, 1079, 1093, 1106–1112, 151–170, 181–182, 184–185, 187–188, 204–212, 227–231, 234–236, 258, 260–264, 266–268, 270–274, 276–278, 280–291, 294–295, 297–301, 304–316, 321–327, 331–368, 379, 389, 39, 394–396, 399, 40, 400–401, 403–408, 41, 411–412, 414–416, 419, 42, 420–421, 424–427, 43, 430–444, 48–51, 516–519, 52, 520–521, 523, 526–529, 53, 533–539, 54, 540–549, 55, 550–559, 56, 560–569, 57, 570–579, 58, 580–589, 59, 590–591, 596–599, 60, 600–606, 608–609, 61, 610–616, 618–619, 62, 620–621, 623–628, 63, 630–631, 633, 635–639, 64, 640–649, 65, 650–659, 66, 660–662, 664, 666–667, 67, 671–679, 68, 680–689, 69, 690–699, 70, 700–709, 71, 710–719, 72, 720–729, 73, 730–739, 74, 740–749, 75, 750–759, 76, 760–769, 77, 770–782, 784–790, 792–800, 802–805, 807–809, 81, 810–812, 814–815, 817–819, 82, 820–829, 83, 830–839, 84, 840–849, 85, 850–859, 86, 860–869, 87, 870–879, 88, 880–882, 884–889, 89, 890–899, 90, 900–901, 903, 91, 913, 92, 929, 93–96, 962, 964–967, 969, 97, 970–979, 98, 980–982, 984–989, 99, 990–995, 998–999
   contacts.ts36.68%100%9.09%37.54%1000–1025, 1041–1071, 1086–1100, 1115–1129, 1144–1163, 1178–1187, 1200–1216, 1228–1250, 134–144, 158–179, 194–211, 225–243, 257–270, 310, 323–329, 336–355, 363–369, 384–399, 415–438, 453–470, 484–502, 519–536, 557–604, 627–656, 674–707, 71–72, 726–729, 73, 730–739, 74, 740–749, 75, 750–757, 76–77, 774–779, 78, 780–803, 820–851, 86–87, 870–879, 88, 880, 89, 894–899, 90, 900–909, 91, 910, 92, 924–929, 93, 930–939, 94, 940–942, 95–96, 961–969, 97, 970–974, 98–99, 991–999
   copilot.ts49.61%100%0.83%52.28%1000–1005, 1017–1020, 1034–1040, 1053–1056, 1066–1069, 1080–1085, 1098–1101, 1112–1116, 1137–1167, 1186–1216, 1232–1235, 1250–1260, 1277–1295, 1310–1319, 132, 1320, 133, 1335–1339, 134, 1340–1343, 135–136, 1360–1369, 137, 1370–1386, 1402–1409, 141, 1410–1425, 1442–1453, 1466–1477, 1490–1501, 1514–1518, 1532–1539, 154–155, 1554–1560, 1578–1581, 1591–1596, 1606–1609, 1620–1626, 1636–1641, 1651–1656, 1667–1672, 1683–1688, 1700–1716, 1728–1734, 1752–1755, 176, 1769, 177, 1770–1779, 178, 1780–1785, 179, 1798–1799, 180, 1800–1803, 181, 1815–1818, 182–183, 1830–1833, 184, 1845–1849, 185, 1850, 186, 1864–1869, 187, 1870, 188, 1886–1889, 189, 1890–1899, 190, 1900–1903, 191, 1918–1919, 192, 1920–1927, 193–194, 1940–1943, 195, 1955–1958, 196–197, 1970–1975, 198, 1989,

Bump package.json to the current calendar version and sync the skill metadata
version (npm run sync-skill). Add a CHANGELOG entry for this line covering the
`login --json` unattended mode and the unattended-login guide + reference
scripts (device-login, refresh-token, TOTP/TAP enrollment).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XDLvytPEefgPnLm2vk7FRQ
@github-actions

Copy link
Copy Markdown

Coverage after merging claude/agent-login-flows-dxfk01 into main will be

43.96%

Coverage Report
FileStmtsBranchesFuncsLinesUncovered Lines
src/commands
   approvals.ts34.75%100%33.33%34.80%100–106, 109, 114–115, 121–135, 141–160, 171–200, 213–241, 245–254, 258–263, 42–43, 46–47, 60–61, 66–81, 90–91, 96–99
   auto-reply.ts18.90%100%100%17.90%100–104, 106–116, 118–119, 122–125, 127–132, 134–135, 137–140, 142–143, 145–150, 152–153, 155–159, 161–181, 185–190, 192, 28, 32–40, 42–43, 46–51, 53–54, 57–66, 68–78, 80–82, 84–87, 90–95, 97–98
   bookings.ts38.87%100%9.52%40.17%100, 1000–1009, 101, 1010–1019, 102–103, 1033–1039, 104, 1040–1047, 105–106, 1062, 1076–1096, 116–128, 139–149, 160–175, 186–201, 212–227, 237–251, 262–270, 281–298, 309–323, 336–348, 360–374, 386–398, 412–432, 444–458, 471–483, 495–510, 523–537, 551–567, 581–595, 609–623, 638–649, 65, 650–653, 66, 667–669, 67, 670–679, 68, 680–687, 69–70, 701–709, 71, 710–719, 72, 720–721, 73, 736–739, 74, 740–749, 75, 750–759, 76, 760–761, 773–788, 802–816, 831, 845–865, 879–893, 908, 922–939, 94, 940–942, 956–970, 985, 99, 999
   calendar.ts27.07%100%50%26.34%100, 1006, 101, 1010–1014, 1016–1019, 1021–1033, 1035–1041, 1043–1051, 1060–1064, 1066, 1072–1077, 1079, 1093, 1106–1112, 151–170, 181–182, 184–185, 187–188, 204–212, 227–231, 234–236, 258, 260–264, 266–268, 270–274, 276–278, 280–291, 294–295, 297–301, 304–316, 321–327, 331–368, 379, 389, 39, 394–396, 399, 40, 400–401, 403–408, 41, 411–412, 414–416, 419, 42, 420–421, 424–427, 43, 430–444, 48–51, 516–519, 52, 520–521, 523, 526–529, 53, 533–539, 54, 540–549, 55, 550–559, 56, 560–569, 57, 570–579, 58, 580–589, 59, 590–591, 596–599, 60, 600–606, 608–609, 61, 610–616, 618–619, 62, 620–621, 623–628, 63, 630–631, 633, 635–639, 64, 640–649, 65, 650–659, 66, 660–662, 664, 666–667, 67, 671–679, 68, 680–689, 69, 690–699, 70, 700–709, 71, 710–719, 72, 720–729, 73, 730–739, 74, 740–749, 75, 750–759, 76, 760–769, 77, 770–782, 784–790, 792–800, 802–805, 807–809, 81, 810–812, 814–815, 817–819, 82, 820–829, 83, 830–839, 84, 840–849, 85, 850–859, 86, 860–869, 87, 870–879, 88, 880–882, 884–889, 89, 890–899, 90, 900–901, 903, 91, 913, 92, 929, 93–96, 962, 964–967, 969, 97, 970–979, 98, 980–982, 984–989, 99, 990–995, 998–999
   contacts.ts36.68%100%9.09%37.54%1000–1025, 1041–1071, 1086–1100, 1115–1129, 1144–1163, 1178–1187, 1200–1216, 1228–1250, 134–144, 158–179, 194–211, 225–243, 257–270, 310, 323–329, 336–355, 363–369, 384–399, 415–438, 453–470, 484–502, 519–536, 557–604, 627–656, 674–707, 71–72, 726–729, 73, 730–739, 74, 740–749, 75, 750–757, 76–77, 774–779, 78, 780–803, 820–851, 86–87, 870–879, 88, 880, 89, 894–899, 90, 900–909, 91, 910, 92, 924–929, 93, 930–939, 94, 940–942, 95–96, 961–969, 97, 970–974, 98–99, 991–999
   copilot.ts49.61%100%0.83%52.28%1000–1005, 1017–1020, 1034–1040, 1053–1056, 1066–1069, 1080–1085, 1098–1101, 1112–1116, 1137–1167, 1186–1216, 1232–1235, 1250–1260, 1277–1295, 1310–1319, 132, 1320, 133, 1335–1339, 134, 1340–1343, 135–136, 1360–1369, 137, 1370–1386, 1402–1409, 141, 1410–1425, 1442–1453, 1466–1477, 1490–1501, 1514–1518, 1532–1539, 154–155, 1554–1560, 1578–1581, 1591–1596, 1606–1609, 1620–1626, 1636–1641, 1651–1656, 1667–1672, 1683–1688, 1700–1716, 1728–1734, 1752–1755, 176, 1769, 177, 1770–1779, 178, 1780–1785, 179, 1798–1799, 180, 1800–1803, 181, 1815–1818, 182–183, 1830–1833, 184, 1845–1849, 185, 1850, 186, 1864–1869, 187, 1870, 188, 1886–1889, 189, 1890–1899, 190, 1900–1903, 191, 1918–1919, 192, 1920–1927, 193–194, 1940–1943, 195, 1955–1958, 196–197, 1970–1975, 198, 1989,

…amples

refresh-token-lock.ts:
- Release the lock only if we still own it (unique per-acquisition owner token
  written as a third line; verified before unlink). Previously the finally
  block unlinked by path unconditionally, so a slow holder whose lock had been
  reclaimed as stale would delete a legitimate successor's lock and break mutual
  exclusion — the exact shared-RT double-refresh race the lock prevents. Adds a
  regression test.
- Raise DEFAULT_MAX_WAIT_MS (60s -> 180s) so it exceeds staleMs; a waiter can now
  reclaim a stale lock instead of hard-failing sibling processes before the lock
  is even eligible to be considered stale.

auth.ts / graph-auth.ts:
- Drop the dead `getUnifiedRefreshTokenFromEnv() ?? envRefreshToken`; env is read
  once at entry and does not change mid-run, so both operands were identical.

examples/unattended-login/refresh-token.sh:
- Fail fast on an emitted `error` event instead of burning the full 30s waiting
  for a device_code that will never arrive (e.g. missing EWS_CLIENT_ID).
- Make the post-login whoami/verify-token best-effort so a transient failure
  under `set -e` no longer flips an already-persisted successful login to FAILURE.

examples/unattended-login/device-login.mjs:
- Require M365_USER_CODE (unless M365_VERIFICATION_URI_COMPLETE is set) up front,
  instead of silently filling an empty code and failing opaquely later.

Full suite: 864 pass, 0 fail; typecheck and biome clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XDLvytPEefgPnLm2vk7FRQ
@github-actions

Copy link
Copy Markdown

Coverage after merging claude/agent-login-flows-dxfk01 into main will be

43.96%

Coverage Report
FileStmtsBranchesFuncsLinesUncovered Lines
src/commands
   approvals.ts34.75%100%33.33%34.80%100–106, 109, 114–115, 121–135, 141–160, 171–200, 213–241, 245–254, 258–263, 42–43, 46–47, 60–61, 66–81, 90–91, 96–99
   auto-reply.ts18.90%100%100%17.90%100–104, 106–116, 118–119, 122–125, 127–132, 134–135, 137–140, 142–143, 145–150, 152–153, 155–159, 161–181, 185–190, 192, 28, 32–40, 42–43, 46–51, 53–54, 57–66, 68–78, 80–82, 84–87, 90–95, 97–98
   bookings.ts38.87%100%9.52%40.17%100, 1000–1009, 101, 1010–1019, 102–103, 1033–1039, 104, 1040–1047, 105–106, 1062, 1076–1096, 116–128, 139–149, 160–175, 186–201, 212–227, 237–251, 262–270, 281–298, 309–323, 336–348, 360–374, 386–398, 412–432, 444–458, 471–483, 495–510, 523–537, 551–567, 581–595, 609–623, 638–649, 65, 650–653, 66, 667–669, 67, 670–679, 68, 680–687, 69–70, 701–709, 71, 710–719, 72, 720–721, 73, 736–739, 74, 740–749, 75, 750–759, 76, 760–761, 773–788, 802–816, 831, 845–865, 879–893, 908, 922–939, 94, 940–942, 956–970, 985, 99, 999
   calendar.ts27.07%100%50%26.34%100, 1006, 101, 1010–1014, 1016–1019, 1021–1033, 1035–1041, 1043–1051, 1060–1064, 1066, 1072–1077, 1079, 1093, 1106–1112, 151–170, 181–182, 184–185, 187–188, 204–212, 227–231, 234–236, 258, 260–264, 266–268, 270–274, 276–278, 280–291, 294–295, 297–301, 304–316, 321–327, 331–368, 379, 389, 39, 394–396, 399, 40, 400–401, 403–408, 41, 411–412, 414–416, 419, 42, 420–421, 424–427, 43, 430–444, 48–51, 516–519, 52, 520–521, 523, 526–529, 53, 533–539, 54, 540–549, 55, 550–559, 56, 560–569, 57, 570–579, 58, 580–589, 59, 590–591, 596–599, 60, 600–606, 608–609, 61, 610–616, 618–619, 62, 620–621, 623–628, 63, 630–631, 633, 635–639, 64, 640–649, 65, 650–659, 66, 660–662, 664, 666–667, 67, 671–679, 68, 680–689, 69, 690–699, 70, 700–709, 71, 710–719, 72, 720–729, 73, 730–739, 74, 740–749, 75, 750–759, 76, 760–769, 77, 770–782, 784–790, 792–800, 802–805, 807–809, 81, 810–812, 814–815, 817–819, 82, 820–829, 83, 830–839, 84, 840–849, 85, 850–859, 86, 860–869, 87, 870–879, 88, 880–882, 884–889, 89, 890–899, 90, 900–901, 903, 91, 913, 92, 929, 93–96, 962, 964–967, 969, 97, 970–979, 98, 980–982, 984–989, 99, 990–995, 998–999
   contacts.ts36.68%100%9.09%37.54%1000–1025, 1041–1071, 1086–1100, 1115–1129, 1144–1163, 1178–1187, 1200–1216, 1228–1250, 134–144, 158–179, 194–211, 225–243, 257–270, 310, 323–329, 336–355, 363–369, 384–399, 415–438, 453–470, 484–502, 519–536, 557–604, 627–656, 674–707, 71–72, 726–729, 73, 730–739, 74, 740–749, 75, 750–757, 76–77, 774–779, 78, 780–803, 820–851, 86–87, 870–879, 88, 880, 89, 894–899, 90, 900–909, 91, 910, 92, 924–929, 93, 930–939, 94, 940–942, 95–96, 961–969, 97, 970–974, 98–99, 991–999
   copilot.ts49.61%100%0.83%52.28%1000–1005, 1017–1020, 1034–1040, 1053–1056, 1066–1069, 1080–1085, 1098–1101, 1112–1116, 1137–1167, 1186–1216, 1232–1235, 1250–1260, 1277–1295, 1310–1319, 132, 1320, 133, 1335–1339, 134, 1340–1343, 135–136, 1360–1369, 137, 1370–1386, 1402–1409, 141, 1410–1425, 1442–1453, 1466–1477, 1490–1501, 1514–1518, 1532–1539, 154–155, 1554–1560, 1578–1581, 1591–1596, 1606–1609, 1620–1626, 1636–1641, 1651–1656, 1667–1672, 1683–1688, 1700–1716, 1728–1734, 1752–1755, 176, 1769, 177, 1770–1779, 178, 1780–1785, 179, 1798–1799, 180, 1800–1803, 181, 1815–1818, 182–183, 1830–1833, 184, 1845–1849, 185, 1850, 186, 1864–1869, 187, 1870, 188, 1886–1889, 189, 1890–1899, 190, 1900–1903, 191, 1918–1919, 192, 1920–1927, 193–194, 1940–1943, 195, 1955–1958, 196–197, 1970–1975, 198, 1989,

… dedup)

login.ts:
- emitEvent now writes JSON events synchronously to fd 1 (writeSync) instead of
  process.stdout.write. On POSIX a piped stdout is async/buffered, so an error
  event emitted immediately before process.exit(1) could be truncated and a
  piping consumer would never see it. Verified: `login --json` with no client id,
  piped, now reliably delivers the error event. Events also stay correctly ordered.
- Dedup the `humanLog` closure (previously defined identically in both
  performDeviceCodeFlow and the command action) into one makeHumanLog factory.

refresh-token-lock.ts:
- Clamp maxWaitMs up to at least staleMs (Math.max) so the "wait long enough to
  reclaim a stale lock" invariant holds even for a custom staleMs, not just the
  defaults.

Full suite: 864 pass, 0 fail; typecheck and biome clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XDLvytPEefgPnLm2vk7FRQ
@github-actions

Copy link
Copy Markdown

Coverage after merging claude/agent-login-flows-dxfk01 into main will be

43.96%

Coverage Report
FileStmtsBranchesFuncsLinesUncovered Lines
src/commands
   approvals.ts34.75%100%33.33%34.80%100–106, 109, 114–115, 121–135, 141–160, 171–200, 213–241, 245–254, 258–263, 42–43, 46–47, 60–61, 66–81, 90–91, 96–99
   auto-reply.ts18.90%100%100%17.90%100–104, 106–116, 118–119, 122–125, 127–132, 134–135, 137–140, 142–143, 145–150, 152–153, 155–159, 161–181, 185–190, 192, 28, 32–40, 42–43, 46–51, 53–54, 57–66, 68–78, 80–82, 84–87, 90–95, 97–98
   bookings.ts38.87%100%9.52%40.17%100, 1000–1009, 101, 1010–1019, 102–103, 1033–1039, 104, 1040–1047, 105–106, 1062, 1076–1096, 116–128, 139–149, 160–175, 186–201, 212–227, 237–251, 262–270, 281–298, 309–323, 336–348, 360–374, 386–398, 412–432, 444–458, 471–483, 495–510, 523–537, 551–567, 581–595, 609–623, 638–649, 65, 650–653, 66, 667–669, 67, 670–679, 68, 680–687, 69–70, 701–709, 71, 710–719, 72, 720–721, 73, 736–739, 74, 740–749, 75, 750–759, 76, 760–761, 773–788, 802–816, 831, 845–865, 879–893, 908, 922–939, 94, 940–942, 956–970, 985, 99, 999
   calendar.ts27.07%100%50%26.34%100, 1006, 101, 1010–1014, 1016–1019, 1021–1033, 1035–1041, 1043–1051, 1060–1064, 1066, 1072–1077, 1079, 1093, 1106–1112, 151–170, 181–182, 184–185, 187–188, 204–212, 227–231, 234–236, 258, 260–264, 266–268, 270–274, 276–278, 280–291, 294–295, 297–301, 304–316, 321–327, 331–368, 379, 389, 39, 394–396, 399, 40, 400–401, 403–408, 41, 411–412, 414–416, 419, 42, 420–421, 424–427, 43, 430–444, 48–51, 516–519, 52, 520–521, 523, 526–529, 53, 533–539, 54, 540–549, 55, 550–559, 56, 560–569, 57, 570–579, 58, 580–589, 59, 590–591, 596–599, 60, 600–606, 608–609, 61, 610–616, 618–619, 62, 620–621, 623–628, 63, 630–631, 633, 635–639, 64, 640–649, 65, 650–659, 66, 660–662, 664, 666–667, 67, 671–679, 68, 680–689, 69, 690–699, 70, 700–709, 71, 710–719, 72, 720–729, 73, 730–739, 74, 740–749, 75, 750–759, 76, 760–769, 77, 770–782, 784–790, 792–800, 802–805, 807–809, 81, 810–812, 814–815, 817–819, 82, 820–829, 83, 830–839, 84, 840–849, 85, 850–859, 86, 860–869, 87, 870–879, 88, 880–882, 884–889, 89, 890–899, 90, 900–901, 903, 91, 913, 92, 929, 93–96, 962, 964–967, 969, 97, 970–979, 98, 980–982, 984–989, 99, 990–995, 998–999
   contacts.ts36.68%100%9.09%37.54%1000–1025, 1041–1071, 1086–1100, 1115–1129, 1144–1163, 1178–1187, 1200–1216, 1228–1250, 134–144, 158–179, 194–211, 225–243, 257–270, 310, 323–329, 336–355, 363–369, 384–399, 415–438, 453–470, 484–502, 519–536, 557–604, 627–656, 674–707, 71–72, 726–729, 73, 730–739, 74, 740–749, 75, 750–757, 76–77, 774–779, 78, 780–803, 820–851, 86–87, 870–879, 88, 880, 89, 894–899, 90, 900–909, 91, 910, 92, 924–929, 93, 930–939, 94, 940–942, 95–96, 961–969, 97, 970–974, 98–99, 991–999
   copilot.ts49.61%100%0.83%52.28%1000–1005, 1017–1020, 1034–1040, 1053–1056, 1066–1069, 1080–1085, 1098–1101, 1112–1116, 1137–1167, 1186–1216, 1232–1235, 1250–1260, 1277–1295, 1310–1319, 132, 1320, 133, 1335–1339, 134, 1340–1343, 135–136, 1360–1369, 137, 1370–1386, 1402–1409, 141, 1410–1425, 1442–1453, 1466–1477, 1490–1501, 1514–1518, 1532–1539, 154–155, 1554–1560, 1578–1581, 1591–1596, 1606–1609, 1620–1626, 1636–1641, 1651–1656, 1667–1672, 1683–1688, 1700–1716, 1728–1734, 1752–1755, 176, 1769, 177, 1770–1779, 178, 1780–1785, 179, 1798–1799, 180, 1800–1803, 181, 1815–1818, 182–183, 1830–1833, 184, 1845–1849, 185, 1850, 186, 1864–1869, 187, 1870, 188, 1886–1889, 189, 1890–1899, 190, 1900–1903, 191, 1918–1919, 192, 1920–1927, 193–194, 1940–1943, 195, 1955–1958, 196–197, 1970–1975, 198, 1989,

Comment thread src/commands/login.ts Fixed
…ing --json flush

The previous fix switched emitEvent to fs.writeSync(1, ...) to guarantee JSON error
events flush before process.exit on a POSIX pipe. CodeQL flags fs.writeSync as a
file-system sink fed by the OAuth network response (alert #85: "network data written
to file system"), which process.stdout.write is not.

Revert emitEvent to process.stdout.write (CodeQL-clean) and add an async fatalJson()
helper for the terminal error paths: it awaits the stdout write callback (so the event
flushes before exit) with a fallback timer in case the callback never fires (EPIPE).
Non-fatal events (device_code / authenticated / complete) flush naturally as the flow
continues, so they keep using emitEvent.

Verified: `login --json` with no client id, piped, still reliably delivers the error
event. Full suite 864 pass / 0 fail; typecheck and biome clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XDLvytPEefgPnLm2vk7FRQ
@github-actions

Copy link
Copy Markdown

Coverage after merging claude/agent-login-flows-dxfk01 into main will be

43.96%

Coverage Report
FileStmtsBranchesFuncsLinesUncovered Lines
src/commands
   approvals.ts34.75%100%33.33%34.80%100–106, 109, 114–115, 121–135, 141–160, 171–200, 213–241, 245–254, 258–263, 42–43, 46–47, 60–61, 66–81, 90–91, 96–99
   auto-reply.ts18.90%100%100%17.90%100–104, 106–116, 118–119, 122–125, 127–132, 134–135, 137–140, 142–143, 145–150, 152–153, 155–159, 161–181, 185–190, 192, 28, 32–40, 42–43, 46–51, 53–54, 57–66, 68–78, 80–82, 84–87, 90–95, 97–98
   bookings.ts38.87%100%9.52%40.17%100, 1000–1009, 101, 1010–1019, 102–103, 1033–1039, 104, 1040–1047, 105–106, 1062, 1076–1096, 116–128, 139–149, 160–175, 186–201, 212–227, 237–251, 262–270, 281–298, 309–323, 336–348, 360–374, 386–398, 412–432, 444–458, 471–483, 495–510, 523–537, 551–567, 581–595, 609–623, 638–649, 65, 650–653, 66, 667–669, 67, 670–679, 68, 680–687, 69–70, 701–709, 71, 710–719, 72, 720–721, 73, 736–739, 74, 740–749, 75, 750–759, 76, 760–761, 773–788, 802–816, 831, 845–865, 879–893, 908, 922–939, 94, 940–942, 956–970, 985, 99, 999
   calendar.ts27.07%100%50%26.34%100, 1006, 101, 1010–1014, 1016–1019, 1021–1033, 1035–1041, 1043–1051, 1060–1064, 1066, 1072–1077, 1079, 1093, 1106–1112, 151–170, 181–182, 184–185, 187–188, 204–212, 227–231, 234–236, 258, 260–264, 266–268, 270–274, 276–278, 280–291, 294–295, 297–301, 304–316, 321–327, 331–368, 379, 389, 39, 394–396, 399, 40, 400–401, 403–408, 41, 411–412, 414–416, 419, 42, 420–421, 424–427, 43, 430–444, 48–51, 516–519, 52, 520–521, 523, 526–529, 53, 533–539, 54, 540–549, 55, 550–559, 56, 560–569, 57, 570–579, 58, 580–589, 59, 590–591, 596–599, 60, 600–606, 608–609, 61, 610–616, 618–619, 62, 620–621, 623–628, 63, 630–631, 633, 635–639, 64, 640–649, 65, 650–659, 66, 660–662, 664, 666–667, 67, 671–679, 68, 680–689, 69, 690–699, 70, 700–709, 71, 710–719, 72, 720–729, 73, 730–739, 74, 740–749, 75, 750–759, 76, 760–769, 77, 770–782, 784–790, 792–800, 802–805, 807–809, 81, 810–812, 814–815, 817–819, 82, 820–829, 83, 830–839, 84, 840–849, 85, 850–859, 86, 860–869, 87, 870–879, 88, 880–882, 884–889, 89, 890–899, 90, 900–901, 903, 91, 913, 92, 929, 93–96, 962, 964–967, 969, 97, 970–979, 98, 980–982, 984–989, 99, 990–995, 998–999
   contacts.ts36.68%100%9.09%37.54%1000–1025, 1041–1071, 1086–1100, 1115–1129, 1144–1163, 1178–1187, 1200–1216, 1228–1250, 134–144, 158–179, 194–211, 225–243, 257–270, 310, 323–329, 336–355, 363–369, 384–399, 415–438, 453–470, 484–502, 519–536, 557–604, 627–656, 674–707, 71–72, 726–729, 73, 730–739, 74, 740–749, 75, 750–757, 76–77, 774–779, 78, 780–803, 820–851, 86–87, 870–879, 88, 880, 89, 894–899, 90, 900–909, 91, 910, 92, 924–929, 93, 930–939, 94, 940–942, 95–96, 961–969, 97, 970–974, 98–99, 991–999
   copilot.ts49.61%100%0.83%52.28%1000–1005, 1017–1020, 1034–1040, 1053–1056, 1066–1069, 1080–1085, 1098–1101, 1112–1116, 1137–1167, 1186–1216, 1232–1235, 1250–1260, 1277–1295, 1310–1319, 132, 1320, 133, 1335–1339, 134, 1340–1343, 135–136, 1360–1369, 137, 1370–1386, 1402–1409, 141, 1410–1425, 1442–1453, 1466–1477, 1490–1501, 1514–1518, 1532–1539, 154–155, 1554–1560, 1578–1581, 1591–1596, 1606–1609, 1620–1626, 1636–1641, 1651–1656, 1667–1672, 1683–1688, 1700–1716, 1728–1734, 1752–1755, 176, 1769, 177, 1770–1779, 178, 1780–1785, 179, 1798–1799, 180, 1800–1803, 181, 1815–1818, 182–183, 1830–1833, 184, 1845–1849, 185, 1850, 186, 1864–1869, 187, 1870, 188, 1886–1889, 189, 1890–1899, 190, 1900–1903, 191, 1918–1919, 192, 1920–1927, 193–194, 1940–1943, 195, 1955–1958, 196–197, 1970–1975, 198, 1989,

@markus-lassfolk
markus-lassfolk merged commit 9bce56d into main Jul 18, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants