@botharness/ui 0.1.0-alpha.1 → 1.0.1

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/lib/client.js CHANGED
@@ -214,13 +214,13 @@ window.__ModuleLoader__.load({
214
214
  0
215
215
  ].map((bit) => n >> bit & 255);
216
216
  }
217
- function hex(values) {
217
+ function hex$1(values) {
218
218
  return `#${values.map((v) => Math.min(255, Math.max(0, Math.round(v))).toString(16).padStart(2, "0")).join("")}`;
219
219
  }
220
- const shade = (color, k) => hex(channels$1(color).map((v) => v * k));
220
+ const shade = (color, k) => hex$1(channels$1(color).map((v) => v * k));
221
221
  const mix$1 = (base, tint, k) => {
222
222
  const t = channels$1(tint);
223
- return hex(channels$1(base).map((v, i) => v * (1 - k) + t[i] * k));
223
+ return hex$1(channels$1(base).map((v, i) => v * (1 - k) + t[i] * k));
224
224
  };
225
225
  function paint$1(grid, test, color) {
226
226
  for (let y = 0; y < N$2; y++) for (let x = 0; x < N$2; x++) if (test(x, y)) grid[y][x] = color;
@@ -844,7 +844,7 @@ window.__ModuleLoader__.load({
844
844
  const k = (n + hue * 2) % 12;
845
845
  return l - s * Math.min(l, .12) * Math.max(-1, Math.min(k - 3, 9 - k, 1));
846
846
  };
847
- return hex([
847
+ return hex$1([
848
848
  f(0),
849
849
  f(8),
850
850
  f(4)
@@ -2793,6 +2793,138 @@ window.__ModuleLoader__.load({
2793
2793
  };
2794
2794
  }
2795
2795
  //#endregion
2796
+ //#region packages/core/src/marketplace/altcha.ts
2797
+ const ALTCHA_ALGORITHM = "SHA-256";
2798
+ const encoder = new TextEncoder();
2799
+ function hex(buffer) {
2800
+ return [...new Uint8Array(buffer)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
2801
+ }
2802
+ async function sha256Hex(text) {
2803
+ return hex(await crypto.subtle.digest("SHA-256", encoder.encode(text)));
2804
+ }
2805
+ function record$1(value) {
2806
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
2807
+ }
2808
+ function parseChallenge(value) {
2809
+ const source = record$1(value);
2810
+ if (source === void 0 || source["algorithm"] !== "SHA-256") return void 0;
2811
+ const { challenge, maxnumber, salt, signature } = source;
2812
+ if (typeof challenge !== "string" || typeof salt !== "string" || typeof signature !== "string") return;
2813
+ if (typeof maxnumber !== "number" || !Number.isSafeInteger(maxnumber) || maxnumber < 0) return;
2814
+ return {
2815
+ algorithm: ALTCHA_ALGORITHM,
2816
+ challenge,
2817
+ maxnumber,
2818
+ salt,
2819
+ signature
2820
+ };
2821
+ }
2822
+ function encodeSolution(solution) {
2823
+ return btoa(JSON.stringify(solution));
2824
+ }
2825
+ async function solveChallenge(challenge, options = {}) {
2826
+ const batch = options.batch ?? 2e3;
2827
+ for (let start = 0; start <= challenge.maxnumber; start += batch) {
2828
+ if (options.signal?.aborted === true) return void 0;
2829
+ const numbers = Array.from({ length: Math.min(batch, challenge.maxnumber - start + 1) }, (_, index) => start + index);
2830
+ const number = numbers[(await Promise.all(numbers.map((number) => sha256Hex(`${challenge.salt}${number}`)))).indexOf(challenge.challenge)];
2831
+ if (number !== void 0) return encodeSolution({
2832
+ algorithm: ALTCHA_ALGORITHM,
2833
+ challenge: challenge.challenge,
2834
+ number,
2835
+ salt: challenge.salt,
2836
+ signature: challenge.signature
2837
+ });
2838
+ await options.yieldEvery?.();
2839
+ }
2840
+ }
2841
+ //#endregion
2842
+ //#region packages/core/src/marketplace/client.ts
2843
+ function record(value) {
2844
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
2845
+ }
2846
+ function parseEntry(value) {
2847
+ const source = record(value);
2848
+ if (source === void 0) return void 0;
2849
+ if (![
2850
+ "id",
2851
+ "owner",
2852
+ "name",
2853
+ "fullName",
2854
+ "pushedAt",
2855
+ "htmlUrl",
2856
+ "cloneUrl",
2857
+ "defaultBranch"
2858
+ ].every((key) => typeof source[key] === "string")) return void 0;
2859
+ const description = source["description"];
2860
+ const stars = source["stars"];
2861
+ const topics = source["topics"];
2862
+ const head = record(source["headCommit"]);
2863
+ if (description !== null && typeof description !== "string") return void 0;
2864
+ if (typeof stars !== "number" || !Array.isArray(topics)) return void 0;
2865
+ const displayName = source["displayName"];
2866
+ const roles = source["roles"];
2867
+ return {
2868
+ id: source["id"],
2869
+ owner: source["owner"],
2870
+ name: source["name"],
2871
+ fullName: source["fullName"],
2872
+ displayName: typeof displayName === "string" && displayName.length > 0 ? displayName : null,
2873
+ roles: Array.isArray(roles) ? roles.filter((role) => typeof role === "string") : [],
2874
+ description,
2875
+ topics: topics.filter((topic) => typeof topic === "string"),
2876
+ stars,
2877
+ pushedAt: source["pushedAt"],
2878
+ htmlUrl: source["htmlUrl"],
2879
+ cloneUrl: source["cloneUrl"],
2880
+ defaultBranch: source["defaultBranch"],
2881
+ headCommit: head !== void 0 && typeof head["sha"] === "string" && typeof head["committedAt"] === "string" ? {
2882
+ sha: head["sha"],
2883
+ committedAt: head["committedAt"]
2884
+ } : null
2885
+ };
2886
+ }
2887
+ function parseMarketplacePage(value) {
2888
+ const source = record(value);
2889
+ if (source === void 0 || !Array.isArray(source["bots"])) return void 0;
2890
+ const bots = source["bots"].map(parseEntry);
2891
+ if (!bots.every((bot) => bot !== void 0)) return void 0;
2892
+ const nextCursor = source["nextCursor"];
2893
+ return typeof nextCursor === "string" ? {
2894
+ bots,
2895
+ nextCursor
2896
+ } : { bots };
2897
+ }
2898
+ function parseMarketplaceDetail(value) {
2899
+ const source = record(value);
2900
+ const bot = parseEntry(source?.["bot"]);
2901
+ const readme = source?.["readme"];
2902
+ const commitSha = source?.["commitSha"];
2903
+ if (bot === void 0 || readme !== null && typeof readme !== "string") return void 0;
2904
+ if (commitSha !== null && typeof commitSha !== "string") return void 0;
2905
+ return {
2906
+ bot,
2907
+ readme,
2908
+ commitSha
2909
+ };
2910
+ }
2911
+ function parseMarketplaceTopics(value) {
2912
+ const topics = record(value)?.["topics"];
2913
+ if (!Array.isArray(topics)) return void 0;
2914
+ const parsed = topics.map((item) => {
2915
+ const source = record(item);
2916
+ return typeof source?.["topic"] === "string" && typeof source["count"] === "number" ? {
2917
+ topic: source["topic"],
2918
+ count: source["count"]
2919
+ } : void 0;
2920
+ });
2921
+ return parsed.every((item) => item !== void 0) ? parsed : void 0;
2922
+ }
2923
+ function parseMarketplaceSubmission(value) {
2924
+ const bot = parseEntry(record(value)?.["bot"]);
2925
+ return bot === void 0 ? void 0 : { bot };
2926
+ }
2927
+ //#endregion
2796
2928
  //#region packages/client/src/client/roster.ts
2797
2929
  function emptyRosterSnapshot() {
2798
2930
  return {
@@ -3531,6 +3663,41 @@ window.__ModuleLoader__.load({
3531
3663
  if (bot === void 0) throw new Error("invalid create response");
3532
3664
  return bot;
3533
3665
  }
3666
+ async function loadMarketplacePage(call, query = {}) {
3667
+ const page = parseMarketplacePage(await unwrap(call, "marketplaceList", { query }));
3668
+ if (page === void 0) throw new Error("invalid marketplaceList response");
3669
+ return page;
3670
+ }
3671
+ async function loadMarketplaceDetail(call, id) {
3672
+ const detail = parseMarketplaceDetail(await unwrap(call, "marketplaceDetail", { id }));
3673
+ if (detail === void 0) throw new Error("invalid marketplaceDetail response");
3674
+ return detail;
3675
+ }
3676
+ async function loadMarketplaceTopics(call) {
3677
+ const topics = parseMarketplaceTopics({ topics: await unwrap(call, "marketplaceTopics", {}) });
3678
+ if (topics === void 0) throw new Error("invalid marketplaceTopics response");
3679
+ return topics;
3680
+ }
3681
+ async function loadMarketplaceChallenge(call) {
3682
+ const challenge = parseChallenge(await unwrap(call, "marketplaceChallenge", {}));
3683
+ if (challenge === void 0) throw new Error("invalid marketplaceChallenge response");
3684
+ return challenge;
3685
+ }
3686
+ async function submitMarketplaceRepository(call, url, altcha) {
3687
+ const result = parseMarketplaceSubmission(await unwrap(call, "marketplaceSubmit", {
3688
+ url,
3689
+ altcha
3690
+ }));
3691
+ if (result === void 0) throw new Error("invalid marketplaceSubmit response");
3692
+ return result.bot;
3693
+ }
3694
+ async function reportMarketplaceBot(call, id, altcha, reason) {
3695
+ await unwrap(call, "marketplaceReport", {
3696
+ id,
3697
+ altcha,
3698
+ ...reason === void 0 ? {} : { reason }
3699
+ });
3700
+ }
3534
3701
  async function loadChannels(call, signal) {
3535
3702
  return parseChannelRecords(await unwrap(call, "channels", {}, signal));
3536
3703
  }
@@ -4499,7 +4666,8 @@ window.__ModuleLoader__.load({
4499
4666
  ]) || source?.["localChannelId"] !== void 0 && (typeof source["localChannelId"] !== "string" || source["localChannelId"].length === 0) || !Number.isInteger(source?.["grantRevision"]) || Number(source?.["grantRevision"]) < 1 || event?.["version"] !== 1 || ![
4500
4667
  "feishu",
4501
4668
  "slack",
4502
- "discord"
4669
+ "discord",
4670
+ "weixin"
4503
4671
  ].includes(String(event["channel"])) || !strings(event, [
4504
4672
  "botId",
4505
4673
  "fingerprint",
@@ -4519,7 +4687,7 @@ window.__ModuleLoader__.load({
4519
4687
  "messageId",
4520
4688
  "resourceKey",
4521
4689
  "name"
4522
- ]) && (asRecord$1(file)?.["sizeBytes"] === void 0 || Number.isSafeInteger(asRecord$1(file)?.["sizeBytes"]) && Number(asRecord$1(file)?.["sizeBytes"]) > 0 && Number(asRecord$1(file)?.["sizeBytes"]) <= 26214400) && (asRecord$1(file)?.["mediaType"] === void 0 || typeof asRecord$1(file)?.["mediaType"] === "string"))) || replay?.["kind"] !== "provider-redelivery" || replay["resumeCursor"] !== false || replay["gapPossible"] !== true) throw new BridgeCallError("invalid-response", "Invalid source");
4690
+ ]) && (asRecord$1(file)?.["sizeBytes"] === void 0 || Number.isSafeInteger(asRecord$1(file)?.["sizeBytes"]) && Number(asRecord$1(file)?.["sizeBytes"]) > 0 && Number(asRecord$1(file)?.["sizeBytes"]) <= Number.MAX_SAFE_INTEGER) && (asRecord$1(file)?.["mediaType"] === void 0 || typeof asRecord$1(file)?.["mediaType"] === "string"))) || replay?.["kind"] !== "provider-redelivery" || replay["resumeCursor"] !== false || replay["gapPossible"] !== true) throw new BridgeCallError("invalid-response", "Invalid source");
4523
4691
  if (source?.["receptionPaths"] !== void 0 && (!Array.isArray(source["receptionPaths"]) || !source["receptionPaths"].every((value) => {
4524
4692
  const path = asRecord$1(value);
4525
4693
  return path && strings(path, [
@@ -6342,6 +6510,12 @@ window.__ModuleLoader__.load({
6342
6510
  profileActivity: (channelId) => loadProfileActivity(call, channelId),
6343
6511
  overviewMemory: (after) => loadOverviewMemory(call, after),
6344
6512
  overviewUsage: (period, after) => loadOverviewUsage(call, period, after),
6513
+ marketplaceList: (query) => loadMarketplacePage(call, query),
6514
+ marketplaceChallenge: () => loadMarketplaceChallenge(call),
6515
+ marketplaceSubmit: (url, altcha) => submitMarketplaceRepository(call, url, altcha),
6516
+ marketplaceReport: (id, altcha, reason) => reportMarketplaceBot(call, id, altcha, reason),
6517
+ marketplaceTopics: () => loadMarketplaceTopics(call),
6518
+ marketplaceDetail: (id) => loadMarketplaceDetail(call, id),
6345
6519
  profileUsage: (channelId, filter) => loadProfileUsage(call, channelId, filter),
6346
6520
  channelActivityToday: () => loadChannelActivityToday(call),
6347
6521
  groupProfileActivity: (channelId) => loadGroupProfileActivity(call, channelId),
@@ -9919,6 +10093,74 @@ window.__ModuleLoader__.load({
9919
10093
  "roster.menu.createBot": "创建 PersonaBot",
9920
10094
  "roster.menu.createChannel": "创建频道",
9921
10095
  "roster.menu.createSection": "创建频道分组",
10096
+ "roster.menu.marketplace": "Bot 市场",
10097
+ "market.title": "Bot 市场",
10098
+ "market.description": "浏览公开 GitHub 仓库分享的 Bot,安装后会成为一个新的 PersonaBot。",
10099
+ "market.submit.label": "GitHub 仓库地址",
10100
+ "market.submit.placeholder": "贴入 https://github.com/owner/repo 立即收录",
10101
+ "market.submit.hint": "仓库需公开,并在 GitHub 上添加 botharness-bot 话题。",
10102
+ "market.submit.action": "收录",
10103
+ "market.submit.busy": "收录中…",
10104
+ "market.submit.done": "已收录 {name}",
10105
+ "market.loading": "正在加载 Bot 市场…",
10106
+ "market.loadMore": "加载更多",
10107
+ "market.loadingMore": "加载中…",
10108
+ "market.retry": "重试",
10109
+ "market.empty": "还没有收录的 Bot。贴入一个仓库地址试试。",
10110
+ "market.search.label": "搜索 Bot",
10111
+ "market.search.placeholder": "搜索名称、描述、话题或 README",
10112
+ "market.search.relevance": "搜索结果按相关度排序,最多显示前 200 个。",
10113
+ "market.search.empty": "没有符合条件的 Bot。换个关键词或话题试试。",
10114
+ "market.sort.label": "排序",
10115
+ "market.sort.updated": "最近更新",
10116
+ "market.sort.stars": "最多 Star",
10117
+ "market.topics.label": "按话题筛选",
10118
+ "market.topics.all": "全部",
10119
+ "market.detail.open": "查看 {name} 详情",
10120
+ "market.detail.github": "在 GitHub 查看",
10121
+ "market.detail.loading": "正在加载 README…",
10122
+ "market.detail.error": "README 加载失败:{error}",
10123
+ "market.detail.noReadme": "这个仓库没有 README。",
10124
+ "market.error.botNotFound": "这个 Bot 已不在市场中。",
10125
+ "market.challenge.verifying": "正在验证…",
10126
+ "market.report.open": "举报",
10127
+ "market.report.title": "举报这个 Bot",
10128
+ "market.report.hint": "不需要账号。多个不同来源举报后,这个 Bot 会先从市场中隐藏,等待管理员复核。",
10129
+ "market.report.reason": "理由(可选)",
10130
+ "market.report.placeholder": "例如:冒充他人、垃圾内容、恶意指令",
10131
+ "market.report.send": "提交举报",
10132
+ "market.report.sending": "提交中…",
10133
+ "market.report.doneTitle": "已收到举报",
10134
+ "market.report.done": "谢谢。达到一定数量的独立举报后,这个 Bot 会先被隐藏,等待管理员复核。",
10135
+ "market.error.challengeUnavailable": "人机验证暂时不可用,请稍后重试。",
10136
+ "market.error.challenge": "人机验证没有通过或已过期,请重试。",
10137
+ "market.error.rateLimited": "操作太频繁,请稍后再试。",
10138
+ "market.error.repositoryRateLimited": "这个仓库刚刚收录过,5 分钟后才能再次收录。",
10139
+ "market.error.invalidReport": "举报理由最多 500 字。",
10140
+ "market.stars": "★ {count}",
10141
+ "market.updated": "更新于 {date}",
10142
+ "market.install": "安装",
10143
+ "market.install.failed": "安装失败:{error}",
10144
+ "market.back": "返回",
10145
+ "market.confirm.title": "安装 {name}",
10146
+ "market.confirm.source": "来源",
10147
+ "market.confirm.commit": "最新提交",
10148
+ "market.confirm.commitValue": "{sha} · {date}",
10149
+ "market.confirm.noCommit": "未知",
10150
+ "market.confirm.riskTitle": "这是第三方仓库",
10151
+ "market.confirm.risk": "仓库文件会成为新 Bot 的 Memory,可能包含有害内容或会被 Bot 执行的指令。请只安装你信任的仓库。",
10152
+ "market.confirm.newer": "安装时会克隆当时的最新提交,可能比这里显示的更新。",
10153
+ "market.confirm.install": "确认安装",
10154
+ "market.confirm.installing": "安装中…",
10155
+ "market.error.load": "加载失败:{error}",
10156
+ "market.error.unavailable": "Bot 市场暂时无法访问,请稍后重试。",
10157
+ "market.error.invalidUrl": "请贴入 GitHub 仓库地址,例如 https://github.com/owner/repo。",
10158
+ "market.error.notFound": "找不到这个仓库,请确认地址正确且仓库是公开的。",
10159
+ "market.error.private": "这个仓库是私有的,公开后才能收录。",
10160
+ "market.error.archived": "这个仓库已归档,无法收录。",
10161
+ "market.error.missingTopic": "仓库还没有 botharness-bot 话题。在 GitHub 仓库的 About 中添加后再试。",
10162
+ "market.error.blocked": "这个仓库已被屏蔽或因举报隐藏,无法收录。",
10163
+ "market.error.upstream": "GitHub 暂时无法访问,请稍后重试。",
9922
10164
  "section.rename.title": "重命名频道分组",
9923
10165
  "section.delete.title": "删除频道分组",
9924
10166
  "section.delete.description": "将把“{name}”从名册中移除。其中的频道会移出分组、变为未分组频道,不会被删除。",
@@ -9942,6 +10184,37 @@ window.__ModuleLoader__.load({
9942
10184
  "bot.create.importing": "正在克隆仓库…",
9943
10185
  "bot.create.source.label": "记忆来源",
9944
10186
  "bot.create.source.empty": "从空白创建",
10187
+ "bot.create.persona.preset": "人格起点",
10188
+ "bot.create.persona.blank": "空白",
10189
+ "bot.create.persona.colleague": "同事",
10190
+ "bot.create.persona.roleplay": "角色扮演",
10191
+ "bot.create.persona.label": "人格(可选)",
10192
+ "bot.create.persona.hint": "只是起始文本,可自由修改;不会改变工具权限或运行能力。",
10193
+ "bot.create.persona.placeholder": "描述这个 Bot 的身份、表达方式和边界,也可以留空。",
10194
+ "bot.create.persona.colleague.seed": `# 身份
10195
+ 我叫 [名字],是一名 [岗位]。我的背景、性格和价值观:[填写]。
10196
+
10197
+ # 职责
10198
+ 我主要负责 [工作范围]。擅长:[填写];需要他人协助的领域:[填写]。
10199
+
10200
+ # 协作方式
10201
+ 我通常以 [语气和沟通方式] 与 Human 和同事协作。遇到不确定的信息会说明;需要澄清或授权时会先询问。
10202
+
10203
+ # 边界
10204
+ 我的承诺和需要避免的行为:[填写]。
10205
+ `,
10206
+ "bot.create.persona.roleplay.seed": `# 角色与世界
10207
+ 我叫 [名字],是 [种族 / 身份],生活在 [时代、世界和背景]。
10208
+
10209
+ # 个性与动机
10210
+ 我的经历、信念、目标和关系:[填写]。
10211
+
10212
+ # 声音与表现
10213
+ 我的语气、用词和行为习惯:[填写]。标志性的表达:[填写]。
10214
+
10215
+ # 互动边界
10216
+ 与 Human 的关系、需要遵守的设定和希望避开的内容:[填写]。
10217
+ `,
9945
10218
  "bot.create.source.git": "从 Git 仓库导入",
9946
10219
  "bot.create.gitUrl.label": "Git 仓库地址",
9947
10220
  "bot.create.gitUrl.placeholder": "https://github.com/owner/repo.git",
@@ -10041,6 +10314,12 @@ window.__ModuleLoader__.load({
10041
10314
  "im.origin": "外部来源",
10042
10315
  "im.senderLabel": "发送人 ID",
10043
10316
  "im.platformLabel": "平台",
10317
+ "im.platform.weixin": "微信",
10318
+ "im.receiveHintDM": "只接收扫码绑定者的微信私聊文本,进入此 Bot 的 Inbox,不占用本地 DM 历史。",
10319
+ "im.receptionDM": "微信私聊接收已连接",
10320
+ "im.receiveEnableDM": "接收微信私聊",
10321
+ "im.receiveDisableDM": "关闭微信私聊收件",
10322
+ "im.receptionDMOff": "微信私聊收件已关闭",
10044
10323
  "im.platform.feishu": "Lark/飞书",
10045
10324
  "im.conversationLabel": "外部频道",
10046
10325
  "im.conversationId": "外部频道 ID",
@@ -10183,6 +10462,9 @@ window.__ModuleLoader__.load({
10183
10462
  "im.originDetails": "来源详情",
10184
10463
  "im.threadLabel": "话题",
10185
10464
  "im.groupLabel": "群聊",
10465
+ "im.dmLabel": "私聊",
10466
+ "im.weixinUser": "微信用户",
10467
+ "im.weixinReplyOnly": "支持回复已收取的微信私聊文字和文件;主动发送尚未开放。",
10186
10468
  "im.receivedAs": "接收身份:{name}",
10187
10469
  "im.readDetails": "读取记录 · {count} 次",
10188
10470
  "im.sourceError": "无法读取已保存的外部消息",
@@ -11385,6 +11667,74 @@ window.__ModuleLoader__.load({
11385
11667
  "roster.menu.createBot": "Create PersonaBot",
11386
11668
  "roster.menu.createChannel": "Create channel",
11387
11669
  "roster.menu.createSection": "Create channel section",
11670
+ "roster.menu.marketplace": "Bot Marketplace",
11671
+ "market.title": "Bot Marketplace",
11672
+ "market.description": "Browse Bots shared as public GitHub repositories. Installing one creates a new PersonaBot.",
11673
+ "market.submit.label": "GitHub repository URL",
11674
+ "market.submit.placeholder": "Paste https://github.com/owner/repo to list it now",
11675
+ "market.submit.hint": "The repository must be public and carry the botharness-bot topic on GitHub.",
11676
+ "market.submit.action": "List",
11677
+ "market.submit.busy": "Listing…",
11678
+ "market.submit.done": "Listed {name}",
11679
+ "market.loading": "Loading Bot Marketplace…",
11680
+ "market.loadMore": "Load more",
11681
+ "market.loadingMore": "Loading…",
11682
+ "market.retry": "Retry",
11683
+ "market.empty": "No Bots listed yet. Paste a repository URL to add one.",
11684
+ "market.search.label": "Search Bots",
11685
+ "market.search.placeholder": "Search names, descriptions, topics or READMEs",
11686
+ "market.search.relevance": "Results are ordered by relevance, up to the first 200.",
11687
+ "market.search.empty": "No Bots match. Try another keyword or topic.",
11688
+ "market.sort.label": "Sort",
11689
+ "market.sort.updated": "Recently updated",
11690
+ "market.sort.stars": "Most stars",
11691
+ "market.topics.label": "Filter by topic",
11692
+ "market.topics.all": "All",
11693
+ "market.detail.open": "View {name} details",
11694
+ "market.detail.github": "View on GitHub",
11695
+ "market.detail.loading": "Loading README…",
11696
+ "market.detail.error": "Could not load README: {error}",
11697
+ "market.detail.noReadme": "This repository has no README.",
11698
+ "market.error.botNotFound": "This Bot is no longer in the Marketplace.",
11699
+ "market.challenge.verifying": "Verifying…",
11700
+ "market.report.open": "Report",
11701
+ "market.report.title": "Report this Bot",
11702
+ "market.report.hint": "No account needed. After reports from several different sources, this Bot is hidden from the Marketplace until an administrator reviews it.",
11703
+ "market.report.reason": "Reason (optional)",
11704
+ "market.report.placeholder": "For example: impersonation, spam, harmful instructions",
11705
+ "market.report.send": "Send report",
11706
+ "market.report.sending": "Sending…",
11707
+ "market.report.doneTitle": "Report received",
11708
+ "market.report.done": "Thank you. After enough independent reports, this Bot is hidden until an administrator reviews it.",
11709
+ "market.error.challengeUnavailable": "Verification is unavailable right now. Try again later.",
11710
+ "market.error.challenge": "Verification failed or expired. Try again.",
11711
+ "market.error.rateLimited": "Too many requests. Try again later.",
11712
+ "market.error.repositoryRateLimited": "This repository was just listed. Try again in 5 minutes.",
11713
+ "market.error.invalidReport": "The reason can be at most 500 characters.",
11714
+ "market.stars": "★ {count}",
11715
+ "market.updated": "Updated {date}",
11716
+ "market.install": "Install",
11717
+ "market.install.failed": "Install failed: {error}",
11718
+ "market.back": "Back",
11719
+ "market.confirm.title": "Install {name}",
11720
+ "market.confirm.source": "Source",
11721
+ "market.confirm.commit": "Latest commit",
11722
+ "market.confirm.commitValue": "{sha} · {date}",
11723
+ "market.confirm.noCommit": "Unknown",
11724
+ "market.confirm.riskTitle": "This is a third-party repository",
11725
+ "market.confirm.risk": "Its files become the new Bot's Memory and may contain harmful content or instructions the Bot will follow. Install only repositories you trust.",
11726
+ "market.confirm.newer": "Install clones the latest commit at that moment, which may be newer than shown here.",
11727
+ "market.confirm.install": "Install",
11728
+ "market.confirm.installing": "Installing…",
11729
+ "market.error.load": "Could not load: {error}",
11730
+ "market.error.unavailable": "Bot Marketplace is unreachable right now. Try again later.",
11731
+ "market.error.invalidUrl": "Paste a GitHub repository URL, such as https://github.com/owner/repo.",
11732
+ "market.error.notFound": "Repository not found. Check the URL and that the repository is public.",
11733
+ "market.error.private": "This repository is private. Make it public to list it.",
11734
+ "market.error.archived": "This repository is archived and cannot be listed.",
11735
+ "market.error.missingTopic": "The repository does not have the botharness-bot topic yet. Add it under About on GitHub and retry.",
11736
+ "market.error.blocked": "This repository is blocked or hidden after reports.",
11737
+ "market.error.upstream": "GitHub is unreachable right now. Try again later.",
11388
11738
  "section.rename.title": "Rename channel section",
11389
11739
  "section.delete.title": "Delete channel section",
11390
11740
  "section.delete.description": "Removes “{name}” from the roster. Its channels leave the section and become unsectioned; they are not deleted.",
@@ -11408,6 +11758,37 @@ window.__ModuleLoader__.load({
11408
11758
  "bot.create.importing": "Cloning repository…",
11409
11759
  "bot.create.source.label": "Memory source",
11410
11760
  "bot.create.source.empty": "Start empty",
11761
+ "bot.create.persona.preset": "Persona starting point",
11762
+ "bot.create.persona.blank": "Blank",
11763
+ "bot.create.persona.colleague": "Colleague",
11764
+ "bot.create.persona.roleplay": "Roleplay",
11765
+ "bot.create.persona.label": "Persona (optional)",
11766
+ "bot.create.persona.hint": "Starting text only, freely editable; tools, permissions and runtime capabilities stay the same.",
11767
+ "bot.create.persona.placeholder": "Describe this Bot’s identity, voice and boundaries, or leave it blank.",
11768
+ "bot.create.persona.colleague.seed": `# Identity
11769
+ My name is [name] and I am a [role]. My background, personality and values: [fill in].
11770
+
11771
+ # Responsibilities
11772
+ I am responsible for [scope]. My strengths: [fill in]; areas where I need help: [fill in].
11773
+
11774
+ # Collaboration
11775
+ I work with the Human and colleagues using [tone and communication style]. I explain uncertainty and ask when clarification or authorization is needed.
11776
+
11777
+ # Boundaries
11778
+ My commitments and behaviors to avoid: [fill in].
11779
+ `,
11780
+ "bot.create.persona.roleplay.seed": `# Character and world
11781
+ My name is [name]. I am a [species / identity] living in [era, world and setting].
11782
+
11783
+ # Personality and motivation
11784
+ My history, beliefs, goals and relationships: [fill in].
11785
+
11786
+ # Voice and expression
11787
+ My tone, vocabulary and habits: [fill in]. Characteristic phrases: [fill in].
11788
+
11789
+ # Interaction boundaries
11790
+ My relationship with the Human, established lore and topics to avoid: [fill in].
11791
+ `,
11411
11792
  "bot.create.source.git": "Import Git repository",
11412
11793
  "bot.create.gitUrl.label": "Git repository URL",
11413
11794
  "bot.create.gitUrl.placeholder": "https://github.com/owner/repo.git",
@@ -11507,6 +11888,12 @@ window.__ModuleLoader__.load({
11507
11888
  "im.origin": "External origin",
11508
11889
  "im.senderLabel": "Sender ID",
11509
11890
  "im.platformLabel": "Platform",
11891
+ "im.platform.weixin": "WeChat",
11892
+ "im.receiveHintDM": "Receive plain text from the QR-paired owner into this Bot’s Inbox, without adding local DM history.",
11893
+ "im.receptionDM": "WeChat DM intake connected",
11894
+ "im.receiveEnableDM": "Receive WeChat DMs",
11895
+ "im.receiveDisableDM": "Disable WeChat DM intake",
11896
+ "im.receptionDMOff": "WeChat DM intake disabled",
11510
11897
  "im.platform.feishu": "Lark/Feishu",
11511
11898
  "im.conversationLabel": "External channel",
11512
11899
  "im.conversationId": "External channel ID",
@@ -11649,6 +12036,9 @@ window.__ModuleLoader__.load({
11649
12036
  "im.originDetails": "Origin details",
11650
12037
  "im.threadLabel": "Topic",
11651
12038
  "im.groupLabel": "Group",
12039
+ "im.dmLabel": "Direct message",
12040
+ "im.weixinUser": "WeChat user",
12041
+ "im.weixinReplyOnly": "Replies to received WeChat DM text and files are supported; proactive sending is not available yet.",
11652
12042
  "im.receivedAs": "Received as {name}",
11653
12043
  "im.readDetails": "Read history · {count} reads",
11654
12044
  "im.sourceError": "Unable to read the retained external message",
@@ -14440,7 +14830,7 @@ window.__ModuleLoader__.load({
14440
14830
  const active = (item) => item.state === "thinking" || item.state === "working";
14441
14831
  return [...items.filter(active), ...items.filter((item) => !active(item))].slice(0, Math.min(max, 3));
14442
14832
  }
14443
- function PersonaBotFacepile({ items, renderAvatar, size, max = 3, className, t = zhTranslate }) {
14833
+ function PersonaBotFacepile({ items, renderAvatar, indicator = true, size, max = 3, className, t = zhTranslate }) {
14444
14834
  if (items.length === 0) return null;
14445
14835
  const visible = personaBotActivityPreview(items, max);
14446
14836
  const overflow = items.length - visible.length;
@@ -14450,6 +14840,7 @@ window.__ModuleLoader__.load({
14450
14840
  const avatar = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PersonaBotAvatar, {
14451
14841
  ...item,
14452
14842
  size,
14843
+ indicator,
14453
14844
  t
14454
14845
  }, item.personaBotId);
14455
14846
  return renderAvatar === void 0 ? avatar : renderAvatar(item, avatar);
@@ -14480,6 +14871,7 @@ window.__ModuleLoader__.load({
14480
14871
  }) : null,
14481
14872
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PersonaBotFacepile, {
14482
14873
  items: members,
14874
+ indicator: false,
14483
14875
  size: 22,
14484
14876
  t,
14485
14877
  renderAvatar: (item, avatar) => {
@@ -15008,8 +15400,16 @@ window.__ModuleLoader__.load({
15008
15400
  const gitUrlId = (0, react.useId)();
15009
15401
  const roleId = (0, react.useId)();
15010
15402
  const descriptionId = (0, react.useId)();
15403
+ const personaId = (0, react.useId)();
15404
+ const personaPresetId = (0, react.useId)();
15011
15405
  const [displayName, setDisplayName] = (0, react.useState)("");
15012
15406
  const [source, setSource] = (0, react.useState)("empty");
15407
+ const [personaPreset, setPersonaPreset] = (0, react.useState)("blank");
15408
+ const [personaDrafts, setPersonaDrafts] = (0, react.useState)(() => ({
15409
+ blank: "",
15410
+ colleague: t("bot.create.persona.colleague.seed"),
15411
+ roleplay: t("bot.create.persona.roleplay.seed")
15412
+ }));
15013
15413
  const [gitUrl, setGitUrl] = (0, react.useState)("");
15014
15414
  const [roleDraft, setRoleDraft] = (0, react.useState)("");
15015
15415
  const [description, setDescription] = (0, react.useState)("");
@@ -15032,7 +15432,7 @@ window.__ModuleLoader__.load({
15032
15432
  setCause(void 0);
15033
15433
  actions.createBot({
15034
15434
  displayName: displayName.trim(),
15035
- ...source === "git" ? { gitUrl: gitUrl.trim() } : {},
15435
+ ...source === "git" ? { gitUrl: gitUrl.trim() } : { persona: personaDrafts[personaPreset] },
15036
15436
  roles: submittedRoles,
15037
15437
  ...submittedDescription.length === 0 ? {} : { description: submittedDescription }
15038
15438
  }, sectionId).then(() => {
@@ -15169,6 +15569,46 @@ window.__ModuleLoader__.load({
15169
15569
  onChange: (event) => setDescription(event.currentTarget.value)
15170
15570
  })
15171
15571
  }),
15572
+ source === "empty" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.SegmentedControl, {
15573
+ id: personaPresetId,
15574
+ label: t("bot.create.persona.preset"),
15575
+ value: personaPreset,
15576
+ options: [
15577
+ {
15578
+ value: "blank",
15579
+ label: t("bot.create.persona.blank")
15580
+ },
15581
+ {
15582
+ value: "colleague",
15583
+ label: t("bot.create.persona.colleague")
15584
+ },
15585
+ {
15586
+ value: "roleplay",
15587
+ label: t("bot.create.persona.roleplay")
15588
+ }
15589
+ ],
15590
+ disabled: creating,
15591
+ onChange: setPersonaPreset
15592
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
15593
+ id: personaId,
15594
+ label: t("bot.create.persona.label"),
15595
+ hint: t("bot.create.persona.hint"),
15596
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
15597
+ id: personaId,
15598
+ className: "bh-name-input bh-personabot-persona",
15599
+ rows: 6,
15600
+ value: personaDrafts[personaPreset],
15601
+ disabled: creating,
15602
+ placeholder: t("bot.create.persona.placeholder"),
15603
+ onChange: (event) => {
15604
+ const value = event.currentTarget.value;
15605
+ setPersonaDrafts((current) => ({
15606
+ ...current,
15607
+ [personaPreset]: value
15608
+ }));
15609
+ }
15610
+ })
15611
+ })] }) : null,
15172
15612
  cause === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
15173
15613
  className: "bh-modal-error",
15174
15614
  role: "alert",
@@ -15179,6 +15619,710 @@ window.__ModuleLoader__.load({
15179
15619
  });
15180
15620
  }
15181
15621
  //#endregion
15622
+ //#region packages/client/src/client/marketplace.tsx
15623
+ const marketplaceErrorKeys = {
15624
+ "marketplace-unavailable": "market.error.unavailable",
15625
+ "invalid-repository-url": "market.error.invalidUrl",
15626
+ "repository-not-found": "market.error.notFound",
15627
+ "repository-private": "market.error.private",
15628
+ "repository-archived": "market.error.archived",
15629
+ "repository-missing-topic": "market.error.missingTopic",
15630
+ "repository-blocked": "market.error.blocked",
15631
+ "upstream-unavailable": "market.error.upstream",
15632
+ "bot-not-found": "market.error.botNotFound",
15633
+ "challenge-unavailable": "market.error.challengeUnavailable",
15634
+ "challenge-invalid": "market.error.challenge",
15635
+ "challenge-expired": "market.error.challenge",
15636
+ "challenge-replayed": "market.error.challenge",
15637
+ "rate-limited": "market.error.rateLimited",
15638
+ "repository-rate-limited": "market.error.repositoryRateLimited",
15639
+ "invalid-report": "market.error.invalidReport"
15640
+ };
15641
+ async function proveHuman(actions) {
15642
+ const payload = await solveChallenge(await actions.marketplaceChallenge(), { yieldEvery: () => new Promise((resolve) => setTimeout(resolve, 0)) });
15643
+ if (payload === void 0) throw new BridgeCallError("challenge-invalid", "challenge could not be solved");
15644
+ return payload;
15645
+ }
15646
+ function marketplaceError(error, t) {
15647
+ if (error instanceof BridgeCallError) {
15648
+ const key = marketplaceErrorKeys[error.code];
15649
+ if (key !== void 0) return t(key);
15650
+ }
15651
+ return errorMessage(error);
15652
+ }
15653
+ function shortSha(sha) {
15654
+ return sha.slice(0, 7);
15655
+ }
15656
+ function marketplaceName(bot) {
15657
+ return bot.displayName ?? bot.name;
15658
+ }
15659
+ function MarketplaceName({ bot }) {
15660
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
15661
+ className: "bh-market-name",
15662
+ children: [
15663
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: marketplaceName(bot) }),
15664
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
15665
+ className: "bh-market-owner",
15666
+ children: bot.displayName === null ? bot.owner : bot.fullName
15667
+ }),
15668
+ bot.roles.map((role) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tag, {
15669
+ tone: "outline",
15670
+ children: role
15671
+ }, role))
15672
+ ]
15673
+ });
15674
+ }
15675
+ function day(value) {
15676
+ return value.slice(0, 10);
15677
+ }
15678
+ const VISIBLE_TOPICS = 12;
15679
+ const MAX_QUERY_LENGTH = 100;
15680
+ function queryFor(filters, cursor) {
15681
+ return {
15682
+ sort: filters.sort,
15683
+ ...filters.query.length === 0 ? {} : { q: filters.query },
15684
+ ...filters.topic === void 0 ? {} : { topic: filters.topic },
15685
+ ...cursor === void 0 ? {} : { cursor }
15686
+ };
15687
+ }
15688
+ function mergeBots(current, next) {
15689
+ const seen = new Set(next.map((bot) => bot.id));
15690
+ return [...current.filter((bot) => !seen.has(bot.id)), ...next];
15691
+ }
15692
+ function MarketplaceMeta({ bot, t }) {
15693
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
15694
+ className: "bh-market-meta",
15695
+ children: [
15696
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("market.stars", { count: String(bot.stars) }) }),
15697
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("market.updated", { date: day(bot.pushedAt) }) }),
15698
+ bot.topics.map((topic) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tag, {
15699
+ tone: "neutral",
15700
+ children: topic
15701
+ }, topic))
15702
+ ]
15703
+ });
15704
+ }
15705
+ function MarketplaceRow({ bot, t, onOpen, onInstall }) {
15706
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
15707
+ className: "bh-market-row",
15708
+ role: "listitem",
15709
+ "data-market-bot": bot.fullName,
15710
+ children: [
15711
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PersonaBotAvatar, {
15712
+ personaBotId: "",
15713
+ name: marketplaceName(bot),
15714
+ size: 36,
15715
+ indicator: false,
15716
+ t
15717
+ }),
15718
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
15719
+ type: "button",
15720
+ className: "bh-market-copy bh-market-open",
15721
+ "aria-label": t("market.detail.open", { name: bot.fullName }),
15722
+ onClick: onOpen,
15723
+ children: [
15724
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MarketplaceName, { bot }),
15725
+ bot.description === null ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
15726
+ className: "bh-market-description",
15727
+ children: bot.description
15728
+ }),
15729
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MarketplaceMeta, {
15730
+ bot,
15731
+ t
15732
+ })
15733
+ ]
15734
+ }),
15735
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
15736
+ variant: "outline",
15737
+ onClick: onInstall,
15738
+ children: t("market.install")
15739
+ })
15740
+ ]
15741
+ });
15742
+ }
15743
+ function ReportPanel({ state, t, onReason }) {
15744
+ if (state.status === "closed") return null;
15745
+ if (state.status === "done") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
15746
+ className: "bh-market-report",
15747
+ role: "status",
15748
+ "data-market-report": "done",
15749
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("market.report.doneTitle") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("market.report.done") })]
15750
+ });
15751
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
15752
+ className: "bh-market-report",
15753
+ "data-market-report": "open",
15754
+ children: [
15755
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("market.report.title") }),
15756
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
15757
+ className: "bh-market-hint",
15758
+ children: t("market.report.hint")
15759
+ }),
15760
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("market.report.reason") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
15761
+ rows: 3,
15762
+ maxLength: 500,
15763
+ placeholder: t("market.report.placeholder"),
15764
+ value: state.reason,
15765
+ disabled: state.phase !== "idle",
15766
+ onChange: (event) => onReason(event.currentTarget.value)
15767
+ })] }),
15768
+ state.phase === "verifying" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
15769
+ className: "bh-market-hint",
15770
+ role: "status",
15771
+ children: t("market.challenge.verifying")
15772
+ }) : null,
15773
+ state.cause === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
15774
+ className: "bh-modal-error",
15775
+ role: "alert",
15776
+ children: marketplaceError(state.cause, t)
15777
+ })
15778
+ ]
15779
+ });
15780
+ }
15781
+ function MarketplaceDetailView({ bot, state, t, onRetry, report }) {
15782
+ const labels = (0, react.useMemo)(() => ({
15783
+ code: {
15784
+ copyLabel: t("message.code.copy"),
15785
+ copiedLabel: t("message.code.copied")
15786
+ },
15787
+ footnotes: t("message.footnotes")
15788
+ }), [t]);
15789
+ const shown = state.status === "ready" ? state.detail.bot : bot;
15790
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
15791
+ className: "bh-market-detail",
15792
+ "data-market-detail": shown.fullName,
15793
+ children: [
15794
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
15795
+ className: "bh-market-confirm-head",
15796
+ children: [
15797
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PersonaBotAvatar, {
15798
+ personaBotId: "",
15799
+ name: marketplaceName(shown),
15800
+ size: 44,
15801
+ indicator: false,
15802
+ t
15803
+ }),
15804
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
15805
+ className: "bh-market-copy",
15806
+ children: [
15807
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MarketplaceName, { bot: shown }),
15808
+ shown.description === null ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
15809
+ className: "bh-market-description",
15810
+ children: shown.description
15811
+ }),
15812
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MarketplaceMeta, {
15813
+ bot: shown,
15814
+ t
15815
+ })
15816
+ ]
15817
+ }),
15818
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
15819
+ className: "bh-market-github",
15820
+ href: shown.htmlUrl,
15821
+ target: "_blank",
15822
+ rel: "noreferrer",
15823
+ children: t("market.detail.github")
15824
+ })
15825
+ ]
15826
+ }),
15827
+ report,
15828
+ state.status === "loading" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
15829
+ className: "bh-market-state",
15830
+ role: "status",
15831
+ children: t("market.detail.loading")
15832
+ }) : null,
15833
+ state.status === "error" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
15834
+ className: "bh-market-state",
15835
+ role: "alert",
15836
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("market.detail.error", { error: marketplaceError(state.cause, t) }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
15837
+ variant: "outline",
15838
+ onClick: onRetry,
15839
+ children: t("market.retry")
15840
+ })]
15841
+ }) : null,
15842
+ state.status === "ready" && state.detail.readme === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
15843
+ className: "bh-market-state",
15844
+ children: t("market.detail.noReadme")
15845
+ }) : null,
15846
+ state.status === "ready" && state.detail.readme !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
15847
+ className: "bh-market-readme",
15848
+ "data-market-readme": true,
15849
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.MarkdownText, {
15850
+ text: state.detail.readme,
15851
+ labels
15852
+ })
15853
+ }) : null
15854
+ ]
15855
+ });
15856
+ }
15857
+ function InstallConfirmation({ bot, t, cause }) {
15858
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
15859
+ className: "bh-market-confirm",
15860
+ children: [
15861
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
15862
+ className: "bh-market-confirm-head",
15863
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(PersonaBotAvatar, {
15864
+ personaBotId: "",
15865
+ name: marketplaceName(bot),
15866
+ size: 44,
15867
+ indicator: false,
15868
+ t
15869
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
15870
+ className: "bh-market-copy",
15871
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MarketplaceName, { bot }), bot.description === null ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
15872
+ className: "bh-market-description",
15873
+ children: bot.description
15874
+ })]
15875
+ })]
15876
+ }),
15877
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("dl", {
15878
+ className: "bh-market-facts",
15879
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", { children: t("market.confirm.source") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
15880
+ href: bot.htmlUrl,
15881
+ target: "_blank",
15882
+ rel: "noreferrer",
15883
+ children: bot.fullName
15884
+ }) })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", { children: t("market.confirm.commit") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", {
15885
+ "data-market-commit": true,
15886
+ children: bot.headCommit === null ? t("market.confirm.noCommit") : t("market.confirm.commitValue", {
15887
+ sha: shortSha(bot.headCommit.sha),
15888
+ date: day(bot.headCommit.committedAt)
15889
+ })
15890
+ })] })]
15891
+ }),
15892
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
15893
+ className: "bh-market-risk",
15894
+ role: "note",
15895
+ children: [
15896
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("market.confirm.riskTitle") }),
15897
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("market.confirm.risk") }),
15898
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("market.confirm.newer") })
15899
+ ]
15900
+ }),
15901
+ cause === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
15902
+ className: "bh-modal-error",
15903
+ role: "alert",
15904
+ children: t("market.install.failed", { error: personaBotCreateError(cause, t) })
15905
+ })
15906
+ ]
15907
+ });
15908
+ }
15909
+ function MarketplaceModal({ actions, t, onClose, onInstalled }) {
15910
+ const [list, setList] = (0, react.useState)({ status: "loading" });
15911
+ const [filters, setFilters] = (0, react.useState)({
15912
+ sort: "updated",
15913
+ query: ""
15914
+ });
15915
+ const [searchText, setSearchText] = (0, react.useState)("");
15916
+ const [topics, setTopics] = (0, react.useState)([]);
15917
+ const [url, setUrl] = (0, react.useState)("");
15918
+ const [submitPhase, setSubmitPhase] = (0, react.useState)("idle");
15919
+ const submitting = submitPhase !== "idle";
15920
+ const [report, setReport] = (0, react.useState)({ status: "closed" });
15921
+ const [submitCause, setSubmitCause] = (0, react.useState)(void 0);
15922
+ const [submitted, setSubmitted] = (0, react.useState)(void 0);
15923
+ const [selected, setSelected] = (0, react.useState)(void 0);
15924
+ const [viewing, setViewing] = (0, react.useState)(void 0);
15925
+ const [detail, setDetail] = (0, react.useState)({ status: "loading" });
15926
+ const detailGeneration = (0, react.useRef)(0);
15927
+ const [installing, setInstalling] = (0, react.useState)(false);
15928
+ const [installCause, setInstallCause] = (0, react.useState)(void 0);
15929
+ const generation = (0, react.useRef)(0);
15930
+ const searchTimer = (0, react.useRef)(void 0);
15931
+ const latestFilters = (0, react.useRef)(filters);
15932
+ const sortId = (0, react.useId)();
15933
+ const load = (next = filters) => {
15934
+ const ticket = ++generation.current;
15935
+ latestFilters.current = next;
15936
+ setFilters(next);
15937
+ setList({ status: "loading" });
15938
+ actions.marketplaceList(queryFor(next)).then((page) => {
15939
+ if (ticket !== generation.current) return;
15940
+ setList({
15941
+ status: "ready",
15942
+ bots: page.bots,
15943
+ ...page.nextCursor === void 0 ? {} : { nextCursor: page.nextCursor },
15944
+ loadingMore: false
15945
+ });
15946
+ }, (cause) => {
15947
+ if (ticket === generation.current) setList({
15948
+ status: "error",
15949
+ cause
15950
+ });
15951
+ });
15952
+ };
15953
+ const loadOnMount = useMountedResource(() => {
15954
+ load();
15955
+ actions.marketplaceTopics().then((loaded) => setTopics(loaded.slice(0, VISIBLE_TOPICS)), () => setTopics([]));
15956
+ return () => {
15957
+ generation.current += 1;
15958
+ clearTimeout(searchTimer.current);
15959
+ };
15960
+ }, []);
15961
+ const search = (text) => {
15962
+ setSearchText(text);
15963
+ clearTimeout(searchTimer.current);
15964
+ searchTimer.current = setTimeout(() => {
15965
+ const query = text.trim();
15966
+ const current = latestFilters.current;
15967
+ if (query !== current.query) load({
15968
+ ...current,
15969
+ query
15970
+ });
15971
+ }, 300);
15972
+ };
15973
+ const chooseTopic = (topic) => {
15974
+ const { topic: _previous, ...rest } = filters;
15975
+ load(topic === void 0 ? rest : {
15976
+ ...rest,
15977
+ topic
15978
+ });
15979
+ };
15980
+ const loadMore = () => {
15981
+ if (list.status !== "ready" || list.nextCursor === void 0 || list.loadingMore) return;
15982
+ const ticket = generation.current;
15983
+ const current = list;
15984
+ setList({
15985
+ ...current,
15986
+ loadingMore: true
15987
+ });
15988
+ actions.marketplaceList(queryFor(filters, current.nextCursor)).then((page) => {
15989
+ if (ticket !== generation.current) return;
15990
+ setList({
15991
+ status: "ready",
15992
+ bots: mergeBots(current.bots, page.bots),
15993
+ ...page.nextCursor === void 0 ? {} : { nextCursor: page.nextCursor },
15994
+ loadingMore: false
15995
+ });
15996
+ }, (cause) => {
15997
+ if (ticket === generation.current) setList({
15998
+ status: "error",
15999
+ cause
16000
+ });
16001
+ });
16002
+ };
16003
+ const openDetail = (bot) => {
16004
+ const ticket = ++detailGeneration.current;
16005
+ setViewing(bot);
16006
+ setReport({ status: "closed" });
16007
+ setDetail({ status: "loading" });
16008
+ actions.marketplaceDetail(bot.id).then((loaded) => {
16009
+ if (ticket === detailGeneration.current) setDetail({
16010
+ status: "ready",
16011
+ detail: loaded
16012
+ });
16013
+ }, (cause) => {
16014
+ if (ticket === detailGeneration.current) setDetail({
16015
+ status: "error",
16016
+ cause
16017
+ });
16018
+ });
16019
+ };
16020
+ const submit = () => {
16021
+ const trimmed = url.trim();
16022
+ if (trimmed.length === 0 || submitting) return;
16023
+ setSubmitPhase("verifying");
16024
+ setSubmitCause(void 0);
16025
+ setSubmitted(void 0);
16026
+ proveHuman(actions).then((altcha) => {
16027
+ setSubmitPhase("sending");
16028
+ return actions.marketplaceSubmit(trimmed, altcha);
16029
+ }).then((bot) => {
16030
+ setSubmitPhase("idle");
16031
+ setUrl("");
16032
+ setSubmitted(bot.fullName);
16033
+ setList((current) => current.status === "ready" ? {
16034
+ ...current,
16035
+ bots: [bot, ...current.bots.filter((item) => item.id !== bot.id)]
16036
+ } : {
16037
+ status: "ready",
16038
+ bots: [bot],
16039
+ loadingMore: false
16040
+ });
16041
+ }, (cause) => {
16042
+ setSubmitPhase("idle");
16043
+ setSubmitCause(cause);
16044
+ });
16045
+ };
16046
+ const sendReport = (bot) => {
16047
+ if (report.status !== "open" || report.phase !== "idle") return;
16048
+ const reason = report.reason.trim();
16049
+ const ticket = detailGeneration.current;
16050
+ setReport({
16051
+ status: "open",
16052
+ reason: report.reason,
16053
+ phase: "verifying"
16054
+ });
16055
+ proveHuman(actions).then((altcha) => {
16056
+ if (ticket === detailGeneration.current) setReport({
16057
+ status: "open",
16058
+ reason: report.reason,
16059
+ phase: "sending"
16060
+ });
16061
+ return actions.marketplaceReport(bot.id, altcha, reason.length === 0 ? void 0 : reason);
16062
+ }).then(() => {
16063
+ if (ticket === detailGeneration.current) setReport({ status: "done" });
16064
+ }, (cause) => {
16065
+ if (ticket === detailGeneration.current) setReport({
16066
+ status: "open",
16067
+ reason: report.reason,
16068
+ phase: "idle",
16069
+ cause
16070
+ });
16071
+ });
16072
+ };
16073
+ const install = () => {
16074
+ if (selected === void 0 || installing) return;
16075
+ setInstalling(true);
16076
+ setInstallCause(void 0);
16077
+ actions.createBot({
16078
+ displayName: marketplaceName(selected),
16079
+ gitUrl: selected.cloneUrl,
16080
+ roles: selected.roles,
16081
+ ...selected.description === null ? {} : { description: selected.description }
16082
+ }).then(() => {
16083
+ setInstalling(false);
16084
+ onInstalled();
16085
+ }, (cause) => {
16086
+ setInstalling(false);
16087
+ setInstallCause(cause);
16088
+ });
16089
+ };
16090
+ if (selected !== void 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Modal, {
16091
+ open: true,
16092
+ onClose: () => {
16093
+ if (!installing) onClose();
16094
+ },
16095
+ closeLabel: t("common.close"),
16096
+ title: t("market.confirm.title", { name: marketplaceName(selected) }),
16097
+ className: "bh-market-modal",
16098
+ footer: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
16099
+ variant: "outline",
16100
+ disabled: installing,
16101
+ onClick: () => {
16102
+ setSelected(void 0);
16103
+ setInstallCause(void 0);
16104
+ },
16105
+ children: t("market.back")
16106
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
16107
+ variant: "primary",
16108
+ disabled: installing,
16109
+ onClick: install,
16110
+ children: installing ? t("market.confirm.installing") : t("market.confirm.install")
16111
+ })] }),
16112
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InstallConfirmation, {
16113
+ bot: selected,
16114
+ t,
16115
+ cause: installCause
16116
+ })
16117
+ });
16118
+ if (viewing !== void 0) {
16119
+ const shown = detail.status === "ready" ? detail.detail.bot : viewing;
16120
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Modal, {
16121
+ open: true,
16122
+ onClose,
16123
+ closeLabel: t("common.close"),
16124
+ title: marketplaceName(shown),
16125
+ className: "bh-market-modal",
16126
+ footer: report.status === "open" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
16127
+ variant: "outline",
16128
+ disabled: report.phase !== "idle",
16129
+ onClick: () => setReport({ status: "closed" }),
16130
+ children: t("common.cancel")
16131
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
16132
+ variant: "primary",
16133
+ disabled: report.phase !== "idle",
16134
+ onClick: () => sendReport(shown),
16135
+ children: report.phase === "verifying" ? t("market.challenge.verifying") : report.phase === "sending" ? t("market.report.sending") : t("market.report.send")
16136
+ })] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
16137
+ report.status === "done" ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
16138
+ variant: "ghost",
16139
+ className: "bh-market-report-open",
16140
+ onClick: () => setReport({
16141
+ status: "open",
16142
+ reason: "",
16143
+ phase: "idle"
16144
+ }),
16145
+ children: t("market.report.open")
16146
+ }),
16147
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
16148
+ variant: "outline",
16149
+ onClick: () => {
16150
+ detailGeneration.current += 1;
16151
+ setViewing(void 0);
16152
+ },
16153
+ children: t("market.back")
16154
+ }),
16155
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
16156
+ variant: "primary",
16157
+ onClick: () => {
16158
+ setSelected(shown);
16159
+ setInstallCause(void 0);
16160
+ },
16161
+ children: t("market.install")
16162
+ })
16163
+ ] }),
16164
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MarketplaceDetailView, {
16165
+ bot: viewing,
16166
+ state: detail,
16167
+ t,
16168
+ onRetry: () => openDetail(viewing),
16169
+ report: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReportPanel, {
16170
+ state: report,
16171
+ t,
16172
+ onReason: (reason) => setReport((current) => current.status === "open" ? {
16173
+ ...current,
16174
+ reason,
16175
+ cause: void 0
16176
+ } : current)
16177
+ })
16178
+ })
16179
+ });
16180
+ }
16181
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Modal, {
16182
+ open: true,
16183
+ onClose,
16184
+ closeLabel: t("common.close"),
16185
+ title: t("market.title"),
16186
+ description: t("market.description"),
16187
+ className: "bh-market-modal",
16188
+ footer: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
16189
+ variant: "outline",
16190
+ onClick: onClose,
16191
+ children: t("common.close")
16192
+ }),
16193
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
16194
+ className: "bh-market",
16195
+ ref: loadOnMount,
16196
+ children: [
16197
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
16198
+ className: "bh-market-submit",
16199
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
16200
+ "aria-label": t("market.submit.label"),
16201
+ placeholder: t("market.submit.placeholder"),
16202
+ autoCapitalize: "none",
16203
+ autoComplete: "off",
16204
+ spellCheck: false,
16205
+ value: url,
16206
+ disabled: submitting,
16207
+ onChange: (event) => setUrl(event.currentTarget.value),
16208
+ onKeyDown: (event) => {
16209
+ if (event.key !== "Enter") return;
16210
+ event.preventDefault();
16211
+ submit();
16212
+ }
16213
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
16214
+ variant: "primary",
16215
+ disabled: submitting || url.trim().length === 0,
16216
+ onClick: submit,
16217
+ children: submitPhase === "verifying" ? t("market.challenge.verifying") : submitting ? t("market.submit.busy") : t("market.submit.action")
16218
+ })]
16219
+ }),
16220
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
16221
+ className: "bh-market-hint",
16222
+ children: submitted === void 0 ? t("market.submit.hint") : t("market.submit.done", { name: submitted })
16223
+ }),
16224
+ submitCause === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
16225
+ className: "bh-modal-error",
16226
+ role: "alert",
16227
+ children: marketplaceError(submitCause, t)
16228
+ }),
16229
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
16230
+ className: "bh-market-browse",
16231
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
16232
+ type: "search",
16233
+ "aria-label": t("market.search.label"),
16234
+ placeholder: t("market.search.placeholder"),
16235
+ autoComplete: "off",
16236
+ maxLength: MAX_QUERY_LENGTH,
16237
+ value: searchText,
16238
+ onChange: (event) => search(event.currentTarget.value)
16239
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.SegmentedControl, {
16240
+ id: sortId,
16241
+ label: t("market.sort.label"),
16242
+ value: filters.sort,
16243
+ options: [{
16244
+ value: "updated",
16245
+ label: t("market.sort.updated")
16246
+ }, {
16247
+ value: "stars",
16248
+ label: t("market.sort.stars")
16249
+ }],
16250
+ disabled: filters.query.length > 0,
16251
+ onChange: (sort) => load({
16252
+ ...filters,
16253
+ sort
16254
+ })
16255
+ })]
16256
+ }),
16257
+ filters.query.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
16258
+ className: "bh-market-hint",
16259
+ children: t("market.search.relevance")
16260
+ }) : null,
16261
+ topics.length === 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
16262
+ className: "bh-market-topics",
16263
+ role: "group",
16264
+ "aria-label": t("market.topics.label"),
16265
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
16266
+ type: "button",
16267
+ className: "bh-market-topic",
16268
+ "aria-pressed": filters.topic === void 0,
16269
+ onClick: () => chooseTopic(void 0),
16270
+ children: t("market.topics.all")
16271
+ }), topics.map(({ topic, count }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
16272
+ type: "button",
16273
+ className: "bh-market-topic",
16274
+ "data-market-topic": topic,
16275
+ "aria-pressed": filters.topic === topic,
16276
+ onClick: () => chooseTopic(filters.topic === topic ? void 0 : topic),
16277
+ children: [topic, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
16278
+ className: "bh-market-topic-count",
16279
+ children: count
16280
+ })]
16281
+ }, topic))]
16282
+ }),
16283
+ list.status === "loading" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
16284
+ className: "bh-market-state",
16285
+ role: "status",
16286
+ children: t("market.loading")
16287
+ }) : null,
16288
+ list.status === "error" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
16289
+ className: "bh-market-state",
16290
+ role: "alert",
16291
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("market.error.load", { error: marketplaceError(list.cause, t) }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
16292
+ variant: "outline",
16293
+ onClick: () => load(),
16294
+ children: t("market.retry")
16295
+ })]
16296
+ }) : null,
16297
+ list.status === "ready" && list.bots.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
16298
+ className: "bh-market-state",
16299
+ children: filters.query.length > 0 || filters.topic !== void 0 ? t("market.search.empty") : t("market.empty")
16300
+ }) : null,
16301
+ list.status === "ready" && list.bots.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
16302
+ className: "bh-market-list",
16303
+ role: "list",
16304
+ "aria-label": t("market.title"),
16305
+ children: list.bots.map((bot) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MarketplaceRow, {
16306
+ bot,
16307
+ t,
16308
+ onOpen: () => openDetail(bot),
16309
+ onInstall: () => {
16310
+ setSelected(bot);
16311
+ setInstallCause(void 0);
16312
+ }
16313
+ }, bot.id))
16314
+ }) : null,
16315
+ list.status === "ready" && list.nextCursor !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
16316
+ variant: "outline",
16317
+ disabled: list.loadingMore,
16318
+ onClick: loadMore,
16319
+ children: list.loadingMore ? t("market.loadingMore") : t("market.loadMore")
16320
+ }) : null
16321
+ ]
16322
+ })
16323
+ });
16324
+ }
16325
+ //#endregion
15182
16326
  //#region packages/client/src/client/section-management.tsx
