feat: outlook-cli — cross-platform Outlook CLI with multi-account, encrypted auth, and AI agent integration#1
Merged
Merged
Conversation
- CLI framework with Commander.js: auth, account, mail, calendar, contacts subcommands - MSAL PKCE interactive auth + device code flow for headless VMs - AES-256-GCM encrypted token cache (PBKDF2 310K iterations) - JWT scope validation: rejects tokens with Mail.Send (defense-in-depth) - Multi-account support with ~/.outlook-cli/accounts.json registry - Graph API client with 401 retry, 429 rate-limit handling, pagination - Mail: inbox, read, search, folders, draft, reply, forward, move, flag, mark-read - Calendar: today, week, range, view, list-calendars, create - Contacts: search (personal + GAL/People) - Output formatter: human-readable tables + --json for AI agents - 35 passing unit tests (crypto, token-validator, account-manager, formatter) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… OpenClaw/NanoClaw integration - docs/AZURE-SETUP.md: Step-by-step Azure App Registration guide - docs/MULTI-ACCOUNT.md: Multi-account usage and management - docs/SECURITY.md: Security model, threat model, token lifecycle - skill/SKILL.md: OpenClaw skill definition with tool descriptions - skill/NANOCLAW.md: NanoClaw container integration guide - README.md: Full usage documentation Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Token cache: encrypt/decrypt roundtrip, file updates with new salt/iv, wrong passphrase, large MSAL cache payloads - Formatter: mail list/detail, folder list, event list/detail, calendar list, contacts list, edge cases (null, empty, missing fields, truncation) - Total: 57 tests passing Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add src/input.js with loadInput(), mergeInput(), and resolveBody() utilities - Every command now accepts --input <file> to load params from JSON (use '-' for stdin) - CLI flags always override JSON values for flexibility - Add --body-file and --body-content-type to mail reply, forward, and calendar create - Auto-detect HTML content type from .html/.htm file extensions - Positional args (messageId, query, eventId) can be specified in JSON when using --input - Support recipients as arrays in JSON: to, cc, bcc, attendees - Add 24 new tests for input loading, merging, and body resolution (81 total) - Add docs/JSON-INPUT.md with complete JSON schema reference for all commands - Update README with JSON input examples, body-file docs, and stdin piping Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add --format <type> global option: text (default), json, markdown, html - Add --output <file> global option to write to file instead of stdout - Keep --json as shorthand for --format json (backward compatible) - Create src/output/markdown.js with full markdown table renderers - Create src/output/html.js with HTML table renderers (XSS-safe escaping) - Create src/output/render.js dispatcher: format selection + file writing - Refactor src/output/formatter.js: all functions return strings - Update all CLI commands (mail, calendar, contacts) to use output() helper - Confirmation prompts auto-skip for non-text formats and --output - Update docs to use 'Microsoft Entra ID' naming (formerly Azure AD) - Add direct Entra admin center link for App registrations - Add 32 new render tests covering all formats (113 total, all passing) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
All docs now recommend clone + npm install + npm link instead of global install. npm link creates a symlink (undo with npm unlink), keeping the global namespace clean during development. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add .vscode/, .idea/, .env.*, swap files to .gitignore - Remove accidentally committed .vscode/settings.json - Remove package-lock.json from tracking (regenerated by npm install) - Keep .env.example explicitly included Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Handle MSAL v5 device code callback when response fields are undefined (e.g., invalid client ID or misconfigured App Registration) - Use cancel flag instead of throwing inside callback to avoid UV_HANDLE_CLOSING - Use process.exitCode instead of process.exit() for clean async shutdown - Add actionable error messages for common auth failures: invalid_grant, AADSTS700016, consent required - Add troubleshooting section to AZURE-SETUP.md for device code issues - Migrate auth.js to import output() from render.js Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace all process.exit(1) calls in auth.js with process.exitCode = 1 to avoid libuv UV_HANDLE_CLOSING assertions when MSAL has pending handles. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
MSAL v5 swallows the HTTP error from Microsoft's device code endpoint and calls the callback with undefined values, hiding the actual error. Added a raw HTTP pre-flight check to the device code endpoint before calling MSAL. This captures and displays the exact Microsoft error (AADSTS codes) with targeted fix guidance for each failure type: - AADSTS700016: client ID not found - AADSTS700038: invalid application identifier - AADSTS7000218: public client flows not enabled - invalid_scope: missing API permissions Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When the pre-flight check detects AADSTS9002346 (personal accounts only) or AADSTS50194 (org accounts only), automatically retry with the correct authority endpoint (/consumers or /organizations) instead of failing. The corrected tenant is saved to the account config so future logins work without auto-correction. Tested: with a personal-account-only App Registration and /common default, the CLI auto-switches to /consumers and successfully obtains a device code. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…detection Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Microsoft returns opaque (non-JWT) access tokens for personal accounts. The token validator now gracefully handles these instead of throwing 'Invalid JWT format: expected 3 parts'. - decodeJwtPayload returns null for non-JWT tokens instead of throwing - validateTokenScopes accepts MSAL result objects (checks scopes array) - Graph client passes full MSAL result for scope validation - Added 5 new tests for opaque tokens and MSAL result validation (118 total) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Contact aliases: outlook-cli contacts alias set/remove/list
Aliases resolve in --to, --cc, --bcc, --attendees, and --as
- Delegate access: --as <email-or-alias> global flag
Uses /users/{email} Graph API path for delegate mailbox access
- mail send command: send existing drafts on behalf of delegates
Only available with --as flag (direct send blocked for safety)
- Graph API: all modules use client.userPath instead of /me
- Scopes: added Mail.Read.Shared, Mail.ReadWrite.Shared,
Mail.Send.Shared, Calendars.Read.Shared
- Security: Mail.Send.Shared removed from forbidden list
(it's separate from Mail.Send and requires owner permission)
- 17 new tests for aliases and delegate paths (135 total)
- New docs/DELEGATE-ACCESS.md with full usage guide
- Updated AZURE-SETUP.md, README.md, token-validator tests
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ence Add module-level docstrings, JSDoc comments, and inline comments across 16 source files. Comments focus on WHY decisions were made, ORDER of operations, Graph API endpoints, and C# porting notes. Files commented: - graph/mail.js: endpoint docs, permission separation (createDraft vs sendDraft) - graph/calendar.js: calendarView vs events, Prefer timezone header - graph/contacts.js: dual-source search strategy, deduplication logic - contacts/aliases.js: file storage design, case-insensitivity, resolve functions - accounts/manager.js: singleton pattern, accounts.json format, config dir structure - input.js: merge precedence (CLI > JSON), stdin pipe detection (isTTY) - config.js: config resolution order (defaults < env vars < config file) - output/formatter.js: format type system, truncation, backward-compat aliases - output/render.js: format selection dispatch, file output path - output/markdown.js: pipe/newline escaping strategy - output/html.js: XSS-safe escaping, raw body passthrough - cli/auth.js: authority auto-retry loop, flow selection - cli/account.js: two-step add flow (add then login) - cli/mail.js: delegate resolution, alias resolution, send safety gate - cli/calendar.js: attendee alias resolution, delegate access - cli/contacts.js: alias subcommand structure, search vs local commands Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- docs/TESTING.md: comprehensive guide for running, diagnosing, and writing tests - docs/DIRECTORY-STRUCTURE.md: full project layout and module dependency graph - package.json: add test:verbose and test:coverage npm scripts - .gitignore: add .NET build artifact patterns (csharp/**/bin/, obj/) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Watch system uses Microsoft Graph delta queries for efficient change tracking:
- src/graph/delta.js: Delta query engine with token persistence
- src/watch/watcher.js: Poll loop orchestrator with JSONL/text output
- src/cli/watch.js: CLI command (--interval, --jsonl, --no-mail, --no-calendar)
Output modes:
- text: Human-readable notifications with timestamps and icons
- jsonl: One JSON object per line for piping to AI agents
Features:
- Initial sync establishes baseline (no flood of old messages)
- Delta tokens persisted to ~/.outlook-cli/delta-{alias}.json
- Expired tokens auto-fallback to full re-sync
- Graceful shutdown on Ctrl+C (saves state)
- Heartbeat events for agent liveness monitoring
Tests: 25 new tests (delta engine + watcher + CLI integration)
Docs: WATCH-MODE.md with full usage guide and agent integration examples
Total: 160 tests, all passing
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Complete C# port of the Node.js CLI using System.CommandLine + MSAL.NET: Architecture (mirrors Node.js 1:1): - Security/CryptoService.cs: AES-256-GCM, interoperable binary format - Security/TokenValidator.cs: Forbidden scope checker (Mail.Send blocked) - Auth/MsalClientFactory.cs: MSAL PublicClientApplication factory - Auth/TokenCacheHelper.cs: Encrypted MSAL cache (same format as Node.js) - Auth/AuthFlows.cs: PKCE + device code with pre-flight diagnostics - Accounts/AccountManager.cs: Multi-account registry (accounts.json) - Contacts/AliasManager.cs: Contact aliases (aliases.json) - Graph/GraphClient.cs: HTTP client with auth, retry, rate-limit, delegation - Graph/MailService.cs: 11 mail endpoints - Graph/CalendarService.cs: 6 calendar endpoints - Graph/ContactsService.cs: Dual-source contact search - Output/OutputFormatter.cs: text/json/markdown/html formatters - Program.cs: System.CommandLine wiring for all commands Interoperability: - Same encrypted cache file format (Node.js and C# can share tokens) - Same accounts.json and aliases.json formats - Same CLI command names, options, and output formats - Same security model (FORBIDDEN_SCOPES, scope validation) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Added detailed WHY and WHAT ORDER comments to foundational modules: - bin/outlook-cli.js: Entry point explanation - src/auth/msal-client.js: Scope design, PublicClient rationale, token lifecycle - src/auth/auth-flows.js: Step-by-step flows, MSAL v5 bug workaround - src/auth/token-cache.js: MSAL ICachePlugin architecture - src/security/crypto.js: Binary format spec, PBKDF2 parameters - src/security/token-validator.js: Forbidden vs allowed scope rationale - src/graph/client.js: Delegate access mechanism, retry behavior These comments serve as the reference guide for maintaining both the Node.js and C# implementations. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Move Node.js source from src/ to src/node/ - Move C# source from csharp/OutlookCli/ to src/dotnet/ (flattened) - Move tests to test/node/<category>/, test/shared/ - Update all import paths in test files - Update bin/outlook-cli.js entry point - Update outlook-cli.sln project reference - Update .github/copilot-instructions.md for new paths - Update .gitignore for new dotnet path - All 160 tests pass, dotnet build succeeds Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…utlookCliJsonContext - AccountManager: Use OutlookCliJsonContext.Default.AccountsData for serialize/deserialize - AliasManager: Use OutlookCliJsonContext.Default.DictionaryStringString - TokenValidator: Use OutlookCliJsonContext.Default.JsonElement - GraphClient: Change body param from object? to string? bodyJson, use context for response deserialization - MailService: Build request bodies with JsonObject/JsonArray instead of anonymous types - CalendarService: Accept pre-serialized JSON string for event creation - ContactsService: Use context for Dict<string, JsonElement> serialization - OutputFormatter: Use OutlookCliJsonContext.Default.JsonElement for all formatting - AuthFlows: Use context for error response deserialization - Program.cs: Replace anonymous types with SuccessResponse DTOs and JsonObject, add typed ToJsonElement<T> helper, use NodeToElement for JsonObject conversion All JsonSerializer calls now use OutlookCliJsonContext.Default.* type info, ensuring compatibility with JsonSerializerIsReflectionEnabledByDefault=false. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- build.ps1: PowerShell 7+ script for building, testing, and publishing - Supports 'node', 'dotnet', and 'all' targets - -Publish flag for NativeAOT compilation - -Runtime flag for specific RIDs (win-x64, win-arm64, osx-arm64, linux-x64) - -TestOnly flag to skip builds - Summary table at completion showing pass/fail per step - .gitignore: Add publish/ directory for NativeAOT output binaries Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- New permissions resolver (src/node/security/permissions.js): - resolvePermissions(): walks defaults → parent chain → account overlay - +/- modifier arithmetic for allowed/forbidden scopes - send_to recipient whitelist with union inheritance - Circular parent detection - validateRecipients(): enforces send_to whitelist before Graph API calls - Updated token-validator.js: accepts optional forbiddenList parameter, backward-compatible (falls back to DEFAULT_FORBIDDEN_SCOPES) - 23 new tests covering resolver, modifiers, send_to, circular detection Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- README.md: Rewritten with dual-implementation focus, architecture diagram, security model, permissions, OpenClaw/NanoClaw integration overview - docs/src-node.md: Node.js build, run, test instructions and project structure - docs/src-dotnet.md: .NET build, NativeAOT publish, supported RIDs, build.ps1 - docs/src-tests.md: Test architecture, vitest conventions, adding new tests - docs/architecture.md: Shared config formats, Graph API, security, Claw integration - docs/usage.md: Complete command-line reference with examples Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Phase 4 — Watch Rules Engine: - schedule.js: Duration (30s/5m/1h), cron, and time-of-day schedule parsing - config.js: Watch config JSON loader with schema validation - rules.js: Condition matching engine (from, subject_contains, has_attachments, etc.) - actions.js: Sequential action executor with template substitution and shell escaping - manager.js: Multi-job orchestrator with shared GraphClients per account - Updated watch CLI with --config and --validate flags (backward compatible) - 84 new tests for schedule, config, rules, actions, and manager Phase 5 — SQLite Operation Logging: - database.js: better-sqlite3 with WAL mode, schema migrations - logger.js: Correlation ID-based operation logging (start/complete/fail) with list, search, compact, and stats queries - 26 new tests for database and logger All 293 tests pass. Existing watcher.js unchanged for backward compatibility. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
New test files (14): - Graph API: mail (48), calendar (23), contacts (16), client (23) tests - Telemetry: collector module + 29 tests - Stress fixtures: generate-mail.js, generate-calendar.js - Scale: pagination-scale (1K-10K msgs), formatter-scale (5K items) - Integrity: message-fidelity (round-trip), sqlite-integrity (10K writes) - Performance: throughput benchmarks, memory profiling - Soak: long-running-watch (stability), continuous-sync (100 cycles) Enhanced existing tests (+16): - crypto.test.js: 1MB payload, binary data, corruption, salt uniqueness - token-validator.test.js: Mail.Send rejection, custom forbidden, opaque tokens - watcher.test.js: 100-msg batch, error recovery, rapid start/stop New modules: - src/node/telemetry/collector.js: Ring buffer, SQLite persistence, snapshots Added comprehensive JSDoc comments to all test files. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ture Updated .github/copilot-instructions.md with: - Watch rules engine (multi-job, conditions, actions, channel delivery) - Configurable permissions (defaults, parent chains, send_to whitelist) - SQLite operation logging (WAL mode, correlation IDs) - Telemetry collector (ring buffer, snapshots) - Stress test tiers (scale, integrity, performance, soak) All 501 tests pass. Dotnet builds with 0 errors. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Two bugs prevented C# from reading Node.js-encrypted token caches: 1. IV size mismatch: Node.js used 16-byte IV, but .NET AesGcm only supports 12-byte (NIST SP 800-38D). Changed both to 12 bytes. Node.js decrypt has backward-compat fallback for 16-byte caches. 2. Hostname casing: Environment.MachineName uppercases on Windows (JSTALL-W5), but Node.js os.hostname() preserves original casing (JStall-W5). Switched C# to Dns.GetHostName() for matching casing. Both implementations now share the same encrypted token cache seamlessly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Step-by-step guide covering: - Azure App Registration (portal, Azure CLI, and PowerShell script) - Installing Node.js and .NET versions - Authentication (browser and device-code flows) - All common operations with examples - Multi-account setup and permission configuration - Watch mode with rules engine - OpenClaw/NanoClaw integration (Skill + Channel Factory modes) - Team rollout instructions - Troubleshooting guide Written for a broad audience — no prior Azure experience required. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- DIAGNOSTICS: Add SQLite query examples, example output, telemetry details, troubleshooting scenarios - WATCH-MODE: Add decision matrix, adaptive polling details, multi-job config examples, C# notes - self-hosting: Add short IDs section, common workflows, config resolution, expanded troubleshooting Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Split monolithic docs/usage.md into docs/usage/ directory with per-command files: - README.md: global options, short ID system, output formats, delegate access - mail.md: all 11 mail subcommands with every option, examples, workflows - calendar.md: all 6 calendar subcommands with timezone handling - contacts.md: search + alias management with examples - auth.md: login flows (browser + device code), token lifecycle, troubleshooting - account.md: multi-account management with configuration details - watch.md: all 3 modes (poll/fast-poll/webhook) with comparison table - diagnostics.md: doctor, log search/summary, upgrade, telemetry Key fixes from old docs: - Examples now use short numeric IDs (1, 2, 3) not AAMkAG... - Option names corrected: --body-content-type (not --body-type), --all (not --reply-all), --timezone (not --tz) - Short ID behavior fully documented (last-results.json, clobber problem) - doctor, log, upgrade commands documented for first time - Added mandatory doc-update rule to agents/MAKING-CHANGES.md Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Root causes fixed:
- E2E helper spawned src/node/cli/index.js (module-only) instead of
bin/outlook-cli.js (entry point), causing empty stdout
- runCliJson added --yes to ALL commands but read-only commands
(inbox, search, read) reject unknown options; --json mode alone
suppresses confirmations via needsConfirmation()
New features (both Node.js and C#):
- "me" keyword in --to/--cc/--bcc resolves to active account email
(account-context-aware, not just default account)
- mail delete command (soft-delete to Deleted Items via Graph API)
- mail move now returns {success, messageId, movedTo} instead of
raw Graph response
E2E test fixes:
- Move test uses well-known DeletedItems folder (not custom folder)
- Cleanup uses mail delete instead of raw runCli
All 16 E2E tests pass (10 lifecycle + 6 send). All 753 unit tests pass.
NativeAOT binary rebuilt and verified.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…overhaul
Major changes:
- Restructured test directories: test/node/ -> test/unit/, test/e2e/ -> test/integration/
- Added 14 integration test files (~97 tests) covering all CLI commands
- Added comprehensive JSDoc documentation to every integration test file explaining
WHY each test exists, WHAT Graph API behavior it validates, HOW to debug failures,
cross-runtime notes, and citations to Microsoft Graph API documentation
- Enhanced helpers.js with detailed documentation on rate limiting, eventual
consistency, cross-runtime testing, and debugging tips
C# fixes (discovered during real-world integration testing):
- Fixed exit code propagation: Environment.ExitCode was swallowed by InvokeAsync()
- Added doctor --json structured output with {checks: [{name, status, message}]}
- Fixed forward command 'me' resolution using ResolveRecipientToken()
- Added calendar delete command
Test infrastructure improvements:
- Added E2E_SUITE_TIMEOUT to all afterAll hooks (prevents cleanup timeouts)
- Cross-runtime assertions accept both Node.js and C# JSON shapes
- pollUntil with exponential backoff for Graph eventual consistency
- Updated package.json scripts: test:unit, test:integration, test:all
Agent docs updates:
- agents/TESTING.md: Updated test organization, added integration test table
- agents/COMMON-PITFALLS.md: Added 4 new pitfalls (#23-#26) covering cross-runtime
JSON shapes, afterAll timeouts, C# exit codes, and Graph eventual consistency
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add per-account permission support to accounts.json schema (v3): - 'mode' field: 'full' (default) or 'read-only' - 'scopes' field: custom MSAL scope list for fine-grained control Two-layer enforcement: - Layer 1 (security): per-account MSAL scopes at login - read-only accounts request only read scopes, Graph API enforces via 403 - Layer 2 (UX): CLI write guard blocks all write commands before calling Graph, with clear error message and exit code 1 New CLI commands: - account add --read-only / --scopes (both runtimes) - account set <alias> --read-only / --mode / --scopes (both runtimes) - account list shows [read-only] tag Write guard covers all 10 write commands: mail: send, draft, reply, forward, move, flag, mark-read, delete calendar: create, delete Schema migration v2->v3 adds mode:'full' to existing accounts. Integration tests: 11 tests covering add, list, write guard for all write commands, and mode change - passing on both runtimes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… format
- listMessages/searchMessages now return {messages, nextLink, count} instead of
plain arrays, enabling cursor-based pagination for large mailboxes
- Added --skip option to mail inbox and mail search in both Node.js and C#
- Fixed all unit tests expecting old array return format
- Fixed soak tests (concurrent-instance, real-api-soak) for new format
- Wired telemetry into GraphClient (graph.request/error/throttle events)
- Added telemetry show/summary/clear commands (src/node/cli/telemetry.js)
- Added --telemetry global option to CLI entry
- Fixed body-content-type auto-detection from .html file extension
- Added 5 new integration test files (33 new tests):
output-format-structure, mail-content-types, workflow-chains,
calendar-combinations, cross-runtime-parity
- Rebuilt NativeAOT binary with all C# changes
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Both Node.js and C# implementations: - mail inbox and mail search now support --page next/prev - Page state persisted in ~/.outlook-cli/page-state.json - Pagination hints shown on stderr (Page N, use --page next) - C# MailService returns (Messages, HasNextLink) tuple - NativeAOT-safe JSON serialization in PageState.cs - Node.js page-state.js module with save/get/clear Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
6 tests covering save/get/clear page state, independent cursors per command, and null nextLink (last page) scenarios. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Both runtimes: - --body-preview: show only ~255 char body preview instead of full body - --truncate <chars>: truncate body to N chars with notice - C# uses NativeAOT-safe JsonDocument.Parse/Clone pattern - Useful for large HTML emails that clutter CLI output Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- docs/usage/mail.md: Added --skip, --page, --body-preview, --truncate docs - agents/COMMON-PITFALLS.md: Added pitfalls 29-31 (return format change, Commander.js defaults, page state vs short IDs) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
New module src/node/output/html-to-text.js converts HTML email bodies to readable plain text in the terminal: - Strips style/script blocks, HTML comments - Converts p/div/br/hr to line breaks - Converts links to 'text (url)' format - Converts lists to bullet points (•) - Converts bold/italic to *text*/_text_ - Decodes HTML entities - Handles real-world Outlook HTML patterns (MsoNormal, WordSection1) Integrated into text formatter (formatter.js): - mailDetail auto-converts HTML bodies - eventDetail auto-converts HTML bodies - Falls back to plain text for non-HTML content 20 new unit tests covering: - Basic tag stripping, entity decoding - Block elements, links, lists, bold/italic - Style/script removal - Realistic email HTML and Outlook HTML patterns Total: 779 tests passing (26 new this session) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Created src/dotnet/Output/HtmlToText.cs with source-generated regexes (NativeAOT-safe, no reflection) - Integrated into TextFormatter.MailDetail and EventDetail - Handles: paragraphs, links, lists, bold/italic, entities, style/script - Both Node.js and C# now render HTML emails as clean text in CLI Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Connect telemetry to SQLite database on first command action (async context) - Flush events to DB on command completion (both success and error paths) - telemetry show/summary load persisted data from DB even without --telemetry - Pack graphMethod, command, success into metadata JSON column - Map snake_case DB columns to camelCase on read for getSummary() compat - Real-world test: inbox shows 1210ms Graph API latency, 19ms CLI overhead Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Soak tests were using loadConfig() + 'default' alias which failed when no default account exists. Now uses resolveAccount() with the test account alias from OUTLOOK_CLI_TEST_ACCOUNT env var, consistent with all other integration tests. Files fixed: - test/integration/soak/concurrent-instance.test.js - test/integration/soak/real-api-soak.test.js - test/integration/soak/watch-soak.test.js Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
New test files: - test/integration/pagination-live.test.js (9 tests) --top, --skip, --page next/prev, search pagination, page hints Verified on both Node.js and C# runtimes - test/integration/telemetry-validation.test.js (6 tests) graph.request capture, cli lifecycle events, summary stats, cross-invocation persistence, text format output Total integration tests: 156 (was 141) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
New test files: - test/integration/mail-combinations.test.js (6 tests) Multi-flag drafts, body preview/truncation, send→search→read chains, flag+read state persistence, cross-format consistency - test/integration/html-rendering.test.js (5 tests) HTML-to-text conversion, link handling, style block removal, JSON preserves HTML, plain text passthrough Both verified on Node.js and C# runtimes. Total integration tests: ~172 across 25 files. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
New pitfalls from this session: 32. Soak tests must use OUTLOOK_CLI_TEST_ACCOUNT 33. Telemetry requires SQLite for cross-invocation persistence 34. Telemetry DB snake_case vs JS camelCase mapping 35. C# regex must use source generators for NativeAOT 36. HTML-to-text conversion only in text format Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…orce file permissions - Create docs/SECURITY-DESIGN.md: 13-section enterprise security review document with STRIDE threat model, NIST/OWASP citations, and code file index - Fix docs/SECURITY.md: correct forbidden scopes (only Mail.ReadWrite.All), expand scope table from 6 to 11, document send capability, read-only mode, and configurable permissions system - Add SECURITY.md (repo root): GitHub vulnerability disclosure policy - Add chmod 600 for cache files on Unix (Node.js + C#) as defense-in-depth - Add chmod 700 for config directory on Unix (Node.js) - Both implementations: best-effort, no-op on Windows, won't crash on failure Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Create vitest.config.js with JSON reporter → test-results/ - Create scripts/test-report.js: console table, JSON, and markdown outputs with summary, per-file breakdown, slowest tests, and category counts - Add npm scripts: test:report, test:report:json, test:report:markdown, test:ci - Create test/fixtures/email-bodies/ with 5 realistic emails: newsletter.html (marketing with tables, CSS, images) corporate-reply.html (Outlook reply chain with MsoNormal classes) invoice.html (structured doc with line items, addresses, payment info) plain-text.txt (multi-paragraph professional email) unicode-intl.txt (Japanese, French, Chinese, Arabic, emoji, special chars) - Add test-results/ to .gitignore Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Graph API: listAttachments, getAttachment, addAttachment, createUploadSession - CLI commands: mail attachments, mail download-attachment - mail read --attachments flag to show attachment details - mail draft --attach to attach files to drafts (with MIME type detection) - Formatter: attachmentList table and inline display in mailDetail - C# parity: MailService attachment methods, Program.cs commands, OutputFormatter.AttachmentList with file size formatting - 16 new unit tests (10 Graph API, 6 formatter) - All 795 tests pass, NativeAOT binary verified Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…tfalls, mail attachments - Create docs/usage/errors.md with comprehensive error codes, Graph API errors, and troubleshooting - Add enterprise deployment section to self-hosting.md (secrets mgmt, monitoring, multi-instance, backup) - Add 6 new pitfalls to COMMON-PITFALLS.md (attachments, formatter, System.CommandLine, NativeAOT, docs, permissions) - Update TESTING.md with test reporting dashboard, fixtures, and npm scripts - Expand mail.md with attachment workflows, delete command, draft attachment examples - Add errors.md to usage README navigation Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…rios (104 tests) Phase 7 tests covering: - Email size spectrum: tiny to 50KB+ HTML, fixtures (newsletter, invoice, reply chain), HTML entity decoding, link extraction, formatting preservation, performance check - International content: CJK (Japanese, Chinese, Korean), RTL (Arabic, Hebrew), emoji (compound, flags, ZWJ), European accents, mixed scripts, edge cases - Error scenarios: deleteMessage unit tests, Graph API error codes (401/403/404/429), two-step operation failures, delegate access errors, ID encoding, race conditions Total tests: 899 (was 795) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…tion tests ci.yml: Runs on every push/PR - Node.js unit tests on Ubuntu + Windows (matrix) - C# debug build verification - NativeAOT publish for win-x64 and linux-x64 - Test report artifact upload and PR summary comments integration.yml: Manual dispatch + nightly schedule - Real M365 API integration tests - Supports node/dotnet/both runtime selection - Credential injection via GitHub secrets - Rate-limit-safe sequential test execution - Automatic credential cleanup Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Zero-dependency PII detection and tokenized replacement: - Email addresses, phone numbers (US/intl), credit cards (Luhn-validated), SSNs, IPv4, GitHub tokens, AWS keys, long hex strings - Deterministic tokens: **REDACTED-N:type** with in-memory-only mapping - Agent round-trip: redact → LLM response → reconstruct - Custom patterns via options, filtered type selection - Node.js: src/node/security/redactor.js (createRedactor factory) - C#: src/dotnet/Security/Redactor.cs (source-generated regex for NativeAOT) CLI integration: - --redact flag on mail read, mail inbox, mail search - JSON output includes _redactionMapping for agent workflows - 45 unit tests covering all PII types, reconstruction, edge cases Total tests: 944 (was 899) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…tion, security - Fix incorrect 'Cannot send email' claim (Mail.Send IS allowed) - Add attachment commands (list, download, attach on draft) - Add --redact PII redaction flag documentation - Add pagination flags (--top, --skip, --page) - Add diagnostics commands (doctor, telemetry, log) - Add recommended two-account pattern (read-only primary + read-write agent) - Add PII token format reference table - Update NanoClaw CLAUDE.md integration example with new commands Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Node.js: src/node/graph/drive.js + src/node/cli/drive.js (6 commands) C#: src/dotnet/Graph/DriveService.cs + Program.cs drive command group Both: Files.Read + Files.ReadWrite scopes added to MSAL clients - drive list [path] — list files/folders with pagination (--top) - drive upload <file> [remotePath] — upload files (<4MB inline) - drive download <itemId> [localPath] — download by item ID - drive search <query> — search files - drive mkdir <name> [parentPath] — create folders - drive share <itemId> — create sharing links (view/edit, org/anonymous) - GraphClient: added put() method for binary uploads, raw response for downloads - Read-only accounts: Files.Read scope only, write-guard blocks upload/mkdir/share - 22 new unit tests covering all operations, delegation, and error handling Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adding new MSAL scopes (e.g., Files.Read, Files.ReadWrite for OneDrive) invalidates existing cached tokens. MSAL's acquireTokenSilent cannot upgrade tokens silently -- users must run 'auth login' again. Changes: - Node.js: detect consent_required vs generic interaction_required - Node.js: improved error messages explain 'CLI update added new permissions' - C#: MsalUiRequiredException handler now logs cause (consent vs other) - docs/usage/errors.md: new 'Re-authentication required' section with technical explanation of why scope changes break tokens - agents/COMMON-PITFALLS.md: pitfall #43 covers the full scope migration workflow and what developers must do when adding scopes BREAKING: Users upgrading from before the OneDrive commit (9f31f2f) must run 'outlook-cli auth login --account <alias>' for each account. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Remove Files.Read/Files.ReadWrite from default MSAL scope lists (Node.js + C#) to prevent token acquisition failures when Azure App Registration doesn't have Files permissions configured - Add DRIVE_SCOPES/DriveScopes as separate opt-in scope arrays - Classify AADSTS70000 sub-errors: unauthorized scopes vs abuse detection - Add AUTH_SCOPES_UNAUTHORIZED and AUTH_RATE_LIMITED error codes with clear user-facing messages and remediation steps - Fix MSAL error classification ordering: check AADSTS-specific codes (MFA, password change) before generic invalid_grant catch-all - Add 10 new unit tests for MSAL error classification - Add 2 new unit tests for client error code mapping - Update docs: errors.md, SECURITY.md, self-hosting.md, COMMON-PITFALLS.md 978 tests pass, 0 failures. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
jeffstall
added a commit
that referenced
this pull request
Apr 18, 2026
- bridge-start.ps1: detect already-running bridge process and exit gracefully - bridge-start.ps1: detect port-in-use by non-bridge processes - Bridge Program.cs: catch port-in-use (AddressAlreadyInUse) with actionable message - Bridge Program.cs: catch-all for unhandled errors with friendly one-liner - Agent instructions: mandate publishing BOTH CLI and bridge binaries after every change - BUILDING.md: add bridge publish to validation sequence - MAKING-CHANGES.md: add bridge publish to both commit checklists - COMMON-PITFALLS.md: expand pitfall #1 to cover bridge binary staleness Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
outlook-cli — Cross-Platform Microsoft Outlook CLI
What this PR adds
A complete CLI tool for Microsoft Outlook via the Microsoft Graph API.
Core Features
Security Model
AI Agent Integration
--jsonoutput for structured data consumptionskill/SKILL.mdwith full tool definitionsskill/NANOCLAW.mdwith container setup guideTesting
Documentation
docs/AZURE-SETUP.md— Step-by-step Azure App Registrationdocs/MULTI-ACCOUNT.md— Multi-account usage guidedocs/SECURITY.md— Security model and threat analysisREADME.md— Complete usage documentationNext Steps (after merge)