notion-agent-cli 0.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- notion_agent_cli-0.1.1/.claude/skills/notion-agent/SKILL.md +132 -0
- notion_agent_cli-0.1.1/.github/workflows/release.yml +46 -0
- notion_agent_cli-0.1.1/.github/workflows/test.yml +30 -0
- notion_agent_cli-0.1.1/.gitignore +35 -0
- notion_agent_cli-0.1.1/LICENSE +21 -0
- notion_agent_cli-0.1.1/PKG-INFO +245 -0
- notion_agent_cli-0.1.1/README.md +208 -0
- notion_agent_cli-0.1.1/docs/01-notion-chat-protocol.md +525 -0
- notion_agent_cli-0.1.1/docs/02-architecture.md +95 -0
- notion_agent_cli-0.1.1/docs/AGENTS.md +259 -0
- notion_agent_cli-0.1.1/docs/NEXT-SESSION.md +172 -0
- notion_agent_cli-0.1.1/docs/RELEASING.md +91 -0
- notion_agent_cli-0.1.1/docs/ROADMAP.md +147 -0
- notion_agent_cli-0.1.1/docs/STATUS.md +245 -0
- notion_agent_cli-0.1.1/pyproject.toml +81 -0
- notion_agent_cli-0.1.1/src/notion_agent_cli/__init__.py +28 -0
- notion_agent_cli-0.1.1/src/notion_agent_cli/account.py +141 -0
- notion_agent_cli-0.1.1/src/notion_agent_cli/agents.py +129 -0
- notion_agent_cli-0.1.1/src/notion_agent_cli/bootstrap.py +253 -0
- notion_agent_cli-0.1.1/src/notion_agent_cli/cli/__init__.py +6 -0
- notion_agent_cli-0.1.1/src/notion_agent_cli/cli/__main__.py +764 -0
- notion_agent_cli-0.1.1/src/notion_agent_cli/exceptions.py +47 -0
- notion_agent_cli-0.1.1/src/notion_agent_cli/models.py +167 -0
- notion_agent_cli-0.1.1/src/notion_agent_cli/ndjson.py +301 -0
- notion_agent_cli-0.1.1/src/notion_agent_cli/profile.py +196 -0
- notion_agent_cli-0.1.1/src/notion_agent_cli/provider.py +550 -0
- notion_agent_cli-0.1.1/src/notion_agent_cli/serve.py +318 -0
- notion_agent_cli-0.1.1/src/notion_agent_cli/thread_state.py +73 -0
- notion_agent_cli-0.1.1/src/notion_agent_cli/transcript.py +310 -0
- notion_agent_cli-0.1.1/src/notion_agent_cli/types.py +27 -0
- notion_agent_cli-0.1.1/tests/__init__.py +0 -0
- notion_agent_cli-0.1.1/tests/fixtures/get_available_models.json +282 -0
- notion_agent_cli-0.1.1/tests/fixtures/get_custom_agents.json +273 -0
- notion_agent_cli-0.1.1/tests/test_agents.py +224 -0
- notion_agent_cli-0.1.1/tests/test_bootstrap.py +297 -0
- notion_agent_cli-0.1.1/tests/test_cli_doctor.py +52 -0
- notion_agent_cli-0.1.1/tests/test_cli_init.py +74 -0
- notion_agent_cli-0.1.1/tests/test_error_codes.py +305 -0
- notion_agent_cli-0.1.1/tests/test_models_refresh.py +253 -0
- notion_agent_cli-0.1.1/tests/test_notion_agent_cli.py +911 -0
- notion_agent_cli-0.1.1/tests/test_profile.py +220 -0
- notion_agent_cli-0.1.1/tests/test_serve.py +330 -0
- notion_agent_cli-0.1.1/tests/test_thread_state.py +90 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: notion-agent
|
|
3
|
+
description: Ask Notion's ✦ AI / Custom Agent (e.g. Jarvis) via the local `notion-agent` CLI. Use when the user wants a workspace-aware answer from their bound Notion agent, wants to pipe a draft through Notion AI, list their agents / threads, or continue a prior thread. Trigger phrases include "ask Jarvis", "Notion AI", "via the chat panel", "notion-agent", "Custom Agent".
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
## When to use
|
|
7
|
+
|
|
8
|
+
- The user wants an answer that depends on **their Notion workspace**
|
|
9
|
+
(their notes, their Custom Agent's instructions / skill pages) and a
|
|
10
|
+
public Claude can't see that context.
|
|
11
|
+
- The user mentions a Custom Agent by name ("Jarvis", "the chat panel
|
|
12
|
+
one", "my Notion agent") — that's a workspace-bound persona, not a
|
|
13
|
+
general assistant.
|
|
14
|
+
- The user wants to dump a draft into Notion's ✦ AI for them to react
|
|
15
|
+
to inside Notion (the conversation surfaces in the chat panel under
|
|
16
|
+
the bound persona).
|
|
17
|
+
|
|
18
|
+
Skip this skill for general programming / world-knowledge questions —
|
|
19
|
+
answer those directly. Notion's chat costs the user API quota.
|
|
20
|
+
|
|
21
|
+
## Quick start
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
# One-shot chat — prints the assistant reply to stdout
|
|
25
|
+
notion-agent chat "summarize my last week of project notes"
|
|
26
|
+
|
|
27
|
+
# Streaming output for long replies
|
|
28
|
+
notion-agent chat --stream "draft a status update from this week's notes"
|
|
29
|
+
|
|
30
|
+
# Structured JSON (text + usage + thread_id) for piping into other tools
|
|
31
|
+
notion-agent chat --json "what tickets did I close in May?"
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Check `notion-agent --help` for the full subcommand list. Six
|
|
35
|
+
subcommands: `init`, `chat`, `doctor`, `models refresh`, `agents list`,
|
|
36
|
+
`threads list`.
|
|
37
|
+
|
|
38
|
+
## Pre-flight
|
|
39
|
+
|
|
40
|
+
Before the first call, the user needs an account file at
|
|
41
|
+
`~/.notionagents/notion_account.json`. If chat errors with
|
|
42
|
+
`[account_missing]`:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
# Paste the full document.cookie string from a logged-in Notion tab
|
|
46
|
+
notion-agent init --cookie "token_v2=...; notion_user_id=...; ..."
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
`init` auto-extracts `token_v2` / `notion_user_id` / `notion_browser_id`
|
|
50
|
+
from the pasted cookie string. Add `--agent-name "Jarvis"
|
|
51
|
+
--agent-page-id <uuid>` to bind to a Custom Agent so threads surface in
|
|
52
|
+
the chat panel.
|
|
53
|
+
|
|
54
|
+
If chat errors with `[auth_invalid]`, the `token_v2` cookie expired —
|
|
55
|
+
re-run `init` with a fresh one.
|
|
56
|
+
|
|
57
|
+
## Multi-turn continuation
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
# Turn 1 — get the thread_id from --json output
|
|
61
|
+
THREAD=$(notion-agent chat --json "start a brainstorm on X" | jq -r .thread_id)
|
|
62
|
+
|
|
63
|
+
# Turn 2+ — pass --thread-id to continue
|
|
64
|
+
notion-agent chat --thread-id "$THREAD" "now narrow down to 3 ideas"
|
|
65
|
+
notion-agent chat --thread-id "$THREAD" "expand idea 1"
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
State is stored under `~/.notionagents/threads/<thread-id>.json`.
|
|
69
|
+
The thread's original model is locked — `--model` is ignored on
|
|
70
|
+
continuation. If state is missing the error is `[thread_state_missing]`
|
|
71
|
+
and the user should drop `--thread-id` to start fresh.
|
|
72
|
+
|
|
73
|
+
## Looking up agent / thread ids
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
notion-agent agents list --limit 10 # 19-ish agents, page_id + activity
|
|
77
|
+
notion-agent threads list --limit 10 # recent threads, newest first
|
|
78
|
+
notion-agent threads list --agent <id> # threads under one agent
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
JSON variants (`--json`) for both. Use `agents list` when the user asks
|
|
82
|
+
to bind a new Custom Agent during `init` — the page_id column is what
|
|
83
|
+
goes into `--agent-page-id`.
|
|
84
|
+
|
|
85
|
+
## Troubleshooting
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
notion-agent doctor # human-readable 6-step report
|
|
89
|
+
notion-agent doctor --json # structured, for cron / automation
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
`doctor` checks: account file readable, required fields, Custom Agent
|
|
93
|
+
binding (informational), `/loadUserContent` ping, bound space_id still
|
|
94
|
+
in workspace list, user model map present. Each line tagged
|
|
95
|
+
`[ok] / [FAIL] / [..]`.
|
|
96
|
+
|
|
97
|
+
If Notion rotates internal model ids (the `apricot-sorbet-high` style):
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
notion-agent models refresh # writes ~/.notionagents/models.json
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Don't
|
|
104
|
+
|
|
105
|
+
- **Don't paste real `token_v2` values into prompts** — `init` already
|
|
106
|
+
bound it, and the CLI handles auth transparently. The token is
|
|
107
|
+
long-lived (~months); only re-run `init` when `doctor` says
|
|
108
|
+
`[auth_invalid]`.
|
|
109
|
+
- **Don't loop more than ~3 chats per user task** — each call is a real
|
|
110
|
+
Notion API hit consuming the operator's quota. If a batch run is
|
|
111
|
+
unavoidable, ask the user first.
|
|
112
|
+
- **Don't write to `~/.notionagents/`** directly — the CLI owns it.
|
|
113
|
+
Account file, models map, and thread state are all CLI-managed.
|
|
114
|
+
- **Don't switch `--model` mid-thread** — the flag is silently ignored
|
|
115
|
+
on `--thread-id` continuations because Notion locks the model to
|
|
116
|
+
turn 1. If the user wants a different model, start a new thread.
|
|
117
|
+
|
|
118
|
+
## ErrorCode reference (for `--json` consumers)
|
|
119
|
+
|
|
120
|
+
| Code | Meaning | Action |
|
|
121
|
+
|---|---|---|
|
|
122
|
+
| `account_missing` | No file at `~/.notionagents/notion_account.json` | Run `init` |
|
|
123
|
+
| `account_invalid` | File present but missing required fields | Re-run `init` |
|
|
124
|
+
| `auth_invalid` | 401/403 — `token_v2` expired or rejected | Re-run `init` with fresh cookie |
|
|
125
|
+
| `thread_state_missing`| `--thread-id` used but no saved state | Drop `--thread-id` to start fresh |
|
|
126
|
+
| `premium_required` | Notion premium feature gated | Tell the user their plan blocks this |
|
|
127
|
+
| `empty_text` | 200 but empty stream | Likely transient — retry once |
|
|
128
|
+
| `http_error` | Non-200 from Notion | Likely transient — retry once |
|
|
129
|
+
| `transport` | Network failure | Check connectivity |
|
|
130
|
+
|
|
131
|
+
`NotionAgentError.code` is a `StrEnum` — branching on the literal
|
|
132
|
+
string is fine.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- 'v*'
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
build:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
- uses: actions/setup-python@v5
|
|
17
|
+
with:
|
|
18
|
+
python-version: '3.11'
|
|
19
|
+
- name: Install build tooling
|
|
20
|
+
run: python -m pip install --upgrade pip build twine
|
|
21
|
+
- name: Build sdist + wheel
|
|
22
|
+
run: python -m build
|
|
23
|
+
- name: Verify metadata + README rendering
|
|
24
|
+
run: twine check --strict dist/*
|
|
25
|
+
- uses: actions/upload-artifact@v4
|
|
26
|
+
with:
|
|
27
|
+
name: dist
|
|
28
|
+
path: dist/
|
|
29
|
+
|
|
30
|
+
publish:
|
|
31
|
+
needs: build
|
|
32
|
+
runs-on: ubuntu-latest
|
|
33
|
+
# Must match the Pending Publisher environment configured on PyPI.
|
|
34
|
+
# Configure required reviewers on this environment in GitHub repo
|
|
35
|
+
# settings to gate the upload step.
|
|
36
|
+
environment:
|
|
37
|
+
name: pypi
|
|
38
|
+
url: https://pypi.org/project/notion-agent-cli/
|
|
39
|
+
permissions:
|
|
40
|
+
id-token: write # OIDC token for Trusted Publishing
|
|
41
|
+
steps:
|
|
42
|
+
- uses: actions/download-artifact@v4
|
|
43
|
+
with:
|
|
44
|
+
name: dist
|
|
45
|
+
path: dist/
|
|
46
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
name: Test
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
test:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
strategy:
|
|
16
|
+
fail-fast: false
|
|
17
|
+
matrix:
|
|
18
|
+
python-version: ['3.11', '3.12']
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v4
|
|
21
|
+
- uses: actions/setup-python@v5
|
|
22
|
+
with:
|
|
23
|
+
python-version: ${{ matrix.python-version }}
|
|
24
|
+
cache: pip
|
|
25
|
+
- name: Install (with dev extra)
|
|
26
|
+
run: python -m pip install -e ".[dev]"
|
|
27
|
+
- name: Lint
|
|
28
|
+
run: ruff check src tests
|
|
29
|
+
- name: Test
|
|
30
|
+
run: pytest -q
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
*.egg-info/
|
|
7
|
+
.eggs/
|
|
8
|
+
build/
|
|
9
|
+
dist/
|
|
10
|
+
.pytest_cache/
|
|
11
|
+
.ruff_cache/
|
|
12
|
+
.mypy_cache/
|
|
13
|
+
.coverage
|
|
14
|
+
htmlcov/
|
|
15
|
+
|
|
16
|
+
# Virtualenvs
|
|
17
|
+
.venv/
|
|
18
|
+
venv/
|
|
19
|
+
env/
|
|
20
|
+
|
|
21
|
+
# IDE
|
|
22
|
+
.vscode/
|
|
23
|
+
.idea/
|
|
24
|
+
*.swp
|
|
25
|
+
|
|
26
|
+
# OS
|
|
27
|
+
.DS_Store
|
|
28
|
+
Thumbs.db
|
|
29
|
+
|
|
30
|
+
# Notion CLI artifacts (real credentials — never commit)
|
|
31
|
+
notion_account.json
|
|
32
|
+
.notion-agent/
|
|
33
|
+
|
|
34
|
+
# Local session state (oh-my-claudecode, etc.)
|
|
35
|
+
.omc/
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 chenyqthu
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: notion-agent-cli
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Call Notion's reverse-engineered ✦ AI / Custom Agent endpoint from the command line
|
|
5
|
+
Project-URL: Homepage, https://github.com/chenyqthu/notion-agent-cli
|
|
6
|
+
Project-URL: Source, https://github.com/chenyqthu/notion-agent-cli
|
|
7
|
+
Project-URL: Issues, https://github.com/chenyqthu/notion-agent-cli/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/chenyqthu/notion-agent-cli/releases
|
|
9
|
+
Author: chenyqthu
|
|
10
|
+
License: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: ai,cli,custom-agent,jarvis,notion
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Environment :: Console
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Operating System :: MacOS
|
|
18
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Topic :: Communications :: Chat
|
|
22
|
+
Classifier: Topic :: Office/Business
|
|
23
|
+
Requires-Python: >=3.11
|
|
24
|
+
Requires-Dist: brotli>=1.1
|
|
25
|
+
Requires-Dist: httpx>=0.27
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: fastapi>=0.110; extra == 'dev'
|
|
28
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
29
|
+
Requires-Dist: pytest-timeout>=2.3; extra == 'dev'
|
|
30
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
31
|
+
Requires-Dist: ruff>=0.4; extra == 'dev'
|
|
32
|
+
Requires-Dist: uvicorn[standard]>=0.27; extra == 'dev'
|
|
33
|
+
Provides-Extra: serve
|
|
34
|
+
Requires-Dist: fastapi>=0.110; extra == 'serve'
|
|
35
|
+
Requires-Dist: uvicorn[standard]>=0.27; extra == 'serve'
|
|
36
|
+
Description-Content-Type: text/markdown
|
|
37
|
+
|
|
38
|
+
# notion-agent-cli
|
|
39
|
+
|
|
40
|
+
> 把你的 Notion ✦ AI / Custom Agent 变成一条 shell 命令。
|
|
41
|
+
|
|
42
|
+
A small Python CLI + library that talks **directly** to Notion's internal
|
|
43
|
+
`POST /api/v3/runInferenceTranscript` endpoint using a `token_v2` cookie
|
|
44
|
+
copied from a logged-in browser session. No `notion_manager` Go proxy, no
|
|
45
|
+
account pool — just your own session.
|
|
46
|
+
|
|
47
|
+
The conversations show up in Notion's ✦ AI chat panel under whichever
|
|
48
|
+
Custom Agent persona you bind (e.g. Jarvis), so you can switch between
|
|
49
|
+
talking to your agent from Notion's UI and from the shell.
|
|
50
|
+
|
|
51
|
+
## Status
|
|
52
|
+
|
|
53
|
+
**v0.1.0 — first tagged release.** The library is live-validated against
|
|
54
|
+
Notion's real endpoint and adopted by at least one downstream project
|
|
55
|
+
([NotionAgents](https://github.com/chenyqthu/NotionAgents) uses
|
|
56
|
+
`NotionAgentClient` as its inference backend). See
|
|
57
|
+
[`docs/STATUS.md`](docs/STATUS.md) for the full live-test ledger and
|
|
58
|
+
[`docs/ROADMAP.md`](docs/ROADMAP.md) for what's still planned.
|
|
59
|
+
|
|
60
|
+
## Install
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
pipx install notion-agent-cli # recommended — isolated venv, one-line upgrade
|
|
64
|
+
# or
|
|
65
|
+
pip install notion-agent-cli # if you don't use pipx
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
For the optional FastAPI HTTP wrapper (`notion-agent serve`):
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
pipx install 'notion-agent-cli[serve]'
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
For local development from a clone, `pip install -e ".[dev]"`. The
|
|
75
|
+
release flow is in [`docs/RELEASING.md`](docs/RELEASING.md).
|
|
76
|
+
|
|
77
|
+
## Quickstart
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
# One-shot setup: paste the full `document.cookie` string copied from
|
|
81
|
+
# a logged-in Notion tab (DevTools → Application → Cookies → notion.so,
|
|
82
|
+
# right-click any row → "Copy all as ..."). The CLI extracts token_v2 /
|
|
83
|
+
# notion_user_id / notion_browser_id, calls /api/v3/loadUserContent to
|
|
84
|
+
# fetch workspace metadata, and picks the (only) workspace for you.
|
|
85
|
+
notion-agent init --cookie "notion_browser_id=...; notion_user_id=...; token_v2=v03%3A..."
|
|
86
|
+
|
|
87
|
+
# Want to keep the cookie out of shell history? Read it from stdin:
|
|
88
|
+
pbpaste | notion-agent init --cookie -
|
|
89
|
+
|
|
90
|
+
# Chat (returns the full reply after streaming completes).
|
|
91
|
+
notion-agent chat "用一句话确认你在线。"
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
For Jarvis-style custom agent binding (so threads surface in the ✦ AI
|
|
95
|
+
chat panel under your agent name), grab the agent's instructions page id
|
|
96
|
+
from its URL and pass it to `init`:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
notion-agent init --cookie "..." \
|
|
100
|
+
--agent-name Jarvis \
|
|
101
|
+
--agent-page-id 2e115375-830d-8184-bf4d-f427d847c6bc
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## CLI surface
|
|
105
|
+
|
|
106
|
+
```text
|
|
107
|
+
notion-agent init Bootstrap notion_account.json from a token_v2 cookie.
|
|
108
|
+
notion-agent chat Send one prompt, print the reply (text / --json / --stream / --ndjson).
|
|
109
|
+
notion-agent doctor Validate the account file and ping /loadUserContent.
|
|
110
|
+
notion-agent models refresh — pull the current alias→Notion-id map.
|
|
111
|
+
notion-agent agents list — custom agents in the bound workspace.
|
|
112
|
+
notion-agent threads list — recent chat threads (filter by --agent).
|
|
113
|
+
notion-agent profile list / current / use <name> / migrate <name>
|
|
114
|
+
notion-agent serve Local FastAPI server (optional [serve] extra).
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Run `notion-agent <sub> --help` for flag details. Every subcommand
|
|
118
|
+
accepts `--account PATH` to point at a non-default credential file.
|
|
119
|
+
|
|
120
|
+
## Continuation (multi-turn chats)
|
|
121
|
+
|
|
122
|
+
`chat` saves per-thread continuation state under
|
|
123
|
+
`~/.notionagents/threads/<thread-id>.json` after every successful turn,
|
|
124
|
+
so the next turn can be a follow-up in the same chat-panel thread:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
# Turn 1 — capture the thread id.
|
|
128
|
+
THREAD=$(notion-agent chat --json "summarize my day so far" | jq -r .thread_id)
|
|
129
|
+
|
|
130
|
+
# Turn 2 — reuse it.
|
|
131
|
+
notion-agent chat --thread-id "$THREAD" "now translate it to chinese"
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
The thread's model is locked to whatever turn 1 chose — passing
|
|
135
|
+
`--model` on a `--thread-id` continuation is silently ignored, since
|
|
136
|
+
mid-thread model switches occasionally make Notion reject the partial
|
|
137
|
+
transcript.
|
|
138
|
+
|
|
139
|
+
## Multi-workspace profiles
|
|
140
|
+
|
|
141
|
+
Run `notion-agent init` once per workspace into a named profile file,
|
|
142
|
+
then swap between them with `profile use`:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
notion-agent init --cookie "..." --account ~/.notionagents/tplink.json
|
|
146
|
+
notion-agent init --cookie "..." --account ~/.notionagents/personal.json
|
|
147
|
+
notion-agent profile use tplink # symlinks notion_account.json → tplink.json
|
|
148
|
+
notion-agent profile current # prints "tplink"
|
|
149
|
+
notion-agent profile list # both, * marks active
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
If you started before profiles existed, `profile migrate <name>` renames
|
|
153
|
+
your existing `notion_account.json` into a named profile and replaces
|
|
154
|
+
the original with a symlink — non-destructive, no re-init required.
|
|
155
|
+
|
|
156
|
+
## Local HTTP server
|
|
157
|
+
|
|
158
|
+
`notion-agent serve` wraps the CLI in a small FastAPI app for
|
|
159
|
+
orchestrators (n8n, Make, internal Go services) that prefer HTTP over
|
|
160
|
+
shell-out. Install with the `[serve]` extra:
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
pip install -e ".[serve]"
|
|
164
|
+
notion-agent serve --host 127.0.0.1 --port 8000
|
|
165
|
+
|
|
166
|
+
# In another shell:
|
|
167
|
+
curl -sN -X POST localhost:8000/chat \
|
|
168
|
+
-H 'content-type: application/json' \
|
|
169
|
+
-d '{"prompt":"hi","stream":true}' # SSE; data: {"text":"..."} per chunk
|
|
170
|
+
curl -s localhost:8000/healthz
|
|
171
|
+
curl -s localhost:8000/agents
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Endpoints: `POST /chat` (blocking JSON or SSE), `GET /healthz` (doctor's
|
|
175
|
+
checks, 200 / 503), `GET /agents`, `GET /threads`. Error codes match
|
|
176
|
+
the library's [`ErrorCode`](src/notion_agent_cli/exceptions.py) taxonomy
|
|
177
|
+
so callers branch on `code` without parsing messages.
|
|
178
|
+
|
|
179
|
+
## Calling this from an AI agent
|
|
180
|
+
|
|
181
|
+
Two paths, pick the one that matches your runtime:
|
|
182
|
+
|
|
183
|
+
- **Claude Code** — the repo ships a skill at
|
|
184
|
+
[`.claude/skills/notion-agent/`](.claude/skills/notion-agent/). Open
|
|
185
|
+
this repo (or a downstream project that depends on the CLI) and the
|
|
186
|
+
skill auto-loads via its frontmatter trigger phrases ("ask Jarvis",
|
|
187
|
+
"Notion AI", "via the chat panel").
|
|
188
|
+
- **Anything else** (Codex, Gemini, openclaw, your own orchestrator) —
|
|
189
|
+
read [`docs/AGENTS.md`](docs/AGENTS.md). It has a paste-ready
|
|
190
|
+
system-prompt snippet at the bottom covering the CLI contract,
|
|
191
|
+
preflight, error codes, and safety rules in ~40 lines.
|
|
192
|
+
|
|
193
|
+
Both paths shell out to the same `notion-agent` binary — make sure it
|
|
194
|
+
is on `$PATH` for whatever runtime you use.
|
|
195
|
+
|
|
196
|
+
## Why this exists
|
|
197
|
+
|
|
198
|
+
Notion's Custom Agents went paid in May 2026 ($10 / 1k credits,
|
|
199
|
+
30–60 credits per run, Business+ only). Their built-in ✦ AI chat is
|
|
200
|
+
free for any Plus seat but only addressable from the UI — not from a
|
|
201
|
+
cron job, a webhook, or a shell pipe. This CLI bridges that gap by
|
|
202
|
+
mirroring exactly what the SPA sends, so you get the same
|
|
203
|
+
free-quota model from `bash` / `python` / wherever.
|
|
204
|
+
|
|
205
|
+
Reverse-engineering notes + the captured chat-panel traffic that this
|
|
206
|
+
implementation is built against live in
|
|
207
|
+
[`docs/01-notion-chat-protocol.md`](docs/01-notion-chat-protocol.md).
|
|
208
|
+
|
|
209
|
+
## Library use
|
|
210
|
+
|
|
211
|
+
```python
|
|
212
|
+
import asyncio
|
|
213
|
+
from notion_agent_cli import NotionAgentClient
|
|
214
|
+
|
|
215
|
+
async def main():
|
|
216
|
+
client = NotionAgentClient("~/.notionagents/notion_account.json")
|
|
217
|
+
try:
|
|
218
|
+
resp = await client.complete(prompt="今天周几?")
|
|
219
|
+
print(resp.text)
|
|
220
|
+
print(f"usage: in={resp.usage.input_tokens} out={resp.usage.output_tokens}")
|
|
221
|
+
|
|
222
|
+
# Reuse the thread for a follow-up:
|
|
223
|
+
followup = await client.complete(
|
|
224
|
+
prompt="and yesterday?", thread_id=resp.thread_id,
|
|
225
|
+
)
|
|
226
|
+
print(followup.text)
|
|
227
|
+
finally:
|
|
228
|
+
await client.aclose()
|
|
229
|
+
|
|
230
|
+
asyncio.run(main())
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
Async streaming consumers can pass `on_text_delta=` (sync) or
|
|
234
|
+
`on_text_delta_async=` (coroutine, awaited per chunk — used by the
|
|
235
|
+
FastAPI SSE branch). For raw NDJSON passthrough use
|
|
236
|
+
`client.stream_lines(...)`.
|
|
237
|
+
|
|
238
|
+
The public surface kept stable across v0.1: `NotionAgentClient`,
|
|
239
|
+
`NotionAgentError`, `ChatResponse`, `TokenUsage`, `ErrorCode`. Sub-modules
|
|
240
|
+
(`transcript`, `ndjson`, `account`, `models`, `profile`, `serve`) are
|
|
241
|
+
importable but their internals can shift between minor releases.
|
|
242
|
+
|
|
243
|
+
## License
|
|
244
|
+
|
|
245
|
+
MIT. See [`LICENSE`](LICENSE).
|