{"_id":"@biks2013/outlook-cli","name":"@biks2013/outlook-cli","dist-tags":{"latest":"3.1.1"},"versions":{"3.1.1":{"name":"@biks2013/outlook-cli","version":"3.1.1","description":"CLI for Outlook web — authenticate via Playwright, manage inbox, calendar, folders, and attachments through the Outlook REST v2.0 API.","main":"dist/cli.js","bin":{"outlook-cli":"dist/cli.js"},"scripts":{"build":"tsc","postbuild":"chmod +x dist/cli.js","cli":"ts-node src/cli.ts","test":"vitest run","test:watch":"vitest","prepublishOnly":"npm run build && npm test"},"keywords":["outlook","microsoft","email","cli","playwright","calendar","automation"],"author":{"name":"Giorgos Marinos"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/BikS2013/outlook-tool.git"},"bugs":{"url":"https://github.com/BikS2013/outlook-tool/issues"},"homepage":"https://github.com/BikS2013/outlook-tool#readme","engines":{"node":">=20"},"publishConfig":{"access":"public"},"type":"commonjs","devDependencies":{"@playwright/test":"^1.59.1","@types/node":"^25.6.0","ts-node":"^10.9.2","typescript":"^6.0.3","vitest":"^4.1.4"},"dependencies":{"commander":"^14.0.3","dotenv":"^17.0.0","playwright":"^1.59.1"},"gitHead":"1fee061302646d2b454eabc5442e7130fd442220","_id":"@biks2013/outlook-cli@3.1.1","_nodeVersion":"25.9.0","_npmVersion":"11.12.1","dist":{"integrity":"sha512-G6OeoZGJLvEdl8DEIZl39hQFz21upASwF74KbJ+GKHDcIiVumKn1WVVYVvwead37h052TopyrQj4GUo6ysawpw==","shasum":"b855404d8b974f6efcdf8d0d01279e8ca640304d","tarball":"https://registry.npmjs.org/@biks2013/outlook-cli/-/outlook-cli-3.1.1.tgz","fileCount":63,"unpackedSize":386377,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIE7LktcLtMIGKpie3+rDXhnNeN8Nr14kFHg+Rd/Dc7Y8AiEAia+XEVb8Ytt63jUg6p9X+XNN0Z9phI0+Vbl2S8FU/aQ="}]},"_npmUser":{"name":"giorgos-marinos","email":"giorgos.marinos@gmail.com"},"directories":{},"maintainers":[{"name":"giorgos-marinos","email":"giorgos.marinos@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/outlook-cli_3.1.1_1777575382454_0.7886524455890276"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-30T18:56:22.309Z","3.1.1":"2026-04-30T18:56:22.657Z","modified":"2026-04-30T18:56:22.893Z"},"maintainers":[{"name":"giorgos-marinos","email":"giorgos.marinos@gmail.com"}],"description":"CLI for Outlook web — authenticate via Playwright, manage inbox, calendar, folders, and attachments through the Outlook REST v2.0 API.","homepage":"https://github.com/BikS2013/outlook-tool#readme","keywords":["outlook","microsoft","email","cli","playwright","calendar","automation"],"repository":{"type":"git","url":"git+https://github.com/BikS2013/outlook-tool.git"},"author":{"name":"Giorgos Marinos"},"bugs":{"url":"https://github.com/BikS2013/outlook-tool/issues"},"license":"MIT","readme":"# outlook-cli\n\nA TypeScript command-line tool for reading Outlook mail and calendar, and\nmanaging mail folders, by reusing an interactively captured Outlook-web\nsession — no app registration, no tenant admin, no API keys.\n\n---\n\n## Rationale\n\nThe usual ways to script against an Outlook/Exchange mailbox are all heavy:\n\n- **Microsoft Graph** requires an app registration, admin consent, OAuth client\n  credentials, a tenant willing to grant `Mail.*` / `Calendars.*` scopes, and a\n  working redirect-URI plumbing. Fine for a service; overkill for one person who\n  just wants to read their own inbox from a script.\n- **EWS / MAPI** is deprecated, on-prem-flavored, and Windows-centric.\n- **IMAP/SMTP** is usually disabled in modern tenants.\n\nFor a single user who is already allowed to sign in to `outlook.office.com` in\na browser, there is a much shorter path: log in **once** in a headed Chrome\nwindow, grab the Bearer token and cookies that the web UI itself uses, cache\nthem securely, and drive the same **`https://outlook.office.com/api/v2.0/...`**\nREST surface that Outlook-web talks to.\n\nThat is what this tool does.\n\n### What it gives you\n\n- `login` — a one-shot headed Playwright Chrome window that you use to sign in\n  normally (including MFA / conditional access). The tool snoops the first\n  outbound request bearing `Authorization: Bearer`, extracts the token +\n  cookies, and writes them atomically to `$HOME/.tool-agents/outlook-cli/session.json`\n  (mode `0600`, parent dir `0700`).\n- `auth-check` — non-interactive verification that the cached session is still\n  accepted.\n- `list-mail`, `get-mail`, `download-attachments` — inbox + message access,\n  including saving attachments to a directory.\n- `list-calendar`, `get-event` — calendar window listing and single-event retrieval.\n- `list-folders`, `find-folder`, `create-folder`, `move-mail` — full folder\n  management: list, resolve by name/path/id, create (idempotently), and move\n  messages across folders.\n- Every subcommand re-uses the cached session. When the token expires the tool\n  auto-reopens the Playwright window for a silent re-auth (unless\n  `--no-auto-reauth` is passed, which makes expired-session failures hard).\n\n### What it deliberately does **not** do\n\n- It does not send mail, delete messages, or modify calendar events. It is a\n  **read + organize** surface, not a full client.\n- It does not persist anything upstream. The session file is local-only.\n- It does not bypass conditional access, MFA, or any tenant policy — you log in\n  exactly the way the browser would.\n\n### Security posture in one line\n\nThe session file contains a live Bearer token + cookies. It is written atomically\nunder a 0700 directory with mode 0600, is never printed or logged (body-snippet\nredaction runs on every error path), and is `.gitignore`d alongside the\nPlaywright profile dir.\n\n---\n\n## Prerequisites\n\n### Runtime environment\n\n- **Node.js 20 LTS or newer** (tested on 22). Older Node versions lack the\n  global `fetch` and other APIs the tool relies on.\n- **npm 10+** (bundled with Node 20+). The repo ships a `package-lock.json`; no\n  yarn/pnpm support is assumed.\n- **git** — only needed to clone the repo.\n\n### Browser (critical)\n\n- A **real Google Chrome or Microsoft Edge installation on your machine**.\n  Playwright launches your *installed* browser via the `channel` mechanism\n  (`chromium.launchPersistentContext({ channel: ... })`), it does **not**\n  download its own Chromium build. You therefore do **not** need to run\n  `npx playwright install`.\n- Accepted channel values: `chrome` (default), `chrome-beta`, `chrome-dev`,\n  `msedge`, `msedge-beta`. Whichever you pick must actually be installed and\n  locatable by Playwright.\n- macOS, Linux, and Windows are all supported so long as the chosen channel\n  exists on the system.\n\n### Network\n\n- Outbound HTTPS to `https://outlook.office.com/*` and the Microsoft sign-in\n  chain (`login.microsoftonline.com`, conditional-access endpoints your tenant\n  routes through, etc.).\n- No inbound ports are opened. No proxy is configured; use your system proxy.\n\n### Account\n\n- A **Microsoft 365 / Office 365 mailbox** you can sign into at\n  `outlook.office.com` (work or school, or a personal MSA that resolves to\n  that endpoint). Legacy `outlook.live.com` / `hotmail.com` consumer mailboxes\n  use a different API surface and are **not** supported by this tool.\n- Your tenant's conditional-access / MFA policies apply exactly as they would\n  in the browser — you complete them in the Playwright window during `login`.\n\n### Platform / permissions\n\n- Write access to `$HOME` (the session file lives at\n  `$HOME/.tool-agents/outlook-cli/session.json`, parent dir `0700`, file `0600`).\n- On **macOS / Linux**, the POSIX file-mode enforcement is strict.\n- On **Windows**, `fs.chmod` is largely a no-op — the session file is still\n  written atomically, but filesystem ACL hardening is your responsibility.\n  The rest of the tool (Playwright, HTTP, commander wiring) works unchanged.\n\n---\n\n## Libraries & tools used\n\n### Runtime (production)\n\n| Package | Version | Why |\n|---|---|---|\n| [`commander`](https://github.com/tj/commander.js) | `^14.0.3` | CLI parser — subcommands, option mixing, help output. |\n\nThat is the entire runtime footprint. Everything else (HTTP, JSON parsing,\nfile IO, crypto, timezone math, token base64-URL decoding) uses Node's\nbuilt-in `node:*` modules.\n\n### Development / build / test\n\n| Package | Version | Why |\n|---|---|---|\n| [`typescript`](https://www.typescriptlang.org/) | `^6.0.3` | Language. Compiled to CommonJS into `dist/`. |\n| [`ts-node`](https://typestrong.org/ts-node/) | `^10.9.2` | Run `.ts` directly (`npm run cli` / `npx ts-node src/cli.ts`). |\n| [`@types/node`](https://www.npmjs.com/package/@types/node) | `^25.6.0` | Type definitions for Node core APIs. |\n| [`playwright`](https://playwright.dev/) | `^1.59.1` | Drives the headed Chrome window during `login` and captures the outbound Bearer token + cookies. |\n| [`@playwright/test`](https://playwright.dev/docs/intro) | `^1.59.1` | Test-runner companion (present as a dev-dep; no live browser tests run in CI). |\n| [`vitest`](https://vitest.dev/) | `^4.1.4` | Test framework for the 208 unit + integration tests in `test_scripts/`. |\n\n### External binaries you provide\n\n- **Google Chrome** or **Microsoft Edge** (see Browser section above).\n- **Node.js 20+** runtime.\n- **git** for cloning.\n\nNo global npm tools need to be installed ahead of time. `npm install` in the\nrepo root fetches everything else.\n\n---\n\n## Build\n\n```bash\ngit clone <this-repo> outlook-tool\ncd outlook-tool\nnpm install\nnpm run build          # emits dist/cli.js (chmod +x in postbuild)\n```\n\nOptional: link the CLI globally so you can call it as `outlook-cli` from anywhere:\n\n```bash\nnpm link                # installs a symlink at $(npm prefix -g)/bin/outlook-cli\n```\n\nIf you previously linked a differently named package, remove the stale symlink\nfirst (`rm $(which outlook-cli)`), then re-run `npm link`.\n\nYou can also run the TypeScript sources directly without building:\n\n```bash\nnpx ts-node src/cli.ts <subcommand> [options]\n# or\nnpm run cli -- <subcommand> [options]\n```\n\n---\n\n## Run the tests\n\n```bash\nnpm test               # vitest run — 208 tests across 17 files\nnpm run test:watch     # incremental\n```\n\n---\n\n## First use\n\n```bash\noutlook-cli login\n```\n\nA Chrome window opens at `https://outlook.office.com/`. Sign in normally.\nThe tool watches outbound requests, captures the first Bearer token it sees,\ncloses the window, and writes `~/.tool-agents/outlook-cli/session.json`.\n\nAfter that, every subcommand reads that file. You don't need to run `login`\nagain until the token expires (typically hours), and even then the default\nbehavior is to auto-reopen the browser for a silent refresh.\n\nQuick verification:\n\n```bash\noutlook-cli auth-check\n# {\n#   \"status\": \"ok\",\n#   \"tokenExpiresAt\": \"2026-04-22T15:03:25.000Z\",\n#   \"account\": { \"upn\": \"you@yourtenant.com\" }\n# }\n```\n\n---\n\n## Configuration\n\nThree runtime-plumbing settings exist. Each has a default, so **no\nconfiguration is required** for a basic install:\n\n| Setting | CLI flag | Env var | Default |\n|---|---|---|---|\n| Per-REST-call HTTP timeout | `--timeout <ms>` | `OUTLOOK_CLI_HTTP_TIMEOUT_MS` | `30000` (30 s) |\n| Max wait for interactive login | `--login-timeout <ms>` | `OUTLOOK_CLI_LOGIN_TIMEOUT_MS` | `300000` (5 min) |\n| Playwright Chrome channel | `--chrome-channel <name>` | `OUTLOOK_CLI_CHROME_CHANNEL` | `chrome` |\n\nPrecedence: CLI flag > env var > default. A malformed flag or env value still\nthrows `ConfigurationError` (exit 3); the default only covers the unset case.\n\nIf you want persistent overrides, `source ./outlook-cli.env` in your shell or\nappend that line to `~/.zshrc` / `~/.bashrc`.\n\nOther (always-optional) flags are listed in `outlook-cli --help` and in\n`docs/design/configuration-guide.md`.\n\n### Exit codes\n\n| Code | Meaning |\n|---|---|\n| `0` | Success |\n| `1` | Unexpected error |\n| `2` | Invalid usage / bad argv |\n| `3` | Configuration error (malformed flag or env var) |\n| `4` | Auth failure (expired/rejected session, user cancelled login, `--no-auto-reauth` with no cache) |\n| `5` | Upstream API error (non-401 HTTP error, timeout, network failure, pagination limit) |\n| `6` | IO error — includes folder collision without `--idempotent`, file collision without `--overwrite` |\n\n---\n\n## Usage examples\n\n### Mail\n\n```bash\n# Most-recent 5 inbox messages as a human-readable table\noutlook-cli list-mail --top 5 --table\n\n# A specific message, body as text, written to disk\noutlook-cli get-mail AAMkAGI... --body text > message.json\n\n# Save all non-inline attachments to ./att\noutlook-cli download-attachments AAMkAGI... --out ./att\n```\n\n### Mail received between two timestamps\n\n`list-mail` supports `--from` / `--to` for a ReceivedDateTime window.\nEach bound accepts ISO8601 or the keyword grammar `now` / `now + Nd` /\n`now - Nd`. Lower bound is inclusive, upper bound is exclusive — so\nday-aligned windows do not overlap.\n\n```bash\n# Everything received in the last 7 days\noutlook-cli list-mail --from \"now - 7d\" --table\n\n# Absolute window (one calendar month)\noutlook-cli list-mail --from 2026-04-01T00:00:00Z --to 2026-05-01T00:00:00Z --top 100\n\n# Combine with folder selection\noutlook-cli list-mail --folder \"Inbox/Projects/Alpha\" \\\n                      --from \"now - 30d\" --top 50\n\n# Just the count of matching messages (no payload, server-side $count)\noutlook-cli list-mail --just-count\noutlook-cli list-mail --folder Archive --from \"now - 365d\" --just-count\n# → { \"count\": 4273, \"exact\": true }\n```\n\n### Full thread of an email\n\n`get-thread` walks the `ConversationId` of a message and returns every\nmessage in that conversation regardless of folder.\n\n```bash\n# From a known message id — fetches ConversationId first, then the whole thread\noutlook-cli get-thread AAMkAGI...\n\n# Table view, oldest-first\noutlook-cli get-thread AAMkAGI... --table\n\n# Newest-first, body omitted for a compact JSON dump\noutlook-cli get-thread AAMkAGI... --order desc --body none > thread.json\n\n# If you already have the conversation id, skip the first hop\noutlook-cli get-thread \"conv:AAQkAGI...\"\n```\n\n### Calendar\n\n```bash\n# Next 14 days\noutlook-cli list-calendar --from now --to \"now + 14d\" --table\n\n# One event\noutlook-cli get-event AAMkAGI...\n```\n\n### Folders\n\n```bash\n# List top-level folders\noutlook-cli list-folders --table\n\n# Walk the whole tree (bounded)\noutlook-cli list-folders --recursive --table\n\n# Resolve a folder by display-name path\noutlook-cli find-folder \"Inbox/Projects/Alpha\"\n\n# Create a nested folder idempotently (no-op if it already exists)\noutlook-cli create-folder \"Inbox/Projects/Alpha\" --create-parents --idempotent\n\n# Move messages to a folder (by alias, path, or id:<raw>)\noutlook-cli move-mail AAMk... AAMk... --to \"Inbox/Archive-2026\"\noutlook-cli move-mail AAMk... --to Archive\noutlook-cli move-mail AAMk... --to \"id:AAMkAGI...\" --continue-on-error\n```\n\n### List mail from an arbitrary folder\n\n```bash\n# By display-name path (resolved once, then listed)\noutlook-cli list-mail --folder \"Inbox/Projects/Alpha\" --top 10 --table\n\n# By explicit id (skips resolution)\noutlook-cli list-mail --folder-id AAMkAGI... --top 20\n\n# With an anchor — resolve \"Projects/Alpha\" under Inbox\noutlook-cli list-mail --folder-parent Inbox --folder \"Projects/Alpha\"\n```\n\n`outlook-cli <subcommand> --help` shows the complete flag set for each.\n\n---\n\n## Output modes\n\nEvery subcommand supports two formats:\n\n- `--json` (default) — stable, stdout, pipe into `jq` / scripts.\n- `--table` — human-readable, compact columns.\n\nThey are mutually exclusive. Errors are always emitted as JSON on **stderr**\nwith `code`, optional `message`, and setting-specific fields (e.g.\n`missingSetting`, `destination`, `failed[]`).\n\n---\n\n## Project layout\n\n```\nsrc/\n  cli.ts                 # commander wiring, global options, error mapping\n  auth/                  # Playwright login flow, token capture\n  session/               # atomic session-file IO, locking, JWT parsing\n  http/                  # OutlookClient + error types + REST DTOs\n  folders/               # folder resolver (path, well-known, id) + types\n  commands/              # one file per subcommand\n  output/                # JSON / table formatter\n  config/                # loadConfig, env + flag precedence, defaults\n  util/                  # redaction, filename safety, misc helpers\ntest_scripts/            # vitest suites (208 tests)\ndocs/\n  design/                # refined specs, plans, project-design, config guide\n  reference/             # codebase scans\n  research/              # deep-dive docs on Outlook REST v2.0 quirks\n```\n\nEvery meaningful behavior is documented in\n[`docs/design/project-design.md`](docs/design/project-design.md), and every\nphase of work has a `plan-NNN-*.md` alongside it.\n\n---\n\n## What's new\n\nSee [`CHANGELOG.md`](CHANGELOG.md) for the release history. Current version is\nrecorded in `package.json`.\n\n---\n\n## License\n\nISC.\n","readmeFilename":"README.md","_rev":"1-adba395b2e105e8e5a44ef2cffa2502e"}