@xmanrui/dsh-im 0.17.0 → 0.18.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.
Files changed (49) hide show
  1. package/README.en.md +3 -2
  2. package/README.md +3 -2
  3. package/lib/client.js +964 -538
  4. package/lib/index.js +189 -144
  5. package/package.json +1 -1
  6. package/plugin-src/client/agent-preset.js +110 -0
  7. package/plugin-src/client/channels/dingtalk/api.js +5 -0
  8. package/plugin-src/client/channels/dingtalk/index.js +48 -2
  9. package/plugin-src/client/channels/feishu/api.js +5 -0
  10. package/plugin-src/client/channels/feishu/index.js +39 -2
  11. package/plugin-src/client/channels/qq/api.js +5 -0
  12. package/plugin-src/client/channels/qq/index.js +37 -6
  13. package/plugin-src/client/channels/shared/token-api.js +5 -0
  14. package/plugin-src/client/channels/shared/token-channel.js +34 -6
  15. package/plugin-src/client/channels/wecom/api.js +5 -0
  16. package/plugin-src/client/channels/wecom/index.js +37 -6
  17. package/plugin-src/client/channels/weixin/api.js +15 -0
  18. package/plugin-src/client/channels/weixin/index.js +54 -4
  19. package/plugin-src/client/channels/whatsapp/api.js +5 -0
  20. package/plugin-src/client/channels/whatsapp/index.js +30 -4
  21. package/plugin-src/client/i18n.js +29 -0
  22. package/plugin-src/client/styles.js +8 -0
  23. package/plugin-src/host/channels/dingtalk/production.mjs +10 -4
  24. package/plugin-src/host/channels/dingtalk/rpc.mjs +12 -0
  25. package/plugin-src/host/channels/feishu/connection-supervisor.mjs +1 -1
  26. package/plugin-src/host/channels/feishu/production.mjs +6 -3
  27. package/plugin-src/host/channels/feishu/rpc.mjs +17 -0
  28. package/plugin-src/host/channels/qq/production.mjs +10 -4
  29. package/plugin-src/host/channels/qq/rpc.mjs +12 -0
  30. package/plugin-src/host/channels/shared/agent-preset-rpc.mjs +25 -0
  31. package/plugin-src/host/channels/shared/production.mjs +10 -4
  32. package/plugin-src/host/channels/shared/rpc.mjs +12 -0
  33. package/plugin-src/host/channels/shared/workspace-rpc.mjs +2 -0
  34. package/plugin-src/host/channels/slack/production.mjs +10 -4
  35. package/plugin-src/host/channels/slack/rpc.mjs +13 -0
  36. package/plugin-src/host/channels/wecom/production.mjs +10 -4
  37. package/plugin-src/host/channels/wecom/rpc.mjs +12 -0
  38. package/plugin-src/host/channels/weixin/production.mjs +10 -4
  39. package/plugin-src/host/channels/weixin/rpc.mjs +15 -0
  40. package/plugin-src/host/channels/whatsapp/production.mjs +10 -4
  41. package/plugin-src/host/channels/whatsapp/rpc.mjs +12 -0
  42. package/src/channels/discord/discord-api.mjs +1 -1
  43. package/src/channels/shared/agent-preset.mjs +74 -0
  44. package/src/channels/shared/bot-workspace-store.mjs +141 -14
  45. package/src/channels/shared/harness-client.mjs +6 -4
  46. package/src/channels/shared/image-prompt.mjs +32 -1
  47. package/src/channels/weixin/weixin-api.mjs +1 -1
  48. package/src/channels/weixin/weixin-bridge.mjs +23 -3
  49. package/src/channels/weixin/weixin-controller.mjs +15 -0
@@ -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';
@@ -63,7 +64,9 @@ export async function createProductionController(ctx, config = {}, internals = {
63
64
  if (canListConfiguredBots) {
64
65
  await workspaces.reconcile(configuredBots.map((bot) => bot.botId));
65
66
  }
66
- await Promise.all(configuredBots.map((bot) => workspaces.ensure(bot.botId)));
67
+ await Promise.all(configuredBots.map((bot) => workspaces.ensure(bot.botId, {
68
+ defaultAgentPreset: config.agentPreset,
69
+ })));
67
70
  const observedConfigStore = typeof configStore.remove === 'function'
68
71
  ? observeBotWorkspaceRemovals(configStore, { workspaces })
69
72
  : configStore;
@@ -89,7 +92,6 @@ export async function createProductionController(ctx, config = {}, internals = {
89
92
  const harness = new Harness({
90
93
  baseUrl: harnessOrigin(ctx.webServer, config.harnessBaseUrl),
91
94
  workspace: defaultWorkspace,
92
- ...(config.agentPreset == null ? {} : { agentPreset: config.agentPreset }),
93
95
  autostart: false,
94
96
  dshBin: config.dshBin ?? 'dsh',
95
97
  ...(commandExecutor ? { commandExecutor } : {}),
@@ -103,7 +105,7 @@ export async function createProductionController(ctx, config = {}, internals = {
103
105
  logger,
104
106
  createRuntime: async ({ botId, config: botConfig, clientSecret }) => {
105
107
  const state = await stateFor(botId);
106
- await workspaces.ensure(botId);
108
+ await workspaces.ensure(botId, { defaultAgentPreset: config.agentPreset });
107
109
  const workspaceScope = createBotWorkspaceScope(harness, { botId, workspaces, state });
108
110
  return new Runtime({
109
111
  config: botConfig,
@@ -135,7 +137,11 @@ export async function createProductionController(ctx, config = {}, internals = {
135
137
  }
136
138
  },
137
139
  });
138
- const controller = createWorkspaceAwareController(coreController, { workspaces, stateFor });
140
+ const controller = createWorkspaceAwareController(coreController, {
141
+ workspaces,
142
+ stateFor,
143
+ agentPresetCatalog: () => listAgentPresetCatalog(ctx),
144
+ });
139
145
  const supervisor = createSupervisor({
140
146
  controller,
141
147
  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 DINGTALK_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
  approveSender: 'bot.sender.approve',
20
22
  revokeSender: 'bot.sender.revoke',
21
23
  });
@@ -88,6 +90,10 @@ function payloadFailure(endpoint, payload) {
88
90
  return validWorkspacePayload(payload)
89
91
  ? null : '请输入工作区绝对路径。';
90
92
  }
93
+ if (endpoint === DINGTALK_ENDPOINTS.setAgentPreset) {
94
+ return validAgentPresetPayload(payload)
95
+ ? null : '请选择 Agent Preset。';
96
+ }
91
97
  if (endpoint === DINGTALK_ENDPOINTS.approveSender) {
92
98
  return exactKeys(payload, ['botId', 'requestId', 'confirm'])
93
99
  && validId(payload.botId)
@@ -245,6 +251,12 @@ export function createDingtalkRpcHandler(controller, { encodeQr = qrDataUrl } =
245
251
  await controller.updateWorkspace(payload.botId, payload.workspace),
246
252
  cachedEncode,
247
253
  );
254
+ } else if (endpoint === DINGTALK_ENDPOINTS.setAgentPreset) {
255
+ if (typeof controller.updateAgentPreset !== 'function') throw new Error('Agent preset update is unavailable');
256
+ value = await publicStatus(
257
+ await controller.updateAgentPreset(payload.botId, payload.agentPreset),
258
+ cachedEncode,
259
+ );
248
260
  } else if (endpoint === DINGTALK_ENDPOINTS.approveSender) {
249
261
  value = await publicStatus(
250
262
  await controller.approveSender(payload.botId, payload.requestId),
@@ -129,7 +129,7 @@ export class ConnectionSupervisor {
129
129
  await this.#controller.initialize();
130
130
  if (this.#closed) return;
131
131
 
132
- const status = this.#controller.status();
132
+ const status = await this.#controller.status();
133
133
  this.#resolveReady?.(status);
134
134
  this.#resolveReady = null;
135
135
  const current = totals(status);
@@ -20,6 +20,7 @@ import {
20
20
  createWorkspaceAwareController,
21
21
  observeBotWorkspaceRemovals,
22
22
  } from '../../../../src/channels/shared/bot-workspace-store.mjs';
23
+ import { listAgentPresetCatalog } from '../../../../src/channels/shared/agent-preset.mjs';
23
24
 
24
25
  function harnessOrigin(webServer, configured) {
25
26
  if (configured !== undefined) return new URL(configured);
@@ -77,7 +78,9 @@ export async function createProductionController(ctx, config = {}, internals = {
77
78
  if (canListConfiguredBots) {
78
79
  await workspaces.reconcile(configuredBots.map((bot) => bot.id));
79
80
  }
80
- await Promise.all(configuredBots.map((bot) => workspaces.ensure(bot.id)));
81
+ await Promise.all(configuredBots.map((bot) => workspaces.ensure(bot.id, {
82
+ defaultAgentPreset: config.agentPreset,
83
+ })));
81
84
  const observedConfigStore = typeof configStore.removeBot === 'function'
82
85
  ? observeBotWorkspaceRemovals(configStore, {
83
86
  workspaces,
@@ -115,7 +118,6 @@ export async function createProductionController(ctx, config = {}, internals = {
115
118
  const harness = new Harness({
116
119
  baseUrl: harnessOrigin(ctx.webServer, config.harnessBaseUrl),
117
120
  workspace: defaultWorkspace,
118
- ...(config.agentPreset == null ? {} : { agentPreset: config.agentPreset }),
119
121
  // This plugin is already hosted by a running DSH process. Starting a
120
122
  // second DSH would create a competing server and lifecycle.
121
123
  autostart: false,
@@ -133,7 +135,7 @@ export async function createProductionController(ctx, config = {}, internals = {
133
135
  createRuntime: async ({ botId, config: botConfig, appSecret, repair }) => {
134
136
  const state = await stateFor(botConfig);
135
137
  const id = botId ?? botConfig.id ?? botConfig.appId;
136
- await workspaces.ensure(id);
138
+ await workspaces.ensure(id, { defaultAgentPreset: config.agentPreset });
137
139
  const workspaceScope = createBotWorkspaceScope(harness, { botId: id, workspaces, state });
138
140
  return new Runtime({
139
141
  lark,
@@ -166,6 +168,7 @@ export async function createProductionController(ctx, config = {}, internals = {
166
168
  const controller = createWorkspaceAwareController(coreController, {
167
169
  workspaces,
168
170
  stateFor: stateForBotId,
171
+ agentPresetCatalog: () => listAgentPresetCatalog(ctx),
169
172
  });
170
173
 
171
174
  const supervisor = createSupervisor({
@@ -1,7 +1,12 @@
1
1
  import QRCode from 'qrcode';
2
+ import {
3
+ normalizeAgentPresetCatalog,
4
+ normalizeAgentPresetId,
5
+ } from '../../../../src/channels/shared/agent-preset.mjs';
2
6
  import { publicConnectionTestResult } from '../../../../src/channels/shared/connection-test.mjs';
3
7
  import { resolveRpcAuthority } from '../../rpc-authority.mjs';
4
8
  import { publicWorkspaceError, validWorkspacePayload } from '../shared/workspace-rpc.mjs';
9
+ import { validAgentPresetPayload } from '../shared/agent-preset-rpc.mjs';
5
10
  import {
6
11
  FEISHU_ENDPOINTS,
7
12
  FEISHU_RPC_CHANNEL,
@@ -253,6 +258,7 @@ function publicBotEntry(entry) {
253
258
  state: connectionState(source, registration, connected),
254
259
  connected,
255
260
  configured: source.configured === true,
261
+ agentPreset: normalizeAgentPresetId(source.agentPreset),
256
262
  bot: publicBot(source.bot),
257
263
  health: publicHealth(source, connected),
258
264
  };
@@ -294,6 +300,7 @@ export async function toPublicFeishuStatus(status, { encodeQr = qrCodeDataUrl }
294
300
  bot: publicBot(source.bot),
295
301
  health: publicHealth(source, connected),
296
302
  bots,
303
+ agentPresetCatalog: normalizeAgentPresetCatalog(source.agentPresetCatalog),
297
304
  totals: {
298
305
  configured: bots.length || (source.configured === true ? 1 : 0),
299
306
  connected: bots.length ? bots.filter((bot) => bot.connected).length : (connected ? 1 : 0),
@@ -378,6 +385,10 @@ function validPayload(endpoint, payload) {
378
385
  return validWorkspacePayload(payload)
379
386
  ? null : '请输入工作区绝对路径。';
380
387
  }
388
+ if (endpoint === FEISHU_ENDPOINTS.setAgentPreset) {
389
+ return validAgentPresetPayload(payload)
390
+ ? null : '请选择 Agent Preset。';
391
+ }
381
392
  return 'Unknown Feishu endpoint.';
382
393
  }
383
394
 
@@ -606,6 +617,12 @@ export function createFeishuRpcHandler(controller, { encodeQr = qrCodeDataUrl }
606
617
  await controller.updateWorkspace(payload.botId, payload.workspace),
607
618
  { encodeQr: cachedEncodeQr },
608
619
  );
620
+ } else if (endpoint === FEISHU_ENDPOINTS.setAgentPreset) {
621
+ if (typeof controller.updateAgentPreset !== 'function') throw new Error('Agent preset update is unavailable');
622
+ value = await toPublicFeishuStatus(
623
+ await controller.updateAgentPreset(payload.botId, payload.agentPreset),
624
+ { encodeQr: cachedEncodeQr },
625
+ );
609
626
  } else {
610
627
  if (typeof controller.deleteBot !== 'function') throw new Error('Multi-bot delete is unavailable');
611
628
  value = await toPublicFeishuStatus(await controller.deleteBot(payload.botId), { encodeQr: cachedEncodeQr });
@@ -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;
@@ -80,7 +83,6 @@ export async function createProductionController(ctx, config = {}, internals = {
80
83
  const harness = new Harness({
81
84
  baseUrl: harnessOrigin(ctx.webServer, config.harnessBaseUrl),
82
85
  workspace: defaultWorkspace,
83
- ...(config.agentPreset == null ? {} : { agentPreset: config.agentPreset }),
84
86
  autostart: false,
85
87
  dshBin: config.dshBin ?? 'dsh',
86
88
  ...(commandExecutor ? { commandExecutor } : {}),
@@ -94,7 +96,7 @@ export async function createProductionController(ctx, config = {}, internals = {
94
96
  logger,
95
97
  createRuntime: async ({ botId, config: botConfig, appSecret }) => {
96
98
  const state = await stateFor(botId);
97
- await workspaces.ensure(botId);
99
+ await workspaces.ensure(botId, { defaultAgentPreset: config.agentPreset });
98
100
  const workspaceScope = createBotWorkspaceScope(harness, { botId, workspaces, state });
99
101
  return new Runtime({
100
102
  config: botConfig,
@@ -125,7 +127,11 @@ export async function createProductionController(ctx, config = {}, internals = {
125
127
  }
126
128
  },
127
129
  });
128
- const controller = createWorkspaceAwareController(coreController, { workspaces, stateFor });
130
+ const controller = createWorkspaceAwareController(coreController, {
131
+ workspaces,
132
+ stateFor,
133
+ agentPresetCatalog: () => listAgentPresetCatalog(ctx),
134
+ });
129
135
  const supervisor = createSupervisor({
130
136
  controller,
131
137
  harness,
@@ -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, { workspaces, stateFor });
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, { workspaces, stateFor });
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, { workspaces, stateFor });
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, { workspaces, stateFor });
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,