()
| 649 | // - result write: fire-and-forget, archive latency covers the drain |
| 650 | // - 401 retry: only if first archive 401s, shares the same budget |
| 651 | async function teardown(): Promise<void> { |
| 652 | if (tornDown) return |
| 653 | tornDown = true |
| 654 | refresh.cancelAll() |
| 655 | clearTimeout(connectDeadline) |
| 656 | flushGate.drop() |
| 657 | |
| 658 | // Fire the result message before archive — transport.write() only awaits |
| 659 | // enqueue (SerialBatchEventUploader resolves once buffered, drain is |
| 660 | // async). Archiving before close() gives the uploader's drain loop a |
| 661 | // window (typical archive ≈ 100-500ms) to POST the result without an |
| 662 | // explicit sleep. close() sets closed=true which interrupts drain at the |
| 663 | // next while-check, so close-before-archive drops the result. |
| 664 | transport.reportState('idle') |
| 665 | void transport.write(makeResultMessage(sessionId)) |
| 666 | |
| 667 | let token = runtimeAuth.getAccessToken() |
| 668 | let status = await archiveSession( |
| 669 | sessionId, |
| 670 | baseUrl, |
| 671 | token, |
| 672 | runtimeAuth.orgUUID, |
| 673 | cfg.teardown_archive_timeout_ms, |
| 674 | ) |
| 675 | |
| 676 | // Token is usually fresh (refresh scheduler runs 5min before expiry) but |
| 677 | // laptop-wake past the refresh window leaves getAccessToken() returning a |
| 678 | // stale string. Retry once on 401 — onAuth401 (= handleOAuth401Error) |
| 679 | // clears keychain cache + force-refreshes. No proactive refresh on the |
| 680 | // happy path: the runtime refresh path force-refreshes even valid tokens, |
| 681 | // which would waste budget 99% of the time. try/catch mirrors |
| 682 | // recoverFromAuthFailure: keychain reads can throw (macOS locked after |
| 683 | // wake); an uncaught throw here would skip transport.close + telemetry. |
| 684 | if (status === 401) { |
| 685 | try { |
| 686 | token = await runtimeAuth.refreshAccessToken() |
| 687 | status = await archiveSession( |
| 688 | sessionId, |
| 689 | baseUrl, |
| 690 | token, |
| 691 | runtimeAuth.orgUUID, |
| 692 | cfg.teardown_archive_timeout_ms, |
| 693 | ) |
| 694 | } catch (err) { |
| 695 | logForDebugging( |
| 696 | `[remote-bridge] Teardown 401 retry threw: ${errorMessage(err)}`, |
| 697 | { level: 'error' }, |
| 698 | ) |
| 699 | } |
| 700 | } |
| 701 | |
| 702 | transport.close() |
| 703 | |
| 704 | const archiveStatus: ArchiveTelemetryStatus = |
| 705 | status === 'no_token' |
| 706 | ? 'skipped_no_token' |
| 707 | : status === 'timeout' || status === 'error' |
| 708 | ? 'network_error' |
nothing calls this directly
no test coverage detected