15183
16327
  const DANGER_ACTION_CLASS = "bh-danger-action";
15184
16328
  function globalSortMenuItems(t) {
@@ -16173,6 +17317,7 @@ window.__ModuleLoader__.load({
16173
17317
  if (id === "bot") setCreateRequest({ kind: "bot" });
16174
17318
  if (id === "channel") setCreateRequest({ kind: "channel" });
16175
17319
  if (id === "section") setCreateRequest({ kind: "section" });
17320
+ if (id === "marketplace") setCreateRequest({ kind: "marketplace" });
16176
17321
  };
16177
17322
  const selectSortMenu = (id) => {
16178
17323
  setSortMenuOpen(false);
@@ -17130,6 +18275,16 @@ window.__ModuleLoader__.load({
17130
18275
  setCreateRequest(void 0);
17131
18276
  }
17132
18277
  }) : null,
18278
+ createRequest?.kind === "marketplace" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MarketplaceModal, {
18279
+ t,
18280
+ actions,
18281
+ onClose: () => {
18282
+ setCreateRequest(void 0);
18283
+ },
18284
+ onInstalled: () => {
18285
+ setCreateRequest(void 0);
18286
+ }
18287
+ }) : null,
17133
18288
  createRequest?.kind === "section" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CreateSectionModal, {
17134
18289
  t,
17135
18290
  onCancel: () => {
@@ -17468,6 +18623,11 @@ window.__ModuleLoader__.load({
17468
18623
  id: "section",
17469
18624
  label: t("roster.menu.createSection"),
17470
18625
  icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconFolderOpenOutlineRegular, { size: 16 })
18626
+ },
18627
+ {
18628
+ id: "marketplace",
18629
+ label: t("roster.menu.marketplace"),
18630
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconGlobeOutlineRegular, { size: 16 })
17471
18631
  }
17472
18632
  ];
17473
18633
  }
