Show WhatsApp channel peer next to Agent Preset in session header (4.9.1-ops.20).

Plugins inject conversation.session.header.actions; resolve sessionId back to the bound chat and render nickname/phone or group title.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
oliver 2026-09-05 19:10:07 +08:00
parent 2718586fda
commit 37742043a9
15 changed files with 852 additions and 326 deletions

View file

@ -566,12 +566,12 @@ __export(index_exports, {
name: () => name
});
module.exports = __toCommonJS(index_exports);
var React24 = __toESM(require("react"), 1);
var React25 = __toESM(require("react"), 1);
// package.json
var package_default = {
name: "dsh-im-ops",
version: "4.9.1-ops.19",
version: "4.9.1-ops.20",
description: "Ops fork of dsh-im (all channels kept). Based on @xmanrui/dsh-im@4.9.1 \u2014 access/session policies owned here.",
keywords: [
"deepseek-harness",
@ -852,6 +852,7 @@ var EN = Object.freeze({
"\u673A\u5668\u4EBA\u8BBE\u7F6E\u9875\u7B7E": "Bot settings tabs",
"\u6295\u9012\u8BBE\u7F6E": "Delivery settings",
"\u8BBF\u95EE\u8BBE\u7F6E": "Access settings",
"\u6E20\u9053\u7528\u6237": "Channel user",
"\uFF08\u4E0D\u53EF\u7528\uFF09": " (unavailable)",
"\u7FA4 Agent Preset": "Group Agent Preset",
"\u8DDF\u968F\u5168\u5C40": "Follow global",
@ -11353,7 +11354,8 @@ var WHATSAPP_ENDPOINTS = Object.freeze({
setGroupSessionScope: "bot.group-session-scope.set",
setWorkspace: "bot.workspace.set",
setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
setContextEnhancement: "bot.context-enhancement.set"
setContextEnhancement: "bot.context-enhancement.set",
resolveChannelPeer: "bot.session.channel-peer"
});
var PROVISION_STATES5 = /* @__PURE__ */ new Set(["starting", "pending", "connecting", "connected", "failed", "cancelled"]);
var BOT_STATES = /* @__PURE__ */ new Set(["connected", "connecting", "offline", "error"]);
@ -12113,11 +12115,83 @@ function WhatsappSettingsTab({ rpcCall }) {
));
}
// plugin-src/client/channel-peer-label.js
var React20 = __toESM(require("react"), 1);
var CHANNEL_LABELS = Object.freeze({
whatsapp: "WhatsApp",
feishu: "\u98DE\u4E66",
dingtalk: "\u9489\u9489",
wecom: "\u4F01\u4E1A\u5FAE\u4FE1",
weixin: "\u5FAE\u4FE1",
qq: "QQ",
telegram: "Telegram",
discord: "Discord",
slack: "Slack",
office: "AI Office"
});
function ChannelPeerLabel({ sessionId, resolveChannelPeer }) {
const [peer, setPeer] = React20.useState(null);
React20.useEffect(() => {
if (typeof resolveChannelPeer !== "function" || typeof sessionId !== "string" || !sessionId) {
setPeer(null);
return void 0;
}
const controller = new AbortController();
let active = true;
void (async () => {
try {
const next = await resolveChannelPeer(sessionId, controller.signal);
if (active && !controller.signal.aborted) setPeer(next && typeof next === "object" ? next : null);
} catch {
if (active && !controller.signal.aborted) setPeer(null);
}
})();
return () => {
active = false;
controller.abort();
};
}, [sessionId, resolveChannelPeer]);
if (!peer?.label) return null;
const channelName2 = CHANNEL_LABELS[peer.channel] || peer.channel || "IM";
const title = peer.kind === "group" ? `${channelName2} \xB7 ${peer.label}` : `${channelName2} \xB7 ${peer.label}`;
return h2(
"span",
{
className: "dim-channelPeerLabel",
title,
"aria-label": localizeText("\u6E20\u9053\u7528\u6237") + ` \xB7 ${title}`
},
h2("span", { className: "dim-channelPeerChannel", "aria-hidden": "true" }, channelName2),
h2("span", { className: "dim-channelPeerText" }, peer.label)
);
}
async function resolveWhatsappChannelPeer(whatsappRpcCall, sessionId, signal) {
if (typeof whatsappRpcCall !== "function") return null;
const result = await whatsappRpcCall(
WHATSAPP_ENDPOINTS.resolveChannelPeer,
{ sessionId },
signal
);
const value = unwrapRpcResult9(result);
if (!value || typeof value !== "object") return null;
const label = typeof value.label === "string" ? value.label.trim() : "";
if (!label) return null;
return {
channel: typeof value.channel === "string" ? value.channel : "whatsapp",
botId: typeof value.botId === "string" ? value.botId : "",
kind: value.kind === "group" ? "group" : "direct",
label,
phone: typeof value.phone === "string" ? value.phone : null,
pushName: typeof value.pushName === "string" ? value.pushName : null,
groupTitle: typeof value.groupTitle === "string" ? value.groupTitle : null
};
}
// plugin-src/client/delivery-settings.js
var React22 = __toESM(require("react"), 1);
var React23 = __toESM(require("react"), 1);
// plugin-src/client/access-policy-settings.js
var React20 = __toESM(require("react"), 1);
var React21 = __toESM(require("react"), 1);
var ACCESS_POLICY_ENDPOINT = "bot.access-policy.set";
var GROUP_SESSION_SCOPE_ENDPOINT = "bot.group-session-scope.set";
var ACCESS_CHANNEL_DEFINITIONS = Object.freeze({
@ -12220,8 +12294,8 @@ function ScenePolicyEditor({
unsupported = false,
onChange
}) {
const ownerHelpId = React20.useId();
const emptyAllowlistHelpId = React20.useId();
const ownerHelpId = React21.useId();
const emptyAllowlistHelpId = React21.useId();
const allowlist = policy.mode === "allowlist";
const collectionKey = allowlist ? "users" : "commandPermissionOverrides";
const branchKey = allowlist ? "allowlist" : "open";
@ -12273,7 +12347,7 @@ function ScenePolicyEditor({
h2("strong", null, "\u5F53\u524D\u6E20\u9053\u4E0D\u652F\u6301\u7FA4\u804A"),
h2("p", null, "\u6B64\u533A\u57DF\u65E0\u9700\u914D\u7F6E\uFF0C\u4FDD\u5B58\u79C1\u804A\u8BBE\u7F6E\u65F6\u4F1A\u4FDD\u7559\u73B0\u6709\u7FA4\u804A\u7B56\u7565\u3002")
) : h2(
React20.Fragment,
React21.Fragment,
null,
h2(
"div",
@ -12415,18 +12489,18 @@ function AccessPolicySettingsPage({ channel: channel4, account, rpcCall, onSaved
const initialPolicy = normalizeAccessPolicy(account?.accessPolicy);
const initialKey = JSON.stringify(initialPolicy);
const initialScope = normalizeGroupSessionScope(account?.groupSessionScope);
const [draft, setDraft] = React20.useState(() => clonePolicy(
const [draft, setDraft] = React21.useState(() => clonePolicy(
initialPolicy ?? DEFAULT_ACCESS_POLICY
));
const [groupSessionScope, setGroupSessionScope] = React20.useState(initialScope);
const [saving, setSaving] = React20.useState(false);
const [feedback, setFeedback] = React20.useState(null);
React20.useEffect(() => {
const [groupSessionScope, setGroupSessionScope] = React21.useState(initialScope);
const [saving, setSaving] = React21.useState(false);
const [feedback, setFeedback] = React21.useState(null);
React21.useEffect(() => {
const next = normalizeAccessPolicy(account?.accessPolicy);
setDraft(clonePolicy(next ?? DEFAULT_ACCESS_POLICY));
setGroupSessionScope(normalizeGroupSessionScope(account?.groupSessionScope));
}, [account?.botId, initialKey, account?.groupSessionScope]);
React20.useEffect(() => {
React21.useEffect(() => {
setFeedback(null);
}, [account?.botId]);
if (!definition) {
@ -12579,7 +12653,7 @@ function AccessPolicySettingsPage({ channel: channel4, account, rpcCall, onSaved
}
// plugin-src/client/access-grant-settings.js
var React21 = __toESM(require("react"), 1);
var React22 = __toESM(require("react"), 1);
var ACCESS_GRANT_ENDPOINT = "bot.access-grant.set";
var ACCESS_PENDING_RESOLVE_ENDPOINT = "bot.access-pending.resolve";
var ACCESS_GROUP_TITLES_REFRESH_ENDPOINT = "bot.access-group-titles.refresh";
@ -12671,13 +12745,13 @@ function PhoneTypeahead({
disabled,
requirePhone = true
}) {
const [query, setQuery] = React21.useState(value || "");
const [open, setOpen] = React21.useState(false);
const blurTimer = React21.useRef(null);
React21.useEffect(() => {
const [query, setQuery] = React22.useState(value || "");
const [open, setOpen] = React22.useState(false);
const blurTimer = React22.useRef(null);
React22.useEffect(() => {
setQuery(value || "");
}, [value]);
React21.useEffect(() => () => {
React22.useEffect(() => () => {
if (blurTimer.current) window.clearTimeout(blurTimer.current);
}, []);
const q = query.trim().toLowerCase();
@ -12985,21 +13059,21 @@ function AccessGrantSettingsPage({ channel: channel4, account, rpcCall, onSaved
const initialGrant = normalizeAccessGrant(account?.accessGrant);
const initialKey = JSON.stringify(initialGrant);
const initialScope = normalizeGroupSessionScope(account?.groupSessionScope);
const [draft, setDraft] = React21.useState(() => cloneGrant(initialGrant, ownerPhone));
const [groupSessionScope, setGroupSessionScope] = React21.useState(initialScope);
const [saving, setSaving] = React21.useState(false);
const [feedback, setFeedback] = React21.useState(null);
const [newGroupJid, setNewGroupJid] = React21.useState("");
const [loadingGrant, setLoadingGrant] = React21.useState(!initialGrant);
const contextCatalog = React21.useContext(AgentPresetCatalogContext);
const [presetCatalog, setPresetCatalog] = React21.useState(() => normalizeAgentPresetCatalog(
const [draft, setDraft] = React22.useState(() => cloneGrant(initialGrant, ownerPhone));
const [groupSessionScope, setGroupSessionScope] = React22.useState(initialScope);
const [saving, setSaving] = React22.useState(false);
const [feedback, setFeedback] = React22.useState(null);
const [newGroupJid, setNewGroupJid] = React22.useState("");
const [loadingGrant, setLoadingGrant] = React22.useState(!initialGrant);
const contextCatalog = React22.useContext(AgentPresetCatalogContext);
const [presetCatalog, setPresetCatalog] = React22.useState(() => normalizeAgentPresetCatalog(
account?.agentPresetCatalog ?? contextCatalog ?? EMPTY_AGENT_PRESET_CATALOG
));
React21.useEffect(() => {
React22.useEffect(() => {
setDraft(cloneGrant(normalizeAccessGrant(account?.accessGrant), ownerPhone));
setGroupSessionScope(normalizeGroupSessionScope(account?.groupSessionScope));
}, [account?.botId, initialKey, account?.groupSessionScope, ownerPhone]);
React21.useEffect(() => {
React22.useEffect(() => {
if (typeof rpcCall !== "function" || !account?.botId) return void 0;
let cancelled = false;
setLoadingGrant(true);
@ -13053,7 +13127,7 @@ function AccessGrantSettingsPage({ channel: channel4, account, rpcCall, onSaved
};
}, [account?.botId, rpcCall, ownerPhone]);
const contacts = draft.contacts ?? [];
const knownGroupJids = React21.useMemo(() => {
const knownGroupJids = React22.useMemo(() => {
const set = new Set(Object.keys(draft.groups ?? {}));
for (const contact of contacts) {
for (const jid of contact.groupJids ?? []) set.add(jid);
@ -13063,7 +13137,7 @@ function AccessGrantSettingsPage({ channel: channel4, account, rpcCall, onSaved
}
return [...set].sort();
}, [draft.groups, draft.pending, contacts]);
const actionableContacts = React21.useMemo(
const actionableContacts = React22.useMemo(
() => contacts.filter((contact) => contactNeedsGrantAction(contact, draft, knownGroupJids)),
[contacts, draft, knownGroupJids]
);
@ -13742,13 +13816,13 @@ function TargetForm({
}) {
const editing = mode === "edit";
const initialKind = initialValue?.kind && definition.fields[initialValue.kind] ? initialValue.kind : definition.kinds[0].value;
const [targetId, setTargetId] = React22.useState(initialValue?.targetId ?? "");
const [name2, setName] = React22.useState(initialValue?.name ?? "");
const [kind, setKind] = React22.useState(initialKind);
const [route, setRoute] = React22.useState(initialValue?.route ?? {});
const [error, setError] = React22.useState(null);
const [testing, setTesting] = React22.useState(false);
const [testState, setTestState] = React22.useState(null);
const [targetId, setTargetId] = React23.useState(initialValue?.targetId ?? "");
const [name2, setName] = React23.useState(initialValue?.name ?? "");
const [kind, setKind] = React23.useState(initialKind);
const [route, setRoute] = React23.useState(initialValue?.route ?? {});
const [error, setError] = React23.useState(null);
const [testing, setTesting] = React23.useState(false);
const [testState, setTestState] = React23.useState(null);
const currentTarget = () => {
const normalizedRoute = Object.fromEntries(fieldsFor(definition, kind).map((field) => {
const raw = String(route[field.key] ?? "").trim();
@ -13965,7 +14039,7 @@ function TargetSuggestionPicker({
suggestions.map((suggestion, index) => {
const identity = routeIdentity(definition, suggestion);
const added = configured.has(identity);
return React22.createElement("option", {
return React23.createElement("option", {
key: suggestion.id ?? suggestion.suggestionId ?? `${identity}:${index}`,
value: String(index),
disabled: added
@ -13982,10 +14056,10 @@ function TargetSuggestionPicker({
);
}
function TargetRow({ definition, target, botId, connected, rpcCall, onChanged, onEdit }) {
const [action, setAction] = React22.useState(null);
const [testState, setTestState] = React22.useState(null);
const [copyState, setCopyState] = React22.useState(null);
const [confirmDelete, setConfirmDelete] = React22.useState(false);
const [action, setAction] = React23.useState(null);
const [testState, setTestState] = React23.useState(null);
const [copyState, setCopyState] = React23.useState(null);
const [confirmDelete, setConfirmDelete] = React23.useState(false);
const testTarget = async () => {
setAction("test");
setTestState(null);
@ -14027,7 +14101,7 @@ function TargetRow({ definition, target, botId, connected, rpcCall, onChanged, o
h2(
"div",
{ className: "dim-targetTitle" },
React22.createElement("strong", null, target.name || target.targetId),
React23.createElement("strong", null, target.name || target.targetId),
h2("span", null, kindLabel(definition, target.kind))
),
h2("code", null, `targetId: ${target.targetId}`)
@ -14090,28 +14164,28 @@ function DeliveryTargetSettingsPage({
onBack
}) {
const definition = CHANNEL_DEFINITIONS[channel4];
const [activeTabId, setActiveTabId] = React22.useState(BOT_SETTINGS_TABS[0].id);
const [phase, setPhase] = React22.useState("loading");
const [targets, setTargets] = React22.useState([]);
const [suggestionPhase, setSuggestionPhase] = React22.useState("idle");
const [suggestions, setSuggestions] = React22.useState([]);
const [suggestionError, setSuggestionError] = React22.useState(null);
const [error, setError] = React22.useState(null);
const [editor, setEditor] = React22.useState(null);
const [saving, setSaving] = React22.useState(false);
const [botCopyState, setBotCopyState] = React22.useState(null);
const [accessPolicy, setAccessPolicy] = React22.useState(account.accessPolicy);
const [accessGrant, setAccessGrant] = React22.useState(account.accessGrant);
const mounted = React22.useRef(true);
React22.useEffect(() => {
const [activeTabId, setActiveTabId] = React23.useState(BOT_SETTINGS_TABS[0].id);
const [phase, setPhase] = React23.useState("loading");
const [targets, setTargets] = React23.useState([]);
const [suggestionPhase, setSuggestionPhase] = React23.useState("idle");
const [suggestions, setSuggestions] = React23.useState([]);
const [suggestionError, setSuggestionError] = React23.useState(null);
const [error, setError] = React23.useState(null);
const [editor, setEditor] = React23.useState(null);
const [saving, setSaving] = React23.useState(false);
const [botCopyState, setBotCopyState] = React23.useState(null);
const [accessPolicy, setAccessPolicy] = React23.useState(account.accessPolicy);
const [accessGrant, setAccessGrant] = React23.useState(account.accessGrant);
const mounted = React23.useRef(true);
React23.useEffect(() => {
setAccessPolicy(account.accessPolicy);
setAccessGrant(account.accessGrant);
}, [account.botId, account.accessPolicy, account.accessGrant]);
const invoke = React22.useCallback(async (endpoint, payload = {}, signal) => {
const invoke = React23.useCallback(async (endpoint, payload = {}, signal) => {
if (typeof rpcCall !== "function") throw new Error("\u6295\u9012\u76EE\u6807\u8BBE\u7F6E\u6682\u4E0D\u53EF\u7528\u3002");
return unwrapRpcResult12(await rpcCall(endpoint, payload, signal));
}, [rpcCall]);
const loadTargets = React22.useCallback(async ({ signal, silent = false } = {}) => {
const loadTargets = React23.useCallback(async ({ signal, silent = false } = {}) => {
if (!silent) setPhase("loading");
setError(null);
try {
@ -14125,7 +14199,7 @@ function DeliveryTargetSettingsPage({
setPhase("error");
}
}, [account.botId, invoke]);
const loadSuggestions = React22.useCallback(async () => {
const loadSuggestions = React23.useCallback(async () => {
setSuggestionPhase("loading");
setSuggestionError(null);
try {
@ -14139,7 +14213,7 @@ function DeliveryTargetSettingsPage({
setSuggestionPhase("error");
}
}, [account.botId, definition, invoke]);
React22.useEffect(() => {
React23.useEffect(() => {
mounted.current = true;
const controller = new AbortController();
void loadTargets({ signal: controller.signal });
@ -14266,7 +14340,7 @@ function DeliveryTargetSettingsPage({
rpcCall: accessRpcCall,
onSaved: setAccessPolicy
}) : h2(
React22.Fragment,
React23.Fragment,
null,
h2(
"section",
@ -14970,6 +15044,34 @@ body[data-ds-dark-theme] .dim-page select option, body[data-ds-dark-theme] .dim-
.dim-accessGroupTitleRow { min-width: 0; display: grid; grid-template-columns: minmax(0, 1fr) max-content; gap: 10px; align-items: end; }
.dim-accessNickname { display: block; margin-top: 0; color: var(--dsw-alias-label-secondary, #646a73); font-size: 11px; line-height: 15px; font-weight: 560; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dim-accessNicknameEmpty { color: var(--dsw-alias-label-tertiary, #8f959e); font-weight: 400; }
.dim-channelPeerLabel {
display: inline-flex;
align-items: center;
gap: 6px;
max-width: 260px;
min-width: 0;
height: 22px;
padding: 0 8px 0 6px;
border-radius: 6px;
background: var(--dsw-alias-fill-tsp-secondary, color-mix(in srgb, var(--dsw-alias-label-primary, #1f2329) 6%, transparent));
color: var(--dsw-alias-label-secondary, #646a73);
font-size: 12px;
line-height: 22px;
white-space: nowrap;
overflow: hidden;
}
.dim-channelPeerChannel {
flex: none;
color: var(--dsw-alias-label-tertiary, #8f959e);
font-weight: 560;
}
.dim-channelPeerText {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
font-weight: 500;
color: var(--dsw-alias-label-secondary, #646a73);
}
.dim-accessUserRow { position: relative; z-index: 0; overflow: visible; }
.dim-panel .dim-botCard .dim-cardFooter { margin-top: 0; }
.dim-panel .ddt-headingCopy { display: none; }
@ -15052,7 +15154,7 @@ function installImStyles() {
}
// plugin-src/client/update-panel.js
var React23 = __toESM(require("react"), 1);
var React24 = __toESM(require("react"), 1);
var import_react_dom3 = require("react-dom");
var import_valid = __toESM(require_valid(), 1);
var import_rcompare = __toESM(require_rcompare(), 1);
@ -15142,11 +15244,11 @@ function manualUpdateCommand(snapshot) {
return `dsh plugin --profile ${profileArgument} add -w dsh-im-ops@${version}`;
}
function ManualUpdateCommand({ command, disabled, sourceInstall, desktop }) {
const [copyState, setCopyState] = React23.useState("idle");
const commandRef = React23.useRef(null);
const mounted = React23.useRef(false);
const copying = React23.useRef(false);
React23.useEffect(() => {
const [copyState, setCopyState] = React24.useState("idle");
const commandRef = React24.useRef(null);
const mounted = React24.useRef(false);
const copying = React24.useRef(false);
React24.useEffect(() => {
mounted.current = true;
return () => {
mounted.current = false;
@ -15178,7 +15280,7 @@ function ManualUpdateCommand({ command, disabled, sourceInstall, desktop }) {
{ className: "dim-updateManual", "aria-label": "\u624B\u5DE5\u66F4\u65B0" },
h2("h4", { className: "dim-updateManualHeading" }, "\u624B\u5DE5\u66F4\u65B0"),
command ? h2(
React23.Fragment,
React24.Fragment,
null,
h2("p", { className: "dim-updateManualHint" }, "\u81EA\u52A8\u66F4\u65B0\u5931\u8D25\u53EF\u4EE5\u4F7F\u7528\u547D\u4EE4\u66F4\u65B0\uFF1A"),
h2(
@ -15214,7 +15316,7 @@ function ManualUpdateCommand({ command, disabled, sourceInstall, desktop }) {
"aria-hidden": "true",
focusable: "false"
}, copyState === "copied" ? h2("path", { d: "m4 10 4 4 8-8" }) : h2(
React23.Fragment,
React24.Fragment,
null,
h2("rect", { x: 7, y: 7, width: 10, height: 11, rx: 1.5 }),
h2("path", { d: "M5 13H3.5A1.5 1.5 0 0 1 2 11.5v-8A1.5 1.5 0 0 1 3.5 2h8A1.5 1.5 0 0 1 13 3.5V5" })
@ -15239,10 +15341,10 @@ function ManualUpdateCommand({ command, disabled, sourceInstall, desktop }) {
);
}
function UpdateDialog({ children, onClose }) {
const dialogRef = React23.useRef(null);
const titleId = React23.useId();
const descriptionId = React23.useId();
React23.useEffect(() => {
const dialogRef = React24.useRef(null);
const titleId = React24.useId();
const descriptionId = React24.useId();
React24.useEffect(() => {
const previous = globalThis.document?.activeElement;
dialogRef.current?.focus?.();
return () => {
@ -15300,19 +15402,19 @@ function UpdateDialog({ children, onClose }) {
return typeof document !== "undefined" && document.body ? (0, import_react_dom3.createPortal)(content, document.body) : content;
}
function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
const [snapshot, setSnapshot] = React23.useState(null);
const [action, setAction] = React23.useState("status");
const [error, setError] = React23.useState(null);
const [open, setOpen] = React23.useState(false);
const [uncertainInstall, setUncertainInstall] = React23.useState(false);
const mounted = React23.useRef(false);
const busy = React23.useRef(false);
const readController = React23.useRef(null);
const pollReadController = React23.useRef(null);
const installRequest = React23.useRef(null);
const onStatusRef = React23.useRef(onStatus);
const [snapshot, setSnapshot] = React24.useState(null);
const [action, setAction] = React24.useState("status");
const [error, setError] = React24.useState(null);
const [open, setOpen] = React24.useState(false);
const [uncertainInstall, setUncertainInstall] = React24.useState(false);
const mounted = React24.useRef(false);
const busy = React24.useRef(false);
const readController = React24.useRef(null);
const pollReadController = React24.useRef(null);
const installRequest = React24.useRef(null);
const onStatusRef = React24.useRef(onStatus);
onStatusRef.current = onStatus;
const accept = React23.useCallback((next) => {
const accept = React24.useCallback((next) => {
setSnapshot(next);
onStatusRef.current?.(next);
}, []);
@ -15321,7 +15423,7 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
setUncertainInstall(false);
accept(next);
};
const invoke = React23.useCallback(async (endpoint, payload = {}, signal) => {
const invoke = React24.useCallback(async (endpoint, payload = {}, signal) => {
if (typeof rpcCall !== "function") {
const unavailable = new Error("\u5F53\u524D Host \u4E0D\u652F\u6301\u66F4\u65B0\u63A5\u53E3\uFF0C\u8BF7\u5148\u624B\u52A8\u66F4\u65B0\u63D2\u4EF6\u5E76\u91CD\u542F\u3002");
unavailable.code = "update-unavailable";
@ -15329,7 +15431,7 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
}
return unwrapSnapshot(await rpcCall(endpoint, payload, signal));
}, [rpcCall]);
React23.useEffect(() => {
React24.useEffect(() => {
mounted.current = true;
busy.current = true;
const controller = new AbortController();
@ -15352,7 +15454,7 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
const activeJob = ACTIVE_STATES4.has(snapshot?.job?.state);
const restartRequired = snapshot?.job?.state === "restart-required" || snapshot?.blockedReason === "pending-restart";
const shouldPoll = activeJob || uncertainInstall;
React23.useEffect(() => {
React24.useEffect(() => {
if (!shouldPoll) return void 0;
let controller;
const scheduler = createPollScheduler({
@ -15450,7 +15552,7 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
const manualCommand = manualUpdateCommand(snapshot);
const buttonLabel = action === "checking" ? "\u68C0\u67E5\u4E2D\u2026" : action === "starting" || activeJob ? "\u6B63\u5728\u66F4\u65B0\u2026" : restartRequired ? "\u5F85\u624B\u52A8\u91CD\u542F" : snapshot?.canInstall ? "\u66F4\u65B0\u81F3" : "\u68C0\u67E5\u66F4\u65B0";
return h2(
React23.Fragment,
React24.Fragment,
null,
h2("button", {
type: "button",
@ -15475,13 +15577,13 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
h2("dt", null, "\u8FD0\u884C\u7248\u672C"),
h2("dd", null, `v${snapshot?.runningVersion ?? clientVersion}`),
snapshot?.installedVersion && snapshot.installedVersion !== snapshot.runningVersion ? h2(
React23.Fragment,
React24.Fragment,
null,
h2("dt", null, "\u5DF2\u5B89\u88C5\u7248\u672C"),
h2("dd", null, `v${snapshot.installedVersion}`)
) : null,
targetVersion ? h2(
React23.Fragment,
React24.Fragment,
null,
h2("dt", null, "\u76EE\u6807\u7248\u672C"),
h2("dd", null, `v${targetVersion}`)
@ -15685,20 +15787,20 @@ function IMSettingsTab({
browserLocation = globalThis.location,
navigateToRecoveryUrl = replacePageLocation
}) {
const [selected, setSelected] = React24.useState("weixin");
const [loopbackRecovery, setLoopbackRecovery] = React24.useState(null);
const [runningVersion, setRunningVersion] = React24.useState(IM_PLUGIN_VERSION);
const [deliverySettings, setDeliverySettings] = React24.useState(null);
const githubTooltipId = React24.useId();
const channelSelectId = React24.useId();
const [selected, setSelected] = React25.useState("weixin");
const [loopbackRecovery, setLoopbackRecovery] = React25.useState(null);
const [runningVersion, setRunningVersion] = React25.useState(IM_PLUGIN_VERSION);
const [deliverySettings, setDeliverySettings] = React25.useState(null);
const githubTooltipId = React25.useId();
const channelSelectId = React25.useId();
const active = CHANNELS.find((channel4) => channel4.id === selected) ?? CHANNELS[0];
const reportLoopbackRecovery = React24.useCallback((recovery) => {
const reportLoopbackRecovery = React25.useCallback((recovery) => {
setLoopbackRecovery((current) => current?.url === recovery.url ? current : recovery);
}, []);
const reportUpdateStatus = React24.useCallback((snapshot) => {
const reportUpdateStatus = React25.useCallback((snapshot) => {
setRunningVersion(snapshot.runningVersion);
}, []);
const rpcCalls = React24.useMemo(() => createLoopbackAwareRpcCalls({
const rpcCalls = React25.useMemo(() => createLoopbackAwareRpcCalls({
dingtalkRpcCall,
discordRpcCall,
feishuRpcCall,
@ -15730,7 +15832,7 @@ function IMSettingsTab({
weixinRpcCall,
whatsappRpcCall
]);
const botSettingsContext = React24.useMemo(() => Object.freeze({
const botSettingsContext = React25.useMemo(() => Object.freeze({
openBotSettings: setDeliverySettings
}), []);
return h2(
@ -15881,6 +15983,15 @@ function apply(ctx) {
listDirectory: (path, signal) => callWorkspaceDirectoryApi(ctx, "listDirectory", path, signal),
pickDirectory: () => callWorkspaceDirectoryApi(ctx, "pickDirectory")
});
const resolveChannelPeer = (sessionId, signal) => resolveWhatsappChannelPeer(whatsappRpcCall, sessionId, signal);
ctx.slots.inject("conversation.session.header.actions", () => ctx.slots.register({
name: "conversation.session.header.actions",
id: "im-channel-peer",
// Immediately after Agent Preset (−10); schedule/jobs follow at 10+.
order: 0,
locale: IM_LOCALE_NAMESPACE,
inject: () => ({ resolveChannelPeer })
}, ChannelPeerLabel));
ctx.slots.inject("settings.section", () => ctx.slots.register({
name: "settings.section",
id: "xmanrui-dsh-im",