Metadata-Version: 2.4
Name: notion-agent-cli
Version: 0.1.1
Summary: Call Notion's reverse-engineered ✦ AI / Custom Agent endpoint from the command line
Project-URL: Homepage, https://github.com/chenyqthu/notion-agent-cli
Project-URL: Source, https://github.com/chenyqthu/notion-agent-cli
Project-URL: Issues, https://github.com/chenyqthu/notion-agent-cli/issues
Project-URL: Changelog, https://github.com/chenyqthu/notion-agent-cli/releases
Author: chenyqthu
License: MIT
License-File: LICENSE
Keywords: ai,cli,custom-agent,jarvis,notion
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: MacOS
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Communications :: Chat
Classifier: Topic :: Office/Business
Requires-Python: >=3.11
Requires-Dist: brotli>=1.1
Requires-Dist: httpx>=0.27
Provides-Extra: dev
Requires-Dist: fastapi>=0.110; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest-timeout>=2.3; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.4; extra == 'dev'
Requires-Dist: uvicorn[standard]>=0.27; extra == 'dev'
Provides-Extra: serve
Requires-Dist: fastapi>=0.110; extra == 'serve'
Requires-Dist: uvicorn[standard]>=0.27; extra == 'serve'
Description-Content-Type: text/markdown

# notion-agent-cli

> 把你的 Notion ✦ AI / Custom Agent 变成一条 shell 命令。

A small Python CLI + library that talks **directly** to Notion's internal
`POST /api/v3/runInferenceTranscript` endpoint using a `token_v2` cookie
copied from a logged-in browser session. No `notion_manager` Go proxy, no
account pool — just your own session.

The conversations show up in Notion's ✦ AI chat panel under whichever
Custom Agent persona you bind (e.g. Jarvis), so you can switch between
talking to your agent from Notion's UI and from the shell.

## Status