@@ -18110,6 +19270,7 @@ window.__ModuleLoader__.load({
18110
19270
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PersonaBotFacepile, {
18111
19271
  t,
18112
19272
  className: "bh-composer-activity-facepile",
19273
+ indicator: false,
18113
19274
  items: activity.items,
18114
19275
  renderAvatar: activity.presentation === "group" ? (item, avatar) => {
18115
19276
  const label = item.name + " · " + personaBotPresentationSummary(item.state ?? "idle", item.activity, item.attention, t);
@@ -20303,11 +21464,15 @@ window.__ModuleLoader__.load({
20303
21464
  if (platform === "feishu") return t("im.platform.feishu");
20304
21465
  if (platform === "slack") return "Slack";
20305
21466
  if (platform === "discord") return "Discord";
21467
+ if (platform === "weixin") return t("im.platform.weixin");
20306
21468
  return platform;
20307
21469
  }
20308
21470
  function bridgeSourceLabel(origin, t) {
20309
21471
  return `${externalPlatformLabel(origin.platform, t)} ${origin.conversationName}`;
20310
21472
  }
21473
+ function externalSenderLabel(origin, t) {
21474
+ return origin.senderName ?? (origin.platform === "weixin" ? t("im.weixinUser") : origin.senderId);
21475
+ }
20311
21476
  //#endregion
20312
21477
  //#region packages/client/src/client/bridge-source-author.tsx
20313
21478
  function BridgeSourceDetails({ origin, t }) {
@@ -33914,14 +35079,14 @@ window.__ModuleLoader__.load({
33914
35079
  children: t("im.localTarget")
33915
35080
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MessagingHelp, {
33916
35081
  title: t("im.localTarget"),
33917
- text: grant.receiveTargetChannelId ? t("im.channelTargetHint") : t("im.receiveHint"),
35082
+ text: grant.receiveTargetChannelId ? t("im.channelTargetHint") : t(grant.platform === "weixin" ? "im.receiveHintDM" : "im.receiveHint"),
33918
35083
  t
33919
35084
  })]
33920
35085
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
33921
35086
  id: targetFieldId + "-local",
33922
35087
  "aria-label": t("im.localTarget"),
33923
35088
  value: grant.receiveTargetChannelId ?? "",
33924
- disabled: busy,
35089
+ disabled: busy || grant.platform === "weixin",
33925
35090
  onChange: (event) => {
33926
35091
  const channelId = event.target.value || null;
33927
35092
  operate(() => actions.messagingChannelTarget(slug, grant.id, channelId));
@@ -33945,7 +35110,7 @@ window.__ModuleLoader__.load({
33945
35110
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
33946
35111
  className: "bh-im-status",
33947
35112
  role: "status",
33948
- children: t(`im.reception.${grant.reception ?? "off"}`)
35113
+ children: grant.platform === "weixin" ? t(grant.reception === "receiving" ? "im.receptionDM" : "im.receptionDMOff") : t(`im.reception.${grant.reception ?? "off"}`)
33949
35114
  }),
33950
35115
  grant.receiveScope && grant.groupPolicy ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(GroupReceptionSettings, {
33951
35116
  policy: grant.groupPolicy,
@@ -33962,7 +35127,7 @@ window.__ModuleLoader__.load({
33962
35127
  onClick: () => void operate(async () => {
33963
35128
  await actions.messagingReceive(slug, grant.id, grant.receiveScope === void 0);
33964
35129
  }),
33965
- children: t(grant.receiveScope === void 0 ? "im.receiveEnable" : "im.receiveDisable")
35130
+ children: t(grant.receiveScope === void 0 ? grant.platform === "weixin" ? "im.receiveEnableDM" : "im.receiveEnable" : grant.platform === "weixin" ? "im.receiveDisableDM" : "im.receiveDisable")
33966
35131
  })
33967
35132
  ] }) : null,
33968
35133
  grant.threadPolicies?.length ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ThreadReceptionSettings, {
@@ -33978,7 +35143,7 @@ window.__ModuleLoader__.load({
33978
35143
  return saved;
33979
35144
  }
33980
35145
  }) : null,
33981
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
35146
+ grant.platform === "weixin" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("im.weixinReplyOnly") }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
33982
35147
  className: "bh-im-field",
33983
35148
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("im.message") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
33984
35149
  "aria-label": t("im.message"),
@@ -33987,15 +35152,14 @@ window.__ModuleLoader__.load({
33987
35152
  disabled: busy,
33988
35153
  onChange: (event) => setText(event.target.value)
33989
35154
  })]
33990
- }),
33991
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
35155
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
33992
35156
  size: "sm",
33993
35157
  variant: "primary",
33994
35158
  className: "bh-im-submit",
33995
35159
  disabled: busy || !text.trim() || grant.availability !== "available",
33996
35160
  onClick: () => void send(),
33997
35161
  children: t("im.send")
33998
- })
35162
+ })] })
33999
35163
  ] }) : snapshot.accounts.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("im.setup") }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
34000
35164
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
34001
35165
  className: "bh-im-field",
@@ -38757,6 +39921,7 @@ window.__ModuleLoader__.load({
38757
39921
  }) : channelFacepile.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PersonaBotFacepile, {
38758
39922
  items: channelFacepile,
38759
39923
  size: 22,
39924
+ indicator: false,
38760
39925
  t
38761
39926
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
38762
39927
  className: "bh-channel-mark bh-channel-mark-sm",
@@ -38767,10 +39932,6 @@ window.__ModuleLoader__.load({
38767
39932
  className: "bh-title",
38768
39933
  children: title
38769
39934
  }),
38770
- bot === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PersonaBotStatusBadges, {
38771
- state: botActivity,
38772
- attention: bot.attention
38773
- }),
38774
39935
  bot === void 0 || bot.roles.length === 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
38775
39936
  className: "bh-role-badges",
38776
39937
  children: bot.roles.map((role) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tag, {
@@ -38804,10 +39965,6 @@ window.__ModuleLoader__.load({
38804
39965
  className: "bh-title",
38805
39966
  children: title
38806
39967
  }),
38807
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PersonaBotStatusBadges, {
38808
- state: profileBotActivity,
38809
- attention: profileBot.attention
38810
- }),
38811
39968
  profileBot.roles.length === 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
38812
39969
  className: "bh-role-badges",
38813
39970
  children: profileBot.roles.map((role) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tag, {
@@ -40478,7 +41635,7 @@ window.__ModuleLoader__.load({
40478
41635
  });
40479
41636
  }
40480
41637
  function MessageCard({ message, t }) {
40481
- const name = message.senderName ?? message.senderId;
41638
+ const name = message.senderLabel ?? message.senderName ?? message.senderId;
40482
41639
  const date = new Date(message.at);
40483
41640
  const time = Number.isNaN(date.getTime()) ? message.at : new Intl.DateTimeFormat(void 0, {
40484
41641
  month: "short",
@@ -40549,7 +41706,7 @@ window.__ModuleLoader__.load({
40549
41706
  children: platform
40550
41707
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
40551
41708
  className: "bh-external-scope",
40552
- children: t(hasThread ? "im.threadLabel" : "im.groupLabel")
41709
+ children: t(hasThread ? "im.threadLabel" : source.event.conversation.kind === "dm" ? "im.dmLabel" : "im.groupLabel")
40553
41710
  })]
40554
41711
  }),
40555
41712
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: source.conversationName }),
@@ -40639,6 +41796,11 @@ window.__ModuleLoader__.load({
40639
41796
  sourceEventId: source.id,
40640
41797
  messageId: source.event.messageId,
40641
41798
  senderId: source.event.actor.id,
41799
+ senderLabel: externalSenderLabel({
41800
+ platform: source.platform,
41801
+ senderId: source.event.actor.id,
41802
+ ...source.event.actor.name ? { senderName: source.event.actor.name } : {}
41803
+ }, t),
40642
41804
  ...source.event.actor.name ? { senderName: source.event.actor.name } : {},
40643
41805
  at: source.at,
40644
41806
  text: source.body,
@@ -41276,7 +42438,7 @@ window.__ModuleLoader__.load({
41276
42438
  };
41277
42439
  const memoryChange = item.sourceKind === "memory-change";
41278
42440
  const summary = memoryChange ? item.summary.replace(/^Memory changed since your last turn:\s*/u, "").replace(/^Working Memory changes since your last turn:\s*/u, "").replace(/^\?\? /u, "") : item.summary;
41279
- const author = item.externalOrigin !== void 0 ? `${externalPlatformLabel(item.externalOrigin.platform, t)} · ${item.externalOrigin.senderName ? `${item.externalOrigin.senderName} (${item.externalOrigin.senderId})` : item.externalOrigin.senderId}` : memoryChange ? t("inbox.memoryChange") : item.sourceKind === "assignment-report" ? t("inbox.assignment") : item.authorKind === "human" ? t("main.author.human") : item.authorKind === "bot" ? bots.find((bot) => bot.slug === item.authorBotSlug)?.displayName ?? item.authorBotSlug ?? t("inbox.bot") : t("inbox.system");
42441
+ const author = item.externalOrigin !== void 0 ? `${externalPlatformLabel(item.externalOrigin.platform, t)} · ${externalSenderLabel(item.externalOrigin, t)}` : memoryChange ? t("inbox.memoryChange") : item.sourceKind === "assignment-report" ? t("inbox.assignment") : item.authorKind === "human" ? t("main.author.human") : item.authorKind === "bot" ? bots.find((bot) => bot.slug === item.authorBotSlug)?.displayName ?? item.authorBotSlug ?? t("inbox.bot") : t("inbox.system");
41280
42442
  const open = async () => {
41281
42443
  if (!item.sourceAvailable) return;
41282
42444
  if (item.externalOrigin !== void 0) {
@@ -43890,6 +45052,14 @@ html[data-botharness-motion='reduce'] .bh-section-chevron {
43890
45052
  gap: 12px;
43891
45053
  min-width: 0;
43892
45054
  }
45055
+ .bh-personabot-persona {
45056
+ min-height: 120px;
45057
+ resize: vertical;
45058
+ }
45059
+ .bh-personabot-persona:focus-visible {
45060
+ outline: 2px solid var(--dsw-alias-state-business-primary);
45061
+ outline-offset: 2px;
45062
+ }
43893
45063
  .bh-personabot-field {
43894
45064
  display: flex;
43895
45065
  flex-direction: column;
@@ -43957,6 +45127,260 @@ html[data-botharness-motion='reduce'] .bh-section-chevron {
43957
45127
  font-size: 12px;
43958
45128
  }
43959
45129
 
45130
+ /* Bot Marketplace is portaled with the native Modal; never depend on .bh-root. */
45131
+ .bh-market-modal {
45132
+ width: min(640px, 100%);
45133
+ max-height: 100%;
45134
+ }
45135
+ .bh-market {
45136
+ display: flex;
45137
+ flex-direction: column;
45138
+ gap: 10px;
45139
+ min-width: 0;
45140
+ }
45141
+ .bh-market-submit {
45142
+ display: flex;
45143
+ gap: 8px;
45144
+ min-width: 0;
45145
+ }
45146
+ .bh-market-submit > :first-child {
45147
+ flex: 1;
45148
+ min-width: 0;
45149
+ }
45150
+ .bh-market-hint {
45151
+ color: var(--dsw-alias-label-tertiary);
45152
+ font-size: 12px;
45153
+ line-height: 18px;
45154
+ }
45155
+ .bh-market-state {
45156
+ display: flex;
45157
+ align-items: center;
45158
+ justify-content: space-between;
45159
+ gap: 8px;
45160
+ padding: 24px 0;
45161
+ color: var(--dsw-alias-label-secondary);
45162
+ font-size: 13px;
45163
+ }
45164
+ .bh-market-browse {
45165
+ display: flex;
45166
+ align-items: center;
45167
+ gap: 8px;
45168
+ min-width: 0;
45169
+ }
45170
+ .bh-market-browse > :first-child {
45171
+ flex: 1;
45172
+ min-width: 0;
45173
+ }
45174
+ .bh-market-topics {
45175
+ display: flex;
45176
+ flex-wrap: wrap;
45177
+ gap: 6px;
45178
+ }
45179
+ .bh-market-topic {
45180
+ display: inline-flex;
45181
+ align-items: center;
45182
+ gap: 4px;
45183
+ height: 24px;
45184
+ padding: 0 9px;
45185
+ border: 1px solid var(--dsw-alias-border-l2);
45186
+ border-radius: 12px;
45187
+ background: transparent;
45188
+ color: var(--dsw-alias-label-secondary);
45189
+ font: inherit;
45190
+ font-size: 12px;
45191
+ cursor: pointer;
45192
+ }
45193
+ .bh-market-topic:hover {
45194
+ background: var(--bh-hover);
45195
+ color: var(--dsw-alias-label-primary);
45196
+ }
45197
+ .bh-market-topic[aria-pressed='true'] {
45198
+ border-color: var(--dsw-alias-label-primary);
45199
+ background: var(--dsw-alias-interactive-bg-hover);
45200
+ color: var(--dsw-alias-label-primary);
45201
+ font-weight: 500;
45202
+ }
45203
+ .bh-market-topic-count {
45204
+ color: var(--dsw-alias-label-tertiary);
45205
+ }
45206
+ .bh-market-list {
45207
+ display: flex;
45208
+ flex-direction: column;
45209
+ max-height: min(52vh, 440px);
45210
+ overflow-y: auto;
45211
+ border-top: 1px solid var(--dsw-alias-separator-primary);
45212
+ }
45213
+ .bh-market-row {
45214
+ display: flex;
45215
+ align-items: flex-start;
45216
+ gap: 10px;
45217
+ padding: 12px 0;
45218
+ border-bottom: 1px solid var(--dsw-alias-separator-primary);
45219
+ }
45220
+ .bh-market-copy {
45221
+ display: flex;
45222
+ flex: 1;
45223
+ flex-direction: column;
45224
+ gap: 4px;
45225
+ min-width: 0;
45226
+ }
45227
+ .bh-market-name {
45228
+ display: flex;
45229
+ align-items: baseline;
45230
+ gap: 6px;
45231
+ min-width: 0;
45232
+ color: var(--dsw-alias-label-primary);
45233
+ font-size: 14px;
45234
+ font-weight: 500;
45235
+ line-height: 20px;
45236
+ }
45237
+ .bh-market-owner {
45238
+ color: var(--dsw-alias-label-tertiary);
45239
+ font-size: 12px;
45240
+ font-weight: 400;
45241
+ }
45242
+ .bh-market-description {
45243
+ color: var(--dsw-alias-label-secondary);
45244
+ font-size: 13px;
45245
+ line-height: 18px;
45246
+ overflow-wrap: anywhere;
45247
+ }
45248
+ .bh-market-meta {
45249
+ display: flex;
45250
+ flex-wrap: wrap;
45251
+ align-items: center;
45252
+ gap: 4px 10px;
45253
+ color: var(--dsw-alias-label-tertiary);
45254
+ font-size: 12px;
45255
+ line-height: 18px;
45256
+ }
45257
+ .bh-market-open {
45258
+ padding: 0;
45259
+ border: 0;
45260
+ background: transparent;
45261
+ color: inherit;
45262
+ font: inherit;
45263
+ text-align: left;
45264
+ cursor: pointer;
45265
+ }
45266
+ .bh-market-open:hover .bh-market-name > :first-child {
45267
+ text-decoration: underline;
45268
+ }
45269
+ .bh-market-detail {
45270
+ display: flex;
45271
+ flex-direction: column;
45272
+ gap: 12px;
45273
+ min-width: 0;
45274
+ }
45275
+ .bh-market-github {
45276
+ flex: none;
45277
+ color: var(--bh-accent);
45278
+ font-size: 13px;
45279
+ text-decoration: none;
45280
+ }
45281
+ .bh-market-github:hover {
45282
+ text-decoration: underline;
45283
+ }
45284
+ .bh-market-readme {
45285
+ max-height: min(56vh, 480px);
45286
+ overflow-y: auto;
45287
+ padding-top: 12px;
45288
+ border-top: 1px solid var(--dsw-alias-border-l2);
45289
+ min-width: 0;
45290
+ }
45291
+ .bh-market-readme img {
45292
+ max-width: 100%;
45293
+ }
45294
+ .bh-market-report-open {
45295
+ margin-right: auto;
45296
+ }
45297
+ .bh-market-report {
45298
+ display: flex;
45299
+ flex-direction: column;
45300
+ gap: 6px;
45301
+ padding: 10px 12px;
45302
+ border: 1px solid var(--dsw-alias-border-l2);
45303
+ border-radius: 8px;
45304
+ color: var(--dsw-alias-label-secondary);
45305
+ font-size: 13px;
45306
+ line-height: 18px;
45307
+ }
45308
+ .bh-market-report strong {
45309
+ color: var(--dsw-alias-label-primary);
45310
+ font-size: 14px;
45311
+ font-weight: 500;
45312
+ }
45313
+ .bh-market-report label {
45314
+ display: flex;
45315
+ flex-direction: column;
45316
+ gap: 4px;
45317
+ }
45318
+ .bh-market-report textarea {
45319
+ box-sizing: border-box;
45320
+ width: 100%;
45321
+ resize: vertical;
45322
+ padding: 8px;
45323
+ border: 1px solid var(--dsw-alias-border-l2);
45324
+ border-radius: 8px;
45325
+ background: var(--dsw-alias-bg-base);
45326
+ color: var(--dsw-alias-label-primary);
45327
+ font: inherit;
45328
+ }
45329
+ .bh-market-report textarea:focus-visible {
45330
+ outline: 2px solid var(--bh-accent);
45331
+ outline-offset: 2px;
45332
+ }
45333
+ .bh-market-confirm {
45334
+ display: flex;
45335
+ flex-direction: column;
45336
+ gap: 14px;
45337
+ min-width: 0;
45338
+ }
45339
+ .bh-market-confirm-head {
45340
+ display: flex;
45341
+ align-items: flex-start;
45342
+ gap: 12px;
45343
+ }
45344
+ .bh-market-facts {
45345
+ display: grid;
45346
+ gap: 8px;
45347
+ margin: 0;
45348
+ font-size: 13px;
45349
+ }
45350
+ .bh-market-facts > div {
45351
+ display: grid;
45352
+ grid-template-columns: 96px minmax(0, 1fr);
45353
+ gap: 12px;
45354
+ }
45355
+ .bh-market-facts dt {
45356
+ color: var(--dsw-alias-label-tertiary);
45357
+ }
45358
+ .bh-market-facts dd {
45359
+ margin: 0;
45360
+ color: var(--dsw-alias-label-primary);
45361
+ font-family: var(--dsw-font-family-mono, monospace);
45362
+ overflow-wrap: anywhere;
45363
+ }
45364
+ .bh-market-facts a {
45365
+ color: var(--dsw-alias-state-business-primary);
45366
+ }
45367
+ .bh-market-risk {
45368
+ display: flex;
45369
+ flex-direction: column;
45370
+ gap: 4px;
45371
+ border: 1px solid var(--dsw-alias-state-warning-secondary);
45372
+ border-radius: 8px;
45373
+ padding: 8px 10px;
45374
+ background: var(--dsw-alias-interactive-bg-hover);
45375
+ color: var(--dsw-alias-label-secondary);
45376
+ font-size: 12px;
45377
+ line-height: 18px;
45378
+ }
45379
+ .bh-market-risk strong {
45380
+ color: var(--dsw-alias-state-warning-primary);
45381
+ font-size: 13px;
45382
+ }
45383
+
43960
45384
  /* Hidden Channel recovery is portaled under body; never depend on .bh-root. */
43961
45385
  .bh-hidden-manager {
43962
45386
  display: flex;