@xmanrui/dsh-im 0.17.1 → 0.19.0
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 +3 -2
- package/README.md +3 -2
- package/lib/client.js +1297 -600
- package/lib/index.js +190 -145
- package/package.json +1 -1
- package/plugin-src/client/agent-preset.js +110 -0
- package/plugin-src/client/channels/dingtalk/api.js +5 -0
- package/plugin-src/client/channels/dingtalk/index.js +48 -2
- package/plugin-src/client/channels/feishu/api.js +28 -5
- package/plugin-src/client/channels/feishu/index.js +330 -41
- package/plugin-src/client/channels/feishu/styles.js +46 -0
- package/plugin-src/client/channels/qq/api.js +5 -0
- package/plugin-src/client/channels/qq/index.js +37 -6
- package/plugin-src/client/channels/shared/token-api.js +5 -0
- package/plugin-src/client/channels/shared/token-channel.js +34 -6
- package/plugin-src/client/channels/wecom/api.js +5 -0
- package/plugin-src/client/channels/wecom/index.js +37 -6
- package/plugin-src/client/channels/weixin/api.js +5 -0
- package/plugin-src/client/channels/weixin/index.js +50 -4
- package/plugin-src/client/channels/whatsapp/api.js +5 -0
- package/plugin-src/client/channels/whatsapp/index.js +30 -4
- package/plugin-src/client/i18n.js +59 -0
- package/plugin-src/client/styles.js +8 -0
- package/plugin-src/host/channels/dingtalk/production.mjs +10 -4
- package/plugin-src/host/channels/dingtalk/rpc.mjs +12 -0
- package/plugin-src/host/channels/feishu/connection-supervisor.mjs +1 -1
- package/plugin-src/host/channels/feishu/production.mjs +8 -3
- package/plugin-src/host/channels/feishu/rpc.mjs +78 -7
- package/plugin-src/host/channels/qq/production.mjs +10 -4
- package/plugin-src/host/channels/qq/rpc.mjs +12 -0
- package/plugin-src/host/channels/shared/agent-preset-rpc.mjs +25 -0
- package/plugin-src/host/channels/shared/production.mjs +10 -4
- package/plugin-src/host/channels/shared/rpc.mjs +12 -0
- package/plugin-src/host/channels/shared/workspace-rpc.mjs +2 -0
- package/plugin-src/host/channels/slack/production.mjs +10 -4
- package/plugin-src/host/channels/slack/rpc.mjs +13 -0
- package/plugin-src/host/channels/wecom/production.mjs +10 -4
- package/plugin-src/host/channels/wecom/rpc.mjs +12 -0
- package/plugin-src/host/channels/weixin/production.mjs +10 -4
- package/plugin-src/host/channels/weixin/rpc.mjs +15 -0
- package/plugin-src/host/channels/whatsapp/production.mjs +10 -4
- package/plugin-src/host/channels/whatsapp/rpc.mjs +12 -0
- package/src/channels/discord/discord-api.mjs +1 -1
- package/src/channels/feishu/bridge.mjs +29 -4
- package/src/channels/feishu/feishu-runtime.mjs +14 -0
- package/src/channels/feishu/group-message-permission-manager.mjs +71 -0
- package/src/channels/feishu/group-response-mode.mjs +15 -0
- package/src/channels/feishu/multi-bot-controller.mjs +258 -9
- package/src/channels/feishu/plugin-config-store.mjs +3 -0
- package/src/channels/feishu/repair-manager.mjs +17 -3
- package/src/channels/shared/agent-preset.mjs +74 -0
- package/src/channels/shared/bot-workspace-store.mjs +141 -14
- package/src/channels/shared/harness-client.mjs +6 -4
- package/src/channels/telegram/telegram-api.mjs +31 -0
- package/src/channels/telegram/telegram-runtime.mjs +25 -1
- package/src/channels/weixin/weixin-api.mjs +1 -1
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
} from '../../../../src/channels/shared/connection-test.mjs';
|
|
6
6
|
import { resolveRpcAuthority } from '../../rpc-authority.mjs';
|
|
7
7
|
import { publicWorkspaceError, SET_WORKSPACE_ENDPOINT, validWorkspacePayload } from '../shared/workspace-rpc.mjs';
|
|
8
|
+
import { SET_AGENT_PRESET_ENDPOINT, validAgentPresetPayload } from '../shared/agent-preset-rpc.mjs';
|
|
8
9
|
|
|
9
10
|
export const QQ_RPC_CHANNEL = '/qq';
|
|
10
11
|
export const QQ_ENDPOINTS = Object.freeze({
|
|
@@ -16,6 +17,7 @@ export const QQ_ENDPOINTS = Object.freeze({
|
|
|
16
17
|
reconnectBot: 'bot.reconnect',
|
|
17
18
|
deleteBot: 'bot.delete',
|
|
18
19
|
setWorkspace: SET_WORKSPACE_ENDPOINT,
|
|
20
|
+
setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
|
|
19
21
|
});
|
|
20
22
|
export const QQ_RPC_ENDPOINTS = Object.freeze(Object.values(QQ_ENDPOINTS));
|
|
21
23
|
|
|
@@ -70,6 +72,10 @@ function payloadFailure(endpoint, payload) {
|
|
|
70
72
|
return validWorkspacePayload(payload)
|
|
71
73
|
? null : '请输入工作区绝对路径。';
|
|
72
74
|
}
|
|
75
|
+
if (endpoint === QQ_ENDPOINTS.setAgentPreset) {
|
|
76
|
+
return validAgentPresetPayload(payload)
|
|
77
|
+
? null : '请选择 Agent Preset。';
|
|
78
|
+
}
|
|
73
79
|
return 'Unknown QQ endpoint.';
|
|
74
80
|
}
|
|
75
81
|
|
|
@@ -165,6 +171,12 @@ export function createQqRpcHandler(controller, { encodeQr = qrDataUrl } = {}) {
|
|
|
165
171
|
await controller.updateWorkspace(payload.botId, payload.workspace),
|
|
166
172
|
cachedEncode,
|
|
167
173
|
);
|
|
174
|
+
} else if (endpoint === QQ_ENDPOINTS.setAgentPreset) {
|
|
175
|
+
if (typeof controller.updateAgentPreset !== 'function') throw new Error('Agent preset update is unavailable');
|
|
176
|
+
value = await publicStatus(
|
|
177
|
+
await controller.updateAgentPreset(payload.botId, payload.agentPreset),
|
|
178
|
+
cachedEncode,
|
|
179
|
+
);
|
|
168
180
|
} else {
|
|
169
181
|
value = await publicStatus(await controller.deleteBot(payload.botId), cachedEncode);
|
|
170
182
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { AGENT_PRESET_ID } from '../../../../src/channels/shared/agent-preset.mjs';
|
|
2
|
+
|
|
3
|
+
export const SET_AGENT_PRESET_ENDPOINT = 'bot.preset.set';
|
|
4
|
+
|
|
5
|
+
function isRecord(value) {
|
|
6
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function validAgentPresetPayload(payload) {
|
|
10
|
+
return isRecord(payload)
|
|
11
|
+
&& Object.keys(payload).every((key) => ['botId', 'agentPreset'].includes(key))
|
|
12
|
+
&& typeof payload.botId === 'string'
|
|
13
|
+
&& /^[A-Za-z0-9_-]{1,128}$/.test(payload.botId)
|
|
14
|
+
&& (payload.agentPreset === null
|
|
15
|
+
|| (typeof payload.agentPreset === 'string' && AGENT_PRESET_ID.test(payload.agentPreset)));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function publicAgentPresetError(error) {
|
|
19
|
+
if (![
|
|
20
|
+
'agent-preset-invalid',
|
|
21
|
+
'agent-preset-unavailable',
|
|
22
|
+
'workspace-bot-not-found',
|
|
23
|
+
].includes(error?.code)) return null;
|
|
24
|
+
return { code: error.code, message: error.message };
|
|
25
|
+
}
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
createWorkspaceAwareController,
|
|
12
12
|
observeBotWorkspaceRemovals,
|
|
13
13
|
} from '../../../../src/channels/shared/bot-workspace-store.mjs';
|
|
14
|
+
import { listAgentPresetCatalog } from '../../../../src/channels/shared/agent-preset.mjs';
|
|
14
15
|
|
|
15
16
|
export function harnessOrigin(webServer, configured) {
|
|
16
17
|
if (configured !== undefined) return new URL(configured);
|
|
@@ -59,7 +60,9 @@ export async function createTokenProductionController(ctx, config, internals, de
|
|
|
59
60
|
?? await new WorkspaceStore(paths.workspaces, { defaultWorkspace }).load();
|
|
60
61
|
const configuredBots = configStore.list();
|
|
61
62
|
await workspaces.reconcile(configuredBots.map((bot) => bot.botId));
|
|
62
|
-
await Promise.all(configuredBots.map((bot) => workspaces.ensure(bot.botId
|
|
63
|
+
await Promise.all(configuredBots.map((bot) => workspaces.ensure(bot.botId, {
|
|
64
|
+
defaultAgentPreset: config.agentPreset,
|
|
65
|
+
})));
|
|
63
66
|
const observedConfigStore = typeof configStore.remove === 'function'
|
|
64
67
|
? observeBotWorkspaceRemovals(configStore, { workspaces })
|
|
65
68
|
: configStore;
|
|
@@ -81,7 +84,6 @@ export async function createTokenProductionController(ctx, config, internals, de
|
|
|
81
84
|
const harness = new ResolvedHarness({
|
|
82
85
|
baseUrl: harnessOrigin(ctx.webServer, config.harnessBaseUrl),
|
|
83
86
|
workspace: defaultWorkspace,
|
|
84
|
-
...(config.agentPreset == null ? {} : { agentPreset: config.agentPreset }),
|
|
85
87
|
autostart: false,
|
|
86
88
|
dshBin: config.dshBin ?? 'dsh',
|
|
87
89
|
...(commandExecutor ? { commandExecutor } : {}),
|
|
@@ -95,7 +97,7 @@ export async function createTokenProductionController(ctx, config, internals, de
|
|
|
95
97
|
...(internals.inspectToken ? { inspectToken: internals.inspectToken } : {}),
|
|
96
98
|
createRuntime: async ({ botId, config: botConfig, token }) => {
|
|
97
99
|
const state = await stateFor(botId);
|
|
98
|
-
await workspaces.ensure(botId);
|
|
100
|
+
await workspaces.ensure(botId, { defaultAgentPreset: config.agentPreset });
|
|
99
101
|
const workspaceScope = createBotWorkspaceScope(harness, { botId, workspaces, state });
|
|
100
102
|
return new ResolvedRuntime({
|
|
101
103
|
...channelRuntimeOptions,
|
|
@@ -127,7 +129,11 @@ export async function createTokenProductionController(ctx, config, internals, de
|
|
|
127
129
|
}
|
|
128
130
|
},
|
|
129
131
|
});
|
|
130
|
-
const controller = createWorkspaceAwareController(coreController, {
|
|
132
|
+
const controller = createWorkspaceAwareController(coreController, {
|
|
133
|
+
workspaces,
|
|
134
|
+
stateFor,
|
|
135
|
+
agentPresetCatalog: () => listAgentPresetCatalog(ctx),
|
|
136
|
+
});
|
|
131
137
|
const supervisor = createSupervisor({
|
|
132
138
|
channel,
|
|
133
139
|
controller,
|
|
@@ -5,6 +5,10 @@ import {
|
|
|
5
5
|
SET_WORKSPACE_ENDPOINT,
|
|
6
6
|
validWorkspacePayload,
|
|
7
7
|
} from './workspace-rpc.mjs';
|
|
8
|
+
import {
|
|
9
|
+
SET_AGENT_PRESET_ENDPOINT,
|
|
10
|
+
validAgentPresetPayload,
|
|
11
|
+
} from './agent-preset-rpc.mjs';
|
|
8
12
|
|
|
9
13
|
export const TOKEN_BOT_ENDPOINTS = Object.freeze({
|
|
10
14
|
status: 'connection.status',
|
|
@@ -12,6 +16,7 @@ export const TOKEN_BOT_ENDPOINTS = Object.freeze({
|
|
|
12
16
|
reconnectBot: 'bot.reconnect',
|
|
13
17
|
deleteBot: 'bot.delete',
|
|
14
18
|
setWorkspace: SET_WORKSPACE_ENDPOINT,
|
|
19
|
+
setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
|
|
15
20
|
});
|
|
16
21
|
|
|
17
22
|
const ENDPOINTS = Object.freeze(Object.values(TOKEN_BOT_ENDPOINTS));
|
|
@@ -57,6 +62,10 @@ function payloadFailure(endpoint, payload) {
|
|
|
57
62
|
return validWorkspacePayload(payload)
|
|
58
63
|
? null : '请输入工作区绝对路径。';
|
|
59
64
|
}
|
|
65
|
+
if (endpoint === TOKEN_BOT_ENDPOINTS.setAgentPreset) {
|
|
66
|
+
return validAgentPresetPayload(payload)
|
|
67
|
+
? null : '请选择 Agent Preset。';
|
|
68
|
+
}
|
|
60
69
|
return 'Unknown bot endpoint.';
|
|
61
70
|
}
|
|
62
71
|
|
|
@@ -132,6 +141,9 @@ export function createTokenBotRpcHandler(controller, { channel }) {
|
|
|
132
141
|
} else if (endpoint === TOKEN_BOT_ENDPOINTS.setWorkspace) {
|
|
133
142
|
if (typeof controller.updateWorkspace !== 'function') throw new Error('Workspace update is unavailable');
|
|
134
143
|
value = await controller.updateWorkspace(payload.botId, payload.workspace);
|
|
144
|
+
} else if (endpoint === TOKEN_BOT_ENDPOINTS.setAgentPreset) {
|
|
145
|
+
if (typeof controller.updateAgentPreset !== 'function') throw new Error('Agent preset update is unavailable');
|
|
146
|
+
value = await controller.updateAgentPreset(payload.botId, payload.agentPreset);
|
|
135
147
|
} else {
|
|
136
148
|
value = await controller.deleteBot(payload.botId);
|
|
137
149
|
}
|
|
@@ -20,6 +20,8 @@ export function publicWorkspaceError(error) {
|
|
|
20
20
|
'workspace-not-found',
|
|
21
21
|
'workspace-not-directory',
|
|
22
22
|
'workspace-bot-not-found',
|
|
23
|
+
'agent-preset-invalid',
|
|
24
|
+
'agent-preset-unavailable',
|
|
23
25
|
].includes(error?.code)) return null;
|
|
24
26
|
return { code: error.code, message: error.message };
|
|
25
27
|
}
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
createWorkspaceAwareController,
|
|
13
13
|
observeBotWorkspaceRemovals,
|
|
14
14
|
} from '../../../../src/channels/shared/bot-workspace-store.mjs';
|
|
15
|
+
import { listAgentPresetCatalog } from '../../../../src/channels/shared/agent-preset.mjs';
|
|
15
16
|
import { createTokenConnectionSupervisor } from '../shared/connection-supervisor.mjs';
|
|
16
17
|
import { harnessOrigin, pluginPaths } from '../shared/production.mjs';
|
|
17
18
|
import { createHarnessCommandExecutor } from '../../harness-command-executor.mjs';
|
|
@@ -37,7 +38,9 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
37
38
|
?? await new WorkspaceStore(paths.workspaces, { defaultWorkspace }).load();
|
|
38
39
|
const configuredBots = configStore.list();
|
|
39
40
|
await workspaces.reconcile(configuredBots.map((bot) => bot.botId));
|
|
40
|
-
await Promise.all(configuredBots.map((bot) => workspaces.ensure(bot.botId
|
|
41
|
+
await Promise.all(configuredBots.map((bot) => workspaces.ensure(bot.botId, {
|
|
42
|
+
defaultAgentPreset: config.agentPreset,
|
|
43
|
+
})));
|
|
41
44
|
const observedConfigStore = typeof configStore.remove === 'function'
|
|
42
45
|
? observeBotWorkspaceRemovals(configStore, { workspaces })
|
|
43
46
|
: configStore;
|
|
@@ -59,7 +62,6 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
59
62
|
const harness = new ResolvedHarness({
|
|
60
63
|
baseUrl: harnessOrigin(ctx.webServer, config.harnessBaseUrl),
|
|
61
64
|
workspace: defaultWorkspace,
|
|
62
|
-
...(config.agentPreset == null ? {} : { agentPreset: config.agentPreset }),
|
|
63
65
|
autostart: false,
|
|
64
66
|
dshBin: config.dshBin ?? 'dsh',
|
|
65
67
|
...(commandExecutor ? { commandExecutor } : {}),
|
|
@@ -73,7 +75,7 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
73
75
|
...(internals.inspectCredentials ? { inspectCredentials: internals.inspectCredentials } : {}),
|
|
74
76
|
createRuntime: async ({ botId, config: botConfig, botToken, appToken }) => {
|
|
75
77
|
const state = await stateFor(botId);
|
|
76
|
-
await workspaces.ensure(botId);
|
|
78
|
+
await workspaces.ensure(botId, { defaultAgentPreset: config.agentPreset });
|
|
77
79
|
const workspaceScope = createBotWorkspaceScope(harness, { botId, workspaces, state });
|
|
78
80
|
return new ResolvedRuntime({
|
|
79
81
|
config: botConfig,
|
|
@@ -105,7 +107,11 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
105
107
|
}
|
|
106
108
|
},
|
|
107
109
|
});
|
|
108
|
-
const controller = createWorkspaceAwareController(coreController, {
|
|
110
|
+
const controller = createWorkspaceAwareController(coreController, {
|
|
111
|
+
workspaces,
|
|
112
|
+
stateFor,
|
|
113
|
+
agentPresetCatalog: () => listAgentPresetCatalog(ctx),
|
|
114
|
+
});
|
|
109
115
|
const supervisor = createSupervisor({
|
|
110
116
|
channel: 'slack',
|
|
111
117
|
controller,
|
|
@@ -5,6 +5,10 @@ import {
|
|
|
5
5
|
SET_WORKSPACE_ENDPOINT,
|
|
6
6
|
validWorkspacePayload,
|
|
7
7
|
} from '../shared/workspace-rpc.mjs';
|
|
8
|
+
import {
|
|
9
|
+
SET_AGENT_PRESET_ENDPOINT,
|
|
10
|
+
validAgentPresetPayload,
|
|
11
|
+
} from '../shared/agent-preset-rpc.mjs';
|
|
8
12
|
|
|
9
13
|
export const SLACK_RPC_CHANNEL = '/slack';
|
|
10
14
|
export const SLACK_ENDPOINTS = Object.freeze({
|
|
@@ -13,6 +17,7 @@ export const SLACK_ENDPOINTS = Object.freeze({
|
|
|
13
17
|
reconnectBot: 'bot.reconnect',
|
|
14
18
|
deleteBot: 'bot.delete',
|
|
15
19
|
setWorkspace: SET_WORKSPACE_ENDPOINT,
|
|
20
|
+
setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
|
|
16
21
|
});
|
|
17
22
|
export const SLACK_RPC_ENDPOINTS = Object.freeze(Object.values(SLACK_ENDPOINTS));
|
|
18
23
|
|
|
@@ -66,6 +71,10 @@ function payloadFailure(endpoint, payload) {
|
|
|
66
71
|
return validWorkspacePayload(payload)
|
|
67
72
|
? null : '请输入工作区绝对路径。';
|
|
68
73
|
}
|
|
74
|
+
if (endpoint === SLACK_ENDPOINTS.setAgentPreset) {
|
|
75
|
+
return validAgentPresetPayload(payload)
|
|
76
|
+
? null : '请选择 Agent Preset。';
|
|
77
|
+
}
|
|
69
78
|
return 'Unknown Slack endpoint.';
|
|
70
79
|
}
|
|
71
80
|
|
|
@@ -145,6 +154,10 @@ export function createSlackRpcHandler(controller) {
|
|
|
145
154
|
if (typeof controller.updateWorkspace !== 'function') throw new Error('Workspace update is unavailable');
|
|
146
155
|
value = await controller.updateWorkspace(payload.botId, payload.workspace);
|
|
147
156
|
}
|
|
157
|
+
else if (endpoint === SLACK_ENDPOINTS.setAgentPreset) {
|
|
158
|
+
if (typeof controller.updateAgentPreset !== 'function') throw new Error('Agent preset update is unavailable');
|
|
159
|
+
value = await controller.updateAgentPreset(payload.botId, payload.agentPreset);
|
|
160
|
+
}
|
|
148
161
|
else value = await controller.deleteBot(payload.botId);
|
|
149
162
|
return signal?.aborted
|
|
150
163
|
? { ok: false, error: { code: 'cancelled', message: 'The request was cancelled.' } }
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
createWorkspaceAwareController,
|
|
15
15
|
observeBotWorkspaceRemovals,
|
|
16
16
|
} from '../../../../src/channels/shared/bot-workspace-store.mjs';
|
|
17
|
+
import { listAgentPresetCatalog } from '../../../../src/channels/shared/agent-preset.mjs';
|
|
17
18
|
import { createConnectionSupervisor } from './connection-supervisor.mjs';
|
|
18
19
|
import { createHarnessCommandExecutor } from '../../harness-command-executor.mjs';
|
|
19
20
|
import { createHarnessSessionExecutors } from '../../harness-session-coordinator.mjs';
|
|
@@ -57,7 +58,9 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
57
58
|
?? await new WorkspaceStore(paths.workspaces, { defaultWorkspace }).load();
|
|
58
59
|
const configuredBots = configStore.list();
|
|
59
60
|
await workspaces.reconcile(configuredBots.map((bot) => bot.botId));
|
|
60
|
-
await Promise.all(configuredBots.map((bot) => workspaces.ensure(bot.botId
|
|
61
|
+
await Promise.all(configuredBots.map((bot) => workspaces.ensure(bot.botId, {
|
|
62
|
+
defaultAgentPreset: config.agentPreset,
|
|
63
|
+
})));
|
|
61
64
|
const observedConfigStore = typeof configStore.remove === 'function'
|
|
62
65
|
? observeBotWorkspaceRemovals(configStore, { workspaces })
|
|
63
66
|
: configStore;
|
|
@@ -83,7 +86,6 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
83
86
|
const harness = new Harness({
|
|
84
87
|
baseUrl: harnessOrigin(ctx.webServer, config.harnessBaseUrl),
|
|
85
88
|
workspace: defaultWorkspace,
|
|
86
|
-
...(config.agentPreset == null ? {} : { agentPreset: config.agentPreset }),
|
|
87
89
|
autostart: false,
|
|
88
90
|
dshBin: config.dshBin ?? 'dsh',
|
|
89
91
|
...(commandExecutor ? { commandExecutor } : {}),
|
|
@@ -97,7 +99,7 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
97
99
|
logger,
|
|
98
100
|
createRuntime: async ({ botId, config: botConfig, secret }) => {
|
|
99
101
|
const state = await stateFor(botId);
|
|
100
|
-
await workspaces.ensure(botId);
|
|
102
|
+
await workspaces.ensure(botId, { defaultAgentPreset: config.agentPreset });
|
|
101
103
|
const workspaceScope = createBotWorkspaceScope(harness, { botId, workspaces, state });
|
|
102
104
|
return new Runtime({
|
|
103
105
|
config: botConfig,
|
|
@@ -129,7 +131,11 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
129
131
|
}
|
|
130
132
|
},
|
|
131
133
|
});
|
|
132
|
-
const controller = createWorkspaceAwareController(coreController, {
|
|
134
|
+
const controller = createWorkspaceAwareController(coreController, {
|
|
135
|
+
workspaces,
|
|
136
|
+
stateFor,
|
|
137
|
+
agentPresetCatalog: () => listAgentPresetCatalog(ctx),
|
|
138
|
+
});
|
|
133
139
|
const supervisor = createSupervisor({
|
|
134
140
|
controller,
|
|
135
141
|
harness,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import QRCode from 'qrcode';
|
|
2
2
|
import { resolveRpcAuthority } from '../../rpc-authority.mjs';
|
|
3
3
|
import { publicWorkspaceError, SET_WORKSPACE_ENDPOINT, validWorkspacePayload } from '../shared/workspace-rpc.mjs';
|
|
4
|
+
import { SET_AGENT_PRESET_ENDPOINT, validAgentPresetPayload } from '../shared/agent-preset-rpc.mjs';
|
|
4
5
|
import {
|
|
5
6
|
connectionTestTargetUnavailable,
|
|
6
7
|
publicConnectionTestResult,
|
|
@@ -16,6 +17,7 @@ export const WECOM_ENDPOINTS = Object.freeze({
|
|
|
16
17
|
reconnectBot: 'bot.reconnect',
|
|
17
18
|
deleteBot: 'bot.delete',
|
|
18
19
|
setWorkspace: SET_WORKSPACE_ENDPOINT,
|
|
20
|
+
setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
|
|
19
21
|
});
|
|
20
22
|
export const WECOM_RPC_ENDPOINTS = Object.freeze(Object.values(WECOM_ENDPOINTS));
|
|
21
23
|
|
|
@@ -70,6 +72,10 @@ function payloadFailure(endpoint, payload) {
|
|
|
70
72
|
return validWorkspacePayload(payload)
|
|
71
73
|
? null : '请输入工作区绝对路径。';
|
|
72
74
|
}
|
|
75
|
+
if (endpoint === WECOM_ENDPOINTS.setAgentPreset) {
|
|
76
|
+
return validAgentPresetPayload(payload)
|
|
77
|
+
? null : '请选择 Agent Preset。';
|
|
78
|
+
}
|
|
73
79
|
return 'Unknown Enterprise WeChat endpoint.';
|
|
74
80
|
}
|
|
75
81
|
|
|
@@ -166,6 +172,12 @@ export function createWecomRpcHandler(controller, { encodeQr = qrDataUrl } = {})
|
|
|
166
172
|
await controller.updateWorkspace(payload.botId, payload.workspace),
|
|
167
173
|
cachedEncode,
|
|
168
174
|
);
|
|
175
|
+
} else if (endpoint === WECOM_ENDPOINTS.setAgentPreset) {
|
|
176
|
+
if (typeof controller.updateAgentPreset !== 'function') throw new Error('Agent preset update is unavailable');
|
|
177
|
+
value = await publicStatus(
|
|
178
|
+
await controller.updateAgentPreset(payload.botId, payload.agentPreset),
|
|
179
|
+
cachedEncode,
|
|
180
|
+
);
|
|
169
181
|
} else {
|
|
170
182
|
value = await publicStatus(await controller.deleteBot(payload.botId), cachedEncode);
|
|
171
183
|
}
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
createWorkspaceAwareController,
|
|
15
15
|
observeBotWorkspaceRemovals,
|
|
16
16
|
} from '../../../../src/channels/shared/bot-workspace-store.mjs';
|
|
17
|
+
import { listAgentPresetCatalog } from '../../../../src/channels/shared/agent-preset.mjs';
|
|
17
18
|
import { createConnectionSupervisor } from './connection-supervisor.mjs';
|
|
18
19
|
import { createHarnessCommandExecutor } from '../../harness-command-executor.mjs';
|
|
19
20
|
import { createHarnessSessionExecutors } from '../../harness-session-coordinator.mjs';
|
|
@@ -60,7 +61,9 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
60
61
|
?? await new WorkspaceStore(paths.workspaces, { defaultWorkspace }).load();
|
|
61
62
|
const configuredBots = configStore.list();
|
|
62
63
|
await workspaces.reconcile(configuredBots.map((bot) => bot.botId));
|
|
63
|
-
await Promise.all(configuredBots.map((bot) => workspaces.ensure(bot.botId
|
|
64
|
+
await Promise.all(configuredBots.map((bot) => workspaces.ensure(bot.botId, {
|
|
65
|
+
defaultAgentPreset: config.agentPreset,
|
|
66
|
+
})));
|
|
64
67
|
const observedConfigStore = typeof configStore.remove === 'function'
|
|
65
68
|
? observeBotWorkspaceRemovals(configStore, { workspaces })
|
|
66
69
|
: configStore;
|
|
@@ -83,7 +86,6 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
83
86
|
const harness = new Harness({
|
|
84
87
|
baseUrl: harnessOrigin(ctx.webServer, config.harnessBaseUrl),
|
|
85
88
|
workspace: defaultWorkspace,
|
|
86
|
-
...(config.agentPreset == null ? {} : { agentPreset: config.agentPreset }),
|
|
87
89
|
autostart: false,
|
|
88
90
|
dshBin: config.dshBin ?? 'dsh',
|
|
89
91
|
...(commandExecutor ? { commandExecutor } : {}),
|
|
@@ -97,7 +99,7 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
97
99
|
logger,
|
|
98
100
|
createRuntime: async ({ botId, config: accountConfig, token }) => {
|
|
99
101
|
const state = await stateFor(botId);
|
|
100
|
-
await workspaces.ensure(botId);
|
|
102
|
+
await workspaces.ensure(botId, { defaultAgentPreset: config.agentPreset });
|
|
101
103
|
const workspaceScope = createBotWorkspaceScope(harness, { botId, workspaces, state });
|
|
102
104
|
return new Runtime({
|
|
103
105
|
api,
|
|
@@ -129,7 +131,11 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
129
131
|
}
|
|
130
132
|
},
|
|
131
133
|
});
|
|
132
|
-
const controller = createWorkspaceAwareController(coreController, {
|
|
134
|
+
const controller = createWorkspaceAwareController(coreController, {
|
|
135
|
+
workspaces,
|
|
136
|
+
stateFor,
|
|
137
|
+
agentPresetCatalog: () => listAgentPresetCatalog(ctx),
|
|
138
|
+
});
|
|
133
139
|
const supervisor = createSupervisor({
|
|
134
140
|
controller,
|
|
135
141
|
harness,
|
|
@@ -5,6 +5,10 @@ import {
|
|
|
5
5
|
SET_WORKSPACE_ENDPOINT,
|
|
6
6
|
validWorkspacePayload,
|
|
7
7
|
} from '../shared/workspace-rpc.mjs';
|
|
8
|
+
import {
|
|
9
|
+
SET_AGENT_PRESET_ENDPOINT,
|
|
10
|
+
validAgentPresetPayload,
|
|
11
|
+
} from '../shared/agent-preset-rpc.mjs';
|
|
8
12
|
import {
|
|
9
13
|
connectionTestTargetUnavailable,
|
|
10
14
|
publicConnectionTestResult,
|
|
@@ -20,6 +24,7 @@ export const WEIXIN_ENDPOINTS = Object.freeze({
|
|
|
20
24
|
reconnectBot: 'bot.reconnect',
|
|
21
25
|
deleteBot: 'bot.delete',
|
|
22
26
|
setWorkspace: SET_WORKSPACE_ENDPOINT,
|
|
27
|
+
setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
|
|
23
28
|
});
|
|
24
29
|
export const WEIXIN_RPC_ENDPOINTS = Object.freeze(Object.values(WEIXIN_ENDPOINTS));
|
|
25
30
|
|
|
@@ -74,6 +79,10 @@ function payloadFailure(endpoint, payload) {
|
|
|
74
79
|
return validWorkspacePayload(payload)
|
|
75
80
|
? null : '请输入工作区绝对路径。';
|
|
76
81
|
}
|
|
82
|
+
if (endpoint === WEIXIN_ENDPOINTS.setAgentPreset) {
|
|
83
|
+
return validAgentPresetPayload(payload)
|
|
84
|
+
? null : '请选择 Agent Preset。';
|
|
85
|
+
}
|
|
77
86
|
return 'Unknown Weixin endpoint.';
|
|
78
87
|
}
|
|
79
88
|
|
|
@@ -198,6 +207,12 @@ export function createWeixinRpcHandler(controller, { encodeQr = qrDataUrl } = {}
|
|
|
198
207
|
await controller.updateWorkspace(payload.botId, payload.workspace),
|
|
199
208
|
cachedEncode,
|
|
200
209
|
);
|
|
210
|
+
} else if (endpoint === WEIXIN_ENDPOINTS.setAgentPreset) {
|
|
211
|
+
if (typeof controller.updateAgentPreset !== 'function') throw new Error('Agent preset update is unavailable');
|
|
212
|
+
value = await publicStatus(
|
|
213
|
+
await controller.updateAgentPreset(payload.botId, payload.agentPreset),
|
|
214
|
+
cachedEncode,
|
|
215
|
+
);
|
|
201
216
|
} else {
|
|
202
217
|
value = await publicStatus(await controller.deleteBot(payload.botId), cachedEncode);
|
|
203
218
|
}
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
createWorkspaceAwareController,
|
|
15
15
|
observeBotWorkspaceRemovals,
|
|
16
16
|
} from '../../../../src/channels/shared/bot-workspace-store.mjs';
|
|
17
|
+
import { listAgentPresetCatalog } from '../../../../src/channels/shared/agent-preset.mjs';
|
|
17
18
|
import { createTokenConnectionSupervisor } from '../shared/connection-supervisor.mjs';
|
|
18
19
|
import { createHarnessCommandExecutor } from '../../harness-command-executor.mjs';
|
|
19
20
|
import { createHarnessSessionExecutors } from '../../harness-session-coordinator.mjs';
|
|
@@ -64,7 +65,9 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
64
65
|
?? await new WorkspaceStore(paths.workspaces, { defaultWorkspace }).load();
|
|
65
66
|
const configuredBots = configStore.list();
|
|
66
67
|
await workspaces.reconcile(configuredBots.map((bot) => bot.botId));
|
|
67
|
-
await Promise.all(configuredBots.map((bot) => workspaces.ensure(bot.botId
|
|
68
|
+
await Promise.all(configuredBots.map((bot) => workspaces.ensure(bot.botId, {
|
|
69
|
+
defaultAgentPreset: config.agentPreset,
|
|
70
|
+
})));
|
|
68
71
|
const observedConfigStore = typeof configStore.remove === 'function'
|
|
69
72
|
? observeBotWorkspaceRemovals(configStore, { workspaces })
|
|
70
73
|
: configStore;
|
|
@@ -86,7 +89,6 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
86
89
|
const harness = new Harness({
|
|
87
90
|
baseUrl: harnessOrigin(ctx.webServer, config.harnessBaseUrl),
|
|
88
91
|
workspace: defaultWorkspace,
|
|
89
|
-
...(config.agentPreset == null ? {} : { agentPreset: config.agentPreset }),
|
|
90
92
|
autostart: false,
|
|
91
93
|
dshBin: config.dshBin ?? 'dsh',
|
|
92
94
|
...(commandExecutor ? { commandExecutor } : {}),
|
|
@@ -100,7 +102,7 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
100
102
|
logger,
|
|
101
103
|
createRuntime: async ({ botId, config: botConfig, authDir }) => {
|
|
102
104
|
const state = await stateFor(botId);
|
|
103
|
-
await workspaces.ensure(botId);
|
|
105
|
+
await workspaces.ensure(botId, { defaultAgentPreset: config.agentPreset });
|
|
104
106
|
const workspaceScope = createBotWorkspaceScope(harness, { botId, workspaces, state });
|
|
105
107
|
return new Runtime({
|
|
106
108
|
config: botConfig,
|
|
@@ -136,7 +138,11 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
136
138
|
}
|
|
137
139
|
},
|
|
138
140
|
});
|
|
139
|
-
const controller = createWorkspaceAwareController(coreController, {
|
|
141
|
+
const controller = createWorkspaceAwareController(coreController, {
|
|
142
|
+
workspaces,
|
|
143
|
+
stateFor,
|
|
144
|
+
agentPresetCatalog: () => listAgentPresetCatalog(ctx),
|
|
145
|
+
});
|
|
140
146
|
const supervisor = createSupervisor({
|
|
141
147
|
channel: 'whatsapp',
|
|
142
148
|
controller,
|
|
@@ -3,6 +3,7 @@ import QRCode from 'qrcode';
|
|
|
3
3
|
import { publicConnectionTestResult } from '../../../../src/channels/shared/connection-test.mjs';
|
|
4
4
|
import { resolveRpcAuthority } from '../../rpc-authority.mjs';
|
|
5
5
|
import { publicWorkspaceError, SET_WORKSPACE_ENDPOINT, validWorkspacePayload } from '../shared/workspace-rpc.mjs';
|
|
6
|
+
import { SET_AGENT_PRESET_ENDPOINT, validAgentPresetPayload } from '../shared/agent-preset-rpc.mjs';
|
|
6
7
|
|
|
7
8
|
export const WHATSAPP_RPC_CHANNEL = '/whatsapp';
|
|
8
9
|
export const WHATSAPP_ENDPOINTS = Object.freeze({
|
|
@@ -13,6 +14,7 @@ export const WHATSAPP_ENDPOINTS = Object.freeze({
|
|
|
13
14
|
reconnectBot: 'bot.reconnect',
|
|
14
15
|
deleteBot: 'bot.delete',
|
|
15
16
|
setWorkspace: SET_WORKSPACE_ENDPOINT,
|
|
17
|
+
setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
|
|
16
18
|
});
|
|
17
19
|
export const WHATSAPP_RPC_ENDPOINTS = Object.freeze(Object.values(WHATSAPP_ENDPOINTS));
|
|
18
20
|
|
|
@@ -52,6 +54,10 @@ function payloadFailure(endpoint, payload) {
|
|
|
52
54
|
return validWorkspacePayload(payload)
|
|
53
55
|
? null : '请输入工作区绝对路径。';
|
|
54
56
|
}
|
|
57
|
+
if (endpoint === WHATSAPP_ENDPOINTS.setAgentPreset) {
|
|
58
|
+
return validAgentPresetPayload(payload)
|
|
59
|
+
? null : '请选择 Agent Preset。';
|
|
60
|
+
}
|
|
55
61
|
return 'Unknown WhatsApp endpoint.';
|
|
56
62
|
}
|
|
57
63
|
|
|
@@ -154,6 +160,12 @@ export function createWhatsappRpcHandler(controller, { encodeQr = qrDataUrl } =
|
|
|
154
160
|
await controller.updateWorkspace(payload.botId, payload.workspace),
|
|
155
161
|
cachedEncode,
|
|
156
162
|
);
|
|
163
|
+
} else if (endpoint === WHATSAPP_ENDPOINTS.setAgentPreset) {
|
|
164
|
+
if (typeof controller.updateAgentPreset !== 'function') throw new Error('Agent preset update is unavailable');
|
|
165
|
+
value = await publicStatus(
|
|
166
|
+
await controller.updateAgentPreset(payload.botId, payload.agentPreset),
|
|
167
|
+
cachedEncode,
|
|
168
|
+
);
|
|
157
169
|
} else {
|
|
158
170
|
value = await publicStatus(await controller.deleteBot(payload.botId), cachedEncode);
|
|
159
171
|
}
|
|
@@ -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, 0.
|
|
111
|
+
'user-agent': 'DeepSeek-Harness-dsh-im (https://github.com/xmanrui/dsh-im, 0.19.0)',
|
|
112
112
|
},
|
|
113
113
|
...(body === undefined ? {} : { body: JSON.stringify(body) }),
|
|
114
114
|
signal: requestSignal(signal, timeoutMs),
|
|
@@ -40,6 +40,10 @@ import {
|
|
|
40
40
|
workspaceListCard,
|
|
41
41
|
} from './feishu-cards.mjs';
|
|
42
42
|
import { MAX_WATCHES_PER_KEY } from './state-store.mjs';
|
|
43
|
+
import {
|
|
44
|
+
FEISHU_GROUP_RESPONSE_MODES,
|
|
45
|
+
normalizeFeishuGroupResponseMode,
|
|
46
|
+
} from './group-response-mode.mjs';
|
|
43
47
|
|
|
44
48
|
const INTERACTION_RESOLVED_TEXT = '这个问题已在其他客户端处理,无需再次回答。';
|
|
45
49
|
const RESOLVED_REPLY_TTL_MS = 30 * 60_000;
|
|
@@ -249,6 +253,8 @@ export class FeishuHarnessBridge {
|
|
|
249
253
|
#signal;
|
|
250
254
|
#botId;
|
|
251
255
|
#appId;
|
|
256
|
+
#botOpenId;
|
|
257
|
+
#groupResponseMode;
|
|
252
258
|
#repair;
|
|
253
259
|
#repairOwnerOpenIds;
|
|
254
260
|
#repairAttempt = null;
|
|
@@ -275,6 +281,8 @@ export class FeishuHarnessBridge {
|
|
|
275
281
|
allowedSenderOpenIds = new Set(),
|
|
276
282
|
botId,
|
|
277
283
|
appId,
|
|
284
|
+
botOpenId,
|
|
285
|
+
groupResponseMode = FEISHU_GROUP_RESPONSE_MODES.ALL,
|
|
278
286
|
repair,
|
|
279
287
|
repairOwnerOpenIds,
|
|
280
288
|
repairPollIntervalMs = REPAIR_POLL_INTERVAL_MS,
|
|
@@ -308,6 +316,8 @@ export class FeishuHarnessBridge {
|
|
|
308
316
|
this.#allowedSenderOpenIds = allowedSenderOpenIds;
|
|
309
317
|
this.#botId = nonEmptyString(botId);
|
|
310
318
|
this.#appId = nonEmptyString(appId);
|
|
319
|
+
this.#botOpenId = nonEmptyString(botOpenId);
|
|
320
|
+
this.#groupResponseMode = normalizeFeishuGroupResponseMode(groupResponseMode);
|
|
311
321
|
this.#repair = repair ?? null;
|
|
312
322
|
const repairOwners = repairOwnerOpenIds ?? allowedSenderOpenIds;
|
|
313
323
|
this.#repairOwnerOpenIds = new Set(
|
|
@@ -327,6 +337,18 @@ export class FeishuHarnessBridge {
|
|
|
327
337
|
}
|
|
328
338
|
}
|
|
329
339
|
|
|
340
|
+
setGroupResponseMode(value) {
|
|
341
|
+
this.#groupResponseMode = normalizeFeishuGroupResponseMode(value);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
#isAddressed(event) {
|
|
345
|
+
if (event?.message?.chat_type === 'p2p') return true;
|
|
346
|
+
const mentions = Array.isArray(event?.message?.mentions) ? event.message.mentions : [];
|
|
347
|
+
if (!this.#botOpenId) return mentions.length > 0;
|
|
348
|
+
return mentions.some((mention) => mention?.id?.open_id === this.#botOpenId
|
|
349
|
+
|| mention?.open_id === this.#botOpenId);
|
|
350
|
+
}
|
|
351
|
+
|
|
330
352
|
accept(event) {
|
|
331
353
|
if (this.#signal?.aborted) return Promise.resolve();
|
|
332
354
|
const messageId = nonEmptyString(event?.message?.message_id);
|
|
@@ -337,6 +359,12 @@ export class FeishuHarnessBridge {
|
|
|
337
359
|
this.#logger.warn?.('[dsh-feishu] ignored a message from a sender outside the allowlist');
|
|
338
360
|
return Promise.resolve();
|
|
339
361
|
}
|
|
362
|
+
const addressed = this.#isAddressed(event);
|
|
363
|
+
if (event?.message?.chat_type !== 'p2p'
|
|
364
|
+
&& this.#groupResponseMode === FEISHU_GROUP_RESPONSE_MODES.MENTION
|
|
365
|
+
&& !addressed) {
|
|
366
|
+
return Promise.resolve();
|
|
367
|
+
}
|
|
340
368
|
if (this.#state.hasSeen(messageId) || this.#acceptedMessageIds.has(messageId)) {
|
|
341
369
|
return Promise.resolve();
|
|
342
370
|
}
|
|
@@ -362,8 +390,6 @@ export class FeishuHarnessBridge {
|
|
|
362
390
|
const commandRunner = isControlCommand(commandText)
|
|
363
391
|
? runControlCommand
|
|
364
392
|
: (isModelCommand(commandText) ? runModelCommand : null);
|
|
365
|
-
const addressed = event?.message?.chat_type === 'p2p'
|
|
366
|
-
|| (Array.isArray(event?.message?.mentions) && event.message.mentions.length > 0);
|
|
367
393
|
if (commandRunner && addressed) {
|
|
368
394
|
const processing = this.#processFastCommand(
|
|
369
395
|
event,
|
|
@@ -407,8 +433,7 @@ export class FeishuHarnessBridge {
|
|
|
407
433
|
actor: senderOpenId(event),
|
|
408
434
|
messageId,
|
|
409
435
|
text: extractText(event) ?? '',
|
|
410
|
-
addressed
|
|
411
|
-
|| (Array.isArray(event?.message?.mentions) && event.message.mentions.length > 0),
|
|
436
|
+
addressed,
|
|
412
437
|
hasPendingQuestion: Boolean(pending),
|
|
413
438
|
questionCompletion: pending?.submitting || pending?.claimedReplyMessageId
|
|
414
439
|
? pending.queue
|