**v0.1.0 — first tagged release.** The library is live-validated against
Notion's real endpoint and adopted by at least one downstream project
([NotionAgents](https://github.com/chenyqthu/NotionAgents) uses
`NotionAgentClient` as its inference backend). See
[`docs/STATUS.md`](docs/STATUS.md) for the full live-test ledger and
[`docs/ROADMAP.md`](docs/ROADMAP.md) for what's still planned.

## Install

```bash
pipx install notion-agent-cli      # recommended — isolated venv, one-line upgrade
# or
pip install notion-agent-cli       # if you don't use pipx
```

For the optional FastAPI HTTP wrapper (`notion-agent serve`):

```bash
pipx install 'notion-agent-cli[serve]'
```

For local development from a clone, `pip install -e ".[dev]"`. The
release flow is in [`docs/RELEASING.md`](docs/RELEASING.md).

## Quickstart

```bash
# One-shot setup: paste the full `document.cookie` string copied from
# a logged-in Notion tab (DevTools → Application → Cookies → notion.so,
# right-click any row → "Copy all as ..."). The CLI extracts token_v2 /
# notion_user_id / notion_browser_id, calls /api/v3/loadUserContent to
# fetch workspace metadata, and picks the (only) workspace for you.
notion-agent init --cookie "notion_browser_id=...; notion_user_id=...; token_v2=v03%3A..."

# Want to keep the cookie out of shell history? Read it from stdin:
pbpaste | notion-agent init --cookie -

# Chat (returns the full reply after streaming completes).
notion-agent chat "用一句话确认你在线。"
```

For Jarvis-style custom agent binding (so threads surface in the ✦ AI
chat panel under your agent name), grab the agent's instructions page id
from its URL and pass it to `init`:

```bash
notion-agent init --cookie "..." \
                  --agent-name Jarvis \
                  --agent-page-id 2e115375-830d-8184-bf4d-f427d847c6bc
```

## CLI surface

```text
notion-agent init     Bootstrap notion_account.json from a token_v2 cookie.
notion-agent chat     Send one prompt, print the reply (text / --json / --stream / --ndjson).
notion-agent doctor   Validate the account file and ping /loadUserContent.
notion-agent models   refresh — pull the current alias→Notion-id map.
notion-agent agents   list    — custom agents in the bound workspace.
notion-agent threads  list    — recent chat threads (filter by --agent).
notion-agent profile  list / current / use <name> / migrate <name>
notion-agent serve    Local FastAPI server (optional [serve] extra).
```

Run `notion-agent <sub> --help` for flag details. Every subcommand
accepts `--account PATH` to point at a non-default credential file.

## Continuation (multi-turn chats)

`chat` saves per-thread continuation state under
`~/.notionagents/threads/<thread-id>.json` after every successful turn,
so the next turn can be a follow-up in the same chat-panel thread:

```bash
# Turn 1 — capture the thread id.
THREAD=$(notion-agent chat --json "summarize my day so far" | jq -r .thread_id)

# Turn 2 — reuse it.
notion-agent chat --thread-id "$THREAD" "now translate it to chinese"
```

The thread's model is locked to whatever turn 1 chose — passing
`--model` on a `--thread-id` continuation is silently ignored, since
mid-thread model switches occasionally make Notion reject the partial
transcript.

## Multi-workspace profiles

Run `notion-agent init` once per workspace into a named profile file,
then swap between them with `profile use`:

```bash
notion-agent init --cookie "..." --account ~/.notionagents/tplink.json
notion-agent init --cookie "..." --account ~/.notionagents/personal.json
notion-agent profile use tplink   # symlinks notion_account.json → tplink.json
notion-agent profile current      # prints "tplink"
notion-agent profile list         # both, * marks active
```

If you started before profiles existed, `profile migrate <name>` renames
your existing `notion_account.json` into a named profile and replaces
the original with a symlink — non-destructive, no re-init required.

## Local HTTP server

`notion-agent serve` wraps the CLI in a small FastAPI app for
orchestrators (n8n, Make, internal Go services) that prefer HTTP over
shell-out. Install with the `[serve]` extra:

```bash
pip install -e ".[serve]"
notion-agent serve --host 127.0.0.1 --port 8000

# In another shell:
curl -sN -X POST localhost:8000/chat \
     -H 'content-type: application/json' \
     -d '{"prompt":"hi","stream":true}'   # SSE; data: {"text":"..."} per chunk
curl -s localhost:8000/healthz
curl -s localhost:8000/agents
```

Endpoints: `POST /chat` (blocking JSON or SSE), `GET /healthz` (doctor's
checks, 200 / 503), `GET /agents`, `GET /threads`. Error codes match
the library's [`ErrorCode`](src/notion_agent_cli/exceptions.py) taxonomy
so callers branch on `code` without parsing messages.

## Calling this from an AI agent

Two paths, pick the one that matches your runtime:

- **Claude Code** — the repo ships a skill at
  [`.claude/skills/notion-agent/`](.claude/skills/notion-agent/). Open
  this repo (or a downstream project that depends on the CLI) and the
  skill auto-loads via its frontmatter trigger phrases ("ask Jarvis",
  "Notion AI", "via the chat panel").
- **Anything else** (Codex, Gemini, openclaw, your own orchestrator) —
  read [`docs/AGENTS.md`](docs/AGENTS.md). It has a paste-ready
  system-prompt snippet at the bottom covering the CLI contract,
  preflight, error codes, and safety rules in ~40 lines.

Both paths shell out to the same `notion-agent` binary — make sure it
is on `$PATH` for whatever runtime you use.

## Why this exists

Notion's Custom Agents went paid in May 2026 ($10 / 1k credits,
30–60 credits per run, Business+ only). Their built-in ✦ AI chat is
free for any Plus seat but only addressable from the UI — not from a
cron job, a webhook, or a shell pipe. This CLI bridges that gap by
mirroring exactly what the SPA sends, so you get the same
free-quota model from `bash` / `python` / wherever.

Reverse-engineering notes + the captured chat-panel traffic that this
implementation is built against live in
[`docs/01-notion-chat-protocol.md`](docs/01-notion-chat-protocol.md).

## Library use

```python
import asyncio
from notion_agent_cli import NotionAgentClient

async def main():
    client = NotionAgentClient("~/.notionagents/notion_account.json")
    try:
        resp = await client.complete(prompt="今天周几？")
        print(resp.text)
        print(f"usage: in={resp.usage.input_tokens} out={resp.usage.output_tokens}")

        # Reuse the thread for a follow-up:
        followup = await client.complete(
            prompt="and yesterday?", thread_id=resp.thread_id,
        )
        print(followup.text)
    finally:
        await client.aclose()

asyncio.run(main())
```

Async streaming consumers can pass `on_text_delta=` (sync) or
`on_text_delta_async=` (coroutine, awaited per chunk — used by the
FastAPI SSE branch). For raw NDJSON passthrough use
`client.stream_lines(...)`.

The public surface kept stable across v0.1: `NotionAgentClient`,
`NotionAgentError`, `ChatResponse`, `TokenUsage`, `ErrorCode`. Sub-modules
(`transcript`, `ndjson`, `account`, `models`, `profile`, `serve`) are
importable but their internals can shift between minor releases.

## License

MIT. See [`LICENSE`](LICENSE).
