mirror of
https://github.com/hansjone/dsh-im-ops.git
synced 2026-10-08 23:13:17 +08:00
Add per-DM and per-group Agent Preset overrides (4.9.1-ops.19).
Access grants can pin an agentPreset on direct members and groups; new sessions resolve override over the bot-global preset. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
7993499273
commit
2718586fda
12 changed files with 590 additions and 232 deletions
134
lib/client.js
134
lib/client.js
|
|
@ -571,7 +571,7 @@ var React24 = __toESM(require("react"), 1);
|
|||
// package.json
|
||||
var package_default = {
|
||||
name: "dsh-im-ops",
|
||||
version: "4.9.1-ops.18",
|
||||
version: "4.9.1-ops.19",
|
||||
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,9 @@ 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",
|
||||
"\uFF08\u4E0D\u53EF\u7528\uFF09": " (unavailable)",
|
||||
"\u7FA4 Agent Preset": "Group Agent Preset",
|
||||
"\u8DDF\u968F\u5168\u5C40": "Follow global",
|
||||
"\u2014": "\u2014",
|
||||
"\u6765\u6E90": "Source",
|
||||
"\u6682\u65E0\u7BA1\u7406\u5458\uFF0C\u53EF\u70B9\u4E0B\u65B9\u65B0\u589E\u3002": "No admins yet. Add one below.",
|
||||
|
|
@ -11119,7 +11122,13 @@ var ACCESS_GRANT_VERSION = 1;
|
|||
var ACCESS_PENDING_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
||||
var ACCESS_GRANT_PHONE_MAX_LENGTH = 32;
|
||||
var ACCESS_GRANT_GROUP_JID_PATTERN = /^\d{5,32}@g\.us$/;
|
||||
var AGENT_PRESET_ID = /^[a-z0-9][a-z0-9-]*$/;
|
||||
var CONTROL_CHARACTERS2 = /[\u0000-\u001f\u007f-\u009f\u202a-\u202e\u2066-\u2069]/;
|
||||
function normalizeOptionalAgentPresetId(value) {
|
||||
if (typeof value !== "string") return null;
|
||||
const id5 = value.trim();
|
||||
return AGENT_PRESET_ID.test(id5) ? id5 : null;
|
||||
}
|
||||
function invalidGrant(message) {
|
||||
const error = new TypeError(message);
|
||||
error.code = "access-grant-invalid";
|
||||
|
|
@ -11159,7 +11168,18 @@ function validateMember(input) {
|
|||
}
|
||||
const phone = validateAccessPhone(input.phone ?? input.id);
|
||||
const canExecuteCommands = input.canExecuteCommands === void 0 ? true : Boolean(input.canExecuteCommands);
|
||||
return Object.freeze({ phone, canExecuteCommands });
|
||||
const agentPreset = optionalAgentPreset(input.agentPreset);
|
||||
return Object.freeze({
|
||||
phone,
|
||||
canExecuteCommands,
|
||||
...agentPreset ? { agentPreset } : {}
|
||||
});
|
||||
}
|
||||
function optionalAgentPreset(value) {
|
||||
if (value == null || value === "") return void 0;
|
||||
const id5 = normalizeOptionalAgentPresetId(value);
|
||||
if (!id5) invalidGrant("Agent Preset \u65E0\u6548\u3002");
|
||||
return id5;
|
||||
}
|
||||
function validatePhoneList(input, { emptyMessage, label }) {
|
||||
if (!Array.isArray(input)) invalidGrant(`${label}\u5FC5\u987B\u662F\u6570\u7EC4\u3002`);
|
||||
|
|
@ -11178,8 +11198,10 @@ function validateGroupGrant(input) {
|
|||
invalidGrant("\u7FA4\u6210\u5458\u4E0D\u80FD\u5305\u542B\u91CD\u590D\u7535\u8BDD\u3002");
|
||||
}
|
||||
const title = typeof input.title === "string" ? input.title.trim().slice(0, 128) : "";
|
||||
const agentPreset = optionalAgentPreset(input.agentPreset);
|
||||
return Object.freeze({
|
||||
...title ? { title } : {},
|
||||
...agentPreset ? { agentPreset } : {},
|
||||
admins,
|
||||
members
|
||||
});
|
||||
|
|
@ -12597,6 +12619,7 @@ function cloneGrant(grant, ownerPhone) {
|
|||
directMembers: (base.directMembers ?? []).map((m) => ({ ...m })),
|
||||
groups: Object.fromEntries(Object.entries(base.groups ?? {}).map(([jid, group]) => [jid, {
|
||||
title: group.title ?? "",
|
||||
...group.agentPreset ? { agentPreset: group.agentPreset } : {},
|
||||
admins: [...group.admins ?? []],
|
||||
members: (group.members ?? []).map((m) => ({ ...m }))
|
||||
}])),
|
||||
|
|
@ -12715,14 +12738,52 @@ function PhoneTypeahead({
|
|||
) : null
|
||||
);
|
||||
}
|
||||
function AgentPresetSelect({
|
||||
value,
|
||||
catalog,
|
||||
disabled,
|
||||
onChange,
|
||||
ariaLabel = "Agent Preset"
|
||||
}) {
|
||||
const current = typeof value === "string" ? value : "";
|
||||
const items = [];
|
||||
const seen = /* @__PURE__ */ 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, unavailable: true });
|
||||
}
|
||||
return h2(
|
||||
"select",
|
||||
{
|
||||
className: "dim-accessCompactSelect",
|
||||
value: current,
|
||||
disabled,
|
||||
title: ariaLabel,
|
||||
"aria-label": ariaLabel,
|
||||
onChange: (event) => onChange(event.target.value || "")
|
||||
},
|
||||
h2("option", { value: "" }, "\u8DDF\u968F\u5168\u5C40"),
|
||||
...items.map((item) => h2("option", {
|
||||
key: item.id,
|
||||
value: item.id
|
||||
}, item.unavailable ? item.label + localizeText("\uFF08\u4E0D\u53EF\u7528\uFF09") : item.label))
|
||||
);
|
||||
}
|
||||
function MemberRows({
|
||||
title,
|
||||
members,
|
||||
contacts,
|
||||
disabled,
|
||||
onChange,
|
||||
nested = false
|
||||
nested = false,
|
||||
showAgentPreset = false,
|
||||
presetCatalog = EMPTY_AGENT_PRESET_CATALOG
|
||||
}) {
|
||||
const columns = showAgentPreset ? 5 : 4;
|
||||
return h2(
|
||||
nested ? "div" : "fieldset",
|
||||
{
|
||||
|
|
@ -12735,7 +12796,10 @@ function MemberRows({
|
|||
{ className: "dim-accessTableWrap" },
|
||||
h2(
|
||||
"table",
|
||||
{ className: "dim-accessTable", "data-kind": "member" },
|
||||
{
|
||||
className: "dim-accessTable",
|
||||
"data-kind": showAgentPreset ? "member-preset" : "member"
|
||||
},
|
||||
h2(
|
||||
"thead",
|
||||
null,
|
||||
|
|
@ -12745,10 +12809,14 @@ function MemberRows({
|
|||
h2("th", { scope: "col" }, "\u6635\u79F0"),
|
||||
h2("th", { scope: "col" }, "\u7535\u8BDD"),
|
||||
h2("th", { scope: "col" }, "\u547D\u4EE4\u6743\u9650"),
|
||||
showAgentPreset ? h2("th", { scope: "col" }, "Agent Preset") : null,
|
||||
h2("th", { scope: "col", className: "dim-accessTableActions" }, "\u64CD\u4F5C")
|
||||
)
|
||||
),
|
||||
h2("tbody", null, members.length === 0 ? h2("tr", null, h2("td", { colSpan: 4, className: "dim-accessTableEmpty" }, "\u6682\u65E0\u7528\u6237\uFF0C\u53EF\u70B9\u4E0B\u65B9\u65B0\u589E\u3002")) : members.map((member, index) => h2(
|
||||
h2("tbody", null, members.length === 0 ? h2("tr", null, h2("td", {
|
||||
colSpan: columns,
|
||||
className: "dim-accessTableEmpty"
|
||||
}, "\u6682\u65E0\u7528\u6237\uFF0C\u53EF\u70B9\u4E0B\u65B9\u65B0\u589E\u3002")) : members.map((member, index) => h2(
|
||||
"tr",
|
||||
{ key: `member-${index}` },
|
||||
h2(
|
||||
|
|
@ -12784,6 +12852,23 @@ function MemberRows({
|
|||
h2("option", { value: "deny" }, "\u4E0D\u53EF\u6267\u884C\u547D\u4EE4")
|
||||
)
|
||||
),
|
||||
showAgentPreset ? h2(
|
||||
"td",
|
||||
{ className: "dim-accessTablePreset" },
|
||||
h2(AgentPresetSelect, {
|
||||
value: member.agentPreset ?? "",
|
||||
catalog: presetCatalog,
|
||||
disabled,
|
||||
ariaLabel: "Agent Preset",
|
||||
onChange: (preset) => onChange(members.map((entry, i) => {
|
||||
if (i !== index) return entry;
|
||||
const next = { ...entry };
|
||||
if (preset) next.agentPreset = preset;
|
||||
else delete next.agentPreset;
|
||||
return next;
|
||||
}))
|
||||
})
|
||||
) : null,
|
||||
h2(
|
||||
"td",
|
||||
{ className: "dim-accessTableActions" },
|
||||
|
|
@ -12906,6 +12991,10 @@ function AccessGrantSettingsPage({ channel: channel4, account, rpcCall, onSaved
|
|||
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(
|
||||
account?.agentPresetCatalog ?? contextCatalog ?? EMPTY_AGENT_PRESET_CATALOG
|
||||
));
|
||||
React21.useEffect(() => {
|
||||
setDraft(cloneGrant(normalizeAccessGrant(account?.accessGrant), ownerPhone));
|
||||
setGroupSessionScope(normalizeGroupSessionScope(account?.groupSessionScope));
|
||||
|
|
@ -12918,6 +13007,9 @@ function AccessGrantSettingsPage({ channel: channel4, account, rpcCall, onSaved
|
|||
try {
|
||||
const value = unwrapRpcResult11(await rpcCall("connection.status", {}));
|
||||
if (cancelled) return;
|
||||
if (value?.agentPresetCatalog) {
|
||||
setPresetCatalog(normalizeAgentPresetCatalog(value.agentPresetCatalog));
|
||||
}
|
||||
const bot = Array.isArray(value?.bots) ? value.bots.find((entry) => entry?.botId === account.botId) : null;
|
||||
let grant = normalizeAccessGrant(bot?.accessGrant);
|
||||
if (grant) {
|
||||
|
|
@ -13072,6 +13164,8 @@ function AccessGrantSettingsPage({ channel: channel4, account, rpcCall, onSaved
|
|||
members: draft.directMembers,
|
||||
contacts,
|
||||
disabled: saving,
|
||||
showAgentPreset: true,
|
||||
presetCatalog,
|
||||
onChange: (directMembers) => {
|
||||
setDraft((current) => ({ ...current, directMembers }));
|
||||
setFeedback(null);
|
||||
|
|
@ -13183,6 +13277,30 @@ function AccessGrantSettingsPage({ channel: channel4, account, rpcCall, onSaved
|
|||
})
|
||||
}, "\u540C\u6B65\u7FA4\u540D")
|
||||
),
|
||||
h2(
|
||||
"label",
|
||||
{ className: "dim-accessField" },
|
||||
h2("span", null, "\u7FA4 Agent Preset"),
|
||||
h2(AgentPresetSelect, {
|
||||
value: group.agentPreset ?? "",
|
||||
catalog: presetCatalog,
|
||||
disabled: saving,
|
||||
ariaLabel: "\u7FA4 Agent Preset",
|
||||
onChange: (preset) => setDraft((current) => {
|
||||
const existing = current.groups[groupJid] ?? { title: "", admins: [], members: [] };
|
||||
const nextGroup = { ...existing };
|
||||
if (preset) nextGroup.agentPreset = preset;
|
||||
else delete nextGroup.agentPreset;
|
||||
return {
|
||||
...current,
|
||||
groups: {
|
||||
...current.groups,
|
||||
[groupJid]: nextGroup
|
||||
}
|
||||
};
|
||||
})
|
||||
})
|
||||
),
|
||||
h2(AdminPhones, {
|
||||
title: "\u672C\u7FA4\u7BA1\u7406\u5458",
|
||||
help: "\u672A\u914D\u7F6E\u65F6\uFF0C\u672C\u7FA4\u7533\u8BF7\u56DE\u843D\u7ED9\u5168\u5C40\u7BA1\u7406\u5458\u5BA1\u6279\uFF08\u4ECD\u53EA\u6388\u4E88\u672C\u7FA4\u6743\uFF09\u3002",
|
||||
|
|
@ -14799,6 +14917,12 @@ body[data-ds-dark-theme] .dim-page select option, body[data-ds-dark-theme] .dim-
|
|||
.dim-accessTable[data-kind="member"] th:nth-child(2), .dim-accessTable[data-kind="member"] td:nth-child(2) { width: 36%; }
|
||||
.dim-accessTable[data-kind="member"] th:nth-child(3), .dim-accessTable[data-kind="member"] td:nth-child(3) { width: 28%; }
|
||||
.dim-accessTable[data-kind="member"] th:nth-child(4), .dim-accessTable[data-kind="member"] td:nth-child(4) { width: 16%; }
|
||||
.dim-accessTable[data-kind="member-preset"] th:nth-child(1), .dim-accessTable[data-kind="member-preset"] td:nth-child(1) { width: 16%; }
|
||||
.dim-accessTable[data-kind="member-preset"] th:nth-child(2), .dim-accessTable[data-kind="member-preset"] td:nth-child(2) { width: 26%; }
|
||||
.dim-accessTable[data-kind="member-preset"] th:nth-child(3), .dim-accessTable[data-kind="member-preset"] td:nth-child(3) { width: 18%; }
|
||||
.dim-accessTable[data-kind="member-preset"] th:nth-child(4), .dim-accessTable[data-kind="member-preset"] td:nth-child(4) { width: 26%; }
|
||||
.dim-accessTable[data-kind="member-preset"] th:nth-child(5), .dim-accessTable[data-kind="member-preset"] td:nth-child(5) { width: 14%; }
|
||||
.dim-accessTablePreset { min-width: 0; }
|
||||
.dim-accessTable[data-kind="contact"] th:nth-child(1), .dim-accessTable[data-kind="contact"] td:nth-child(1) { width: 18%; }
|
||||
.dim-accessTable[data-kind="contact"] th:nth-child(2), .dim-accessTable[data-kind="contact"] td:nth-child(2) { width: 26%; }
|
||||
.dim-accessTable[data-kind="contact"] th:nth-child(3), .dim-accessTable[data-kind="contact"] td:nth-child(3) { width: 16%; }
|
||||
|
|
|
|||
436
lib/index.js
436
lib/index.js
File diff suppressed because one or more lines are too long
Loading…
Add table
Add a link
Reference in a new issue