# dsh-x-connect

[简体中文](README.zh-CN.md)

An X API v2 connector for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness). It binds one X account through OAuth 2.0 Authorization Code with PKCE and gives the agent tools to search, read, summarize, and publish posts.

## Features

- Configure and bind an account in **Settings → Plugins → X Connect**.
- Test the saved account connection from the settings card.
- Search recent posts, read a post or a user's timeline, and return structured content for the active DSH model to summarize.
- Publish posts and replies only after DSH asks the user for one-time approval by default.
- Refresh OAuth tokens locally and keep an action/cost audit log.
- Never sends an OAuth client secret: X public-client PKCE needs only a Client ID.

## Install

```sh
dsh plugin --profile web add dsh-x-connect
```

Restart `dsh web`, then open **Settings → Plugins → X Connect**. For local development, pass the repository's absolute path instead of the package name.

## X application setup

1. Create a project and app in the [X Developer Console](https://developer.x.com/).
2. Enable OAuth 2.0 and configure a public/native client with Authorization Code + PKCE.
3. Register `http://127.0.0.1:56130/callback` as a callback URI, or enter another loopback HTTP URI with an explicit port in both X and the plugin.
4. In DSH settings, enter the OAuth 2.0 Client ID and save.
5. Keep `tweet.read`, `users.read`, and `offline.access`; enable `tweet.write` for publishing.
6. Select **Generate authorization link**, authorize in X, return to DSH, and select **Test connection**.

The callback listener starts only while an authorization is pending. It is not a permanent web service and is not exposed beyond the local loopback interface.

## Agent tools

| Tool | Capability |
| --- | --- |
| `x_status` | Account, scopes, callback and estimated usage status |
| `x_connect` / `x_disconnect` | Bind or remove the local OAuth credential |
| `x_me` | Read the bound account profile |
| `x_search` | Search recent X posts |
| `x_user_tweets` | Read recent posts from an account |
| `x_tweet` | Read one post by ID or URL |
| `x_post` | Publish a post or reply after user approval |
| `x_activity_log` | Inspect or, after approval, clear local activity |

“Summarize” is intentionally not a second paid LLM integration: read/search tools return the source posts to the DSH model already handling the conversation.

## Usage examples

No command prefix is required. Ask in natural language; name the tool, call count, and result limit when you want strict cost control.

Read and summarize your five most recent original posts:

```text
Call x_user_tweets exactly once for the currently bound account with maxResults 5.
Exclude reposts and replies. Summarize each result in one sentence with its date and URL.
Do not search other accounts and do not publish anything.
```

Read one exact post:

```text
Call x_tweet exactly once for https://x.com/<username>/status/<post-id>.
Summarize its main claim and one caveat. Do not call x_search or x_post.
```

Reply in two separate turns. First request a draft without publishing:

```text
Call x_tweet exactly once for https://x.com/<username>/status/<post-id>.
Draft a reply under 100 characters with no URL. Show the draft only; do not call x_post.
```

After reviewing the draft, publish it with the numeric ID after `status/`:

```text
Call x_post exactly once to reply to <post-id> with this exact text:
<approved reply text>
Do not alter the text and do not call any other X tool.
```

Keep **Require one-time approval before posting** enabled. DSH shows the final text and estimated cost before `x_post` sends the request; rejecting the approval prevents publication.

## Billing and safety

X API access is pay-per-use and account limits can change. The plugin displays conservative estimates using the documented rates current at release, but the X Developer Console is authoritative. Search/read/post calls may consume credits even when their results are later summarized.

Current public rates in the [official X pricing documentation](https://docs.x.com/x-api/getting-started/pricing) are $0.005 per Post read, $0.010 per User read, $0.015 per plain-text content create request, and $0.200 for content creation containing a URL. Eligible owned reads are $0.001 per resource only when the authenticated user owns the developer app and the endpoint qualifies. The plugin keeps the normal Post-read rate when it cannot prove that eligibility.

The user timeline endpoint requests at least five posts, so start `x_user_tweets` with `maxResults: 5`. Prefer `x_tweet` over `x_search` when you already have the exact URL. X documents daily resource deduplication and no charge for failed requests that return no data, but deduplication is a soft guarantee; use the Developer Console bill as the source of truth and configure a spending limit there.

`x_status` and `x_activity_log` inspect local state without calling X. Calling `x_me`, **Test connection**, or a timeline lookup does call a metered X endpoint.

Credentials are written to `~/.dsh/x-connect/credentials.json` with mode `0600`; they are never returned to the browser card or model. Publishing, model-triggered disconnects, and log deletion use DSH's approval surface and fail closed if no interactive approver is available.

## Development

```sh
npm ci
npm run check
npm pack --dry-run
```

See [SECURITY.md](SECURITY.md) and [CHANGELOG.md](CHANGELOG.md).

## License

MIT
