@xmanrui/dsh-im 1.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +2 -0
- package/README.md +2 -0
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/lib/index.js +149 -149
- package/package.json +2 -1
- package/plugin-src/host/channels/feishu/production.mjs +26 -1
- package/scripts/verify-package.mjs +3 -2
- package/src/channels/discord/discord-api.mjs +1 -1
- package/src/channels/feishu/feishu-app.mjs +18 -15
- package/src/channels/feishu/feishu-runtime.mjs +4 -0
- package/src/channels/weixin/weixin-api.mjs +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xmanrui/dsh-im",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "把九种 IM 机器人和公网 AI Office 接入本机 DeepSeek Harness。 Connect nine IM channels and a public AI Office to a local DeepSeek Harness.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"deepseek-harness",
|
|
@@ -99,6 +99,7 @@
|
|
|
99
99
|
"@larksuiteoapi/node-sdk": "1.73.0",
|
|
100
100
|
"@whiskeysockets/baileys": "7.0.0-rc14",
|
|
101
101
|
"esbuild": "0.25.9",
|
|
102
|
+
"https-proxy-agent": "5.0.1",
|
|
102
103
|
"react": "18.3.1",
|
|
103
104
|
"react-dom": "18.3.1",
|
|
104
105
|
"react-test-renderer": "18.3.1"
|
|
@@ -2,6 +2,7 @@ import { homedir } from 'node:os';
|
|
|
2
2
|
import { join, resolve } from 'node:path';
|
|
3
3
|
import { unlink } from 'node:fs/promises';
|
|
4
4
|
import * as Lark from '@larksuiteoapi/node-sdk';
|
|
5
|
+
import HttpsProxyAgent from 'https-proxy-agent';
|
|
5
6
|
import { createConnectionSupervisor } from './connection-supervisor.mjs';
|
|
6
7
|
import { createHarnessCommandExecutor } from '../../harness-command-executor.mjs';
|
|
7
8
|
import { createHarnessSessionExecutors } from '../../harness-session-coordinator.mjs';
|
|
@@ -22,6 +23,22 @@ import {
|
|
|
22
23
|
} from '../../../../src/channels/shared/bot-workspace-store.mjs';
|
|
23
24
|
import { listAgentPresetCatalog } from '../../../../src/channels/shared/agent-preset.mjs';
|
|
24
25
|
|
|
26
|
+
function webSocketProxyUrl(env) {
|
|
27
|
+
for (const key of ['https_proxy', 'HTTPS_PROXY', 'http_proxy', 'HTTP_PROXY']) {
|
|
28
|
+
const value = env?.[key];
|
|
29
|
+
if (typeof value === 'string' && value.trim()) return value.trim();
|
|
30
|
+
}
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function createFeishuWebSocketAgent(
|
|
35
|
+
env,
|
|
36
|
+
createAgent = (url) => new HttpsProxyAgent(url),
|
|
37
|
+
) {
|
|
38
|
+
const proxyUrl = webSocketProxyUrl(env);
|
|
39
|
+
return proxyUrl ? createAgent(proxyUrl) : undefined;
|
|
40
|
+
}
|
|
41
|
+
|
|
25
42
|
function harnessOrigin(webServer, configured) {
|
|
26
43
|
if (configured !== undefined) return new URL(configured);
|
|
27
44
|
const port = webServer?.port;
|
|
@@ -61,7 +78,11 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
61
78
|
const SessionStateStore = internals.StateStore ?? StateStore;
|
|
62
79
|
const Harness = internals.HarnessClient ?? HarnessClient;
|
|
63
80
|
const Runtime = internals.FeishuRuntime ?? FeishuRuntime;
|
|
64
|
-
const
|
|
81
|
+
const verify = internals.verifyFeishuApp ?? verifyFeishuApp;
|
|
82
|
+
const verifyApp = (options) => verify({
|
|
83
|
+
...options,
|
|
84
|
+
httpInstance: lark.defaultHttpInstance,
|
|
85
|
+
});
|
|
65
86
|
const createSupervisor = internals.createConnectionSupervisor ?? createConnectionSupervisor;
|
|
66
87
|
const logger = typeof ctx.logger === 'function'
|
|
67
88
|
? ctx.logger('dsh-feishu')
|
|
@@ -127,6 +148,8 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
127
148
|
...(controlExecutor ? { controlExecutor } : {}),
|
|
128
149
|
...(sessionMaintenanceExecutor ? { sessionMaintenanceExecutor } : {}),
|
|
129
150
|
});
|
|
151
|
+
const proxyEnv = internals.proxyEnv ?? process.env;
|
|
152
|
+
const wsAgent = createFeishuWebSocketAgent(proxyEnv, internals.createProxyAgent);
|
|
130
153
|
|
|
131
154
|
const coreController = new Controller({
|
|
132
155
|
registerApp: (options) => lark.registerApp(options),
|
|
@@ -153,6 +176,7 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
153
176
|
harness: workspaceScope.harness,
|
|
154
177
|
state: workspaceScope.state,
|
|
155
178
|
replyTimeoutMs: config.replyTimeoutMs ?? 600_000,
|
|
179
|
+
...(wsAgent ? { wsAgent } : {}),
|
|
156
180
|
logger: {
|
|
157
181
|
error: (...args) => logger.error?.(`[${botId ?? botConfig.id}]`, ...args),
|
|
158
182
|
warn: (...args) => logger.warn?.(`[${botId ?? botConfig.id}]`, ...args),
|
|
@@ -190,6 +214,7 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
190
214
|
await supervisor.close();
|
|
191
215
|
await controller.close();
|
|
192
216
|
harness.stopManagedProcess();
|
|
217
|
+
wsAgent?.destroy?.();
|
|
193
218
|
},
|
|
194
219
|
};
|
|
195
220
|
}
|
|
@@ -142,6 +142,7 @@ for (const [name, version] of Object.entries(directDependencies)) {
|
|
|
142
142
|
const bundledBuildDependencies = {
|
|
143
143
|
'@larksuiteoapi/node-sdk': '1.73.0',
|
|
144
144
|
'@whiskeysockets/baileys': '7.0.0-rc14',
|
|
145
|
+
'https-proxy-agent': '5.0.1',
|
|
145
146
|
};
|
|
146
147
|
for (const [name, version] of Object.entries(bundledBuildDependencies)) {
|
|
147
148
|
if (manifest.dependencies?.[name] !== undefined) {
|
|
@@ -157,8 +158,8 @@ if (lock.packages?.['node_modules/protobufjs']?.dev !== true) {
|
|
|
157
158
|
if (manifest.bin?.['dsh-im'] !== 'bin/dsh-im.mjs') {
|
|
158
159
|
throw new Error('package manifest must publish the dsh-im executable');
|
|
159
160
|
}
|
|
160
|
-
if (/(?:from\s*|import\s*\(|require\s*\()\s*["'](?:@larksuiteoapi\/node-sdk|@whiskeysockets\/baileys|protobufjs)(?:\/[^"']*)?["']/.test(host)) {
|
|
161
|
-
throw new Error('host bundle must not import a bundled SDK or protobufjs at runtime');
|
|
161
|
+
if (/(?:from\s*|import\s*\(|require\s*\()\s*["'](?:@larksuiteoapi\/node-sdk|@whiskeysockets\/baileys|https-proxy-agent|protobufjs)(?:\/[^"']*)?["']/.test(host)) {
|
|
162
|
+
throw new Error('host bundle must not import a bundled SDK, proxy agent, or protobufjs at runtime');
|
|
162
163
|
}
|
|
163
164
|
if ((executable.mode & 0o111) === 0) throw new Error('dsh-im CLI is not executable');
|
|
164
165
|
if (/private-bot-token|must-be-rolled-back|DEEPSEEK_API_KEY=/.test(client + host)) {
|
|
@@ -108,7 +108,7 @@ export class DiscordApi {
|
|
|
108
108
|
headers: {
|
|
109
109
|
authorization: `Bot ${this.#token}`,
|
|
110
110
|
'content-type': 'application/json',
|
|
111
|
-
'user-agent': 'DeepSeek-Harness-dsh-im (https://github.com/xmanrui/dsh-im, 1.0.
|
|
111
|
+
'user-agent': 'DeepSeek-Harness-dsh-im (https://github.com/xmanrui/dsh-im, 1.0.2)',
|
|
112
112
|
},
|
|
113
113
|
...(body === undefined ? {} : { body: JSON.stringify(body) }),
|
|
114
114
|
signal: requestSignal(signal, timeoutMs),
|
|
@@ -3,15 +3,12 @@ function endpointFor(domain, path) {
|
|
|
3
3
|
return new URL(path, origin);
|
|
4
4
|
}
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
try {
|
|
9
|
-
body = await response.json();
|
|
10
|
-
} catch {
|
|
6
|
+
function jsonResponse(body, operation) {
|
|
7
|
+
if (!body || typeof body !== 'object' || Array.isArray(body)) {
|
|
11
8
|
throw new Error(`${operation} returned a non-JSON response`);
|
|
12
9
|
}
|
|
13
|
-
if (
|
|
14
|
-
throw new Error(`${operation} failed: ${body
|
|
10
|
+
if (body.code !== 0) {
|
|
11
|
+
throw new Error(`${operation} failed: ${body.msg || `code ${body.code}`}`);
|
|
15
12
|
}
|
|
16
13
|
return body;
|
|
17
14
|
}
|
|
@@ -21,26 +18,32 @@ export async function verifyFeishuApp({
|
|
|
21
18
|
appId,
|
|
22
19
|
appSecret,
|
|
23
20
|
domain = 'feishu',
|
|
24
|
-
|
|
21
|
+
httpInstance,
|
|
25
22
|
timeoutMs = 15000,
|
|
26
23
|
}) {
|
|
27
24
|
if (!appId || !appSecret) throw new Error('Feishu credentials are incomplete');
|
|
28
|
-
|
|
25
|
+
if (!httpInstance || typeof httpInstance.request !== 'function') {
|
|
26
|
+
throw new TypeError('Feishu verification requires an HTTP instance');
|
|
27
|
+
}
|
|
28
|
+
const tokenBody = jsonResponse(await httpInstance.request({
|
|
29
29
|
method: 'POST',
|
|
30
|
+
url: endpointFor(domain, '/open-apis/auth/v3/tenant_access_token/internal').href,
|
|
30
31
|
headers: { 'content-type': 'application/json; charset=utf-8' },
|
|
31
|
-
|
|
32
|
+
data: { app_id: appId, app_secret: appSecret },
|
|
32
33
|
signal: AbortSignal.timeout(timeoutMs),
|
|
33
|
-
|
|
34
|
-
|
|
34
|
+
timeout: timeoutMs,
|
|
35
|
+
}), 'Feishu authentication');
|
|
35
36
|
if (!tokenBody.tenant_access_token) {
|
|
36
37
|
throw new Error('Feishu authentication returned no tenant access token');
|
|
37
38
|
}
|
|
38
39
|
|
|
39
|
-
const
|
|
40
|
+
const botBody = jsonResponse(await httpInstance.request({
|
|
41
|
+
method: 'GET',
|
|
42
|
+
url: endpointFor(domain, '/open-apis/bot/v3/info/').href,
|
|
40
43
|
headers: { authorization: `Bearer ${tokenBody.tenant_access_token}` },
|
|
41
44
|
signal: AbortSignal.timeout(timeoutMs),
|
|
42
|
-
|
|
43
|
-
|
|
45
|
+
timeout: timeoutMs,
|
|
46
|
+
}), 'Feishu bot verification');
|
|
44
47
|
const bot = botBody.bot ?? {};
|
|
45
48
|
return Object.freeze({
|
|
46
49
|
appId,
|
|
@@ -96,6 +96,7 @@ export class FeishuRuntime {
|
|
|
96
96
|
#replyTimeoutMs;
|
|
97
97
|
#connectTimeoutMs;
|
|
98
98
|
#requestTimeoutMs;
|
|
99
|
+
#wsAgent;
|
|
99
100
|
#logger;
|
|
100
101
|
#repair;
|
|
101
102
|
#client = null;
|
|
@@ -122,6 +123,7 @@ export class FeishuRuntime {
|
|
|
122
123
|
replyTimeoutMs = 600000,
|
|
123
124
|
connectTimeoutMs = 15000,
|
|
124
125
|
requestTimeoutMs = DEFAULT_REQUEST_TIMEOUT_MS,
|
|
126
|
+
wsAgent,
|
|
125
127
|
logger = console,
|
|
126
128
|
}) {
|
|
127
129
|
if (!lark) throw new Error('FeishuRuntime requires the Feishu SDK');
|
|
@@ -152,6 +154,7 @@ export class FeishuRuntime {
|
|
|
152
154
|
this.#replyTimeoutMs = replyTimeoutMs;
|
|
153
155
|
this.#connectTimeoutMs = connectTimeoutMs;
|
|
154
156
|
this.#requestTimeoutMs = requestTimeoutMs;
|
|
157
|
+
this.#wsAgent = wsAgent;
|
|
155
158
|
this.#logger = logger;
|
|
156
159
|
this.#status = createBridgeStatus({ allowedSenderCount: normalizedOwners.length });
|
|
157
160
|
}
|
|
@@ -266,6 +269,7 @@ export class FeishuRuntime {
|
|
|
266
269
|
|
|
267
270
|
this.#wsClient = new this.#lark.WSClient({
|
|
268
271
|
...larkConfig,
|
|
272
|
+
...(this.#wsAgent ? { agent: this.#wsAgent } : {}),
|
|
269
273
|
loggerLevel: this.#lark.LoggerLevel.info,
|
|
270
274
|
handshakeTimeoutMs: 15000,
|
|
271
275
|
onReady: () => {
|