mirror of
https://github.com/hansjone/dsh-im-ops.git
synced 2026-10-09 04:13:17 +08:00
feat: 按机器人选择 Agent Preset
在九个渠道的设置页卡片上选择 Host 已有预设;未选则跟随默认,只影响之后新建的会话。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
c3b3a29f27
commit
3161816cc4
51 changed files with 2227 additions and 740 deletions
101
plugin-src/client/agent-preset.js
Normal file
101
plugin-src/client/agent-preset.js
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
import * as React from 'react';
|
||||
|
||||
import { h } from './i18n.js';
|
||||
|
||||
export const SET_AGENT_PRESET_ENDPOINT = 'bot.preset.set';
|
||||
|
||||
const PRESET_ID = /^[a-z0-9][a-z0-9-]*$/;
|
||||
|
||||
export const EMPTY_AGENT_PRESET_CATALOG = Object.freeze({
|
||||
defaultId: '',
|
||||
items: Object.freeze([]),
|
||||
});
|
||||
|
||||
export const AgentPresetCatalogContext = React.createContext(EMPTY_AGENT_PRESET_CATALOG);
|
||||
|
||||
export function normalizeAgentPresetId(value) {
|
||||
if (typeof value !== 'string') return '';
|
||||
const id = value.trim();
|
||||
return PRESET_ID.test(id) ? id : '';
|
||||
}
|
||||
|
||||
export function normalizeAgentPresetCatalog(value) {
|
||||
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
||||
return { defaultId: '', items: [] };
|
||||
}
|
||||
const items = [];
|
||||
const seen = new Set();
|
||||
for (const entry of Array.isArray(value.items) ? value.items : []) {
|
||||
const id = typeof entry === 'string'
|
||||
? normalizeAgentPresetId(entry)
|
||||
: normalizeAgentPresetId(entry?.id);
|
||||
if (!id || seen.has(id)) continue;
|
||||
seen.add(id);
|
||||
const label = typeof entry?.label === 'string' && entry.label.trim()
|
||||
? entry.label.trim().slice(0, 128)
|
||||
: typeof entry?.name === 'string' && entry.name.trim()
|
||||
? entry.name.trim().slice(0, 128)
|
||||
: id;
|
||||
items.push({ id, label });
|
||||
}
|
||||
return {
|
||||
defaultId: normalizeAgentPresetId(value.defaultId),
|
||||
items,
|
||||
};
|
||||
}
|
||||
|
||||
export function AgentPresetEditor({ agentPreset = '', disabled = false, onSave }) {
|
||||
const catalog = React.useContext(AgentPresetCatalogContext) ?? EMPTY_AGENT_PRESET_CATALOG;
|
||||
const current = normalizeAgentPresetId(agentPreset);
|
||||
const [saving, setSaving] = React.useState(false);
|
||||
const [error, setError] = React.useState(null);
|
||||
|
||||
const items = [];
|
||||
const seen = new Set();
|
||||
for (const item of Array.isArray(catalog.items) ? catalog.items : []) {
|
||||
if (!item?.id || seen.has(item.id)) continue;
|
||||
seen.add(item.id);
|
||||
items.push(item);
|
||||
}
|
||||
if (current && !seen.has(current)) {
|
||||
items.push({ id: current, label: current });
|
||||
}
|
||||
|
||||
const inheritLabel = '跟随 Host 默认';
|
||||
|
||||
const change = async (event) => {
|
||||
const next = event.target.value;
|
||||
if (next === current || saving || disabled) return;
|
||||
setSaving(true);
|
||||
setError(null);
|
||||
try {
|
||||
await onSave?.(next || null);
|
||||
} catch (cause) {
|
||||
setError(cause?.message ?? 'Agent Preset 修改失败,请重试。');
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
};
|
||||
|
||||
return h('div', { className: 'dim-preset' },
|
||||
h('div', { className: 'dim-presetHeader' },
|
||||
h('span', null, 'Agent Preset'),
|
||||
saving ? h('span', { className: 'dim-presetStatus' }, '保存中…') : null),
|
||||
React.createElement('select', {
|
||||
className: 'dim-presetSelect',
|
||||
value: current,
|
||||
disabled: disabled || saving,
|
||||
'aria-label': 'Agent Preset',
|
||||
onChange: (event) => { void change(event); },
|
||||
},
|
||||
h('option', { value: '' }, inheritLabel),
|
||||
...items.map((item) => h(
|
||||
'option',
|
||||
{ key: item.id, value: item.id },
|
||||
item.label && item.label !== item.id ? `${item.label}(${item.id})` : item.id,
|
||||
)),
|
||||
),
|
||||
h('small', { className: 'dim-presetHelp' }, '只影响之后新建的会话。'),
|
||||
error ? h('p', { className: 'dim-presetError', role: 'alert' }, error) : null,
|
||||
);
|
||||
}
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
import { normalizeAgentPresetCatalog, normalizeAgentPresetId, SET_AGENT_PRESET_ENDPOINT } from '../../agent-preset.js';
|
||||
|
||||
export const DINGTALK_RPC_CHANNEL = '/dingtalk';
|
||||
|
||||
export const DINGTALK_ENDPOINTS = Object.freeze({
|
||||
|
|
@ -9,6 +11,7 @@ export const DINGTALK_ENDPOINTS = Object.freeze({
|
|||
reconnectBot: 'bot.reconnect',
|
||||
deleteBot: 'bot.delete',
|
||||
setWorkspace: 'bot.workspace.set',
|
||||
setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
|
||||
});
|
||||
|
||||
const ACCOUNT_STATES = new Set(['connected', 'connecting', 'offline', 'error']);
|
||||
|
|
@ -157,6 +160,7 @@ function normalizeBot(value) {
|
|||
connected,
|
||||
configured: value.configured !== false,
|
||||
workspace: optionalString(value.workspace, 4_096) ?? '',
|
||||
agentPreset: normalizeAgentPresetId(value.agentPreset),
|
||||
bot: {
|
||||
name: optionalString(bot.name, 100) ?? '钉钉机器人',
|
||||
clientIdMasked: optionalString(bot.clientIdMasked, 140) ?? '已安全保存',
|
||||
|
|
@ -200,6 +204,7 @@ export function normalizeSnapshot(value) {
|
|||
},
|
||||
provisioning: source.provisioning ? normalizeProvisioning(source.provisioning) : null,
|
||||
testMessage: normalizeTestMessage(source.testMessage),
|
||||
agentPresetCatalog: normalizeAgentPresetCatalog(source.agentPresetCatalog),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,11 @@ import * as React from 'react';
|
|||
import { CredentialActionIcon, CredentialBindingPanel, QrActionIcon } from '../../credential-binding.js';
|
||||
import { h } from '../../i18n.js';
|
||||
import { WorkspaceEditor } from '../../workspace-editor.js';
|
||||
import {
|
||||
AgentPresetCatalogContext,
|
||||
AgentPresetEditor,
|
||||
EMPTY_AGENT_PRESET_CATALOG,
|
||||
} from '../../agent-preset.js';
|
||||
import { useWorkspaceSnapshotFence } from '../../workspace-snapshot-fence.js';
|
||||
import {
|
||||
DINGTALK_ENDPOINTS,
|
||||
|
|
@ -213,6 +218,7 @@ export function AccountCard({
|
|||
removing,
|
||||
onReconnect,
|
||||
onWorkspaceSave,
|
||||
onAgentPresetSave,
|
||||
onRequestRemove,
|
||||
onConfirmRemove,
|
||||
onCancelRemove,
|
||||
|
|
@ -241,6 +247,11 @@ export function AccountCard({
|
|||
disabled: Boolean(busy),
|
||||
onSave: onWorkspaceSave,
|
||||
}),
|
||||
h(AgentPresetEditor, {
|
||||
agentPreset: account.agentPreset,
|
||||
disabled: Boolean(busy),
|
||||
onSave: onAgentPresetSave,
|
||||
}),
|
||||
h('div', { className: 'ddt-accountFooter dim-cardFooter' },
|
||||
summary ? h('div', { className: 'ddt-summary dim-cardSummary' }, summary) : null,
|
||||
feedback ? h('div', {
|
||||
|
|
@ -272,6 +283,7 @@ function AccountList(props) {
|
|||
removing: props.removeTarget === account.botId,
|
||||
onReconnect: () => props.onReconnect(account),
|
||||
onWorkspaceSave: (workspace) => props.onWorkspaceSave(account, workspace),
|
||||
onAgentPresetSave: (agentPreset) => props.onAgentPresetSave(account, agentPreset),
|
||||
onRequestRemove: () => props.onRequestRemove(account),
|
||||
onConfirmRemove: () => props.onConfirmRemove(account),
|
||||
onCancelRemove: props.onCancelRemove,
|
||||
|
|
@ -283,6 +295,7 @@ const EMPTY_TOTALS = Object.freeze({ configured: 0, connected: 0 });
|
|||
export function DingtalkSettingsTab({ rpcCall }) {
|
||||
const [model, setModel] = React.useState({
|
||||
phase: 'loading', bots: [], totals: EMPTY_TOTALS, revision: 0, error: null,
|
||||
agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG,
|
||||
});
|
||||
const [provision, setProvision] = React.useState(null);
|
||||
const [busy, setBusy] = React.useState(false);
|
||||
|
|
@ -389,6 +402,7 @@ export function DingtalkSettingsTab({ rpcCall }) {
|
|||
totals: snapshot.totals,
|
||||
revision: snapshot.revision,
|
||||
error: null,
|
||||
agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
|
||||
});
|
||||
discardStaleFeedback(snapshot);
|
||||
if (restoreProvisioning && snapshot.provisioning) {
|
||||
|
|
@ -504,6 +518,7 @@ export function DingtalkSettingsTab({ rpcCall }) {
|
|||
totals: snapshot.totals,
|
||||
revision: snapshot.revision,
|
||||
error: null,
|
||||
agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
|
||||
});
|
||||
discardStaleFeedback(snapshot);
|
||||
}
|
||||
|
|
@ -638,6 +653,7 @@ export function DingtalkSettingsTab({ rpcCall }) {
|
|||
totals: snapshot.totals,
|
||||
revision: snapshot.revision,
|
||||
error: null,
|
||||
agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
|
||||
});
|
||||
discardStaleFeedback(snapshot);
|
||||
}
|
||||
|
|
@ -700,6 +716,33 @@ export function DingtalkSettingsTab({ rpcCall }) {
|
|||
totals: snapshot.totals,
|
||||
revision: snapshot.revision,
|
||||
error: null,
|
||||
agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
|
||||
});
|
||||
discardStaleFeedback(snapshot);
|
||||
}
|
||||
} finally {
|
||||
const shouldRefresh = workspaceFence.endMutation();
|
||||
if (shouldRefresh && mountedRef.current) void loadStatus({ silent: true });
|
||||
if (mountedRef.current) setBotBusy(account.botId, null);
|
||||
}
|
||||
}, [discardStaleFeedback, invoke, loadStatus, setBotBusy, workspaceFence]);
|
||||
|
||||
const saveAgentPreset = React.useCallback(async (account, agentPreset) => {
|
||||
const snapshotVersion = workspaceFence.beginMutation();
|
||||
setBotBusy(account.botId, 'preset');
|
||||
try {
|
||||
const snapshot = normalizeSnapshot(await invoke(
|
||||
DINGTALK_ENDPOINTS.setAgentPreset,
|
||||
{ botId: account.botId, agentPreset },
|
||||
));
|
||||
if (mountedRef.current && workspaceFence.canCommitMutation(snapshotVersion)) {
|
||||
setModel({
|
||||
phase: 'ready',
|
||||
bots: snapshot.bots,
|
||||
totals: snapshot.totals,
|
||||
revision: snapshot.revision,
|
||||
error: null,
|
||||
agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
|
||||
});
|
||||
discardStaleFeedback(snapshot);
|
||||
}
|
||||
|
|
@ -762,7 +805,9 @@ export function DingtalkSettingsTab({ rpcCall }) {
|
|||
})
|
||||
: null;
|
||||
|
||||
return h('section', { className: 'ddt-page dim-channelPage', 'aria-label': '钉钉设置' },
|
||||
return h(AgentPresetCatalogContext.Provider, {
|
||||
value: model.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
|
||||
}, h('section', { className: 'ddt-page dim-channelPage', 'aria-label': '钉钉设置' },
|
||||
h(Heading, {
|
||||
totals: model.totals,
|
||||
adding: Boolean(provision),
|
||||
|
|
@ -798,11 +843,12 @@ export function DingtalkSettingsTab({ rpcCall }) {
|
|||
removeTarget,
|
||||
onReconnect: (account) => void reconnect(account),
|
||||
onWorkspaceSave: saveWorkspace,
|
||||
onAgentPresetSave: saveAgentPreset,
|
||||
onRequestRemove: (account) => setRemoveTarget(account.botId),
|
||||
onConfirmRemove: (account) => void remove(account),
|
||||
onCancelRemove: () => setRemoveTarget(null),
|
||||
})
|
||||
: null));
|
||||
: null)));
|
||||
}
|
||||
|
||||
export function apply(ctx) {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@
|
|||
* must never be returned by any endpoint on this channel.
|
||||
*/
|
||||
|
||||
import { normalizeAgentPresetCatalog, normalizeAgentPresetId } from "../../agent-preset.js";
|
||||
|
||||
export const FEISHU_RPC_CHANNEL = "/feishu";
|
||||
|
||||
export const FEISHU_ENDPOINTS = Object.freeze({
|
||||
|
|
@ -19,6 +21,7 @@ export const FEISHU_ENDPOINTS = Object.freeze({
|
|||
disconnectBot: "bot.disconnect",
|
||||
deleteBot: "bot.delete",
|
||||
setWorkspace: "bot.workspace.set",
|
||||
setAgentPreset: "bot.preset.set",
|
||||
// Kept for rolling upgrades. The multi-bot UI never calls these endpoints.
|
||||
testConnection: "connection.test",
|
||||
disconnect: "connection.disconnect",
|
||||
|
|
@ -182,6 +185,7 @@ export function normalizeBotConnection(value, fallbackBotId) {
|
|||
connected,
|
||||
configured: value.configured !== false,
|
||||
workspace: optionalString(value.workspace)?.slice(0, 4_096) ?? "",
|
||||
agentPreset: normalizeAgentPresetId(value.agentPreset),
|
||||
bot: normalizeBot(value.bot),
|
||||
health: normalizeHealth(value.health, connected),
|
||||
error: normalizeError(value.error),
|
||||
|
|
@ -236,6 +240,7 @@ export function normalizeBotsSnapshot(value) {
|
|||
? normalizeProvisioning(value.provisioning)
|
||||
: undefined,
|
||||
error: normalizeError(value.error),
|
||||
agentPresetCatalog: normalizeAgentPresetCatalog(value.agentPresetCatalog),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,11 @@ import {
|
|||
} from "./api.js";
|
||||
import { useAnimationFrameScheduler } from "../../lifecycle.js";
|
||||
import { WorkspaceEditor } from "../../workspace-editor.js";
|
||||
import {
|
||||
AgentPresetCatalogContext,
|
||||
AgentPresetEditor,
|
||||
EMPTY_AGENT_PRESET_CATALOG,
|
||||
} from "../../agent-preset.js";
|
||||
import { useWorkspaceSnapshotFence } from "../../workspace-snapshot-fence.js";
|
||||
import { installFeishuStyles } from "./styles.js";
|
||||
|
||||
|
|
@ -418,6 +423,7 @@ export function BotCard({
|
|||
onReconnect,
|
||||
onRepairCallback,
|
||||
onWorkspaceSave,
|
||||
onAgentPresetSave,
|
||||
onRequestRemove,
|
||||
onConfirmRemove,
|
||||
onCancelRemove,
|
||||
|
|
@ -468,6 +474,11 @@ export function BotCard({
|
|||
disabled: Boolean(busy),
|
||||
onSave: onWorkspaceSave,
|
||||
}),
|
||||
h(AgentPresetEditor, {
|
||||
agentPreset: connection.agentPreset,
|
||||
disabled: Boolean(busy),
|
||||
onSave: onAgentPresetSave,
|
||||
}),
|
||||
h("div", { className: "bxf-connectedFooter dim-cardFooter" },
|
||||
summary ? h("div", { className: "bxf-healthSummary dim-cardSummary", "data-error": actionError || connection.error ? "true" : undefined },
|
||||
summary) : null,
|
||||
|
|
@ -524,6 +535,7 @@ function BotList(props) {
|
|||
onReconnect: () => props.onReconnect(bot),
|
||||
onRepairCallback: () => props.onRepairCallback(bot),
|
||||
onWorkspaceSave: (workspace) => props.onWorkspaceSave(bot, workspace),
|
||||
onAgentPresetSave: (agentPreset) => props.onAgentPresetSave(bot, agentPreset),
|
||||
onRequestRemove: () => props.onRequestRemove(bot),
|
||||
onConfirmRemove: () => props.onConfirmRemove(bot),
|
||||
onCancelRemove: props.onCancelRemove,
|
||||
|
|
@ -575,6 +587,7 @@ export function mergeFeishuSnapshotState(
|
|||
provisioning,
|
||||
pageError: null,
|
||||
statusError: null,
|
||||
agentPresetCatalog: snapshot.agentPresetCatalog ?? current.agentPresetCatalog,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -587,6 +600,7 @@ export function FeishuSettingsTab({ rpcCall }) {
|
|||
provisioning: null,
|
||||
pageError: null,
|
||||
statusError: null,
|
||||
agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG,
|
||||
});
|
||||
const [pageBusy, setPageBusy] = React.useState(false);
|
||||
const [provisionBusy, setProvisionBusy] = React.useState(false);
|
||||
|
|
@ -1067,6 +1081,26 @@ export function FeishuSettingsTab({ rpcCall }) {
|
|||
}
|
||||
}, [invoke, loadStatus, mergeSnapshot, setBotBusy, setBotError, workspaceFence]);
|
||||
|
||||
const saveAgentPreset = React.useCallback(async (connection, agentPreset) => {
|
||||
const { botId } = connection;
|
||||
const snapshotVersion = workspaceFence.beginMutation();
|
||||
setBotBusy(botId, "preset");
|
||||
setBotError(botId, null);
|
||||
try {
|
||||
const snapshot = normalizeBotsSnapshot(await invoke(
|
||||
FEISHU_ENDPOINTS.setAgentPreset,
|
||||
{ botId, agentPreset },
|
||||
));
|
||||
if (mountedRef.current && workspaceFence.canCommitMutation(snapshotVersion)) {
|
||||
mergeSnapshot(snapshot);
|
||||
}
|
||||
} finally {
|
||||
const shouldRefresh = workspaceFence.endMutation();
|
||||
if (shouldRefresh && mountedRef.current) void loadStatus({ silent: true });
|
||||
if (mountedRef.current) setBotBusy(botId, null);
|
||||
}
|
||||
}, [invoke, loadStatus, mergeSnapshot, setBotBusy, setBotError, workspaceFence]);
|
||||
|
||||
const requestRemove = React.useCallback((connection) => {
|
||||
setRemoveTargetId(connection.botId);
|
||||
}, []);
|
||||
|
|
@ -1173,7 +1207,9 @@ export function FeishuSettingsTab({ rpcCall }) {
|
|||
else removeButtonRefs.current.delete(botId);
|
||||
}, []);
|
||||
|
||||
return h("section", { className: "bxf-page dim-channelPage", "aria-label": "飞书机器人设置" },
|
||||
return h(AgentPresetCatalogContext.Provider, {
|
||||
value: model.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
|
||||
}, h("section", { className: "bxf-page dim-channelPage", "aria-label": "飞书机器人设置" },
|
||||
h(Heading, {
|
||||
totals: model.totals,
|
||||
onAdd: () => void startProvisioning(),
|
||||
|
|
@ -1217,6 +1253,7 @@ export function FeishuSettingsTab({ rpcCall }) {
|
|||
onReconnect: (bot) => void reconnectOneBot(bot),
|
||||
onRepairCallback: repairCallback,
|
||||
onWorkspaceSave: saveWorkspace,
|
||||
onAgentPresetSave: saveAgentPreset,
|
||||
onRequestRemove: requestRemove,
|
||||
onConfirmRemove: (bot) => void confirmRemove(bot),
|
||||
onCancelRemove: cancelRemove,
|
||||
|
|
@ -1225,7 +1262,7 @@ export function FeishuSettingsTab({ rpcCall }) {
|
|||
})
|
||||
: null,
|
||||
),
|
||||
);
|
||||
));
|
||||
}
|
||||
|
||||
export function apply(ctx) {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import { normalizeAgentPresetCatalog, normalizeAgentPresetId, SET_AGENT_PRESET_ENDPOINT } from '../../agent-preset.js';
|
||||
|
||||
export const QQ_RPC_CHANNEL = '/qq';
|
||||
|
||||
export const QQ_ENDPOINTS = Object.freeze({
|
||||
|
|
@ -9,6 +11,7 @@ export const QQ_ENDPOINTS = Object.freeze({
|
|||
reconnectBot: 'bot.reconnect',
|
||||
deleteBot: 'bot.delete',
|
||||
setWorkspace: 'bot.workspace.set',
|
||||
setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
|
||||
});
|
||||
|
||||
const PROVISION_STATES = new Set(['starting', 'pending', 'refreshing', 'connecting', 'connected', 'failed', 'cancelled']);
|
||||
|
|
@ -82,6 +85,7 @@ function normalizeBot(value) {
|
|||
connected,
|
||||
state: connected ? 'connected' : state,
|
||||
workspace: text(value.workspace, '', 4_096),
|
||||
agentPreset: normalizeAgentPresetId(value.agentPreset),
|
||||
bot: {
|
||||
name: text(value.bot?.name, 'QQ机器人', 100),
|
||||
appIdMasked: text(value.bot?.appIdMasked, '应用标识已安全保存', 140),
|
||||
|
|
@ -117,6 +121,7 @@ export function normalizeSnapshot(value) {
|
|||
bots,
|
||||
totals: { configured: bots.length, connected: bots.filter((bot) => bot.connected).length },
|
||||
provisioning: source.provisioning ? normalizeProvisioning(source.provisioning) : null,
|
||||
agentPresetCatalog: normalizeAgentPresetCatalog(source.agentPresetCatalog),
|
||||
...(testMessage ? { testMessage } : {}),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,11 @@ import { QqLogoGlyph } from '../../channel-logos.js';
|
|||
import { CredentialActionIcon, CredentialBindingPanel, QrActionIcon } from '../../credential-binding.js';
|
||||
import { h } from '../../i18n.js';
|
||||
import { WorkspaceEditor } from '../../workspace-editor.js';
|
||||
import {
|
||||
AgentPresetCatalogContext,
|
||||
AgentPresetEditor,
|
||||
EMPTY_AGENT_PRESET_CATALOG,
|
||||
} from '../../agent-preset.js';
|
||||
import { useWorkspaceSnapshotFence } from '../../workspace-snapshot-fence.js';
|
||||
import { installDingtalkStyles } from '../dingtalk/styles.js';
|
||||
import {
|
||||
|
|
@ -156,6 +161,7 @@ export function AccountCard({
|
|||
removing,
|
||||
onReconnect,
|
||||
onWorkspaceSave,
|
||||
onAgentPresetSave,
|
||||
onRequestRemove,
|
||||
onConfirmRemove,
|
||||
onCancelRemove,
|
||||
|
|
@ -180,6 +186,11 @@ export function AccountCard({
|
|||
disabled: Boolean(busy),
|
||||
onSave: onWorkspaceSave,
|
||||
}),
|
||||
h(AgentPresetEditor, {
|
||||
agentPreset: account.agentPreset,
|
||||
disabled: Boolean(busy),
|
||||
onSave: onAgentPresetSave,
|
||||
}),
|
||||
h('div', { className: 'ddt-accountFooter dim-cardFooter' },
|
||||
summary ? h('div', { className: 'ddt-summary dim-cardSummary' }, summary) : null,
|
||||
feedback ? h('div', {
|
||||
|
|
@ -196,7 +207,10 @@ export function AccountCard({
|
|||
}
|
||||
|
||||
export function QqSettingsTab({ rpcCall }) {
|
||||
const [model, setModel] = React.useState({ phase: 'loading', bots: [], totals: { configured: 0, connected: 0 }, error: null });
|
||||
const [model, setModel] = React.useState({
|
||||
phase: 'loading', bots: [], totals: { configured: 0, connected: 0 }, error: null,
|
||||
agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG,
|
||||
});
|
||||
const [provision, setProvision] = React.useState(null);
|
||||
const [busy, setBusy] = React.useState(false);
|
||||
const [busyByBot, setBusyByBot] = React.useState({});
|
||||
|
|
@ -233,7 +247,10 @@ export function QqSettingsTab({ rpcCall }) {
|
|||
const snapshot = normalizeSnapshot(await invoke(QQ_ENDPOINTS.status, {}, signal));
|
||||
if (!mounted.current || signal?.aborted
|
||||
|| !workspaceFence.canCommitStatus(workspaceVersion)) return undefined;
|
||||
setModel({ phase: 'ready', bots: snapshot.bots, totals: snapshot.totals, error: null });
|
||||
setModel({
|
||||
phase: 'ready', bots: snapshot.bots, totals: snapshot.totals, error: null,
|
||||
agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
|
||||
});
|
||||
if (restore && snapshot.provisioning) setProvision({
|
||||
...snapshot.provisioning,
|
||||
durationMs: Math.max(1, snapshot.provisioning.expiresAt - Date.now()),
|
||||
|
|
@ -297,7 +314,10 @@ export function QqSettingsTab({ rpcCall }) {
|
|||
));
|
||||
if (!mounted.current) return;
|
||||
if (workspaceFence.canCommitMutation(snapshotVersion)) {
|
||||
setModel({ phase: 'ready', bots: snapshot.bots, totals: snapshot.totals, error: null });
|
||||
setModel({
|
||||
phase: 'ready', bots: snapshot.bots, totals: snapshot.totals, error: null,
|
||||
agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
|
||||
});
|
||||
}
|
||||
setCredentialOpen(false);
|
||||
} catch (error) {
|
||||
|
|
@ -357,7 +377,10 @@ export function QqSettingsTab({ rpcCall }) {
|
|||
try {
|
||||
const snapshot = normalizeSnapshot(await invoke(endpoint, payload));
|
||||
if (mounted.current && workspaceFence.canCommitMutation(snapshotVersion)) {
|
||||
setModel({ phase: 'ready', bots: snapshot.bots, totals: snapshot.totals, error: null });
|
||||
setModel({
|
||||
phase: 'ready', bots: snapshot.bots, totals: snapshot.totals, error: null,
|
||||
agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
|
||||
});
|
||||
}
|
||||
return snapshot;
|
||||
} finally {
|
||||
|
|
@ -424,6 +447,12 @@ export function QqSettingsTab({ rpcCall }) {
|
|||
QQ_ENDPOINTS.setWorkspace,
|
||||
{ botId: account.botId, workspace },
|
||||
),
|
||||
onAgentPresetSave: (agentPreset) => botAction(
|
||||
account,
|
||||
'preset',
|
||||
QQ_ENDPOINTS.setAgentPreset,
|
||||
{ botId: account.botId, agentPreset },
|
||||
),
|
||||
onRequestRemove: () => setRemoveTarget(account.botId),
|
||||
onCancelRemove: () => setRemoveTarget(null),
|
||||
onConfirmRemove: async () => {
|
||||
|
|
@ -447,7 +476,9 @@ export function QqSettingsTab({ rpcCall }) {
|
|||
})
|
||||
: null;
|
||||
|
||||
return h('section', { className: 'ddt-page dqq-page dim-channelPage', 'aria-label': 'QQ 设置' },
|
||||
return h(AgentPresetCatalogContext.Provider, {
|
||||
value: model.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
|
||||
}, h('section', { className: 'ddt-page dqq-page dim-channelPage', 'aria-label': 'QQ 设置' },
|
||||
h(Heading, {
|
||||
totals: model.totals,
|
||||
adding: Boolean(provision),
|
||||
|
|
@ -465,7 +496,7 @@ export function QqSettingsTab({ rpcCall }) {
|
|||
provisionView,
|
||||
model.bots.length === 0 && !provision && !credentialOpen
|
||||
? h(EmptyView, { busy, onStart: () => void startProvisioning() }) : null,
|
||||
botList));
|
||||
botList)));
|
||||
}
|
||||
|
||||
export function apply(ctx) {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import { normalizeAgentPresetCatalog, normalizeAgentPresetId, SET_AGENT_PRESET_ENDPOINT } from '../../agent-preset.js';
|
||||
|
||||
const ACCOUNT_STATES = new Set(['connected', 'connecting', 'offline', 'error']);
|
||||
|
||||
function isRecord(value) {
|
||||
|
|
@ -25,6 +27,7 @@ export const TOKEN_BOT_ENDPOINTS = Object.freeze({
|
|||
reconnectBot: 'bot.reconnect',
|
||||
deleteBot: 'bot.delete',
|
||||
setWorkspace: 'bot.workspace.set',
|
||||
setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
|
||||
});
|
||||
|
||||
export function createTokenChannelApi(channel, connectionSummary, {
|
||||
|
|
@ -52,6 +55,7 @@ export function createTokenChannelApi(channel, connectionSummary, {
|
|||
connected,
|
||||
state: connected ? 'connected' : state,
|
||||
workspace: text(value.workspace, '', 4_096),
|
||||
agentPreset: normalizeAgentPresetId(value.agentPreset),
|
||||
bot: {
|
||||
name: text(value.bot?.name, `${channel}机器人`, 100),
|
||||
username: text(value.bot?.username, '', 100),
|
||||
|
|
@ -82,6 +86,7 @@ export function createTokenChannelApi(channel, connectionSummary, {
|
|||
revision: Number.isSafeInteger(source.revision) ? source.revision : 0,
|
||||
bots,
|
||||
totals: { configured: bots.length, connected: bots.filter((bot) => bot.connected).length },
|
||||
agentPresetCatalog: normalizeAgentPresetCatalog(source.agentPresetCatalog),
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,11 @@ import { CredentialActionIcon, CredentialBindingPanel } from '../../credential-b
|
|||
import { h } from '../../i18n.js';
|
||||
import { installDingtalkStyles } from '../dingtalk/styles.js';
|
||||
import { WorkspaceEditor } from '../../workspace-editor.js';
|
||||
import {
|
||||
AgentPresetCatalogContext,
|
||||
AgentPresetEditor,
|
||||
EMPTY_AGENT_PRESET_CATALOG,
|
||||
} from '../../agent-preset.js';
|
||||
import { useWorkspaceSnapshotFence } from '../../workspace-snapshot-fence.js';
|
||||
|
||||
const Button = React.forwardRef(function Button(
|
||||
|
|
@ -63,7 +68,7 @@ export function createTokenChannelSettings(definition) {
|
|||
accountSettingsEndpoint = null,
|
||||
} = definition;
|
||||
|
||||
function AccountCard({ account, busy, testNotice, removing, onReconnect, onWorkspaceSave, onAccountSettingsSave, onRequestRemove, onConfirmRemove, onCancelRemove }) {
|
||||
function AccountCard({ account, busy, testNotice, removing, onReconnect, onWorkspaceSave, onAgentPresetSave, onAccountSettingsSave, onRequestRemove, onConfirmRemove, onCancelRemove }) {
|
||||
const state = busy === 'reconnect' ? 'connecting' : account.state;
|
||||
const tone = account.connected ? 'success' : state === 'error' ? 'error' : 'warning';
|
||||
const stateLabel = account.connected ? '运行正常' : state === 'connecting' ? '正在连接' : '连接未就绪';
|
||||
|
|
@ -90,6 +95,11 @@ export function createTokenChannelSettings(definition) {
|
|||
disabled: Boolean(busy),
|
||||
onSave: onWorkspaceSave,
|
||||
}),
|
||||
h(AgentPresetEditor, {
|
||||
agentPreset: account.agentPreset,
|
||||
disabled: Boolean(busy),
|
||||
onSave: onAgentPresetSave,
|
||||
}),
|
||||
AccountSettings ? h(AccountSettings, {
|
||||
account,
|
||||
busy: Boolean(busy),
|
||||
|
|
@ -122,6 +132,7 @@ export function createTokenChannelSettings(definition) {
|
|||
function SettingsTab({ rpcCall }) {
|
||||
const [model, setModel] = React.useState({
|
||||
phase: 'loading', bots: [], totals: { configured: 0, connected: 0 }, error: null,
|
||||
agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG,
|
||||
});
|
||||
const [credentialOpen, setCredentialOpen] = React.useState(false);
|
||||
const [credentialError, setCredentialError] = React.useState(null);
|
||||
|
|
@ -156,7 +167,10 @@ export function createTokenChannelSettings(definition) {
|
|||
const snapshot = api.normalizeSnapshot(await invoke(endpoints.status, {}, signal));
|
||||
if (!mounted.current || signal?.aborted
|
||||
|| !workspaceFence.canCommitStatus(workspaceVersion)) return;
|
||||
setModel({ phase: 'ready', bots: snapshot.bots, totals: snapshot.totals, error: null });
|
||||
setModel({
|
||||
phase: 'ready', bots: snapshot.bots, totals: snapshot.totals, error: null,
|
||||
agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
|
||||
});
|
||||
} catch (error) {
|
||||
if (error?.name !== 'AbortError' && mounted.current && !signal?.aborted
|
||||
&& workspaceFence.canCommitStatus(workspaceVersion)) {
|
||||
|
|
@ -199,7 +213,10 @@ export function createTokenChannelSettings(definition) {
|
|||
));
|
||||
if (!mounted.current) return;
|
||||
if (workspaceFence.canCommitMutation(snapshotVersion)) {
|
||||
setModel({ phase: 'ready', bots: snapshot.bots, totals: snapshot.totals, error: null });
|
||||
setModel({
|
||||
phase: 'ready', bots: snapshot.bots, totals: snapshot.totals, error: null,
|
||||
agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
|
||||
});
|
||||
}
|
||||
setCredentialOpen(false);
|
||||
} catch (error) {
|
||||
|
|
@ -218,7 +235,10 @@ export function createTokenChannelSettings(definition) {
|
|||
const value = await invoke(endpoint, payload);
|
||||
const snapshot = api.normalizeSnapshot(value);
|
||||
if (mounted.current && workspaceFence.canCommitMutation(snapshotVersion)) {
|
||||
setModel({ phase: 'ready', bots: snapshot.bots, totals: snapshot.totals, error: null });
|
||||
setModel({
|
||||
phase: 'ready', bots: snapshot.bots, totals: snapshot.totals, error: null,
|
||||
agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
|
||||
});
|
||||
}
|
||||
if (mounted.current && operation === 'reconnect') {
|
||||
setTestNoticeByBot((current) => ({
|
||||
|
|
@ -267,6 +287,12 @@ export function createTokenChannelSettings(definition) {
|
|||
endpoints.setWorkspace,
|
||||
{ botId: account.botId, workspace },
|
||||
),
|
||||
onAgentPresetSave: (agentPreset) => botAction(
|
||||
account,
|
||||
'preset',
|
||||
endpoints.setAgentPreset,
|
||||
{ botId: account.botId, agentPreset },
|
||||
),
|
||||
onAccountSettingsSave: AccountSettings && accountSettingsEndpoint
|
||||
? (payload) => botAction(
|
||||
account,
|
||||
|
|
@ -287,7 +313,9 @@ export function createTokenChannelSettings(definition) {
|
|||
})))))
|
||||
: null;
|
||||
|
||||
return h('section', {
|
||||
return h(AgentPresetCatalogContext.Provider, {
|
||||
value: model.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
|
||||
}, h('section', {
|
||||
className: `ddt-page ${pageClass} dim-channelPage`,
|
||||
'aria-label': `${channel} 设置`,
|
||||
},
|
||||
|
|
@ -353,7 +381,7 @@ export function createTokenChannelSettings(definition) {
|
|||
'aria-hidden': 'true',
|
||||
}, h(LogoGlyph, { size: 64 }))))
|
||||
: null,
|
||||
botList));
|
||||
botList)));
|
||||
}
|
||||
|
||||
return { SettingsTab, AccountCard };
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import { normalizeAgentPresetCatalog, normalizeAgentPresetId, SET_AGENT_PRESET_ENDPOINT } from '../../agent-preset.js';
|
||||
|
||||
export const WECOM_RPC_CHANNEL = '/wecom';
|
||||
|
||||
export const WECOM_ENDPOINTS = Object.freeze({
|
||||
|
|
@ -9,6 +11,7 @@ export const WECOM_ENDPOINTS = Object.freeze({
|
|||
reconnectBot: 'bot.reconnect',
|
||||
deleteBot: 'bot.delete',
|
||||
setWorkspace: 'bot.workspace.set',
|
||||
setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
|
||||
});
|
||||
|
||||
const PROVISION_STATES = new Set(['starting', 'pending', 'refreshing', 'connecting', 'connected', 'failed', 'cancelled']);
|
||||
|
|
@ -91,6 +94,7 @@ function normalizeBot(value) {
|
|||
connected,
|
||||
state: connected ? 'connected' : state,
|
||||
workspace: text(value.workspace, '', 4_096),
|
||||
agentPreset: normalizeAgentPresetId(value.agentPreset),
|
||||
bot: {
|
||||
name: text(value.bot?.name, '企业微信机器人', 100),
|
||||
appIdMasked: text(value.bot?.appIdMasked, '应用标识已安全保存', 140),
|
||||
|
|
@ -116,6 +120,7 @@ export function normalizeSnapshot(value) {
|
|||
totals: { configured: bots.length, connected: bots.filter((bot) => bot.connected).length },
|
||||
provisioning: source.provisioning ? normalizeProvisioning(source.provisioning) : null,
|
||||
testMessage: normalizeTestMessage(source.testMessage),
|
||||
agentPresetCatalog: normalizeAgentPresetCatalog(source.agentPresetCatalog),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,11 @@ import { WecomLogoGlyph } from '../../channel-logos.js';
|
|||
import { CredentialActionIcon, CredentialBindingPanel, QrActionIcon } from '../../credential-binding.js';
|
||||
import { h } from '../../i18n.js';
|
||||
import { WorkspaceEditor } from '../../workspace-editor.js';
|
||||
import {
|
||||
AgentPresetCatalogContext,
|
||||
AgentPresetEditor,
|
||||
EMPTY_AGENT_PRESET_CATALOG,
|
||||
} from '../../agent-preset.js';
|
||||
import { useWorkspaceSnapshotFence } from '../../workspace-snapshot-fence.js';
|
||||
import { installDingtalkStyles } from '../dingtalk/styles.js';
|
||||
import {
|
||||
|
|
@ -155,6 +160,7 @@ export function AccountCard({
|
|||
removing,
|
||||
onReconnect,
|
||||
onWorkspaceSave,
|
||||
onAgentPresetSave,
|
||||
onRequestRemove,
|
||||
onConfirmRemove,
|
||||
onCancelRemove,
|
||||
|
|
@ -179,6 +185,11 @@ export function AccountCard({
|
|||
disabled: Boolean(busy),
|
||||
onSave: onWorkspaceSave,
|
||||
}),
|
||||
h(AgentPresetEditor, {
|
||||
agentPreset: account.agentPreset,
|
||||
disabled: Boolean(busy),
|
||||
onSave: onAgentPresetSave,
|
||||
}),
|
||||
h('div', { className: 'ddt-accountFooter dim-cardFooter' },
|
||||
summary ? h('div', { className: 'ddt-summary dim-cardSummary' }, summary) : null,
|
||||
feedback ? h('div', {
|
||||
|
|
@ -195,7 +206,10 @@ export function AccountCard({
|
|||
}
|
||||
|
||||
export function WecomSettingsTab({ rpcCall }) {
|
||||
const [model, setModel] = React.useState({ phase: 'loading', bots: [], totals: { configured: 0, connected: 0 }, error: null });
|
||||
const [model, setModel] = React.useState({
|
||||
phase: 'loading', bots: [], totals: { configured: 0, connected: 0 }, error: null,
|
||||
agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG,
|
||||
});
|
||||
const [provision, setProvision] = React.useState(null);
|
||||
const [busy, setBusy] = React.useState(false);
|
||||
const [busyByBot, setBusyByBot] = React.useState({});
|
||||
|
|
@ -253,7 +267,10 @@ export function WecomSettingsTab({ rpcCall }) {
|
|||
const snapshot = normalizeSnapshot(await invoke(WECOM_ENDPOINTS.status, {}, signal));
|
||||
if (!mounted.current || signal?.aborted
|
||||
|| !workspaceFence.canCommitStatus(workspaceVersion)) return undefined;
|
||||
setModel({ phase: 'ready', bots: snapshot.bots, totals: snapshot.totals, error: null });
|
||||
setModel({
|
||||
phase: 'ready', bots: snapshot.bots, totals: snapshot.totals, error: null,
|
||||
agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
|
||||
});
|
||||
if (restore && snapshot.provisioning) setProvision({
|
||||
...snapshot.provisioning,
|
||||
durationMs: Math.max(1, snapshot.provisioning.expiresAt - Date.now()),
|
||||
|
|
@ -317,7 +334,10 @@ export function WecomSettingsTab({ rpcCall }) {
|
|||
));
|
||||
if (!mounted.current) return;
|
||||
if (workspaceFence.canCommitMutation(snapshotVersion)) {
|
||||
setModel({ phase: 'ready', bots: snapshot.bots, totals: snapshot.totals, error: null });
|
||||
setModel({
|
||||
phase: 'ready', bots: snapshot.bots, totals: snapshot.totals, error: null,
|
||||
agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
|
||||
});
|
||||
}
|
||||
setCredentialOpen(false);
|
||||
} catch (error) {
|
||||
|
|
@ -377,7 +397,10 @@ export function WecomSettingsTab({ rpcCall }) {
|
|||
try {
|
||||
const snapshot = normalizeSnapshot(await invoke(endpoint, payload));
|
||||
if (mounted.current && workspaceFence.canCommitMutation(snapshotVersion)) {
|
||||
setModel({ phase: 'ready', bots: snapshot.bots, totals: snapshot.totals, error: null });
|
||||
setModel({
|
||||
phase: 'ready', bots: snapshot.bots, totals: snapshot.totals, error: null,
|
||||
agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
|
||||
});
|
||||
}
|
||||
return snapshot;
|
||||
} finally {
|
||||
|
|
@ -455,6 +478,12 @@ export function WecomSettingsTab({ rpcCall }) {
|
|||
WECOM_ENDPOINTS.setWorkspace,
|
||||
{ botId: account.botId, workspace },
|
||||
),
|
||||
onAgentPresetSave: (agentPreset) => botAction(
|
||||
account,
|
||||
'preset',
|
||||
WECOM_ENDPOINTS.setAgentPreset,
|
||||
{ botId: account.botId, agentPreset },
|
||||
),
|
||||
onRequestRemove: () => setRemoveTarget(account.botId),
|
||||
onCancelRemove: () => setRemoveTarget(null),
|
||||
onConfirmRemove: async () => {
|
||||
|
|
@ -478,7 +507,9 @@ export function WecomSettingsTab({ rpcCall }) {
|
|||
})
|
||||
: null;
|
||||
|
||||
return h('section', { className: 'ddt-page dwecom-page dim-channelPage', 'aria-label': '企业微信设置' },
|
||||
return h(AgentPresetCatalogContext.Provider, {
|
||||
value: model.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
|
||||
}, h('section', { className: 'ddt-page dwecom-page dim-channelPage', 'aria-label': '企业微信设置' },
|
||||
h(Heading, {
|
||||
totals: model.totals,
|
||||
adding: Boolean(provision),
|
||||
|
|
@ -497,7 +528,7 @@ export function WecomSettingsTab({ rpcCall }) {
|
|||
provisionView,
|
||||
model.bots.length === 0 && !provision && !credentialOpen
|
||||
? h(EmptyView, { busy, onStart: () => void startProvisioning() }) : null,
|
||||
botList));
|
||||
botList)));
|
||||
}
|
||||
|
||||
export function apply(ctx) {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import { normalizeAgentPresetCatalog, normalizeAgentPresetId, SET_AGENT_PRESET_ENDPOINT } from '../../agent-preset.js';
|
||||
|
||||
export const WEIXIN_RPC_CHANNEL = '/weixin';
|
||||
export const WEIXIN_ENDPOINTS = Object.freeze({
|
||||
status: 'connection.status',
|
||||
|
|
@ -8,6 +10,7 @@ export const WEIXIN_ENDPOINTS = Object.freeze({
|
|||
reconnectBot: 'bot.reconnect',
|
||||
deleteBot: 'bot.delete',
|
||||
setWorkspace: 'bot.workspace.set',
|
||||
setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
|
||||
});
|
||||
|
||||
const ACCOUNT_STATES = new Set(['connected', 'connecting', 'offline', 'error']);
|
||||
|
|
@ -115,6 +118,7 @@ function normalizeBot(value) {
|
|||
connected,
|
||||
configured: value.configured === true,
|
||||
workspace: string(value.workspace).slice(0, 4_096),
|
||||
agentPreset: normalizeAgentPresetId(value.agentPreset),
|
||||
bot: {
|
||||
name: string(value.bot.name, '微信机器人'),
|
||||
accountIdMasked: string(value.bot.accountIdMasked, '已安全保存'),
|
||||
|
|
@ -153,6 +157,7 @@ export function normalizeSnapshot(value) {
|
|||
},
|
||||
provisioning: value.provisioning ? normalizeProvisioning(value.provisioning) : null,
|
||||
testMessage: normalizeTestMessage(value.testMessage),
|
||||
agentPresetCatalog: normalizeAgentPresetCatalog(value.agentPresetCatalog),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,11 @@ import {
|
|||
} from './api.js';
|
||||
import { createPollScheduler, useAnimationFrameScheduler } from '../../lifecycle.js';
|
||||
import { WorkspaceEditor } from '../../workspace-editor.js';
|
||||
import {
|
||||
AgentPresetCatalogContext,
|
||||
AgentPresetEditor,
|
||||
EMPTY_AGENT_PRESET_CATALOG,
|
||||
} from '../../agent-preset.js';
|
||||
import { useWorkspaceSnapshotFence } from '../../workspace-snapshot-fence.js';
|
||||
import { installWeixinStyles } from './styles.js';
|
||||
|
||||
|
|
@ -202,6 +207,7 @@ export function AccountCard({
|
|||
removing,
|
||||
onReconnect,
|
||||
onWorkspaceSave,
|
||||
onAgentPresetSave,
|
||||
onRequestRemove,
|
||||
onConfirmRemove,
|
||||
onCancelRemove,
|
||||
|
|
@ -228,6 +234,11 @@ export function AccountCard({
|
|||
disabled: Boolean(busy),
|
||||
onSave: onWorkspaceSave,
|
||||
}),
|
||||
h(AgentPresetEditor, {
|
||||
agentPreset: account.agentPreset,
|
||||
disabled: Boolean(busy),
|
||||
onSave: onAgentPresetSave,
|
||||
}),
|
||||
h('div', { className: 'dxw-accountFooter dim-cardFooter' },
|
||||
summary ? h('div', { className: 'dxw-summary dim-cardSummary' }, summary) : null,
|
||||
feedback ? h('div', {
|
||||
|
|
@ -260,6 +271,7 @@ function AccountList(props) {
|
|||
removing: props.removeTarget === account.botId,
|
||||
onReconnect: () => props.onReconnect(account),
|
||||
onWorkspaceSave: (workspace) => props.onWorkspaceSave(account, workspace),
|
||||
onAgentPresetSave: (agentPreset) => props.onAgentPresetSave(account, agentPreset),
|
||||
onRequestRemove: () => props.onRequestRemove(account),
|
||||
onConfirmRemove: () => props.onConfirmRemove(account),
|
||||
onCancelRemove: props.onCancelRemove,
|
||||
|
|
@ -285,6 +297,7 @@ export function mergeWeixinProvisioningSnapshot(
|
|||
export function WeixinSettingsTab({ rpcCall }) {
|
||||
const [model, setModel] = React.useState({
|
||||
phase: 'loading', bots: [], totals: EMPTY_TOTALS, revision: 0, error: null,
|
||||
agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG,
|
||||
});
|
||||
const [provision, setProvision] = React.useState(null);
|
||||
const [busy, setBusy] = React.useState(false);
|
||||
|
|
@ -327,6 +340,7 @@ export function WeixinSettingsTab({ rpcCall }) {
|
|||
setModel({
|
||||
phase: 'ready', bots: snapshot.bots, totals: snapshot.totals,
|
||||
revision: snapshot.revision, error: null,
|
||||
agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
|
||||
});
|
||||
if (snapshot.provisioning) {
|
||||
setProvision((current) => mergeWeixinProvisioningSnapshot(
|
||||
|
|
@ -515,7 +529,10 @@ export function WeixinSettingsTab({ rpcCall }) {
|
|||
{ botId: account.botId, sendTest: true },
|
||||
));
|
||||
if (mountedRef.current && workspaceFence.canCommitMutation(snapshotVersion)) {
|
||||
setModel((current) => ({ ...current, bots: snapshot.bots, totals: snapshot.totals, revision: snapshot.revision }));
|
||||
setModel((current) => ({
|
||||
...current, bots: snapshot.bots, totals: snapshot.totals, revision: snapshot.revision,
|
||||
agentPresetCatalog: snapshot.agentPresetCatalog ?? current.agentPresetCatalog,
|
||||
}));
|
||||
}
|
||||
const refreshed = snapshot.bots.find((bot) => bot.botId === account.botId);
|
||||
let feedback;
|
||||
|
|
@ -559,6 +576,29 @@ export function WeixinSettingsTab({ rpcCall }) {
|
|||
setModel({
|
||||
phase: 'ready', bots: snapshot.bots, totals: snapshot.totals,
|
||||
revision: snapshot.revision, error: null,
|
||||
agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
|
||||
});
|
||||
}
|
||||
} finally {
|
||||
const shouldRefresh = workspaceFence.endMutation();
|
||||
if (shouldRefresh && mountedRef.current) void loadStatus({ silent: true });
|
||||
if (mountedRef.current) setBotBusy(account.botId, null);
|
||||
}
|
||||
}, [invoke, loadStatus, setBotBusy, workspaceFence]);
|
||||
|
||||
const saveAgentPreset = React.useCallback(async (account, agentPreset) => {
|
||||
const snapshotVersion = workspaceFence.beginMutation();
|
||||
setBotBusy(account.botId, 'preset');
|
||||
try {
|
||||
const snapshot = normalizeSnapshot(await invoke(
|
||||
WEIXIN_ENDPOINTS.setAgentPreset,
|
||||
{ botId: account.botId, agentPreset },
|
||||
));
|
||||
if (mountedRef.current && workspaceFence.canCommitMutation(snapshotVersion)) {
|
||||
setModel({
|
||||
phase: 'ready', bots: snapshot.bots, totals: snapshot.totals,
|
||||
revision: snapshot.revision, error: null,
|
||||
agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
|
||||
});
|
||||
}
|
||||
} finally {
|
||||
|
|
@ -577,7 +617,10 @@ export function WeixinSettingsTab({ rpcCall }) {
|
|||
confirm: true,
|
||||
}));
|
||||
if (mountedRef.current && workspaceFence.canCommitMutation(snapshotVersion)) {
|
||||
setModel((current) => ({ ...current, bots: snapshot.bots, totals: snapshot.totals, revision: snapshot.revision }));
|
||||
setModel((current) => ({
|
||||
...current, bots: snapshot.bots, totals: snapshot.totals, revision: snapshot.revision,
|
||||
agentPresetCatalog: snapshot.agentPresetCatalog ?? current.agentPresetCatalog,
|
||||
}));
|
||||
}
|
||||
setRemoveTarget(null);
|
||||
announce('微信账号及本机凭据已移除。');
|
||||
|
|
@ -617,7 +660,9 @@ export function WeixinSettingsTab({ rpcCall }) {
|
|||
});
|
||||
}
|
||||
|
||||
return h('section', { className: 'dxw-page dim-channelPage', 'aria-label': '微信设置' },
|
||||
return h(AgentPresetCatalogContext.Provider, {
|
||||
value: model.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
|
||||
}, h('section', { className: 'dxw-page dim-channelPage', 'aria-label': '微信设置' },
|
||||
h(Heading, {
|
||||
totals: model.totals,
|
||||
adding: Boolean(provision),
|
||||
|
|
@ -650,12 +695,13 @@ export function WeixinSettingsTab({ rpcCall }) {
|
|||
removeTarget,
|
||||
onReconnect: (account) => void reconnect(account),
|
||||
onWorkspaceSave: saveWorkspace,
|
||||
onAgentPresetSave: saveAgentPreset,
|
||||
onRequestRemove: (account) => setRemoveTarget(account.botId),
|
||||
onConfirmRemove: (account) => void remove(account),
|
||||
onCancelRemove: () => setRemoveTarget(null),
|
||||
})
|
||||
: null),
|
||||
);
|
||||
));
|
||||
}
|
||||
|
||||
export function apply(ctx) {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import { normalizeAgentPresetCatalog, normalizeAgentPresetId, SET_AGENT_PRESET_ENDPOINT } from '../../agent-preset.js';
|
||||
|
||||
export const WHATSAPP_RPC_CHANNEL = '/whatsapp';
|
||||
|
||||
export const WHATSAPP_ENDPOINTS = Object.freeze({
|
||||
|
|
@ -8,6 +10,7 @@ export const WHATSAPP_ENDPOINTS = Object.freeze({
|
|||
reconnectBot: 'bot.reconnect',
|
||||
deleteBot: 'bot.delete',
|
||||
setWorkspace: 'bot.workspace.set',
|
||||
setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
|
||||
});
|
||||
|
||||
const PROVISION_STATES = new Set(['starting', 'pending', 'connecting', 'connected', 'failed', 'cancelled']);
|
||||
|
|
@ -82,6 +85,7 @@ function normalizeBot(value) {
|
|||
connected,
|
||||
state: connected ? 'connected' : state,
|
||||
workspace: text(value.workspace, '', 4_096),
|
||||
agentPreset: normalizeAgentPresetId(value.agentPreset),
|
||||
bot: {
|
||||
name: text(value.bot?.name, 'WhatsApp机器人', 100),
|
||||
idMasked: text(value.bot?.idMasked, 'WhatsApp账号', 140),
|
||||
|
|
@ -109,6 +113,7 @@ export function normalizeSnapshot(value) {
|
|||
bots,
|
||||
totals: { configured: bots.length, connected: bots.filter((bot) => bot.connected).length },
|
||||
provisioning: source.provisioning ? normalizeProvisioning(source.provisioning) : null,
|
||||
agentPresetCatalog: normalizeAgentPresetCatalog(source.agentPresetCatalog),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,11 @@ import { WhatsappLogoGlyph } from '../../channel-logos.js';
|
|||
import { QrActionIcon } from '../../credential-binding.js';
|
||||
import { h } from '../../i18n.js';
|
||||
import { WorkspaceEditor } from '../../workspace-editor.js';
|
||||
import {
|
||||
AgentPresetCatalogContext,
|
||||
AgentPresetEditor,
|
||||
EMPTY_AGENT_PRESET_CATALOG,
|
||||
} from '../../agent-preset.js';
|
||||
import { useWorkspaceSnapshotFence } from '../../workspace-snapshot-fence.js';
|
||||
import { installDingtalkStyles } from '../dingtalk/styles.js';
|
||||
import {
|
||||
|
|
@ -173,6 +178,7 @@ export function WhatsappAccountCard({
|
|||
removing,
|
||||
onReconnect,
|
||||
onWorkspaceSave,
|
||||
onAgentPresetSave,
|
||||
onRequestRemove,
|
||||
onConfirmRemove,
|
||||
onCancelRemove,
|
||||
|
|
@ -205,6 +211,11 @@ export function WhatsappAccountCard({
|
|||
disabled: Boolean(busy),
|
||||
onSave: onWorkspaceSave,
|
||||
}),
|
||||
h(AgentPresetEditor, {
|
||||
agentPreset: account.agentPreset,
|
||||
disabled: Boolean(busy),
|
||||
onSave: onAgentPresetSave,
|
||||
}),
|
||||
h('div', { className: 'ddt-accountFooter dim-cardFooter' },
|
||||
summary ? h('div', { className: 'ddt-summary dim-cardSummary' }, summary) : null,
|
||||
testNotice ? h('div', {
|
||||
|
|
@ -229,6 +240,7 @@ export function WhatsappAccountCard({
|
|||
export function WhatsappSettingsTab({ rpcCall }) {
|
||||
const [model, setModel] = React.useState({
|
||||
phase: 'loading', bots: [], totals: { configured: 0, connected: 0 }, error: null,
|
||||
agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG,
|
||||
});
|
||||
const [provision, setProvision] = React.useState(null);
|
||||
const [busy, setBusy] = React.useState(false);
|
||||
|
|
@ -264,7 +276,10 @@ export function WhatsappSettingsTab({ rpcCall }) {
|
|||
const snapshot = normalizeSnapshot(await invoke(WHATSAPP_ENDPOINTS.status, {}, signal));
|
||||
if (!mounted.current || signal?.aborted
|
||||
|| !workspaceFence.canCommitStatus(workspaceVersion)) return undefined;
|
||||
setModel({ phase: 'ready', bots: snapshot.bots, totals: snapshot.totals, error: null });
|
||||
setModel({
|
||||
phase: 'ready', bots: snapshot.bots, totals: snapshot.totals, error: null,
|
||||
agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
|
||||
});
|
||||
if (restore && snapshot.provisioning) setProvision({
|
||||
...snapshot.provisioning,
|
||||
durationMs: Math.max(1, snapshot.provisioning.expiresAt - Date.now()),
|
||||
|
|
@ -392,7 +407,10 @@ export function WhatsappSettingsTab({ rpcCall }) {
|
|||
const value = await invoke(endpoint, payload);
|
||||
const snapshot = normalizeSnapshot(value);
|
||||
if (mounted.current && workspaceFence.canCommitMutation(snapshotVersion)) {
|
||||
setModel({ phase: 'ready', bots: snapshot.bots, totals: snapshot.totals, error: null });
|
||||
setModel({
|
||||
phase: 'ready', bots: snapshot.bots, totals: snapshot.totals, error: null,
|
||||
agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
|
||||
});
|
||||
if (operation === 'reconnect') {
|
||||
setTestNoticeByBot((current) => ({
|
||||
...current,
|
||||
|
|
@ -441,6 +459,12 @@ export function WhatsappSettingsTab({ rpcCall }) {
|
|||
WHATSAPP_ENDPOINTS.setWorkspace,
|
||||
{ botId: account.botId, workspace },
|
||||
),
|
||||
onAgentPresetSave: (agentPreset) => botAction(
|
||||
account,
|
||||
'preset',
|
||||
WHATSAPP_ENDPOINTS.setAgentPreset,
|
||||
{ botId: account.botId, agentPreset },
|
||||
),
|
||||
onRequestRemove: () => setRemoveTarget(account.botId),
|
||||
onCancelRemove: () => setRemoveTarget(null),
|
||||
onConfirmRemove: async () => {
|
||||
|
|
@ -453,7 +477,9 @@ export function WhatsappSettingsTab({ rpcCall }) {
|
|||
})))))
|
||||
: null;
|
||||
|
||||
return h('section', {
|
||||
return h(AgentPresetCatalogContext.Provider, {
|
||||
value: model.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
|
||||
}, h('section', {
|
||||
className: 'ddt-page dwa-page dim-channelPage',
|
||||
'aria-label': 'WhatsApp 设置',
|
||||
},
|
||||
|
|
@ -490,5 +516,5 @@ export function WhatsappSettingsTab({ rpcCall }) {
|
|||
: model.bots.length === 0
|
||||
? h(EmptyView, { busy, onStart: () => void startProvisioning(false) })
|
||||
: null,
|
||||
botList));
|
||||
botList)));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -117,6 +117,12 @@ const EN = Object.freeze({
|
|||
'工作区路径不存在。': 'The workspace path does not exist.',
|
||||
'工作区路径必须指向一个目录。': 'The workspace path must point to a directory.',
|
||||
'找不到要修改的机器人。': 'The bot could not be found.',
|
||||
'Agent Preset': 'Agent Preset',
|
||||
'跟随 Host 默认': 'Follow the Host default',
|
||||
'只影响之后新建的会话。': 'Only later new sessions use this preset.',
|
||||
'Agent Preset 修改失败,请重试。': 'Could not update the Agent Preset. Try again.',
|
||||
'请选择 Agent Preset。': 'Choose an Agent Preset.',
|
||||
'Agent Preset 无效。': 'The Agent Preset is invalid.',
|
||||
'尚未检查': 'Not checked yet',
|
||||
'刚刚': 'Just now',
|
||||
'检查连接': 'Check connection',
|
||||
|
|
|
|||
|
|
@ -142,6 +142,14 @@ const CSS = String.raw`
|
|||
.dim-panel .dim-workspaceEdit { grid-column: 2; grid-row: 1; padding: 0; border: 0; color: #1677ff; background: transparent; font: inherit; font-weight: 560; white-space: nowrap; cursor: pointer; }
|
||||
.dim-panel .dim-workspaceEdit:disabled { cursor: not-allowed; opacity: .55; }
|
||||
.dim-panel .dim-workspacePath { min-width: 0; max-width: 100%; grid-column: 1 / -1; grid-row: 2; display: block; overflow-x: auto; overflow-y: hidden; color: var(--dsw-alias-label-primary, #1f2329); font: 12px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace; white-space: nowrap; }
|
||||
.dim-panel .dim-preset { min-width: 0; display: grid; grid-template-columns: minmax(0, 1fr) max-content; align-items: center; column-gap: 10px; row-gap: 4px; margin-top: 6px; padding: 6px 10px; border: 1px solid var(--dsw-alias-border-l1, #eef0f3); border-radius: 9px; background: var(--dsw-alias-bg-module-platform, #f7f8fa); }
|
||||
.dim-panel .dim-presetHeader { display: contents; color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; line-height: normal; }
|
||||
.dim-panel .dim-presetHeader > span { grid-column: 1; grid-row: 1; white-space: nowrap; }
|
||||
.dim-panel .dim-presetStatus { grid-column: 2; grid-row: 1; color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 12px; white-space: nowrap; }
|
||||
.dim-panel .dim-presetSelect { min-width: 0; max-width: 100%; grid-column: 1 / -1; grid-row: 2; height: 30px; padding: 0 8px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 7px; color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-bg-layer-1, #fff); font: inherit; font-size: 12px; }
|
||||
.dim-panel .dim-presetSelect:disabled { cursor: not-allowed; opacity: .55; }
|
||||
.dim-panel .dim-presetHelp { grid-column: 1 / -1; grid-row: 3; color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 11px; line-height: 1.4; }
|
||||
.dim-panel .dim-presetError { grid-column: 1 / -1; grid-row: 4; margin: 0; color: var(--dsw-alias-state-error-primary, #d54941); font-size: 12px; line-height: 1.4; }
|
||||
.dim-directoryPickerBackdrop { --dim-blue: var(--dsw-alias-state-business-primary, #3370ff); --dim-blue-soft: color-mix(in srgb, var(--dim-blue) 9%, transparent); position: fixed; inset: 0; z-index: 1000; display: grid; place-items: center; padding: 24px; background: rgb(15 17 21 / 42%); backdrop-filter: blur(3px); }
|
||||
.dim-directoryPickerBackdrop, .dim-directoryPickerBackdrop *, .dim-directoryPickerBackdrop *::before, .dim-directoryPickerBackdrop *::after { box-sizing: border-box; }
|
||||
.dim-directoryPicker { width: min(720px, 100%); height: min(620px, calc(100vh - 48px)); min-height: 420px; display: grid; grid-template-rows: auto minmax(0, 1fr) auto; overflow: hidden; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 18px; outline: none; color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-bg-layer-1, #fff); box-shadow: 0 24px 72px rgb(15 17 21 / 24%); }
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import {
|
|||
createWorkspaceAwareController,
|
||||
observeBotWorkspaceRemovals,
|
||||
} from '../../../../src/channels/shared/bot-workspace-store.mjs';
|
||||
import { listAgentPresetCatalog } from '../../../../src/channels/shared/agent-preset.mjs';
|
||||
import { createConnectionSupervisor } from './connection-supervisor.mjs';
|
||||
import { createHarnessCommandExecutor } from '../../harness-command-executor.mjs';
|
||||
import { createHarnessSessionExecutors } from '../../harness-session-coordinator.mjs';
|
||||
|
|
@ -135,7 +136,11 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|||
}
|
||||
},
|
||||
});
|
||||
const controller = createWorkspaceAwareController(coreController, { workspaces, stateFor });
|
||||
const controller = createWorkspaceAwareController(coreController, {
|
||||
workspaces,
|
||||
stateFor,
|
||||
agentPresetCatalog: await listAgentPresetCatalog(ctx),
|
||||
});
|
||||
const supervisor = createSupervisor({
|
||||
controller,
|
||||
harness,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import QRCode from 'qrcode';
|
||||
import { resolveRpcAuthority } from '../../rpc-authority.mjs';
|
||||
import { publicWorkspaceError, SET_WORKSPACE_ENDPOINT, validWorkspacePayload } from '../shared/workspace-rpc.mjs';
|
||||
import { SET_AGENT_PRESET_ENDPOINT, validAgentPresetPayload } from '../shared/agent-preset-rpc.mjs';
|
||||
import {
|
||||
connectionTestTargetUnavailable,
|
||||
publicConnectionTestResult,
|
||||
|
|
@ -16,6 +17,7 @@ export const DINGTALK_ENDPOINTS = Object.freeze({
|
|||
reconnectBot: 'bot.reconnect',
|
||||
deleteBot: 'bot.delete',
|
||||
setWorkspace: SET_WORKSPACE_ENDPOINT,
|
||||
setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
|
||||
approveSender: 'bot.sender.approve',
|
||||
revokeSender: 'bot.sender.revoke',
|
||||
});
|
||||
|
|
@ -88,6 +90,10 @@ function payloadFailure(endpoint, payload) {
|
|||
return validWorkspacePayload(payload)
|
||||
? null : '请输入工作区绝对路径。';
|
||||
}
|
||||
if (endpoint === DINGTALK_ENDPOINTS.setAgentPreset) {
|
||||
return validAgentPresetPayload(payload)
|
||||
? null : '请选择 Agent Preset。';
|
||||
}
|
||||
if (endpoint === DINGTALK_ENDPOINTS.approveSender) {
|
||||
return exactKeys(payload, ['botId', 'requestId', 'confirm'])
|
||||
&& validId(payload.botId)
|
||||
|
|
@ -245,6 +251,12 @@ export function createDingtalkRpcHandler(controller, { encodeQr = qrDataUrl } =
|
|||
await controller.updateWorkspace(payload.botId, payload.workspace),
|
||||
cachedEncode,
|
||||
);
|
||||
} else if (endpoint === DINGTALK_ENDPOINTS.setAgentPreset) {
|
||||
if (typeof controller.updateAgentPreset !== 'function') throw new Error('Agent preset update is unavailable');
|
||||
value = await publicStatus(
|
||||
await controller.updateAgentPreset(payload.botId, payload.agentPreset),
|
||||
cachedEncode,
|
||||
);
|
||||
} else if (endpoint === DINGTALK_ENDPOINTS.approveSender) {
|
||||
value = await publicStatus(
|
||||
await controller.approveSender(payload.botId, payload.requestId),
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import {
|
|||
createWorkspaceAwareController,
|
||||
observeBotWorkspaceRemovals,
|
||||
} from '../../../../src/channels/shared/bot-workspace-store.mjs';
|
||||
import { listAgentPresetCatalog } from '../../../../src/channels/shared/agent-preset.mjs';
|
||||
|
||||
function harnessOrigin(webServer, configured) {
|
||||
if (configured !== undefined) return new URL(configured);
|
||||
|
|
@ -166,6 +167,7 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|||
const controller = createWorkspaceAwareController(coreController, {
|
||||
workspaces,
|
||||
stateFor: stateForBotId,
|
||||
agentPresetCatalog: await listAgentPresetCatalog(ctx),
|
||||
});
|
||||
|
||||
const supervisor = createSupervisor({
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import QRCode from 'qrcode';
|
|||
import { publicConnectionTestResult } from '../../../../src/channels/shared/connection-test.mjs';
|
||||
import { resolveRpcAuthority } from '../../rpc-authority.mjs';
|
||||
import { publicWorkspaceError, validWorkspacePayload } from '../shared/workspace-rpc.mjs';
|
||||
import { validAgentPresetPayload } from '../shared/agent-preset-rpc.mjs';
|
||||
import {
|
||||
FEISHU_ENDPOINTS,
|
||||
FEISHU_RPC_CHANNEL,
|
||||
|
|
@ -378,6 +379,10 @@ function validPayload(endpoint, payload) {
|
|||
return validWorkspacePayload(payload)
|
||||
? null : '请输入工作区绝对路径。';
|
||||
}
|
||||
if (endpoint === FEISHU_ENDPOINTS.setAgentPreset) {
|
||||
return validAgentPresetPayload(payload)
|
||||
? null : '请选择 Agent Preset。';
|
||||
}
|
||||
return 'Unknown Feishu endpoint.';
|
||||
}
|
||||
|
||||
|
|
@ -606,6 +611,12 @@ export function createFeishuRpcHandler(controller, { encodeQr = qrCodeDataUrl }
|
|||
await controller.updateWorkspace(payload.botId, payload.workspace),
|
||||
{ encodeQr: cachedEncodeQr },
|
||||
);
|
||||
} else if (endpoint === FEISHU_ENDPOINTS.setAgentPreset) {
|
||||
if (typeof controller.updateAgentPreset !== 'function') throw new Error('Agent preset update is unavailable');
|
||||
value = await toPublicFeishuStatus(
|
||||
await controller.updateAgentPreset(payload.botId, payload.agentPreset),
|
||||
{ encodeQr: cachedEncodeQr },
|
||||
);
|
||||
} else {
|
||||
if (typeof controller.deleteBot !== 'function') throw new Error('Multi-bot delete is unavailable');
|
||||
value = await toPublicFeishuStatus(await controller.deleteBot(payload.botId), { encodeQr: cachedEncodeQr });
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import {
|
|||
createWorkspaceAwareController,
|
||||
observeBotWorkspaceRemovals,
|
||||
} from '../../../../src/channels/shared/bot-workspace-store.mjs';
|
||||
import { listAgentPresetCatalog } from '../../../../src/channels/shared/agent-preset.mjs';
|
||||
import { createConnectionSupervisor } from './connection-supervisor.mjs';
|
||||
import { createHarnessCommandExecutor } from '../../harness-command-executor.mjs';
|
||||
import { createHarnessSessionExecutors } from '../../harness-session-coordinator.mjs';
|
||||
|
|
@ -125,7 +126,11 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|||
}
|
||||
},
|
||||
});
|
||||
const controller = createWorkspaceAwareController(coreController, { workspaces, stateFor });
|
||||
const controller = createWorkspaceAwareController(coreController, {
|
||||
workspaces,
|
||||
stateFor,
|
||||
agentPresetCatalog: await listAgentPresetCatalog(ctx),
|
||||
});
|
||||
const supervisor = createSupervisor({
|
||||
controller,
|
||||
harness,
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import {
|
|||
} from '../../../../src/channels/shared/connection-test.mjs';
|
||||
import { resolveRpcAuthority } from '../../rpc-authority.mjs';
|
||||
import { publicWorkspaceError, SET_WORKSPACE_ENDPOINT, validWorkspacePayload } from '../shared/workspace-rpc.mjs';
|
||||
import { SET_AGENT_PRESET_ENDPOINT, validAgentPresetPayload } from '../shared/agent-preset-rpc.mjs';
|
||||
|
||||
export const QQ_RPC_CHANNEL = '/qq';
|
||||
export const QQ_ENDPOINTS = Object.freeze({
|
||||
|
|
@ -16,6 +17,7 @@ export const QQ_ENDPOINTS = Object.freeze({
|
|||
reconnectBot: 'bot.reconnect',
|
||||
deleteBot: 'bot.delete',
|
||||
setWorkspace: SET_WORKSPACE_ENDPOINT,
|
||||
setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
|
||||
});
|
||||
export const QQ_RPC_ENDPOINTS = Object.freeze(Object.values(QQ_ENDPOINTS));
|
||||
|
||||
|
|
@ -70,6 +72,10 @@ function payloadFailure(endpoint, payload) {
|
|||
return validWorkspacePayload(payload)
|
||||
? null : '请输入工作区绝对路径。';
|
||||
}
|
||||
if (endpoint === QQ_ENDPOINTS.setAgentPreset) {
|
||||
return validAgentPresetPayload(payload)
|
||||
? null : '请选择 Agent Preset。';
|
||||
}
|
||||
return 'Unknown QQ endpoint.';
|
||||
}
|
||||
|
||||
|
|
@ -165,6 +171,12 @@ export function createQqRpcHandler(controller, { encodeQr = qrDataUrl } = {}) {
|
|||
await controller.updateWorkspace(payload.botId, payload.workspace),
|
||||
cachedEncode,
|
||||
);
|
||||
} else if (endpoint === QQ_ENDPOINTS.setAgentPreset) {
|
||||
if (typeof controller.updateAgentPreset !== 'function') throw new Error('Agent preset update is unavailable');
|
||||
value = await publicStatus(
|
||||
await controller.updateAgentPreset(payload.botId, payload.agentPreset),
|
||||
cachedEncode,
|
||||
);
|
||||
} else {
|
||||
value = await publicStatus(await controller.deleteBot(payload.botId), cachedEncode);
|
||||
}
|
||||
|
|
|
|||
21
plugin-src/host/channels/shared/agent-preset-rpc.mjs
Normal file
21
plugin-src/host/channels/shared/agent-preset-rpc.mjs
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import { AGENT_PRESET_ID } from '../../../../src/channels/shared/agent-preset.mjs';
|
||||
|
||||
export const SET_AGENT_PRESET_ENDPOINT = 'bot.preset.set';
|
||||
|
||||
function isRecord(value) {
|
||||
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
||||
}
|
||||
|
||||
export function validAgentPresetPayload(payload) {
|
||||
return isRecord(payload)
|
||||
&& Object.keys(payload).every((key) => ['botId', 'agentPreset'].includes(key))
|
||||
&& typeof payload.botId === 'string'
|
||||
&& /^[A-Za-z0-9_-]{1,128}$/.test(payload.botId)
|
||||
&& (payload.agentPreset === null
|
||||
|| (typeof payload.agentPreset === 'string' && AGENT_PRESET_ID.test(payload.agentPreset)));
|
||||
}
|
||||
|
||||
export function publicAgentPresetError(error) {
|
||||
if (!['agent-preset-invalid', 'workspace-bot-not-found'].includes(error?.code)) return null;
|
||||
return { code: error.code, message: error.message };
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@ import {
|
|||
createWorkspaceAwareController,
|
||||
observeBotWorkspaceRemovals,
|
||||
} from '../../../../src/channels/shared/bot-workspace-store.mjs';
|
||||
import { listAgentPresetCatalog } from '../../../../src/channels/shared/agent-preset.mjs';
|
||||
|
||||
export function harnessOrigin(webServer, configured) {
|
||||
if (configured !== undefined) return new URL(configured);
|
||||
|
|
@ -127,7 +128,11 @@ export async function createTokenProductionController(ctx, config, internals, de
|
|||
}
|
||||
},
|
||||
});
|
||||
const controller = createWorkspaceAwareController(coreController, { workspaces, stateFor });
|
||||
const controller = createWorkspaceAwareController(coreController, {
|
||||
workspaces,
|
||||
stateFor,
|
||||
agentPresetCatalog: await listAgentPresetCatalog(ctx),
|
||||
});
|
||||
const supervisor = createSupervisor({
|
||||
channel,
|
||||
controller,
|
||||
|
|
|
|||
|
|
@ -5,6 +5,10 @@ import {
|
|||
SET_WORKSPACE_ENDPOINT,
|
||||
validWorkspacePayload,
|
||||
} from './workspace-rpc.mjs';
|
||||
import {
|
||||
SET_AGENT_PRESET_ENDPOINT,
|
||||
validAgentPresetPayload,
|
||||
} from './agent-preset-rpc.mjs';
|
||||
|
||||
export const TOKEN_BOT_ENDPOINTS = Object.freeze({
|
||||
status: 'connection.status',
|
||||
|
|
@ -12,6 +16,7 @@ export const TOKEN_BOT_ENDPOINTS = Object.freeze({
|
|||
reconnectBot: 'bot.reconnect',
|
||||
deleteBot: 'bot.delete',
|
||||
setWorkspace: SET_WORKSPACE_ENDPOINT,
|
||||
setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
|
||||
});
|
||||
|
||||
const ENDPOINTS = Object.freeze(Object.values(TOKEN_BOT_ENDPOINTS));
|
||||
|
|
@ -57,6 +62,10 @@ function payloadFailure(endpoint, payload) {
|
|||
return validWorkspacePayload(payload)
|
||||
? null : '请输入工作区绝对路径。';
|
||||
}
|
||||
if (endpoint === TOKEN_BOT_ENDPOINTS.setAgentPreset) {
|
||||
return validAgentPresetPayload(payload)
|
||||
? null : '请选择 Agent Preset。';
|
||||
}
|
||||
return 'Unknown bot endpoint.';
|
||||
}
|
||||
|
||||
|
|
@ -132,6 +141,9 @@ export function createTokenBotRpcHandler(controller, { channel }) {
|
|||
} else if (endpoint === TOKEN_BOT_ENDPOINTS.setWorkspace) {
|
||||
if (typeof controller.updateWorkspace !== 'function') throw new Error('Workspace update is unavailable');
|
||||
value = await controller.updateWorkspace(payload.botId, payload.workspace);
|
||||
} else if (endpoint === TOKEN_BOT_ENDPOINTS.setAgentPreset) {
|
||||
if (typeof controller.updateAgentPreset !== 'function') throw new Error('Agent preset update is unavailable');
|
||||
value = await controller.updateAgentPreset(payload.botId, payload.agentPreset);
|
||||
} else {
|
||||
value = await controller.deleteBot(payload.botId);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ export function publicWorkspaceError(error) {
|
|||
'workspace-not-found',
|
||||
'workspace-not-directory',
|
||||
'workspace-bot-not-found',
|
||||
'agent-preset-invalid',
|
||||
].includes(error?.code)) return null;
|
||||
return { code: error.code, message: error.message };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import {
|
|||
createWorkspaceAwareController,
|
||||
observeBotWorkspaceRemovals,
|
||||
} from '../../../../src/channels/shared/bot-workspace-store.mjs';
|
||||
import { listAgentPresetCatalog } from '../../../../src/channels/shared/agent-preset.mjs';
|
||||
import { createTokenConnectionSupervisor } from '../shared/connection-supervisor.mjs';
|
||||
import { harnessOrigin, pluginPaths } from '../shared/production.mjs';
|
||||
import { createHarnessCommandExecutor } from '../../harness-command-executor.mjs';
|
||||
|
|
@ -105,7 +106,11 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|||
}
|
||||
},
|
||||
});
|
||||
const controller = createWorkspaceAwareController(coreController, { workspaces, stateFor });
|
||||
const controller = createWorkspaceAwareController(coreController, {
|
||||
workspaces,
|
||||
stateFor,
|
||||
agentPresetCatalog: await listAgentPresetCatalog(ctx),
|
||||
});
|
||||
const supervisor = createSupervisor({
|
||||
channel: 'slack',
|
||||
controller,
|
||||
|
|
|
|||
|
|
@ -5,6 +5,10 @@ import {
|
|||
SET_WORKSPACE_ENDPOINT,
|
||||
validWorkspacePayload,
|
||||
} from '../shared/workspace-rpc.mjs';
|
||||
import {
|
||||
SET_AGENT_PRESET_ENDPOINT,
|
||||
validAgentPresetPayload,
|
||||
} from '../shared/agent-preset-rpc.mjs';
|
||||
|
||||
export const SLACK_RPC_CHANNEL = '/slack';
|
||||
export const SLACK_ENDPOINTS = Object.freeze({
|
||||
|
|
@ -13,6 +17,7 @@ export const SLACK_ENDPOINTS = Object.freeze({
|
|||
reconnectBot: 'bot.reconnect',
|
||||
deleteBot: 'bot.delete',
|
||||
setWorkspace: SET_WORKSPACE_ENDPOINT,
|
||||
setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
|
||||
});
|
||||
export const SLACK_RPC_ENDPOINTS = Object.freeze(Object.values(SLACK_ENDPOINTS));
|
||||
|
||||
|
|
@ -66,6 +71,10 @@ function payloadFailure(endpoint, payload) {
|
|||
return validWorkspacePayload(payload)
|
||||
? null : '请输入工作区绝对路径。';
|
||||
}
|
||||
if (endpoint === SLACK_ENDPOINTS.setAgentPreset) {
|
||||
return validAgentPresetPayload(payload)
|
||||
? null : '请选择 Agent Preset。';
|
||||
}
|
||||
return 'Unknown Slack endpoint.';
|
||||
}
|
||||
|
||||
|
|
@ -145,6 +154,10 @@ export function createSlackRpcHandler(controller) {
|
|||
if (typeof controller.updateWorkspace !== 'function') throw new Error('Workspace update is unavailable');
|
||||
value = await controller.updateWorkspace(payload.botId, payload.workspace);
|
||||
}
|
||||
else if (endpoint === SLACK_ENDPOINTS.setAgentPreset) {
|
||||
if (typeof controller.updateAgentPreset !== 'function') throw new Error('Agent preset update is unavailable');
|
||||
value = await controller.updateAgentPreset(payload.botId, payload.agentPreset);
|
||||
}
|
||||
else value = await controller.deleteBot(payload.botId);
|
||||
return signal?.aborted
|
||||
? { ok: false, error: { code: 'cancelled', message: 'The request was cancelled.' } }
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import {
|
|||
createWorkspaceAwareController,
|
||||
observeBotWorkspaceRemovals,
|
||||
} from '../../../../src/channels/shared/bot-workspace-store.mjs';
|
||||
import { listAgentPresetCatalog } from '../../../../src/channels/shared/agent-preset.mjs';
|
||||
import { createConnectionSupervisor } from './connection-supervisor.mjs';
|
||||
import { createHarnessCommandExecutor } from '../../harness-command-executor.mjs';
|
||||
import { createHarnessSessionExecutors } from '../../harness-session-coordinator.mjs';
|
||||
|
|
@ -129,7 +130,11 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|||
}
|
||||
},
|
||||
});
|
||||
const controller = createWorkspaceAwareController(coreController, { workspaces, stateFor });
|
||||
const controller = createWorkspaceAwareController(coreController, {
|
||||
workspaces,
|
||||
stateFor,
|
||||
agentPresetCatalog: await listAgentPresetCatalog(ctx),
|
||||
});
|
||||
const supervisor = createSupervisor({
|
||||
controller,
|
||||
harness,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import QRCode from 'qrcode';
|
||||
import { resolveRpcAuthority } from '../../rpc-authority.mjs';
|
||||
import { publicWorkspaceError, SET_WORKSPACE_ENDPOINT, validWorkspacePayload } from '../shared/workspace-rpc.mjs';
|
||||
import { SET_AGENT_PRESET_ENDPOINT, validAgentPresetPayload } from '../shared/agent-preset-rpc.mjs';
|
||||
import {
|
||||
connectionTestTargetUnavailable,
|
||||
publicConnectionTestResult,
|
||||
|
|
@ -16,6 +17,7 @@ export const WECOM_ENDPOINTS = Object.freeze({
|
|||
reconnectBot: 'bot.reconnect',
|
||||
deleteBot: 'bot.delete',
|
||||
setWorkspace: SET_WORKSPACE_ENDPOINT,
|
||||
setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
|
||||
});
|
||||
export const WECOM_RPC_ENDPOINTS = Object.freeze(Object.values(WECOM_ENDPOINTS));
|
||||
|
||||
|
|
@ -70,6 +72,10 @@ function payloadFailure(endpoint, payload) {
|
|||
return validWorkspacePayload(payload)
|
||||
? null : '请输入工作区绝对路径。';
|
||||
}
|
||||
if (endpoint === WECOM_ENDPOINTS.setAgentPreset) {
|
||||
return validAgentPresetPayload(payload)
|
||||
? null : '请选择 Agent Preset。';
|
||||
}
|
||||
return 'Unknown Enterprise WeChat endpoint.';
|
||||
}
|
||||
|
||||
|
|
@ -166,6 +172,12 @@ export function createWecomRpcHandler(controller, { encodeQr = qrDataUrl } = {})
|
|||
await controller.updateWorkspace(payload.botId, payload.workspace),
|
||||
cachedEncode,
|
||||
);
|
||||
} else if (endpoint === WECOM_ENDPOINTS.setAgentPreset) {
|
||||
if (typeof controller.updateAgentPreset !== 'function') throw new Error('Agent preset update is unavailable');
|
||||
value = await publicStatus(
|
||||
await controller.updateAgentPreset(payload.botId, payload.agentPreset),
|
||||
cachedEncode,
|
||||
);
|
||||
} else {
|
||||
value = await publicStatus(await controller.deleteBot(payload.botId), cachedEncode);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import {
|
|||
createWorkspaceAwareController,
|
||||
observeBotWorkspaceRemovals,
|
||||
} from '../../../../src/channels/shared/bot-workspace-store.mjs';
|
||||
import { listAgentPresetCatalog } from '../../../../src/channels/shared/agent-preset.mjs';
|
||||
import { createConnectionSupervisor } from './connection-supervisor.mjs';
|
||||
import { createHarnessCommandExecutor } from '../../harness-command-executor.mjs';
|
||||
import { createHarnessSessionExecutors } from '../../harness-session-coordinator.mjs';
|
||||
|
|
@ -129,7 +130,11 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|||
}
|
||||
},
|
||||
});
|
||||
const controller = createWorkspaceAwareController(coreController, { workspaces, stateFor });
|
||||
const controller = createWorkspaceAwareController(coreController, {
|
||||
workspaces,
|
||||
stateFor,
|
||||
agentPresetCatalog: await listAgentPresetCatalog(ctx),
|
||||
});
|
||||
const supervisor = createSupervisor({
|
||||
controller,
|
||||
harness,
|
||||
|
|
|
|||
|
|
@ -5,6 +5,10 @@ import {
|
|||
SET_WORKSPACE_ENDPOINT,
|
||||
validWorkspacePayload,
|
||||
} from '../shared/workspace-rpc.mjs';
|
||||
import {
|
||||
SET_AGENT_PRESET_ENDPOINT,
|
||||
validAgentPresetPayload,
|
||||
} from '../shared/agent-preset-rpc.mjs';
|
||||
import {
|
||||
connectionTestTargetUnavailable,
|
||||
publicConnectionTestResult,
|
||||
|
|
@ -20,6 +24,7 @@ export const WEIXIN_ENDPOINTS = Object.freeze({
|
|||
reconnectBot: 'bot.reconnect',
|
||||
deleteBot: 'bot.delete',
|
||||
setWorkspace: SET_WORKSPACE_ENDPOINT,
|
||||
setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
|
||||
});
|
||||
export const WEIXIN_RPC_ENDPOINTS = Object.freeze(Object.values(WEIXIN_ENDPOINTS));
|
||||
|
||||
|
|
@ -74,6 +79,10 @@ function payloadFailure(endpoint, payload) {
|
|||
return validWorkspacePayload(payload)
|
||||
? null : '请输入工作区绝对路径。';
|
||||
}
|
||||
if (endpoint === WEIXIN_ENDPOINTS.setAgentPreset) {
|
||||
return validAgentPresetPayload(payload)
|
||||
? null : '请选择 Agent Preset。';
|
||||
}
|
||||
return 'Unknown Weixin endpoint.';
|
||||
}
|
||||
|
||||
|
|
@ -198,6 +207,12 @@ export function createWeixinRpcHandler(controller, { encodeQr = qrDataUrl } = {}
|
|||
await controller.updateWorkspace(payload.botId, payload.workspace),
|
||||
cachedEncode,
|
||||
);
|
||||
} else if (endpoint === WEIXIN_ENDPOINTS.setAgentPreset) {
|
||||
if (typeof controller.updateAgentPreset !== 'function') throw new Error('Agent preset update is unavailable');
|
||||
value = await publicStatus(
|
||||
await controller.updateAgentPreset(payload.botId, payload.agentPreset),
|
||||
cachedEncode,
|
||||
);
|
||||
} else {
|
||||
value = await publicStatus(await controller.deleteBot(payload.botId), cachedEncode);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import {
|
|||
createWorkspaceAwareController,
|
||||
observeBotWorkspaceRemovals,
|
||||
} from '../../../../src/channels/shared/bot-workspace-store.mjs';
|
||||
import { listAgentPresetCatalog } from '../../../../src/channels/shared/agent-preset.mjs';
|
||||
import { createTokenConnectionSupervisor } from '../shared/connection-supervisor.mjs';
|
||||
import { createHarnessCommandExecutor } from '../../harness-command-executor.mjs';
|
||||
import { createHarnessSessionExecutors } from '../../harness-session-coordinator.mjs';
|
||||
|
|
@ -136,7 +137,11 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|||
}
|
||||
},
|
||||
});
|
||||
const controller = createWorkspaceAwareController(coreController, { workspaces, stateFor });
|
||||
const controller = createWorkspaceAwareController(coreController, {
|
||||
workspaces,
|
||||
stateFor,
|
||||
agentPresetCatalog: await listAgentPresetCatalog(ctx),
|
||||
});
|
||||
const supervisor = createSupervisor({
|
||||
channel: 'whatsapp',
|
||||
controller,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import QRCode from 'qrcode';
|
|||
import { publicConnectionTestResult } from '../../../../src/channels/shared/connection-test.mjs';
|
||||
import { resolveRpcAuthority } from '../../rpc-authority.mjs';
|
||||
import { publicWorkspaceError, SET_WORKSPACE_ENDPOINT, validWorkspacePayload } from '../shared/workspace-rpc.mjs';
|
||||
import { SET_AGENT_PRESET_ENDPOINT, validAgentPresetPayload } from '../shared/agent-preset-rpc.mjs';
|
||||
|
||||
export const WHATSAPP_RPC_CHANNEL = '/whatsapp';
|
||||
export const WHATSAPP_ENDPOINTS = Object.freeze({
|
||||
|
|
@ -13,6 +14,7 @@ export const WHATSAPP_ENDPOINTS = Object.freeze({
|
|||
reconnectBot: 'bot.reconnect',
|
||||
deleteBot: 'bot.delete',
|
||||
setWorkspace: SET_WORKSPACE_ENDPOINT,
|
||||
setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
|
||||
});
|
||||
export const WHATSAPP_RPC_ENDPOINTS = Object.freeze(Object.values(WHATSAPP_ENDPOINTS));
|
||||
|
||||
|
|
@ -52,6 +54,10 @@ function payloadFailure(endpoint, payload) {
|
|||
return validWorkspacePayload(payload)
|
||||
? null : '请输入工作区绝对路径。';
|
||||
}
|
||||
if (endpoint === WHATSAPP_ENDPOINTS.setAgentPreset) {
|
||||
return validAgentPresetPayload(payload)
|
||||
? null : '请选择 Agent Preset。';
|
||||
}
|
||||
return 'Unknown WhatsApp endpoint.';
|
||||
}
|
||||
|
||||
|
|
@ -154,6 +160,12 @@ export function createWhatsappRpcHandler(controller, { encodeQr = qrDataUrl } =
|
|||
await controller.updateWorkspace(payload.botId, payload.workspace),
|
||||
cachedEncode,
|
||||
);
|
||||
} else if (endpoint === WHATSAPP_ENDPOINTS.setAgentPreset) {
|
||||
if (typeof controller.updateAgentPreset !== 'function') throw new Error('Agent preset update is unavailable');
|
||||
value = await publicStatus(
|
||||
await controller.updateAgentPreset(payload.botId, payload.agentPreset),
|
||||
cachedEncode,
|
||||
);
|
||||
} else {
|
||||
value = await publicStatus(await controller.deleteBot(payload.botId), cachedEncode);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue