botmux 2.112.0 → 2.114.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bot-registry.d.ts +31 -0
- package/dist/bot-registry.d.ts.map +1 -1
- package/dist/bot-registry.js +8 -0
- package/dist/bot-registry.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +12 -6
- package/dist/cli.js.map +1 -1
- package/dist/core/ask-broker.d.ts +1 -1
- package/dist/core/ask-broker.d.ts.map +1 -1
- package/dist/core/ask-broker.js +2 -1
- package/dist/core/ask-broker.js.map +1 -1
- package/dist/core/ask-types.d.ts +5 -0
- package/dist/core/ask-types.d.ts.map +1 -1
- package/dist/core/ask-types.js.map +1 -1
- package/dist/core/dashboard-ipc-server.d.ts +10 -0
- package/dist/core/dashboard-ipc-server.d.ts.map +1 -1
- package/dist/core/dashboard-ipc-server.js +73 -2
- package/dist/core/dashboard-ipc-server.js.map +1 -1
- package/dist/core/reply-target.d.ts +30 -1
- package/dist/core/reply-target.d.ts.map +1 -1
- package/dist/core/reply-target.js +70 -7
- package/dist/core/reply-target.js.map +1 -1
- package/dist/core/types.d.ts +8 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/core/worker-pool.d.ts +18 -1
- package/dist/core/worker-pool.d.ts.map +1 -1
- package/dist/core/worker-pool.js +67 -11
- package/dist/core/worker-pool.js.map +1 -1
- package/dist/daemon.d.ts +1 -1
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +85 -16
- package/dist/daemon.js.map +1 -1
- package/dist/dashboard/web/bot-defaults-page.d.ts +8 -1
- package/dist/dashboard/web/bot-defaults-page.d.ts.map +1 -1
- package/dist/dashboard/web/bot-defaults-page.js +238 -21
- package/dist/dashboard/web/bot-defaults-page.js.map +1 -1
- package/dist/dashboard/web/bot-defaults.d.ts +23 -0
- package/dist/dashboard/web/bot-defaults.d.ts.map +1 -1
- package/dist/dashboard/web/bot-defaults.js +17 -0
- package/dist/dashboard/web/bot-defaults.js.map +1 -1
- package/dist/dashboard/web/i18n.d.ts.map +1 -1
- package/dist/dashboard/web/i18n.js +54 -2
- package/dist/dashboard/web/i18n.js.map +1 -1
- package/dist/dashboard/web/ui.d.ts +4 -0
- package/dist/dashboard/web/ui.d.ts.map +1 -1
- package/dist/dashboard/web/ui.js +11 -0
- package/dist/dashboard/web/ui.js.map +1 -1
- package/dist/dashboard-web/app.js +1 -1
- package/dist/dashboard-web/chunks/bot-defaults-page-SRCBF7Z3.js +5 -0
- package/dist/dashboard-web/chunks/{chunk-NYZHARIU.js → chunk-3H6MVXXU.js} +8 -6
- package/dist/dashboard-web/chunks/{chunk-YBOHL7T7.js → chunk-77KD4TK2.js} +1 -1
- package/dist/dashboard-web/chunks/{chunk-CMDPZOPK.js → chunk-D2I5F4MY.js} +1 -1
- package/dist/dashboard-web/chunks/{chunk-YWCPDL4S.js → chunk-NGRUZCXP.js} +1 -1
- package/dist/dashboard-web/chunks/{connectors-page-DAMP7TTV.js → connectors-page-RM3BJVMV.js} +1 -1
- package/dist/dashboard-web/chunks/{groups-page-BPFA356N.js → groups-page-3D4YSYLA.js} +1 -1
- package/dist/dashboard-web/chunks/{insights-page-SHOPHTWY.js → insights-page-CRFJKA54.js} +1 -1
- package/dist/dashboard-web/chunks/{monitor-room-PXPJGZCN.js → monitor-room-HLK3XDT2.js} +1 -1
- package/dist/dashboard-web/chunks/{monitoring-page-4DF54M5Y.js → monitoring-page-E3SNZFFN.js} +1 -1
- package/dist/dashboard-web/chunks/{office-page-6OHU6KMZ.js → office-page-MKQ2TENK.js} +1 -1
- package/dist/dashboard-web/chunks/overview-page-ICFKKRLV.js +1 -0
- package/dist/dashboard-web/chunks/{roles-page-JYTBYKEF.js → roles-page-2DFJ7YRU.js} +1 -1
- package/dist/dashboard-web/chunks/{schedules-page-WJ72T7KV.js → schedules-page-S4WDIXH5.js} +1 -1
- package/dist/dashboard-web/chunks/{sessions-page-QC56OIAI.js → sessions-page-4XWGUGB5.js} +1 -1
- package/dist/dashboard-web/chunks/{settings-page-TCKWEXTN.js → settings-page-OBTE4LYP.js} +1 -1
- package/dist/dashboard-web/chunks/{skills-page-RJJNIECW.js → skills-page-CTRQJR47.js} +1 -1
- package/dist/dashboard-web/chunks/{team-federation-page-ZMDX3E7K.js → team-federation-page-GICLDKJP.js} +1 -1
- package/dist/dashboard-web/chunks/{v3-page-VLLAUQ33.js → v3-page-DI55IRL5.js} +1 -1
- package/dist/dashboard-web/chunks/{whiteboards-page-NAO53CKM.js → whiteboards-page-7KFWMILV.js} +1 -1
- package/dist/dashboard-web/style.css +113 -1
- package/dist/dashboard.js +48 -0
- package/dist/dashboard.js.map +1 -1
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +3 -2
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/zh.d.ts.map +1 -1
- package/dist/i18n/zh.js +3 -2
- package/dist/i18n/zh.js.map +1 -1
- package/dist/im/lark/card-handler.d.ts.map +1 -1
- package/dist/im/lark/card-handler.js +9 -2
- package/dist/im/lark/card-handler.js.map +1 -1
- package/dist/im/lark/client.d.ts +30 -0
- package/dist/im/lark/client.d.ts.map +1 -1
- package/dist/im/lark/client.js +63 -13
- package/dist/im/lark/client.js.map +1 -1
- package/dist/im/lark/event-dispatcher.d.ts +7 -3
- package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
- package/dist/im/lark/event-dispatcher.js +66 -15
- package/dist/im/lark/event-dispatcher.js.map +1 -1
- package/dist/im/lark/substitute-command.d.ts.map +1 -1
- package/dist/im/lark/substitute-command.js +10 -1
- package/dist/im/lark/substitute-command.js.map +1 -1
- package/dist/services/oncall-store.d.ts +8 -0
- package/dist/services/oncall-store.d.ts.map +1 -1
- package/dist/services/oncall-store.js +48 -2
- package/dist/services/oncall-store.js.map +1 -1
- package/dist/services/open-platform-rename.d.ts +31 -3
- package/dist/services/open-platform-rename.d.ts.map +1 -1
- package/dist/services/open-platform-rename.js +262 -48
- package/dist/services/open-platform-rename.js.map +1 -1
- package/dist/services/substitute-mode-normalize.d.ts.map +1 -1
- package/dist/services/substitute-mode-normalize.js +17 -1
- package/dist/services/substitute-mode-normalize.js.map +1 -1
- package/dist/services/substitute-mode-store.d.ts +24 -5
- package/dist/services/substitute-mode-store.d.ts.map +1 -1
- package/dist/services/substitute-mode-store.js +36 -16
- package/dist/services/substitute-mode-store.js.map +1 -1
- package/dist/skills/definitions.d.ts +1 -1
- package/dist/skills/definitions.d.ts.map +1 -1
- package/dist/skills/definitions.js +15 -3
- package/dist/skills/definitions.js.map +1 -1
- package/dist/types.d.ts +22 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/bot-routing.d.ts +1 -0
- package/dist/utils/bot-routing.d.ts.map +1 -1
- package/dist/utils/bot-routing.js +1 -1
- package/dist/utils/bot-routing.js.map +1 -1
- package/dist/workflows/migration/v2-run-archive-schema.d.ts +2 -2
- package/dist/workflows/v3/run-envelope.d.ts +16 -16
- package/package.json +1 -1
- package/dist/dashboard-web/chunks/bot-defaults-page-RYBL4ZYE.js +0 -4
- package/dist/dashboard-web/chunks/overview-page-IHIZ6F5M.js +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type BotDefaultsRow, type BotSubstituteTarget, type CliOptionsState } from './bot-defaults.js';
|
|
1
|
+
import { type BotDefaultsRow, type BotSubstituteTarget, type CliOptionsState, type SubstituteTargetResolution } from './bot-defaults.js';
|
|
2
2
|
import { type PageDisposer } from './react-mount.js';
|
|
3
3
|
type PatchBot = (appId: string, patch: Partial<BotDefaultsRow> | ((bot: BotDefaultsRow) => BotDefaultsRow)) => void;
|
|
4
4
|
type CardPrefPatch = Record<string, boolean | string>;
|
|
@@ -15,6 +15,13 @@ type SubstituteTargetDraft = {
|
|
|
15
15
|
name: string;
|
|
16
16
|
persisted: BotSubstituteTarget;
|
|
17
17
|
originalIdField?: SubstituteTargetIdField;
|
|
18
|
+
resolving?: boolean;
|
|
19
|
+
resolution?: {
|
|
20
|
+
ok: boolean;
|
|
21
|
+
name?: string;
|
|
22
|
+
avatarUrl?: string;
|
|
23
|
+
reason?: SubstituteTargetResolution['reason'];
|
|
24
|
+
};
|
|
18
25
|
};
|
|
19
26
|
/**
|
|
20
27
|
* Build the substitute target to PUT for one edited row. Returns null when the id value is
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bot-defaults-page.d.ts","sourceRoot":"","sources":["../../../src/dashboard/web/bot-defaults-page.tsx"],"names":[],"mappings":"AAEA,OAAO,
|
|
1
|
+
{"version":3,"file":"bot-defaults-page.d.ts","sourceRoot":"","sources":["../../../src/dashboard/web/bot-defaults-page.tsx"],"names":[],"mappings":"AAEA,OAAO,EAWL,KAAK,cAAc,EAEnB,KAAK,mBAAmB,EACxB,KAAK,eAAe,EACpB,KAAK,0BAA0B,EAChC,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAkB,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAcrE,KAAK,QAAQ,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,cAAc,KAAK,cAAc,CAAC,KAAK,IAAI,CAAC;AACpH,KAAK,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CAAC,CAAC;AAEtD,KAAK,YAAY,GAAG;IAClB,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,GAAG,CAAC;CACX,CAAC;AAyIF,KAAK,uBAAuB,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;AAEzE,KAAK,qBAAqB,GAAG;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,uBAAuB,CAAC;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,mBAAmB,CAAC;IAC/B,eAAe,CAAC,EAAE,uBAAuB,CAAC;IAC1C,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE;QACX,EAAE,EAAE,OAAO,CAAC;QACZ,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,MAAM,CAAC,EAAE,0BAA0B,CAAC,QAAQ,CAAC,CAAC;KAC/C,CAAC;CACH,CAAC;AAiBF;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,IAAI,CAAC,qBAAqB,EAAE,SAAS,GAAG,SAAS,GAAG,MAAM,GAAG,WAAW,GAAG,iBAAiB,CAAC,GACjG,mBAAmB,GAAG,IAAI,CAc5B;AA2qBD,wBAAgB,eAAe,CAAC,KAAK,EAAE;IACrC,GAAG,EAAE,cAAc,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,eAAe,CAAC;IAC1B,QAAQ,EAAE,QAAQ,CAAC;CACpB,+BAkOA;AAujBD,wBAAgB,sBAAsB,CAAC,KAAK,EAAE;IAAE,GAAG,EAAE,cAAc,CAAC;IAAC,WAAW,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;CAAE,+BA8C9H;AAupCD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,WAAW,GAAG,YAAY,CAErE"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
3
3
|
import { openBotOnboarding } from './bot-onboarding.js';
|
|
4
|
-
import { agentSelectionKey, cliIdOf, displayCliId, fallbackCliOptionsState, fetchBotDefaults, fetchCliOptions, fmtSince, modelSuggestionsForOption, selectedCliOption, } from './bot-defaults.js';
|
|
4
|
+
import { agentSelectionKey, cliIdOf, displayCliId, fallbackCliOptionsState, fetchBotDefaults, fetchCliOptions, fmtSince, modelSuggestionsForOption, resolveSubstituteTarget, selectedCliOption, } from './bot-defaults.js';
|
|
5
5
|
import { mountReactPage } from './react-mount.js';
|
|
6
6
|
import { useT } from './react-hooks.js';
|
|
7
7
|
import { CreateActionButton, DropdownMenu, Html, InfoTip as BaseInfoTip, LoadingState, RefreshIconButton, dropdownLabel, } from './dashboard-components.js';
|
|
8
|
-
import { botAvatarHtml, loadNameMaps, ui } from './ui.js';
|
|
8
|
+
import { botAvatarHtml, loadNameMaps, overrideBotAvatar, ui } from './ui.js';
|
|
9
9
|
function statusClass(status, extra = '') {
|
|
10
10
|
const suffix = status ? ` ${status.ok ? 'hint-ok' : 'hint-warn-inline'}` : '';
|
|
11
11
|
return `oncall-status${extra ? ` ${extra}` : ''}${suffix}`;
|
|
@@ -63,6 +63,13 @@ function nonNegativeInteger(raw, fallback) {
|
|
|
63
63
|
return Number(value);
|
|
64
64
|
}
|
|
65
65
|
const substituteTargetIdFields = ['email', 'openId', 'userId', 'unionId'];
|
|
66
|
+
function parseSubstituteChats(text) {
|
|
67
|
+
const values = text.split(/[\r\n,,;;]+/).map(s => s.trim()).filter(Boolean);
|
|
68
|
+
return [...new Set(values)];
|
|
69
|
+
}
|
|
70
|
+
function formatSubstituteChats(chats) {
|
|
71
|
+
return (chats ?? []).join('\n');
|
|
72
|
+
}
|
|
66
73
|
function substituteTargetIdField(target) {
|
|
67
74
|
return substituteTargetIdFields.find(field => target?.[field]?.trim()) ?? 'email';
|
|
68
75
|
}
|
|
@@ -247,12 +254,114 @@ function BotDefaultsCard(props) {
|
|
|
247
254
|
return (_jsxs("article", { className: "bd-card bd-profile", "data-appid": bot.larkAppId, children: [_jsxs("header", { className: "bd-profile-head", children: [_jsx(Html, { html: botAvatarHtml({ name, larkAppId: bot.larkAppId }) }), _jsxs("div", { className: "bd-profile-id", children: [_jsx("strong", { children: name }), _jsx("code", { children: bot.larkAppId })] })] }), _jsxs("p", { className: "hint-warn-inline", children: ["\u67E5\u8BE2\u5931\u8D25\uFF1A", bot.error] })] }));
|
|
248
255
|
}
|
|
249
256
|
const def = bot.defaultOncall ?? { enabled: false, workingDir: '', since: 0 };
|
|
250
|
-
return (_jsxs("article", { className: "bd-card bd-profile", "data-appid": bot.larkAppId, children: [_jsxs("header", { className: "bd-profile-head", children: [_jsx(
|
|
257
|
+
return (_jsxs("article", { className: "bd-card bd-profile", "data-appid": bot.larkAppId, children: [_jsxs("header", { className: "bd-profile-head", children: [_jsx(BotAvatarControl, { bot: bot, name: name, patchBot: patchBot }), _jsx("div", { className: "bd-profile-main", children: _jsx(BotProfileIdentity, { bot: bot, cli: cli, patchBot: patchBot, meta: (_jsxs(_Fragment, { children: [_jsxs("small", { className: "bd-meta-ok", children: ["\u25CF ", tr('botDefaults.metaOnline')] }), _jsxs("small", { "data-oncall-since": true, children: [tr('botDefaults.lastEnabled'), ": ", fmtSince(def.since ?? 0)] }), _jsx("small", { children: tr('botDefaults.autobound', { count: bot.autoboundChatCount ?? 0 }) })] })) }) })] }), _jsxs("div", { className: "bd-body bd-grid", children: [_jsxs("div", { className: "bd-column", children: [_jsxs("section", { className: "bd-tile", children: [_jsx(BotAgentSection, { bot: bot, sessionFallback: cli, cliState: cliState, patchBot: patchBot }), _jsx(WorkingDirSection, { bot: bot, patchBot: patchBot, putCardPref: putCardPref }), bot.cliId !== 'riff' && _jsx(SandboxSection, { bot: bot, patchBot: patchBot }), bot.cliId !== 'riff' && _jsx(BackendTypeSection, { bot: bot, patchBot: patchBot })] }), _jsx("section", { className: "bd-tile", children: _jsx(RuntimeEnvironmentSection, { bot: bot, patchBot: patchBot }) }), _jsx("section", { className: "bd-tile", children: _jsx(GrantSection, { bot: bot, patchBot: patchBot }) })] }), _jsxs("div", { className: "bd-column", children: [_jsxs("section", { className: "bd-tile", children: [_jsx(SessionModeSection, { bot: bot, patchBot: patchBot, putCardPref: putCardPref }), _jsx(SubstituteModeSection, { bot: bot, patchBot: patchBot }), _jsx(CrossBotSection, { bot: bot, putCardPref: putCardPref }), _jsx(SessionCapSection, { bot: bot, patchBot: patchBot })] }), _jsxs("section", { className: "bd-tile", children: [_jsx(CardBehaviorSection, { bot: bot, putCardPref: putCardPref }), _jsx(CodexAppDisplaySection, { bot: bot, putCardPref: putCardPref }), _jsx(SummaryTriggerSection, { bot: bot, patchBot: patchBot }), _jsx(BrandSection, { bot: bot, patchBot: patchBot })] }), _jsx("section", { className: "bd-tile", children: _jsx(RoleSection, { bot: bot, patchBot: patchBot }) })] })] })] }));
|
|
251
258
|
}
|
|
252
259
|
function RuntimeEnvironmentSection(props) {
|
|
253
260
|
const tr = useT();
|
|
254
261
|
return (_jsxs("section", { className: "bd-section bd-runtime-env", children: [_jsx("h3", { className: "bd-section-title", children: tr('botDefaults.sectionRuntimeEnv') }), _jsx(StartupCommandsSection, { bot: props.bot, patchBot: props.patchBot }), _jsx(LaunchShellSection, { bot: props.bot, patchBot: props.patchBot }), _jsx(EnvSection, { bot: props.bot, patchBot: props.patchBot })] }));
|
|
255
262
|
}
|
|
263
|
+
/** console 头像上传只实测过 512×512 PNG,前端统一归一化成这一形态再上传。 */
|
|
264
|
+
const AVATAR_UPLOAD_SIDE = 512;
|
|
265
|
+
/** 任意用户图片 → 512×512 PNG dataURL(短边 cover 裁剪居中)。 */
|
|
266
|
+
async function normalizeAvatarImage(file) {
|
|
267
|
+
const bitmap = await createImageBitmap(file);
|
|
268
|
+
try {
|
|
269
|
+
const side = Math.min(bitmap.width, bitmap.height);
|
|
270
|
+
if (!side)
|
|
271
|
+
throw new Error('empty image');
|
|
272
|
+
const canvas = document.createElement('canvas');
|
|
273
|
+
canvas.width = AVATAR_UPLOAD_SIDE;
|
|
274
|
+
canvas.height = AVATAR_UPLOAD_SIDE;
|
|
275
|
+
const ctx = canvas.getContext('2d');
|
|
276
|
+
if (!ctx)
|
|
277
|
+
throw new Error('canvas unavailable');
|
|
278
|
+
const sx = (bitmap.width - side) / 2;
|
|
279
|
+
const sy = (bitmap.height - side) / 2;
|
|
280
|
+
ctx.drawImage(bitmap, sx, sy, side, side, 0, 0, AVATAR_UPLOAD_SIDE, AVATAR_UPLOAD_SIDE);
|
|
281
|
+
return canvas.toDataURL('image/png');
|
|
282
|
+
}
|
|
283
|
+
finally {
|
|
284
|
+
bitmap.close();
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
/** 档案头头像:点击选图 → 归一化 → 走开放平台自动化真改飞书应用头像并发版。
|
|
288
|
+
* 与改名同款失败语义:缺飞书 Web 登录态时给扫码入口,登录成功自动重试。 */
|
|
289
|
+
function BotAvatarControl(props) {
|
|
290
|
+
const tr = useT();
|
|
291
|
+
const { bot, name, patchBot } = props;
|
|
292
|
+
const inputRef = useRef(null);
|
|
293
|
+
const [busy, setBusy] = useState(false);
|
|
294
|
+
const [status, setStatus] = useState(null);
|
|
295
|
+
const [loginVisible, setLoginVisible] = useState(false);
|
|
296
|
+
const [loginOpen, setLoginOpen] = useState(false);
|
|
297
|
+
// 待上传图片留到登录成功后重试;成功/明确失败时清掉。
|
|
298
|
+
const pendingRef = useRef(null);
|
|
299
|
+
function avatarFailText(error, message) {
|
|
300
|
+
const known = ['no_session', 'session_expired', 'no_access', 'unsupported_brand'];
|
|
301
|
+
const detail = known.includes(error) ? tr(`botDefaults.avatarWarn.${error}`) : (message || error);
|
|
302
|
+
return tr('botDefaults.avatarFailed', { error: detail });
|
|
303
|
+
}
|
|
304
|
+
const upload = useCallback(async (imageBase64) => {
|
|
305
|
+
setBusy(true);
|
|
306
|
+
setStatus({ text: `⏳ ${tr('botDefaults.avatarUploading')}`, ok: true });
|
|
307
|
+
try {
|
|
308
|
+
const res = await sendJson('PUT', `/api/bots/${encodeURIComponent(bot.larkAppId)}/avatar`, { imageBase64 });
|
|
309
|
+
if (res.ok && res.body.ok) {
|
|
310
|
+
const url = typeof res.body.avatarUrl === 'string' ? res.body.avatarUrl : '';
|
|
311
|
+
if (url)
|
|
312
|
+
overrideBotAvatar(bot.larkAppId, name, url);
|
|
313
|
+
// 行内容不变,触发一次重绘让 orb 读到覆写后的头像映射。
|
|
314
|
+
patchBot(bot.larkAppId, current => ({ ...current }));
|
|
315
|
+
pendingRef.current = null;
|
|
316
|
+
setLoginVisible(false);
|
|
317
|
+
setStatus({ text: `✓ ${tr('botDefaults.avatarOkFeishu')}`, ok: true });
|
|
318
|
+
}
|
|
319
|
+
else {
|
|
320
|
+
const err = String(res.body?.error ?? '');
|
|
321
|
+
const message = typeof res.body?.message === 'string' ? res.body.message : undefined;
|
|
322
|
+
setStatus({ text: `✗ ${avatarFailText(err, message ?? responseErrorText(res))}` });
|
|
323
|
+
const needLogin = err === 'no_session' || err === 'session_expired';
|
|
324
|
+
setLoginVisible(needLogin);
|
|
325
|
+
if (!needLogin)
|
|
326
|
+
pendingRef.current = null;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
catch (e) {
|
|
330
|
+
setStatus({ text: `✗ ${tr('botDefaults.avatarFailed', { error: caughtErrorText(e) })}` });
|
|
331
|
+
}
|
|
332
|
+
finally {
|
|
333
|
+
setBusy(false);
|
|
334
|
+
}
|
|
335
|
+
}, [bot.larkAppId, name, patchBot, tr]);
|
|
336
|
+
async function handleFile(file) {
|
|
337
|
+
if (!file || busy)
|
|
338
|
+
return;
|
|
339
|
+
// 归一化阶段就置 busy:canvas 解码大图有可感知耗时,这个窗口里不该还能
|
|
340
|
+
// 再开一次选图/触发并发提交(服务端另有 per-app 串行队列兜底)。
|
|
341
|
+
setBusy(true);
|
|
342
|
+
let dataUrl;
|
|
343
|
+
try {
|
|
344
|
+
dataUrl = await normalizeAvatarImage(file);
|
|
345
|
+
}
|
|
346
|
+
catch {
|
|
347
|
+
setBusy(false);
|
|
348
|
+
setStatus({ text: `✗ ${tr('botDefaults.avatarBadImage')}` });
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
pendingRef.current = dataUrl;
|
|
352
|
+
await upload(dataUrl);
|
|
353
|
+
}
|
|
354
|
+
return (_jsxs("div", { className: "bd-profile-avatar bd-avatar-editable", "data-avatar-control": true, children: [_jsxs("button", { type: "button", className: "bd-avatar-btn", "data-action": "edit-bot-avatar", title: tr('botDefaults.avatarTitle'), "aria-label": tr('botDefaults.avatarTitle'), disabled: busy, onClick: () => inputRef.current?.click(), children: [_jsx(Html, { html: botAvatarHtml({ name, larkAppId: bot.larkAppId, dot: 'ok' }) }), _jsx("span", { className: "bd-avatar-edit-badge", "aria-hidden": "true", children: busy ? '⏳' : '✎' })] }), _jsx("input", { ref: inputRef, type: "file", accept: "image/*", hidden: true, "data-input": "botAvatarFile", onChange: event => {
|
|
355
|
+
const file = event.currentTarget.files?.[0];
|
|
356
|
+
event.currentTarget.value = ''; // 允许再次选择同一文件
|
|
357
|
+
void handleFile(file);
|
|
358
|
+
} }), status ? (_jsxs("small", { className: statusClass(status, 'bd-avatar-status'), "data-avatar-status": true, children: [status.text, loginVisible ? (_jsx("button", { type: "button", className: "bd-feishu-login", "data-action": "feishu-login-avatar", onClick: () => setLoginOpen(true), children: tr('feishuLogin.entry') })) : null] })) : null, loginOpen ? (_jsx(FeishuLoginModal, { onClose: () => setLoginOpen(false), onSuccess: () => {
|
|
359
|
+
setLoginVisible(false);
|
|
360
|
+
setLoginOpen(false);
|
|
361
|
+
if (pendingRef.current)
|
|
362
|
+
void upload(pendingRef.current);
|
|
363
|
+
} })) : null] }));
|
|
364
|
+
}
|
|
256
365
|
function BotProfileIdentity(props) {
|
|
257
366
|
const tr = useT();
|
|
258
367
|
const { bot, cli, patchBot } = props;
|
|
@@ -1141,7 +1250,22 @@ function SubstituteModeSection(props) {
|
|
|
1141
1250
|
const tr = useT();
|
|
1142
1251
|
const initial = props.bot.substituteMode ?? null;
|
|
1143
1252
|
const [enabled, setEnabled] = useState(initial?.enabled === true);
|
|
1253
|
+
function substituteReasonText(reason) {
|
|
1254
|
+
switch (reason) {
|
|
1255
|
+
case 'cross_app_open_id': return tr('botDefaults.substituteReasonCrossAppOpenId');
|
|
1256
|
+
case 'not_visible': return tr('botDefaults.substituteReasonNotVisible');
|
|
1257
|
+
case 'resolve_failed': return tr('botDefaults.substituteReasonResolveFailed');
|
|
1258
|
+
case 'unresolvable': return tr('botDefaults.substituteReasonUnresolvable');
|
|
1259
|
+
default: return tr('botDefaults.substituteUnresolved');
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1144
1262
|
const [disclosure, setDisclosure] = useState(initial?.disclosure === 'none' ? 'none' : 'prefix');
|
|
1263
|
+
const [replyMode, setReplyMode] = useState(initial?.replyMode === 'quote' ? 'quote' : 'thread');
|
|
1264
|
+
const [controlCard, setControlCard] = useState(initial?.disableControlCard !== true);
|
|
1265
|
+
const [chatsText, setChatsText] = useState(() => formatSubstituteChats(initial?.chats));
|
|
1266
|
+
// 话题群相关开关缺省开:只有显式 false 才是关(与 normalize 语义一致)。
|
|
1267
|
+
const [topicGroups, setTopicGroups] = useState(initial?.topicGroups !== false);
|
|
1268
|
+
const [topicActiveSessionTrigger, setTopicActiveSessionTrigger] = useState(initial?.topicActiveSessionTrigger !== false);
|
|
1145
1269
|
const [status, setStatus] = useState(null);
|
|
1146
1270
|
const [busy, setBusy] = useState(false);
|
|
1147
1271
|
const targetSequence = useRef(0);
|
|
@@ -1155,8 +1279,73 @@ function SubstituteModeSection(props) {
|
|
|
1155
1279
|
name: target?.name ?? '',
|
|
1156
1280
|
persisted: target ? { ...target } : {},
|
|
1157
1281
|
originalIdField: target ? idField : undefined,
|
|
1282
|
+
resolution: target?.name || target?.avatarUrl
|
|
1283
|
+
? { ok: true, name: target.name, avatarUrl: target.avatarUrl }
|
|
1284
|
+
: undefined,
|
|
1158
1285
|
};
|
|
1159
1286
|
}
|
|
1287
|
+
// Monotonic per-row resolve epoch: two quick blurs create two in-flight
|
|
1288
|
+
// requests; only the latest one may apply, or a slow stale response would
|
|
1289
|
+
// overwrite the fresh result (last-completion-wins race).
|
|
1290
|
+
const resolveEpochs = useRef(new Map());
|
|
1291
|
+
async function resolveTargetRow(key) {
|
|
1292
|
+
const epoch = (resolveEpochs.current.get(key) ?? 0) + 1;
|
|
1293
|
+
resolveEpochs.current.set(key, epoch);
|
|
1294
|
+
const isCurrent = () => resolveEpochs.current.get(key) === epoch;
|
|
1295
|
+
setTargetRows(rows => rows.map(row => row.key === key ? { ...row, resolving: true } : row));
|
|
1296
|
+
try {
|
|
1297
|
+
const row = targetRows.find(r => r.key === key);
|
|
1298
|
+
if (!row)
|
|
1299
|
+
return;
|
|
1300
|
+
const idValue = row.idValue.trim();
|
|
1301
|
+
if (!idValue) {
|
|
1302
|
+
setTargetRows(rows => rows.map(r => r.key === key ? { ...r, resolving: false, resolution: undefined } : r));
|
|
1303
|
+
return;
|
|
1304
|
+
}
|
|
1305
|
+
const target = { [row.idField]: idValue };
|
|
1306
|
+
if (row.name.trim())
|
|
1307
|
+
target.name = row.name.trim();
|
|
1308
|
+
const res = await resolveSubstituteTarget(props.bot.larkAppId, target);
|
|
1309
|
+
if (!isCurrent())
|
|
1310
|
+
return;
|
|
1311
|
+
setTargetRows(rows => rows.map(r => {
|
|
1312
|
+
if (r.key !== key)
|
|
1313
|
+
return r;
|
|
1314
|
+
if (!res.ok)
|
|
1315
|
+
return { ...r, resolving: false, resolution: { ok: false } };
|
|
1316
|
+
const entry = res.resolution;
|
|
1317
|
+
if (entry?.ok === true) {
|
|
1318
|
+
// userId passthrough: nothing was verified (no openId / profile) —
|
|
1319
|
+
// keep the editable name input instead of showing a fake chip.
|
|
1320
|
+
if (!entry.openId)
|
|
1321
|
+
return { ...r, resolving: false, resolution: undefined };
|
|
1322
|
+
const persisted = { ...r.persisted };
|
|
1323
|
+
persisted.openId = entry.openId;
|
|
1324
|
+
if (entry.name)
|
|
1325
|
+
persisted.name = entry.name;
|
|
1326
|
+
if (entry.avatarUrl)
|
|
1327
|
+
persisted.avatarUrl = entry.avatarUrl;
|
|
1328
|
+
return {
|
|
1329
|
+
...r,
|
|
1330
|
+
name: entry.name ?? r.name,
|
|
1331
|
+
persisted,
|
|
1332
|
+
resolving: false,
|
|
1333
|
+
resolution: { ok: true, name: entry.name, avatarUrl: entry.avatarUrl },
|
|
1334
|
+
};
|
|
1335
|
+
}
|
|
1336
|
+
return {
|
|
1337
|
+
...r,
|
|
1338
|
+
resolving: false,
|
|
1339
|
+
resolution: { ok: false, reason: entry?.reason },
|
|
1340
|
+
};
|
|
1341
|
+
}));
|
|
1342
|
+
}
|
|
1343
|
+
catch {
|
|
1344
|
+
if (!isCurrent())
|
|
1345
|
+
return;
|
|
1346
|
+
setTargetRows(rows => rows.map(r => r.key === key ? { ...r, resolving: false, resolution: { ok: false } } : r));
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1160
1349
|
const [targetRows, setTargetRows] = useState(() => {
|
|
1161
1350
|
const targets = initial?.targets ?? [];
|
|
1162
1351
|
return targets.length ? targets.map(target => makeTargetDraft(target)) : [makeTargetDraft()];
|
|
@@ -1169,6 +1358,11 @@ function SubstituteModeSection(props) {
|
|
|
1169
1358
|
const next = props.bot.substituteMode ?? null;
|
|
1170
1359
|
setEnabled(next?.enabled === true);
|
|
1171
1360
|
setDisclosure(next?.disclosure === 'none' ? 'none' : 'prefix');
|
|
1361
|
+
setReplyMode(next?.replyMode === 'quote' ? 'quote' : 'thread');
|
|
1362
|
+
setControlCard(next?.disableControlCard !== true);
|
|
1363
|
+
setChatsText(formatSubstituteChats(next?.chats));
|
|
1364
|
+
setTopicGroups(next?.topicGroups !== false);
|
|
1365
|
+
setTopicActiveSessionTrigger(next?.topicActiveSessionTrigger !== false);
|
|
1172
1366
|
const targets = next?.targets ?? [];
|
|
1173
1367
|
setTargetRows(targets.length ? targets.map(target => makeTargetDraft(target)) : [makeTargetDraft()]);
|
|
1174
1368
|
}, [props.bot.larkAppId, props.bot.substituteMode]);
|
|
@@ -1190,6 +1384,11 @@ function SubstituteModeSection(props) {
|
|
|
1190
1384
|
.filter(Boolean);
|
|
1191
1385
|
setEnabled(next?.enabled === true);
|
|
1192
1386
|
setDisclosure(next?.disclosure === 'none' ? 'none' : 'prefix');
|
|
1387
|
+
setReplyMode(next?.replyMode === 'quote' ? 'quote' : 'thread');
|
|
1388
|
+
setControlCard(next?.disableControlCard !== true);
|
|
1389
|
+
setChatsText(formatSubstituteChats(next?.chats));
|
|
1390
|
+
setTopicGroups(next?.topicGroups !== false);
|
|
1391
|
+
setTopicActiveSessionTrigger(next?.topicActiveSessionTrigger !== false);
|
|
1193
1392
|
if (resolution.length) {
|
|
1194
1393
|
skipModeSync.current = true;
|
|
1195
1394
|
setTargetRows(rows => {
|
|
@@ -1200,16 +1399,27 @@ function SubstituteModeSection(props) {
|
|
|
1200
1399
|
if (index < 0)
|
|
1201
1400
|
return row;
|
|
1202
1401
|
const entry = pending.splice(index, 1)[0];
|
|
1203
|
-
if (entry?.ok
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1402
|
+
if (entry?.ok === true) {
|
|
1403
|
+
const persisted = { ...row.persisted };
|
|
1404
|
+
if (entry.openId)
|
|
1405
|
+
persisted.openId = entry.openId;
|
|
1406
|
+
if (row.idField === 'email')
|
|
1407
|
+
persisted.email = input;
|
|
1408
|
+
if (entry.name)
|
|
1409
|
+
persisted.name = entry.name;
|
|
1410
|
+
if (entry.avatarUrl)
|
|
1411
|
+
persisted.avatarUrl = entry.avatarUrl;
|
|
1412
|
+
return {
|
|
1413
|
+
...row,
|
|
1414
|
+
name: entry.name ?? row.name,
|
|
1415
|
+
persisted,
|
|
1416
|
+
resolution: { ok: true, name: entry.name, avatarUrl: entry.avatarUrl },
|
|
1417
|
+
};
|
|
1418
|
+
}
|
|
1419
|
+
return {
|
|
1420
|
+
...row,
|
|
1421
|
+
resolution: { ok: false, reason: entry?.reason },
|
|
1422
|
+
};
|
|
1213
1423
|
});
|
|
1214
1424
|
});
|
|
1215
1425
|
}
|
|
@@ -1256,26 +1466,33 @@ function SubstituteModeSection(props) {
|
|
|
1256
1466
|
setStatus({ text: `✗ ${tr('botDefaults.substituteTargetsInvalid')}` });
|
|
1257
1467
|
return;
|
|
1258
1468
|
}
|
|
1259
|
-
void save({ enabled, targets, disclosure });
|
|
1469
|
+
void save({ enabled, targets, disclosure, chats: parseSubstituteChats(chatsText), replyMode, disableControlCard: !controlCard, topicGroups, topicActiveSessionTrigger });
|
|
1260
1470
|
}
|
|
1261
1471
|
const disclosureOptions = [
|
|
1262
1472
|
{ value: 'prefix', label: tr('botDefaults.substituteDisclosurePrefix') },
|
|
1263
1473
|
{ value: 'none', label: tr('botDefaults.substituteDisclosureNone') },
|
|
1264
1474
|
];
|
|
1265
|
-
|
|
1475
|
+
const replyModeOptions = [
|
|
1476
|
+
{ value: 'thread', label: tr('botDefaults.substituteReplyModeThread') },
|
|
1477
|
+
{ value: 'quote', label: tr('botDefaults.substituteReplyModeQuote') },
|
|
1478
|
+
];
|
|
1479
|
+
return (_jsxs("section", { className: "bd-section bd-substitute-section", children: [_jsx("h3", { className: "bd-section-title", children: tr('botDefaults.sectionSubstitute') }), _jsx(ToggleRow, { checked: enabled, disabled: busy, dataAction: "toggle-substitute-mode", title: tr('botDefaults.substituteEnabled'), help: tr('botDefaults.substituteHelp'), onChange: setEnabled }), _jsx(ToggleRow, { checked: topicGroups, disabled: busy, dataAction: "toggle-substitute-topic-groups", title: tr('botDefaults.substituteTopicGroups'), help: tr('botDefaults.substituteTopicGroupsHelp'), onChange: setTopicGroups }), _jsx(ToggleRow, { checked: topicActiveSessionTrigger, disabled: busy || !topicGroups, dataAction: "toggle-substitute-topic-active", title: tr('botDefaults.substituteTopicActive'), help: tr('botDefaults.substituteTopicActiveHelp'), onChange: setTopicActiveSessionTrigger }), _jsx("div", { className: "bd-row", children: _jsxs("div", { className: "bd-field", children: [_jsx(FieldTitle, { children: tr('botDefaults.substituteDisclosure') }), _jsx(DropdownField, { dataInput: "substituteDisclosure", ariaLabel: tr('botDefaults.substituteDisclosure'), value: disclosure, disabled: busy, options: disclosureOptions, onChange: value => setDisclosure(value) })] }) }), _jsx("div", { className: "bd-row", children: _jsxs("div", { className: "bd-field", children: [_jsx(FieldTitle, { help: tr('botDefaults.substituteReplyModeHelp'), children: tr('botDefaults.substituteReplyMode') }), _jsx(DropdownField, { dataInput: "substituteReplyMode", ariaLabel: tr('botDefaults.substituteReplyMode'), value: replyMode, disabled: busy, options: replyModeOptions, onChange: value => setReplyMode(value) })] }) }), _jsx(ToggleRow, { checked: controlCard, disabled: busy, dataAction: "toggle-substitute-control-card", title: tr('botDefaults.substituteControlCard'), help: tr('botDefaults.substituteControlCardHelp'), onChange: setControlCard }), _jsx("div", { className: "bd-row", children: _jsxs("label", { children: [_jsx(FieldTitle, { help: tr('botDefaults.substituteChatsHelp'), children: tr('botDefaults.substituteChats') }), _jsx("textarea", { "data-input": "substituteChats", rows: 3, placeholder: tr('botDefaults.substituteChatsPlaceholder'), value: chatsText, disabled: busy, onChange: event => setChatsText(event.currentTarget.value) })] }) }), _jsxs("div", { className: "bd-row bd-substitute-targets", children: [_jsx(FieldTitle, { help: tr('botDefaults.substituteTargetsHelp'), children: tr('botDefaults.substituteTargets') }), _jsxs("div", { className: "bd-substitute-target-list", "data-input": "substituteTargets", children: [targetRows.map((target, index) => (_jsxs("div", { className: "bd-substitute-target-row", children: [_jsx(DropdownField, { dataInput: `substituteTargetType-${target.key}`, className: "bd-substitute-target-type", ariaLabel: `${tr('botDefaults.substituteTargetType')} ${index + 1}`, value: target.idField, disabled: busy, options: substituteTargetIdFields.map(value => ({
|
|
1266
1480
|
value,
|
|
1267
1481
|
label: tr(`botDefaults.substituteTarget${value[0].toUpperCase()}${value.slice(1)}`),
|
|
1268
1482
|
})), onChange: idField => {
|
|
1269
1483
|
setTargetRows(rows => rows.map(row => row.key === target.key
|
|
1270
|
-
? { ...row, idField, idValue: row.persisted[idField] ?? '' }
|
|
1484
|
+
? { ...row, idField, idValue: row.persisted[idField] ?? '', resolution: undefined }
|
|
1271
1485
|
: row));
|
|
1272
1486
|
} }), _jsx("input", { className: "bd-substitute-target-id", type: "text", "data-input": `substituteTargetId-${target.key}`, "aria-label": `${tr('botDefaults.substituteTargetType')} ${index + 1}`, placeholder: tr('botDefaults.substituteTargetIdPlaceholder'), value: target.idValue, disabled: busy, onChange: event => {
|
|
1273
1487
|
const idValue = event.currentTarget.value;
|
|
1274
|
-
setTargetRows(rows => rows.map(row => row.key === target.key ? { ...row, idValue } : row));
|
|
1275
|
-
}
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
} }), _jsx("
|
|
1488
|
+
setTargetRows(rows => rows.map(row => row.key === target.key ? { ...row, idValue, resolution: undefined } : row));
|
|
1489
|
+
}, onBlur: () => {
|
|
1490
|
+
if (target.idValue.trim())
|
|
1491
|
+
void resolveTargetRow(target.key);
|
|
1492
|
+
} }), _jsx("div", { className: "bd-substitute-target-name", children: target.resolving ? (_jsx("span", { className: "bd-substitute-target-resolving", children: tr('botDefaults.substituteResolving') })) : target.resolution?.ok === true && (target.name || target.resolution.avatarUrl) ? (_jsxs(_Fragment, { children: [target.resolution.avatarUrl ? (_jsx(Html, { html: botAvatarHtml({ name: target.resolution.name, avatarUrl: target.resolution.avatarUrl, size: 'sm' }) })) : null, _jsx("span", { className: "bd-substitute-target-name-chip", "data-chip": `substituteTargetName-${target.key}`, "aria-label": `${tr('botDefaults.substituteTargetName')} ${index + 1}`, children: target.name })] })) : target.resolution?.ok === false ? (_jsx("span", { className: "bd-substitute-target-resolution-badge", children: substituteReasonText(target.resolution.reason) })) : (_jsx("input", { type: "text", "data-input": `substituteTargetName-${target.key}`, "aria-label": `${tr('botDefaults.substituteTargetName')} ${index + 1}`, placeholder: tr('botDefaults.substituteTargetNamePlaceholder'), value: target.name, disabled: busy, onChange: event => {
|
|
1493
|
+
const name = event.currentTarget.value;
|
|
1494
|
+
setTargetRows(rows => rows.map(row => row.key === target.key ? { ...row, name } : row));
|
|
1495
|
+
} })) }), _jsx("button", { type: "button", className: "bd-substitute-target-remove", "data-action": "remove-substitute-target", title: tr('botDefaults.substituteTargetRemove'), "aria-label": tr('botDefaults.substituteTargetRemove'), disabled: busy, onClick: () => {
|
|
1279
1496
|
setTargetRows(rows => {
|
|
1280
1497
|
const remaining = rows.filter(row => row.key !== target.key);
|
|
1281
1498
|
return remaining.length ? remaining : [makeTargetDraft()];
|