Files
jackwener__opencli/docs/guide/electron-app-cli.md
jakevin 29c135b656 refactor(notion): replace built-in CDP adapter with external ntn CLI (#1559)
* refactor(notion): replace built-in CDP adapter with external ntn CLI

Notion has shipped an official CLI at https://ntn.dev. It uses the
public Notion API (blocks / databases / properties / comments) instead
of reverse-engineering the Desktop UI, so it survives Notion app
updates and exposes a wider command surface than the in-tree adapter
could.

Changes:
  - `src/external-clis.yaml` — register `ntn` as first-class external CLI
    (binary `ntn`, homepage ntn.dev, install via the shell-pipe script
    on mac/linux)
  - `clis/notion/` — entire directory removed (8 commands: status /
    search / read / new / write / sidebar / favorites / export)
  - `docs/adapters/desktop/notion.md` — removed
  - `docs/.vitepress/config.mts` — drop nav entry
  - `docs/adapters/index.md` — drop adapter row
  - `README.md` / `README.zh-CN.md` — drop notion from feature lines,
    drop adapter table row, add `ntn` to CLI hub examples
  - `docs/index.md` / `docs/zh/index.md` / `docs/guide/getting-started.md`
    — drop notion from electron-control feature copy
  - `skills/opencli-usage/SKILL.md` — drop notion from electron list
  - `cli-manifest.json` — rebuilt with --allow-removals=8

Migration for users:
  `curl -fsSL https://ntn.dev | bash`  (or `opencli external install ntn`)
  Then use `opencli ntn <command>` in place of `opencli notion <command>`.

Rationale: the in-tree adapter was reverse-engineered against Notion
Desktop CDP and shipped only 8 commands. The official CLI gives users
the full Notion API surface and reduces our maintenance burden to zero.
Same pattern as gh / obsidian / lark-cli / tg-cli / discord-cli / wx-cli.

Verification:
  - `npx tsc --noEmit` clean
  - `npx vitest run --project unit` → 1091/1 skipped
  - `npm run build` (with --allow-removals=8) — manifest 809 entries
  - grep notion in user-facing docs (README / docs / skills) — only
    descriptive mentions remain in non-blocking places (comparison /
    site-recon / electron how-to / design doc), no broken adapter
    references

* fix(notion): align ntn external migration

* docs(notion): clarify ntn manual install
2026-05-14 16:12:17 +08:00

200 lines
5.4 KiB
Markdown

---
description: How to turn a new Electron desktop app into an OpenCLI adapter
---
# Add a New Electron App CLI
This guide is the **fast entry point** for turning a new Electron desktop application into an OpenCLI adapter.
If you want the full background and deeper SOP, read:
- [CLI-ifying Electron Applications](/advanced/electron)
- [Chrome DevTools Protocol](/advanced/cdp)
- [TypeScript Adapter Guide](/developer/ts-adapter)
## When to use this guide
Use this workflow when the target app:
- is built with **Electron**, or at least exposes a working **Chrome DevTools Protocol (CDP)** endpoint
- can be launched with `--remote-debugging-port=<port>`
- should be automated through its real UI instead of a public HTTP API
If the app is **not** Electron and does **not** expose CDP, use the native desktop automation pattern instead. See [CLI-ifying Electron Applications](/advanced/electron#non-electron-pattern-applescript).
## The shortest path
### 1. Confirm the app is Electron
Typical macOS check:
```bash
ls /Applications/AppName.app/Contents/Frameworks/Electron\ Framework.framework
```
If Electron is present, the next step is usually to launch the app with a debugging port.
### 2. Launch it with CDP enabled
```bash
/Applications/AppName.app/Contents/MacOS/AppName --remote-debugging-port=9222
```
Then point OpenCLI at that CDP endpoint:
```bash
export OPENCLI_CDP_ENDPOINT="http://127.0.0.1:9222"
```
### 3. Start with the 5-command pattern
For a new Electron adapter, implement these commands first in `clis/<app>/`:
- `status.js` — verify the app is reachable through CDP
- `dump.js` — inspect DOM and snapshot structure before guessing selectors
- `read.js` — extract the visible context you actually need
- `send.js` — inject text and submit through the real editor
- `new.js` — create a new session, tab, thread, or document
This is the standard baseline because it gives you:
- a connection check
- a reverse-engineering tool
- one read path
- one write path
- one session reset path
The full rationale and examples are in [CLI-ifying Electron Applications](/advanced/electron).
## Recommended implementation workflow
### Step 1: Build `status`
Goal: prove CDP connectivity before touching app-specific logic.
Typical checks:
- current URL
- document title
- app shell presence
If `status` is unstable, stop there and fix connectivity first.
### Step 2: Build `dump`
Do **not** guess selectors from the rendered UI.
Dump:
- `document.body.innerHTML`
- accessibility snapshot
- any stable attributes such as `data-testid`, `role`, `aria-*`, framework-specific markers
Use the dump to identify real containers, buttons, composers, and conversation regions.
### Step 3: Build `read`
Target only the app region that matters.
Good targets:
- message list
- editor history
- visible thread content
- selected document panel
Avoid dumping the entire page text into the final command output.
### Step 4: Build `send`
Most Electron apps use React-style controlled editors, so direct `.value = ...` assignments are often ignored.
Prefer editor-aware input patterns such as:
- focus the editable region
- use `document.execCommand('insertText', false, text)` when applicable
- use real key presses like `Enter`, `Meta+Enter`, or app-specific shortcuts
### Step 5: Build `new`
Many desktop apps rely on keyboard shortcuts for “new chat”, “new tab”, or “new note”.
Typical pattern:
```ts
const isMac = process.platform === 'darwin';
await page.pressKey(isMac ? 'Meta+N' : 'Control+N');
await page.wait(1);
```
## Where to put files
For a desktop adapter, the usual layout is:
```text
clis/<app>/status.js
clis/<app>/dump.js
clis/<app>/read.js
clis/<app>/send.js
clis/<app>/new.js
clis/<app>/utils.js
```
If the app grows beyond the baseline, add higher-level commands such as:
- `ask`
- `history`
- `model`
- `screenshot`
- `export`
## What to document when you add a new app
When the adapter is ready, also add:
- an adapter doc under `docs/adapters/desktop/`
- command list and examples
- launch instructions with `--remote-debugging-port`
- any required environment variables
- platform-specific caveats
Examples to study:
- `docs/adapters/desktop/codex.md`
- `docs/adapters/desktop/chatwise.md`
- `docs/adapters/desktop/discord.md`
## Common failure modes
### CDP endpoint exists, but commands are flaky
Usually one of these:
- the wrong window/tab is selected
- the app has not finished rendering
- selectors were guessed instead of discovered from `dump`
- the editor is controlled and ignores direct value assignment
### The app is Chromium-based but not truly controllable
Some desktop apps embed Chromium but do not expose a usable CDP surface.
In that case, switch to the non-Electron desktop automation approach instead of forcing the Electron pattern.
### You already have a browser workflow and wonder whether to reuse it
If the app exposes a normal web URL and the browser flow is enough, a browser adapter is usually simpler.
Use an Electron adapter only when the desktop app is the real integration surface.
## Recommended reading order
If you are starting from zero:
1. This page
2. [CLI-ifying Electron Applications](/advanced/electron)
3. [Chrome DevTools Protocol](/advanced/cdp)
4. [TypeScript Adapter Guide](/developer/ts-adapter)
5. One concrete desktop adapter doc under `docs/adapters/desktop/`
## Practical rule
Do not start with a large feature surface.
Start with:
- `status`
- `dump`
- `read`
- `send`
- `new`
Once those are stable, extend outward.