masquerator-coder/dsh-im-gateway ↗★ 1

dsh-im-gateway

DeepSeek Harness (DSH) IM gateway plugin: foolproof multi-channel (5G消息 / email / feishu / wechat / qq / http) -> per-chat persistent Agent -> reply routing. 适合需要将5G消息、微信、飞书等通道接入DSH智能体的用户。

패키지
dsh-im-gateway
호환성
미검증
Harness peer 범위
>=0.1.0-rc.8 <0.2.0
Cordis peer 범위
^4.0.2
버전
0.1.0
라이선스
MIT
최근 업데이트
2026. 9. 15.

같은 패키지 이름의 다른 저장소

설치

$npx -p @deepseek-ai/dsh dsh plugin --profile web add github:masquerator-coder/dsh-im-gateway

Configuration (via cordis.yml)

KeyDefaultMeaning
host127.0.0.1Inbound listen address
port8799Inbound listen port
inboundPath/imWebhook URL path
secret''Optional shared secret; requests must send it in header x-im-secret. Empty = no auth.
chatIdFieldchat_idWebhook JSON body field identifying the chat
textFieldtextWebhook JSON body field carrying the message text
senderFieldsender_idOptional body field for the sender id (used by allowlist + source injection)
allowlist[]Sender allowlist (access control). Non-empty ⇒ only these senderIds may drive the agent; others / sender-less are denied up front
callbackUrl(required)URL the reply is POSTed to
callbackChatHeaderx-im-chat-idHeader holding the chat id on the callback
callbackSecretHeaderx-im-secretHeader holding the secret on the callback
provider''Model provider route override (empty = runtime default model)
model''Model id override (empty = runtime default model)
maxTokens0Positive output cap, or 0 for default
agentPreset''Optional agent preset applied on creation
cwd''Optional working directory for the Agent session (a real Harness workspace)
disposeAfterReplyfalseDispose the Agent after each reply (frees resources, drops context)

⚠️ Only host/port/inboundPath/chatIdField/textField/senderField/allowlist/callbackChatHeader/callbackSecretHeader and the checkbox-like fields are non-sensitive wiring. secret and callbackUrl are deployment secrets — never commit real values. Keep your cordis.yml secret in .env/local overrides and out of the repository.


Usage

The plugin ships in two interchangeable forms:

  • a bundle (recommended for deployment) — installed by package name, loads the built lib/index.js;
  • a local source overlay (development) — --patch against src/ for fast iteration.

Install as a bundle

Add the bundle to a profile. The built lib/ is committed, and the package has no prepare/postinstall script, so nothing runs on install:

dsh plugin --profile demo add github:you/dsh-im-gateway

No allowBuilds entry is needed — on this machine or on any sharee's. pnpm ≥ 10 refuses to run an unapproved dependency build script and exits non-zero, which dsh plugin reports as a failed install ("add the exact key pnpm printed above under allowBuilds in …"), so one stray postinstall anywhere in the runtime dependency closure would break the one-command install for every user. This package therefore guarantees that closure is empty of them: the Feishu SDK — whose hard dependency protobufjs ships a purely cosmetic postinstall — is vendored into lib/vendor/lark-sdk.cjs instead of being installed, and pnpm build runs scripts/check-install-scripts.mjs, which fails the build the moment any runtime dependency (transitively) gains a preinstall/install/postinstall script. Verified by installing this package on a clean profile with no allowBuilds section at all: pnpm exits 0. Contributor rule: because lib/ is committed, every src/ change must ship with its rebuilt lib/ (pnpm build then commit) — otherwise the distributed version runs a stale bundle. For a single-file artifact instead of a Git install, run pnpm pack and dsh plugin --profile demo add ./dsh-im-gateway-.tgz.

Upgrading from a version that needed allowBuilds? If an earlier install failure already wrote a protobufjs: placeholder into your profile's pnpm-workspace.yaml (C:\Users\\.dsh\profiles\ \pnpm-workspace.yaml), delete that line — protobufjs is no longer part of the dependency tree — and re-run the add command.

The bundle's layer is cordis.patch.yml, which inserts the im-gateway row with sensible defaults. Override any key from your profile's own cordis.patch.yml (a later layer wins per row and replaces the whole config, so restate every key).

Load the local source overlay (development)

From the DSH repository root (after the run-from-source path), start the Web UI with this overlay:

pnpm dsh web --patch /path/to/dsh-im-gateway/cordis.yml

cordis.yml example:

- insert:
    - id: im-gateway
      name: './src/index.ts'
      config:
        inboundPath: '/im'
        secret: 'change-me'
        chatIdField: 'chat_id'
        textField: 'text'
        senderField: 'sender_id'
        allowlist: ['user-7']
        callbackUrl: 'https://your-im-bridge.example/reply'
        provider: 'deepseek'
        model: 'deepseek-chat'

External IM → gateway (legacy HTTP webhook)

The settings UI is the primary way to attach channels (see above). The legacy single HTTP webhook path below is retained for back-compat / headless setups.

POST messages to http://: /im:

{ "chat_id": "group-42|user-7", "sender_id": "user-7", "text": "你好" }

Send header x-im-secret: when secret is set. The gateway responds 202 { ok: true } immediately once the message is accepted — it does not wait for the model turn. The agent reply always arrives later over the callback (see below). When allowlist is set and sender_id is not in it (or missing), the message is denied up front (a 202 is still returned) — no agent turn, no reply.

Gateway → external IM (outbound callback)

The collected reply is POSTed to callbackUrl (with up to 2 delivery attempts; a give-up is logged reply NOT delivered):

POST 
x-im-chat-id: group-42|user-7
x-im-secret: change-me

{ "chat_id": "group-42|user-7", "text": "", "ts": 1710000000000 }