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.
Files changed (122) hide show
  1. package/dist/bot-registry.d.ts +31 -0
  2. package/dist/bot-registry.d.ts.map +1 -1
  3. package/dist/bot-registry.js +8 -0
  4. package/dist/bot-registry.js.map +1 -1
  5. package/dist/cli.d.ts.map +1 -1
  6. package/dist/cli.js +12 -6
  7. package/dist/cli.js.map +1 -1
  8. package/dist/core/ask-broker.d.ts +1 -1
  9. package/dist/core/ask-broker.d.ts.map +1 -1
  10. package/dist/core/ask-broker.js +2 -1
  11. package/dist/core/ask-broker.js.map +1 -1
  12. package/dist/core/ask-types.d.ts +5 -0
  13. package/dist/core/ask-types.d.ts.map +1 -1
  14. package/dist/core/ask-types.js.map +1 -1
  15. package/dist/core/dashboard-ipc-server.d.ts +10 -0
  16. package/dist/core/dashboard-ipc-server.d.ts.map +1 -1
  17. package/dist/core/dashboard-ipc-server.js +73 -2
  18. package/dist/core/dashboard-ipc-server.js.map +1 -1
  19. package/dist/core/reply-target.d.ts +30 -1
  20. package/dist/core/reply-target.d.ts.map +1 -1
  21. package/dist/core/reply-target.js +70 -7
  22. package/dist/core/reply-target.js.map +1 -1
  23. package/dist/core/types.d.ts +8 -0
  24. package/dist/core/types.d.ts.map +1 -1
  25. package/dist/core/types.js.map +1 -1
  26. package/dist/core/worker-pool.d.ts +18 -1
  27. package/dist/core/worker-pool.d.ts.map +1 -1
  28. package/dist/core/worker-pool.js +67 -11
  29. package/dist/core/worker-pool.js.map +1 -1
  30. package/dist/daemon.d.ts +1 -1
  31. package/dist/daemon.d.ts.map +1 -1
  32. package/dist/daemon.js +85 -16
  33. package/dist/daemon.js.map +1 -1
  34. package/dist/dashboard/web/bot-defaults-page.d.ts +8 -1
  35. package/dist/dashboard/web/bot-defaults-page.d.ts.map +1 -1
  36. package/dist/dashboard/web/bot-defaults-page.js +238 -21
  37. package/dist/dashboard/web/bot-defaults-page.js.map +1 -1
  38. package/dist/dashboard/web/bot-defaults.d.ts +23 -0
  39. package/dist/dashboard/web/bot-defaults.d.ts.map +1 -1
  40. package/dist/dashboard/web/bot-defaults.js +17 -0
  41. package/dist/dashboard/web/bot-defaults.js.map +1 -1
  42. package/dist/dashboard/web/i18n.d.ts.map +1 -1
  43. package/dist/dashboard/web/i18n.js +54 -2
  44. package/dist/dashboard/web/i18n.js.map +1 -1
  45. package/dist/dashboard/web/ui.d.ts +4 -0
  46. package/dist/dashboard/web/ui.d.ts.map +1 -1
  47. package/dist/dashboard/web/ui.js +11 -0
  48. package/dist/dashboard/web/ui.js.map +1 -1
  49. package/dist/dashboard-web/app.js +1 -1
  50. package/dist/dashboard-web/chunks/bot-defaults-page-SRCBF7Z3.js +5 -0
  51. package/dist/dashboard-web/chunks/{chunk-NYZHARIU.js → chunk-3H6MVXXU.js} +8 -6
  52. package/dist/dashboard-web/chunks/{chunk-YBOHL7T7.js → chunk-77KD4TK2.js} +1 -1
  53. package/dist/dashboard-web/chunks/{chunk-CMDPZOPK.js → chunk-D2I5F4MY.js} +1 -1
  54. package/dist/dashboard-web/chunks/{chunk-YWCPDL4S.js → chunk-NGRUZCXP.js} +1 -1
  55. package/dist/dashboard-web/chunks/{connectors-page-DAMP7TTV.js → connectors-page-RM3BJVMV.js} +1 -1
  56. package/dist/dashboard-web/chunks/{groups-page-BPFA356N.js → groups-page-3D4YSYLA.js} +1 -1
  57. package/dist/dashboard-web/chunks/{insights-page-SHOPHTWY.js → insights-page-CRFJKA54.js} +1 -1
  58. package/dist/dashboard-web/chunks/{monitor-room-PXPJGZCN.js → monitor-room-HLK3XDT2.js} +1 -1
  59. package/dist/dashboard-web/chunks/{monitoring-page-4DF54M5Y.js → monitoring-page-E3SNZFFN.js} +1 -1
  60. package/dist/dashboard-web/chunks/{office-page-6OHU6KMZ.js → office-page-MKQ2TENK.js} +1 -1
  61. package/dist/dashboard-web/chunks/overview-page-ICFKKRLV.js +1 -0
  62. package/dist/dashboard-web/chunks/{roles-page-JYTBYKEF.js → roles-page-2DFJ7YRU.js} +1 -1
  63. package/dist/dashboard-web/chunks/{schedules-page-WJ72T7KV.js → schedules-page-S4WDIXH5.js} +1 -1
  64. package/dist/dashboard-web/chunks/{sessions-page-QC56OIAI.js → sessions-page-4XWGUGB5.js} +1 -1
  65. package/dist/dashboard-web/chunks/{settings-page-TCKWEXTN.js → settings-page-OBTE4LYP.js} +1 -1
  66. package/dist/dashboard-web/chunks/{skills-page-RJJNIECW.js → skills-page-CTRQJR47.js} +1 -1
  67. package/dist/dashboard-web/chunks/{team-federation-page-ZMDX3E7K.js → team-federation-page-GICLDKJP.js} +1 -1
  68. package/dist/dashboard-web/chunks/{v3-page-VLLAUQ33.js → v3-page-DI55IRL5.js} +1 -1
  69. package/dist/dashboard-web/chunks/{whiteboards-page-NAO53CKM.js → whiteboards-page-7KFWMILV.js} +1 -1
  70. package/dist/dashboard-web/style.css +113 -1
  71. package/dist/dashboard.js +48 -0
  72. package/dist/dashboard.js.map +1 -1
  73. package/dist/i18n/en.d.ts.map +1 -1
  74. package/dist/i18n/en.js +3 -2
  75. package/dist/i18n/en.js.map +1 -1
  76. package/dist/i18n/zh.d.ts.map +1 -1
  77. package/dist/i18n/zh.js +3 -2
  78. package/dist/i18n/zh.js.map +1 -1
  79. package/dist/im/lark/card-handler.d.ts.map +1 -1
  80. package/dist/im/lark/card-handler.js +9 -2
  81. package/dist/im/lark/card-handler.js.map +1 -1
  82. package/dist/im/lark/client.d.ts +30 -0
  83. package/dist/im/lark/client.d.ts.map +1 -1
  84. package/dist/im/lark/client.js +63 -13
  85. package/dist/im/lark/client.js.map +1 -1
  86. package/dist/im/lark/event-dispatcher.d.ts +7 -3
  87. package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
  88. package/dist/im/lark/event-dispatcher.js +66 -15
  89. package/dist/im/lark/event-dispatcher.js.map +1 -1
  90. package/dist/im/lark/substitute-command.d.ts.map +1 -1
  91. package/dist/im/lark/substitute-command.js +10 -1
  92. package/dist/im/lark/substitute-command.js.map +1 -1
  93. package/dist/services/oncall-store.d.ts +8 -0
  94. package/dist/services/oncall-store.d.ts.map +1 -1
  95. package/dist/services/oncall-store.js +48 -2
  96. package/dist/services/oncall-store.js.map +1 -1
  97. package/dist/services/open-platform-rename.d.ts +31 -3
  98. package/dist/services/open-platform-rename.d.ts.map +1 -1
  99. package/dist/services/open-platform-rename.js +262 -48
  100. package/dist/services/open-platform-rename.js.map +1 -1
  101. package/dist/services/substitute-mode-normalize.d.ts.map +1 -1
  102. package/dist/services/substitute-mode-normalize.js +17 -1
  103. package/dist/services/substitute-mode-normalize.js.map +1 -1
  104. package/dist/services/substitute-mode-store.d.ts +24 -5
  105. package/dist/services/substitute-mode-store.d.ts.map +1 -1
  106. package/dist/services/substitute-mode-store.js +36 -16
  107. package/dist/services/substitute-mode-store.js.map +1 -1
  108. package/dist/skills/definitions.d.ts +1 -1
  109. package/dist/skills/definitions.d.ts.map +1 -1
  110. package/dist/skills/definitions.js +15 -3
  111. package/dist/skills/definitions.js.map +1 -1
  112. package/dist/types.d.ts +22 -0
  113. package/dist/types.d.ts.map +1 -1
  114. package/dist/utils/bot-routing.d.ts +1 -0
  115. package/dist/utils/bot-routing.d.ts.map +1 -1
  116. package/dist/utils/bot-routing.js +1 -1
  117. package/dist/utils/bot-routing.js.map +1 -1
  118. package/dist/workflows/migration/v2-run-archive-schema.d.ts +2 -2
  119. package/dist/workflows/v3/run-envelope.d.ts +16 -16
  120. package/package.json +1 -1
  121. package/dist/dashboard-web/chunks/bot-defaults-page-RYBL4ZYE.js +0 -4
  122. 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,EAUL,KAAK,cAAc,EAEnB,KAAK,mBAAmB,EACxB,KAAK,eAAe,EACrB,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;AASzE,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;CAC3C,CAAC;AAQF;;;;;;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;AAsiBD,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;AA6+BD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,WAAW,GAAG,YAAY,CAErE"}
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("div", { className: "bd-profile-avatar", children: _jsx(Html, { html: botAvatarHtml({ name, larkAppId: bot.larkAppId, dot: 'ok' }) }) }), _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 }) })] })] })] }));
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 !== true)
1204
- return row;
1205
- const persisted = { ...row.persisted };
1206
- if (entry.openId)
1207
- persisted.openId = entry.openId;
1208
- if (row.idField === 'email')
1209
- persisted.email = input;
1210
- if (entry.name)
1211
- persisted.name = entry.name;
1212
- return { ...row, name: entry.name ?? row.name, persisted };
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
- 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("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) })] }) }), _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 => ({
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
- } }), _jsx("input", { className: "bd-substitute-target-name", 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 => {
1276
- const name = event.currentTarget.value;
1277
- setTargetRows(rows => rows.map(row => row.key === target.key ? { ...row, name } : row));
1278
- } }), _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: () => {
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()];