diff --git a/FORK.md b/FORK.md
new file mode 100644
index 0000000..954a35d
--- /dev/null
+++ b/FORK.md
@@ -0,0 +1,43 @@
+# dsh-im-ops(运维 fork)
+
+本仓库是 [`@xmanrui/dsh-im@4.9.1`](https://github.com/xmanrui/dsh-im) 的 **完整 fork**(九渠道 + AI Office 均保留),用于 Netx / 运维场景下自控:
+
+- 访问控制(入站白名单 / 群策略)
+- 会话策略(例如群聊拆个人 Session)
+- 通告 / 投递默认与文案
+
+上游 remote 名为 `upstream`。不要与社区包 `@xmanrui/dsh-im` 同时装进同一 profile。
+
+## 与已删除的薄壳 `ops-im` 的关系
+
+曾尝试只做 `ops_im__send` 适配层;因 **入站权限/会话仍在 dsh-im**,无法满足所有权诉求,已删除该薄仓库,改为本 fork。
+
+## 安装(替换社区 im)
+
+```powershell
+# 若已安装社区包,先从 profile 移除 @xmanrui/dsh-im
+cd D:\project\chatgpt\dsh-im-ops
+npm install
+npm run build
+dsh plugin --profile web add -w "D:\project\chatgpt\dsh-im-ops"
+# 重启 dsh web
+```
+
+包名:`dsh-im-ops@4.9.1-ops.0`(cordis id:`dsh-im-ops`)。
+
+扫码态一般仍在 `~/.dsh/integrations/…`;换包后若异常,在 IM 设置里重新关联设备。
+
+## 第一刀改动方向(尚未改业务逻辑)
+
+1. WhatsApp / 通用 access mode:运维友好的默认与设置文案
+2. 群 Session key:对齐 oclaw `user_in_chat`(可配置)
+3. 投递默认目标 / 与 netxops 协作说明
+
+业务改动前先 `npm run build` 冒烟扫码与私聊。
+
+## 同步上游
+
+```powershell
+git fetch upstream --tags
+git merge v4.9.x # 或 cherry-pick;冲突自行解决后再 build
+```
diff --git a/README.md b/README.md
index c9af3ee..f65a535 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-
+> **Fork:** dsh-im-ops — ops-owned fork of [@xmanrui/dsh-im@4.9.1](https://github.com/xmanrui/dsh-im). See [FORK.md](FORK.md). Do not install alongside the community package.\n\n
diff --git a/cordis.patch.yml b/cordis.patch.yml
index 6798e9a..0ed36bb 100644
--- a/cordis.patch.yml
+++ b/cordis.patch.yml
@@ -1,3 +1,3 @@
- insert:
- - id: xmanrui-dsh-im
- name: '@xmanrui/dsh-im'
+ - id: dsh-im-ops
+ name: dsh-im-ops
diff --git a/package.json b/package.json
index f74ea77..957ecff 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
- "name": "@xmanrui/dsh-im",
- "version": "4.9.1",
- "description": "把九种 IM 机器人和公网 AI Office 接入本机 DeepSeek Harness。 Connect nine IM channels and a public AI Office to a local DeepSeek Harness.",
+ "name": "dsh-im-ops",
+ "version": "4.9.1-ops.0",
+ "description": "Ops fork of dsh-im (all channels kept). Based on @xmanrui/dsh-im@4.9.1 — access/session policies owned here.",
"keywords": [
"deepseek-harness",
"dsh",
@@ -20,7 +20,9 @@
"telegram",
"discord",
"whatsapp",
- "ai-office"
+ "ai-office",
+ "ops",
+ "fork"
],
"author": "xmanrui (https://github.com/xmanrui)",
"contributors": [
@@ -41,14 +43,11 @@
"type": "module",
"repository": {
"type": "git",
- "url": "git+https://github.com/xmanrui/dsh-im.git"
+ "url": "git+https://github.com/hansjone/dsh-im-ops.git"
},
- "homepage": "https://github.com/xmanrui/dsh-im#readme",
+ "homepage": "https://github.com/hansjone/dsh-im-ops#readme",
"bugs": {
- "url": "https://github.com/xmanrui/dsh-im/issues"
- },
- "publishConfig": {
- "access": "public"
+ "url": "https://github.com/hansjone/dsh-im-ops/issues"
},
"bin": {
"dsh-im": "bin/dsh-im.mjs"
diff --git a/plugin-src/client/build.mjs b/plugin-src/client/build.mjs
index 5b53de7..8e2c519 100644
--- a/plugin-src/client/build.mjs
+++ b/plugin-src/client/build.mjs
@@ -7,7 +7,7 @@ import { build } from 'esbuild';
const sourceDirectory = dirname(fileURLToPath(import.meta.url));
const packageRoot = resolve(sourceDirectory, '../..');
const outputPath = resolve(packageRoot, 'lib/client.js');
-const loaderId = process.env.DSH_IM_CLIENT_ID ?? '@xmanrui/dsh-im';
+const loaderId = process.env.DSH_IM_CLIENT_ID ?? 'dsh-im-ops';
const result = await build({
entryPoints: [resolve(sourceDirectory, 'index.js')],
diff --git a/plugin-src/client/channels/dingtalk/styles.js b/plugin-src/client/channels/dingtalk/styles.js
index 189b84d..b9479f6 100644
--- a/plugin-src/client/channels/dingtalk/styles.js
+++ b/plugin-src/client/channels/dingtalk/styles.js
@@ -128,7 +128,7 @@ export function installDingtalkStyles() {
const existing = document.querySelector(`style[data-plugin-css="${DINGTALK_STYLE_ID}"]`);
if (existing) return () => {};
const style = document.createElement('style');
- style.dataset.plugin = '@xmanrui/dsh-im';
+ style.dataset.plugin = 'dsh-im-ops';
style.dataset.pluginCss = DINGTALK_STYLE_ID;
style.textContent = CSS;
document.head.appendChild(style);
diff --git a/plugin-src/client/channels/discord/styles.js b/plugin-src/client/channels/discord/styles.js
index 9a67dd2..dd4f3a1 100644
--- a/plugin-src/client/channels/discord/styles.js
+++ b/plugin-src/client/channels/discord/styles.js
@@ -11,7 +11,7 @@ export function installDiscordStyles() {
const existing = document.querySelector(`style[data-plugin-css="${DISCORD_STYLE_ID}"]`);
if (existing) return () => {};
const style = document.createElement('style');
- style.dataset.plugin = '@xmanrui/dsh-im';
+ style.dataset.plugin = 'dsh-im-ops';
style.dataset.pluginCss = DISCORD_STYLE_ID;
style.textContent = CSS;
document.head.appendChild(style);
diff --git a/plugin-src/client/channels/feishu/styles.js b/plugin-src/client/channels/feishu/styles.js
index ba000da..67b0303 100644
--- a/plugin-src/client/channels/feishu/styles.js
+++ b/plugin-src/client/channels/feishu/styles.js
@@ -562,7 +562,7 @@ export function installFeishuStyles() {
}
const style = document.createElement("style");
- style.dataset.plugin = "@xmanrui/dsh-im";
+ style.dataset.plugin = "dsh-im-ops";
style.dataset.pluginCss = FEISHU_STYLE_ID;
style.textContent = CSS;
document.head.appendChild(style);
diff --git a/plugin-src/client/channels/qq/styles.js b/plugin-src/client/channels/qq/styles.js
index 6131c08..af465e8 100644
--- a/plugin-src/client/channels/qq/styles.js
+++ b/plugin-src/client/channels/qq/styles.js
@@ -11,7 +11,7 @@ export function installQqStyles() {
const existing = document.querySelector(`style[data-plugin-css="${QQ_STYLE_ID}"]`);
if (existing) return () => {};
const style = document.createElement('style');
- style.dataset.plugin = '@xmanrui/dsh-im';
+ style.dataset.plugin = 'dsh-im-ops';
style.dataset.pluginCss = QQ_STYLE_ID;
style.textContent = CSS;
document.head.appendChild(style);
diff --git a/plugin-src/client/channels/slack/styles.js b/plugin-src/client/channels/slack/styles.js
index 388141f..8173254 100644
--- a/plugin-src/client/channels/slack/styles.js
+++ b/plugin-src/client/channels/slack/styles.js
@@ -26,7 +26,7 @@ export function installSlackStyles() {
const existing = document.querySelector(`style[data-plugin-css="${SLACK_STYLE_ID}"]`);
if (existing) return () => {};
const style = document.createElement('style');
- style.dataset.plugin = '@xmanrui/dsh-im';
+ style.dataset.plugin = 'dsh-im-ops';
style.dataset.pluginCss = SLACK_STYLE_ID;
style.textContent = CSS;
document.head.appendChild(style);
diff --git a/plugin-src/client/channels/telegram/styles.js b/plugin-src/client/channels/telegram/styles.js
index 9c9c6bb..e6cd34f 100644
--- a/plugin-src/client/channels/telegram/styles.js
+++ b/plugin-src/client/channels/telegram/styles.js
@@ -11,7 +11,7 @@ export function installTelegramStyles() {
const existing = document.querySelector(`style[data-plugin-css="${TELEGRAM_STYLE_ID}"]`);
if (existing) return () => {};
const style = document.createElement('style');
- style.dataset.plugin = '@xmanrui/dsh-im';
+ style.dataset.plugin = 'dsh-im-ops';
style.dataset.pluginCss = TELEGRAM_STYLE_ID;
style.textContent = CSS;
document.head.appendChild(style);
diff --git a/plugin-src/client/channels/wecom/styles.js b/plugin-src/client/channels/wecom/styles.js
index 1406c54..5871ee1 100644
--- a/plugin-src/client/channels/wecom/styles.js
+++ b/plugin-src/client/channels/wecom/styles.js
@@ -11,7 +11,7 @@ export function installWecomStyles() {
const existing = document.querySelector(`style[data-plugin-css="${WECOM_STYLE_ID}"]`);
if (existing) return () => {};
const style = document.createElement('style');
- style.dataset.plugin = '@xmanrui/dsh-im';
+ style.dataset.plugin = 'dsh-im-ops';
style.dataset.pluginCss = WECOM_STYLE_ID;
style.textContent = CSS;
document.head.appendChild(style);
diff --git a/plugin-src/client/channels/weixin/styles.js b/plugin-src/client/channels/weixin/styles.js
index 152e2c0..fa07a82 100644
--- a/plugin-src/client/channels/weixin/styles.js
+++ b/plugin-src/client/channels/weixin/styles.js
@@ -107,7 +107,7 @@ export function installWeixinStyles() {
const existing = document.querySelector(`style[data-plugin-css="${WEIXIN_STYLE_ID}"]`);
if (existing) return () => {};
const style = document.createElement('style');
- style.dataset.plugin = '@xmanrui/dsh-im';
+ style.dataset.plugin = 'dsh-im-ops';
style.dataset.pluginCss = WEIXIN_STYLE_ID;
style.textContent = CSS;
document.head.appendChild(style);
diff --git a/plugin-src/client/channels/whatsapp/styles.js b/plugin-src/client/channels/whatsapp/styles.js
index 338f042..524733f 100644
--- a/plugin-src/client/channels/whatsapp/styles.js
+++ b/plugin-src/client/channels/whatsapp/styles.js
@@ -11,7 +11,7 @@ export function installWhatsappStyles() {
const existing = document.querySelector(`style[data-plugin-css="${WHATSAPP_STYLE_ID}"]`);
if (existing) return () => {};
const style = document.createElement('style');
- style.dataset.plugin = '@xmanrui/dsh-im';
+ style.dataset.plugin = 'dsh-im-ops';
style.dataset.pluginCss = WHATSAPP_STYLE_ID;
style.textContent = CSS;
document.head.appendChild(style);
diff --git a/plugin-src/client/styles.js b/plugin-src/client/styles.js
index 90a5e9c..0e3a08b 100644
--- a/plugin-src/client/styles.js
+++ b/plugin-src/client/styles.js
@@ -543,7 +543,7 @@ export function installImStyles() {
const existing = document.querySelector(`style[data-plugin-css="${IM_STYLE_ID}"]`);
if (existing) return () => {};
const style = document.createElement('style');
- style.dataset.plugin = '@xmanrui/dsh-im';
+ style.dataset.plugin = 'dsh-im-ops';
style.dataset.pluginCss = IM_STYLE_ID;
style.textContent = CSS;
document.head.appendChild(style);
diff --git a/plugin-src/client/update-panel.js b/plugin-src/client/update-panel.js
index 5e95e8b..b0432ac 100644
--- a/plugin-src/client/update-panel.js
+++ b/plugin-src/client/update-panel.js
@@ -109,7 +109,7 @@ function manualUpdateCommand(snapshot) {
? [...targets, snapshot.runningVersion, snapshot.installedVersion]
.filter(validVersion).sort(compareVersionsDescending)[0]
: 'latest';
- return `dsh plugin --profile ${profileArgument} add -w @xmanrui/dsh-im@${version}`;
+ return `dsh plugin --profile ${profileArgument} add -w dsh-im-ops@${version}`;
}
function ManualUpdateCommand({ command, disabled, sourceInstall, desktop }) {
@@ -167,7 +167,7 @@ function ManualUpdateCommand({ command, disabled, sourceInstall, desktop }) {
: h(React.Fragment, null,
h('rect', { x: 7, y: 7, width: 10, height: 11, rx: 1.5 }),
h('path', { d: 'M5 13H3.5A1.5 1.5 0 0 1 2 11.5v-8A1.5 1.5 0 0 1 3.5 2h8A1.5 1.5 0 0 1 13 3.5V5' }))))),
- command.endsWith('@xmanrui/dsh-im@latest') ? h('p', { className: 'dim-updateManualHint' },
+ command.endsWith('dsh-im-ops@latest') ? h('p', { className: 'dim-updateManualHint' },
'尚未确认目标版本,此命令安装执行时 npm 的 latest 版本。') : null,
h('p', { className: 'dim-updateManualHint' }, desktop
? '请在当前 Desktop 的内置终端执行,完成后手动重启。'
diff --git a/plugin-src/host/update-runtime.mjs b/plugin-src/host/update-runtime.mjs
index 243b38d..1e0589e 100644
--- a/plugin-src/host/update-runtime.mjs
+++ b/plugin-src/host/update-runtime.mjs
@@ -6,7 +6,7 @@ import { fileURLToPath } from 'node:url';
import semver from 'semver';
-export const PACKAGE_NAME = '@xmanrui/dsh-im';
+export const PACKAGE_NAME = 'dsh-im-ops';
export const NPM_REGISTRY = 'https://registry.npmjs.org/';
const INSTALL_TIMEOUT_MS = 15 * 60_000;
diff --git a/plugin-src/host/update-service.mjs b/plugin-src/host/update-service.mjs
index a7c9ed9..1e80a84 100644
--- a/plugin-src/host/update-service.mjs
+++ b/plugin-src/host/update-service.mjs
@@ -51,7 +51,7 @@ export async function fetchNpmRelease(fetchImpl = globalThis.fetch, timeoutMs =
const integrity = value.dist?.integrity;
if (tarball.origin !== new URL(NPM_REGISTRY).origin || tarball.username || tarball.password
|| tarball.search || tarball.hash
- || tarball.pathname !== `/@xmanrui/dsh-im/-/dsh-im-${version}.tgz`
+ || tarball.pathname !== `/dsh-im-ops/-/dsh-im-${version}.tgz`
|| typeof integrity !== 'string' || !/^sha512-[A-Za-z0-9+/]{86}==$/.test(integrity)) {
throw updateError('invalid-release');
}