Skip to content
dsh.fish
Bundle

dsh-web-kimi

Kimi Coding web search and fetch providers for the DeepSeek Harness — one coding-plan key fills both ctx.web seams, with a dedicated settings GUI card and credential fallbacks down to the Kimi CLI config file

Source
kenny2077
License
MIT
Updated
Updated yesterday

Readme

# dsh-web-kimi

**One Kimi Coding key, both halves of the web pipeline — web search *and* web fetch for the DeepSeek Harness.**

[![CI](https://github.com/kenny2077/dsh-web-kimi/actions/workflows/ci.yml/badge.svg)](https://github.com/kenny2077/dsh-web-kimi/actions/workflows/ci.yml)
[![npm version](https://img.shields.io/npm/v/dsh-web-kimi)](https://www.npmjs.com/package/dsh-web-kimi)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)

English | [中文](README.zh.md)

- [What it is](#what-it-is)
- [Why](#why)
- [How it works](#how-it-works)
- [Quick start](#quick-start)
- [Settings GUI card](#settings-gui-card)
- [Configuration](#configuration)
- [Wire mapping](#wire-mapping)
- [Troubleshooting](#troubleshooting)
- [How it compares](#how-it-compares)
- [Known limitations](#known-limitations)
- [Repository architecture](#repository-architecture)
- [Development](#development)
- [Credits](#credits)
- [License](#license)

## What it is

A DSH plugin that registers **two providers into the `ctx.web` seam**:

- **`kimi-coding`** — a `WebSearchProvider` backed by the Kimi Coding API search endpoint (`POST /v1/search`). Search results map to the harness's `WebSearchSource` shape: `url`, `title`, `snippet`, and `publishedAt` (from the result `date`).
- **`kimi-coding-fetch`** — a `WebFetchProvider` backed by the Kimi Coding API fetch endpoint (`POST /v1/fetch`). Any http(s) URL is extracted server-side — JavaScript rendering included — and returned as clean markdown.

Both halves authenticate with the **same credential** and read the **same settings section**, so a key saved once covers every web operation. A dedicated settings card in the GUI stores the key through the DSH credentials service, and a saved value takes effect on the very next search or fetch — no restart.

```
                 ┌─────────────────────────────────────────────┐
                 │                ctx.web seam                 │
                 ├──────────────────────┬──────────────────────┤
                 │   web_search tool    │    web_fetch tool     │
                 │  WebSearchProvider   │   WebFetchProvider    │
                 │      kimi-coding     │   kimi-coding-fetch   │
                 └──────────┬───────────┴──────────┬───────────┘
                            │  POST /v1/search     │  POST /v1/fetch
                            ▼                      ▼
                 ┌─────────────────────────────────────────────┐
                 │        api.kimi.com/coding/v1               │
                 │        one Bearer key, one subscription     │
                 └─────────────────────────────────────────────┘
```

## Why

The DSH ecosystem already has search aggregators and OAuth bridges; what it lacked was a **vendor-native two-seam integration for Kimi**. This package brings the coding-plan subscription you already pay for into both `web_search` routing and `web_fetch` routing — no per-call search billing, no second account, no aggregator in the middle.

If you already run Kimi Code (the CLI), it is **zero-config**: the credential chain falls back through your `~/.kimi-code/config.toml` and picks the key up on its own.

## How it works

1. **Registration** — the plugin injects into `ctx.web` and registers both providers. The bundle overlay selects `searchProvider: kimi-coding` and `fetchProvider: kimi-coding-fetch` on install.
2. **Credential resolution** (per operation, no restart) — the card's stored value wins, then the credentials service's secondary reference, then the config literal, then the launching environment, then the Kimi CLI config file:

   ```
   settings card  →  KIMI_CODING_API_KEY  →  KIMI_API_KEY  →  config apiKey
        →  launch environment  →  ~/.kimi-code/config.toml  →  ~/.kimi/config.toml
   ```

3. **Request shaping** — `text_query` plus a server-side `limit` (clamped to the API's 1–20 bound), `enable_page_crawling` from the toggle, `timeout_seconds`, and a fresh `X-Msh-Tool-Call-Id` correlation id on every call.
4. **Hardening** — only absolute http(s) URLs are forwarded or surfaced; search response bodies are bounded at 5 MB before buffering; fetch content is capped at 2 MB with a `truncated` flag. Every failure surfaces as a typed `WebError` (`WEB_PROVIDER_ERROR`, `WEB_PROVIDER_CREDENTIAL_MISSING`, `WEB_ABORTED`) with the upstream HTTP status preserved in the message.

The `web_fetch` **tool** itself stays gated by `dsh-tool-web` (the harness keeps fetch off by default over SSRF concerns); this package registers the provider and the routing for when you enable it.

## Quick start

```bash
dsh plugin --profile web add dsh-web-kimi
```

Then open **Settings → Web Search (Kimi)** in the DSH web GUI, paste your Kimi Coding API key, and Save. The key lands in the DSH credentials service (`~/.dsh/.credentials.yaml`) — never in `settings.yaml`.

No GUI? Any of these works too:

```bash
export KIMI_CODING_API_KEY=sk-...        # launching environment
# or: store through the credentials service under KIMI_CODING_API_KEY / KIMI_API_KEY
# or: ~/.kimi/config.toml with api_key = "..."   (Kimi Code CLI users — picked up automatically)
```

The provider id `kimi-coding` deliberately matches [quei4r/dsh-host-kimi-search](https://github.com/quei4r/dsh-host-kimi-search), so installing this package is a **drop-in replacement** for that script, not a duplicate-id conflict.

## Settings GUI card

The card covers three fields, all stored under credential references:

| Field | Type | Stored as |
| --- | --- | --- |
| API key | password | `KIMI_CODING_API_KEY` |
| Base URL | text | `KIMI_SEARCH_BASE_URL` |
| Page crawling | select (`true` / `false`) | `KIMI_SEARCH_PAGE_CRAWLING` |

- A masked input, a "Get an API key ↗" link straight to the [Kimi console](https://platform.kimi.com/console/account), and a live configured/not-configured badge that refreshes on save.
- Leave a field blank to keep its current value; Reset clears all three references.
- The CLI-config fallback is **read-only** — this package never writes `~/.kimi/config.toml`.

## Configuration

Settings section `web-kimi` (file-based edits of the same fields the card writes):

| Field | Default | Meaning |
| --- | --- | --- |
| `apiKey` | — | Literal key; a stored credential-reference value wins over it |
| `apiKeyEnv` | `KIMI_CODING_API_KEY` | Primary credential reference |
| `baseURL` | `https://api.kimi.com/coding/v1` | Endpoint base (`/search`, `/fetch` appended) |
| `pageCrawling` | `false` | Send `enable_page_crawling` so results carry full `content` |
| `timeoutSeconds` | `30` | Server-side `timeout_seconds` |

## Wire mapping

| Kimi `/v1/search` field | `WebSearchSource` |
| --- | --- |
| `url` | `url` (required; non-http(s) results dropped) |
| `title` | `title` (omitted when blank) |
| `snippet`, then `content` | `snippet` (first non-blank wins) |
| `date` | `publishedAt` (omitted when blank) |
| `site_name`, `icon`, `mime` | not mapped |

`/v1/fetch` responses arrive as markdown and map to `WebFetchResult { statusCode, body: { kind: 'text' }, truncated }` — content past 2 MB is capped and flagged.

## Troubleshooting

Every failure carries the HTTP status in its message, which makes most problems one-glance diagnosable:

| You see | What it means | What to do |
| --- | --- | --- |
| `url.not_found` | The base URL points at a surface without `/search` — typically the chat API (`https://api.moonshot.cn/v1`) | Set the base URL to `https://api.kimi.com/coding/v1`; the coding endpoints need a coding credential |
| `Kimi search error (HTTP 401): …` | The key was recognized but rejected — it is not a Coding credential | Use a Kimi Coding API key, not a chat/API-platform key |
| `Kimi search error (HTTP 403): …` | The key is valid, but the plan lacks the search/fetch service | Enable the search/fetch service on the coding plan |
| `… (HTTP 5xx): …` / non-JSON body | Upstream-side fault | Retry; the status tells you it is not your config |
| `WEB_PROVIDER_CREDENTIAL_MISSING` | No key resolved anywhere in the chain | Paste it in the settings card, store it under `KIMI_CODING_API_KEY` / `KIMI_API_KEY`, export it, or put it in `~/.kimi/config.toml` |
| Results carry no `content` | Page crawling is off | Turn on the page-crawling toggle |

## How it compares

| | dsh-web-kimi | dsh-web-search-doubao | dsh-web-search-zai | quei4r/dsh-host-kimi-search |
| --- | --- | --- | --- | --- |
| Seams filled | search + fetch | search | search | search |
| Credential | one coding-plan key | separate Doubao Search key | reuses `ZAI_API_KEY` | coding key chain |
| Settings GUI card | yes | yes | — | — |
| CLI-config fallback | yes | — | — | yes |
| Typed error taxonomy | yes | yes | yes | partial |
| On npm | yes | yes | yes | — |

## Known limitations

- **Entitlement**: the coding-plan account must include the search/fetch service; otherwise the endpoint answers 403.
- **`content` needs crawling**: result bodies stay empty unless `pageCrawling` is on.
- **Fetch tool gating**: enable `web_fetch` in `dsh-tool-web` to route fetches through this provider.
- **One `searchProvider` per profile**: installing this plugin switches the profile's selection from any previous search plugin; remove it (or override the config) to switch back.
- **No DeepSeek-key fallback** (a deliberate divergence from quei4r's chain): a DeepSeek API key would only 401 at `api.kimi.com`.

## Repository architecture

```
dsh-web-kimi/
├── package.json            # dsh.bundle.patch + dsh.client manifest, exports ./client
├── tsdown.config.ts        # client-half build (ModuleLoader-wrapped browser bundle)
├── cordis.patch.yml        # searchProvider + fetchProvider selection, insert entry
├── src/
│   ├── index.ts            # node entry: Config, credential chain, apply()
│   ├── provider.ts         # KimiSearchProvider + shared headers/abort plumbing
│   ├── fetch-provider.ts   # KimiFetchProvider (the second seam)
│   ├── types.ts            # wire types
│   ├── invariant.ts        # no-op invariant companion
│   └── client/
│       ├── card.tsx        # reusable settings-card factory
│       └── index.tsx       # Kimi instantiation (refs, locales, console link)
├── tests/                  # 79 unit tests across search, fetch, and card suites
└── lib/                    # committed build output — git installs need no build step
```

## Development

```bash
pnpm install
pnpm typecheck && pnpm build && pnpm test
```

- Node 22.19+ / 24, pnpm 11 — same floor as the harness.
- The test suites run against a mocked `fetch` (79 tests); the live smoke in `tests/kimi.e2e.ts` self-skips without `$KIMI_CODING_API_KEY`.
- CI runs the full gate (typecheck, build, test) on Node 22/24 × Ubuntu/Windows on every push and PR.

## Credits

The multi-source credential chain (`KIMI_CODING_API_KEY` → `KIMI_API_KEY` → Kimi CLI config) and the http(s)-only/size-cap hardening originate from [quei4r/dsh-host-kimi-search](https://github.com/quei4r/dsh-host-kimi-search) — this package extends that idea to the fetch seam, a settings card, a test suite, and npm distribution. Card architecture follows the verified `settings.section`/credential-reference conventions of the sibling DSH search plugins.

## License

[MIT](LICENSE)

Install

dsh plugin --profile web add github:kenny2077/dsh-web-kimi

Profile: web

  • This package builds from source on install. pnpm will ask you to allow its build script — that is permission to run the package’s code on your machine, outside the agent sandbox. Only allow sources you trust.
  • This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.
Source