botmux 2.51.1 → 2.52.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +5 -1
- package/README.md +5 -1
- package/dist/bot-registry.d.ts +30 -0
- package/dist/bot-registry.d.ts.map +1 -1
- package/dist/bot-registry.js +31 -0
- package/dist/bot-registry.js.map +1 -1
- package/dist/core/dashboard-ipc-server.d.ts.map +1 -1
- package/dist/core/dashboard-ipc-server.js +37 -0
- package/dist/core/dashboard-ipc-server.js.map +1 -1
- package/dist/daemon.d.ts +3 -0
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +138 -10
- package/dist/daemon.js.map +1 -1
- package/dist/dashboard/web/bot-defaults.d.ts.map +1 -1
- package/dist/dashboard/web/bot-defaults.js +114 -0
- package/dist/dashboard/web/bot-defaults.js.map +1 -1
- package/dist/dashboard/web/i18n.d.ts.map +1 -1
- package/dist/dashboard/web/i18n.js +22 -0
- package/dist/dashboard/web/i18n.js.map +1 -1
- package/dist/dashboard-web/app.js +449 -426
- package/dist/dashboard.js +20 -0
- package/dist/dashboard.js.map +1 -1
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +7 -1
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/zh.d.ts.map +1 -1
- package/dist/i18n/zh.js +7 -1
- package/dist/i18n/zh.js.map +1 -1
- package/dist/im/lark/card-builder.d.ts +4 -2
- package/dist/im/lark/card-builder.d.ts.map +1 -1
- package/dist/im/lark/card-builder.js +15 -3
- package/dist/im/lark/card-builder.js.map +1 -1
- package/dist/im/lark/card-handler.d.ts.map +1 -1
- package/dist/im/lark/card-handler.js +6 -4
- package/dist/im/lark/card-handler.js.map +1 -1
- package/dist/im/lark/event-dispatcher.d.ts +12 -0
- package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
- package/dist/im/lark/event-dispatcher.js +39 -31
- package/dist/im/lark/event-dispatcher.js.map +1 -1
- package/dist/im/lark/grant-command.d.ts +13 -0
- package/dist/im/lark/grant-command.d.ts.map +1 -1
- package/dist/im/lark/grant-command.js +49 -3
- package/dist/im/lark/grant-command.js.map +1 -1
- package/dist/im/lark/grant-pending.d.ts +7 -4
- package/dist/im/lark/grant-pending.d.ts.map +1 -1
- package/dist/im/lark/grant-pending.js +12 -6
- package/dist/im/lark/grant-pending.js.map +1 -1
- package/dist/services/grant-prefs-store.d.ts +23 -0
- package/dist/services/grant-prefs-store.d.ts.map +1 -0
- package/dist/services/grant-prefs-store.js +94 -0
- package/dist/services/grant-prefs-store.js.map +1 -0
- package/dist/services/grant-store.d.ts +34 -2
- package/dist/services/grant-store.d.ts.map +1 -1
- package/dist/services/grant-store.js +160 -9
- package/dist/services/grant-store.js.map +1 -1
- package/dist/services/quota-dedup.d.ts +33 -0
- package/dist/services/quota-dedup.d.ts.map +1 -0
- package/dist/services/quota-dedup.js +67 -0
- package/dist/services/quota-dedup.js.map +1 -0
- package/package.json +1 -1
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
"use strict";(()=>{var Xe=class{sessions=new Map;schedules=new Map;online=!0;listeners=new Set;upsertSessions(t){for(let n of t)this.sessions.set(n.sessionId,n);this.emit()}upsertSchedules(t){for(let n of t)this.schedules.set(n.id,n);this.emit()}applySse(t,n){if(t==="session.spawned")this.sessions.set(n.session.sessionId,n.session);else if(t==="session.update"){let s=this.sessions.get(n.sessionId);s&&this.sessions.set(n.sessionId,{...s,...n.patch})}else if(t==="session.exited"){let s=this.sessions.get(n.sessionId);s&&this.sessions.set(n.sessionId,{...s,status:"closed"})}else if(t==="schedule.created")this.schedules.set(n.schedule.id,n.schedule);else if(t==="schedule.updated"){let s=this.schedules.get(n.id);s&&this.schedules.set(n.id,{...s,...n.patch})}else if(t==="schedule.deleted")this.schedules.delete(n.id);else return;this.emit()}setOnline(t){this.online!==t&&(this.online=t,this.emit())}on(t){return this.listeners.add(t),()=>this.listeners.delete(t)}emit(){for(let t of this.listeners)t()}},F=new Xe;async function gt(){let[e,t]=await Promise.all([fetch("/api/sessions").then(a=>a.json()),fetch("/api/schedules").then(a=>a.json())]);F.upsertSessions(e.sessions??[]),F.upsertSchedules(t.schedules??[]);let n=new EventSource("/events"),s=["session.spawned","session.update","session.exited","schedule.created","schedule.updated","schedule.deleted","schedule.fired","heartbeat"];for(let a of s)n.addEventListener(a,r=>{try{let l=JSON.parse(r.data);F.applySse(a,l.body??l)}catch{}});n.onerror=()=>F.setOnline(!1),n.onopen=()=>F.setOnline(!0)}var Ze="botmux.dashboard.locale",wn={"app.name":"botmux","app.subtitle":"\u98DE\u4E66 AI CLI \u63A7\u5236\u53F0","time.secondsAgo":"{value} \u79D2\u524D","time.minutesAgo":"{value} \u5206\u949F\u524D","time.hoursAgo":"{value} \u5C0F\u65F6\u524D","nav.overview":"\u603B\u89C8","nav.sessions":"\u4F1A\u8BDD","nav.groups":"\u7FA4\u7EC4","nav.schedules":"\u5B9A\u65F6","nav.botDefaults":"Bot \u914D\u7F6E","status.live":"\u5B9E\u65F6\u8FDE\u63A5","status.disconnected":"\u8FDE\u63A5\u65AD\u5F00","status.system":"\u7CFB\u7EDF","status.light":"\u6D45\u8272","status.dark":"\u6697\u9ED1","status.language":"\u8BED\u8A00","status.theme":"\u4E3B\u9898","botOnboarding.add":"\u6DFB\u52A0\u673A\u5668\u4EBA","botOnboarding.title":"\u626B\u7801\u6DFB\u52A0\u673A\u5668\u4EBA","botOnboarding.intro":"\u7528\u98DE\u4E66 App \u626B\u7801\u521B\u5EFA PersonalAgent \u5E94\u7528\uFF0C\u6210\u529F\u540E\u4F1A\u5199\u5165\u672C\u673A bots.json\u3002","botOnboarding.starting":"\u6B63\u5728\u751F\u6210\u4E8C\u7EF4\u7801...","botOnboarding.waiting":"\u8BF7\u7528\u98DE\u4E66 App \u626B\u7801\u786E\u8BA4\u3002","botOnboarding.verifying":"\u626B\u7801\u6210\u529F\uFF0C\u6B63\u5728\u6821\u9A8C\u51ED\u8BC1...","botOnboarding.completed":"\u673A\u5668\u4EBA\u5DF2\u6DFB\u52A0\u3002","botOnboarding.failed":"\u6DFB\u52A0\u5931\u8D25","botOnboarding.openLink":"\u6253\u4E0D\u5F00\u626B\u7801\uFF1F\u5728\u6D4F\u89C8\u5668\u4E2D\u6253\u5F00","botOnboarding.close":"\u5173\u95ED","botOnboarding.restartHint":"\u5DF2\u5199\u5165 bots.json\u3002\u6267\u884C pnpm daemon:restart \u540E\u65B0\u673A\u5668\u4EBA\u751F\u6548\u3002","botOnboarding.qrAlt":"\u98DE\u4E66\u626B\u7801\u6DFB\u52A0\u673A\u5668\u4EBA\u4E8C\u7EF4\u7801","overview.title":"\u63A7\u5236\u53F0\u603B\u89C8","overview.subtitle":"\u8DE8 bot\u3001\u7FA4\u804A\u3001\u4F1A\u8BDD\u548C\u5B9A\u65F6\u4EFB\u52A1\u7684\u5B9E\u65F6\u7BA1\u63A7\u9762\u3002","overview.openSessions":"\u6D3B\u8DC3\u4F1A\u8BDD","overview.workingSessions":"\u5DE5\u4F5C\u4E2D","overview.onlineBots":"\u5728\u7EBF Bot","overview.schedules":"\u5B9A\u65F6\u4EFB\u52A1","overview.groups":"\u7FA4\u804A\u8986\u76D6","overview.enabledSchedules":"\u5DF2\u542F\u7528","overview.total":"\u603B\u8BA1","overview.active":"\u6D3B\u8DC3","overview.daemonRegistry":"daemon \u6CE8\u518C\u8868","overview.chatMatrix":"\u7FA4\u804A\u77E9\u9635","overview.recentSessions":"\u6700\u8FD1\u4F1A\u8BDD","overview.nextSchedules":"\u5373\u5C06\u6267\u884C","overview.noSessions":"\u6682\u65E0\u4F1A\u8BDD\u3002","overview.noSchedules":"\u6682\u65E0\u5B9A\u65F6\u4EFB\u52A1\u3002","sessions.title":"\u4F1A\u8BDD\u63A7\u5236","sessions.subtitle":"\u5B9A\u4F4D\u98DE\u4E66\u8BDD\u9898\u3001\u6253\u5F00 Web Terminal\u3001\u5173\u95ED\u6216\u6062\u590D CLI \u4F1A\u8BDD\u3002","sessions.search":"\u641C\u7D22\u5DE5\u4F5C\u76EE\u5F55 / \u6807\u9898 / ID","sessions.anyStatus":"\u5168\u90E8\u72B6\u6001","sessions.adoptAny":"adopt: \u5168\u90E8","sessions.adoptYes":"adopt: \u662F","sessions.adoptNo":"adopt: \u5426","sessions.activeOnly":"\u4EC5\u6D3B\u8DC3","sessions.closeSelected":"\u5173\u95ED\u9009\u4E2D","sessions.clearSelection":"\u53D6\u6D88\u9009\u62E9","sessions.selectedCount":"\u5DF2\u9009 {count} \u4E2A\u4F1A\u8BDD","sessions.bot":"bot","sessions.cli":"CLI","sessions.status":"\u72B6\u6001","sessions.titleCol":"\u6807\u9898","sessions.workingDir":"\u5DE5\u4F5C\u76EE\u5F55","sessions.created":"\u521B\u5EFA","sessions.last":"\u6700\u8FD1","sessions.adopt":"\u63A5\u5165","sessions.actions":"\u64CD\u4F5C","sessions.details":"\u8BE6\u60C5","sessions.copy":"\u590D\u5236","sessions.copied":"\u5DF2\u590D\u5236","sessions.locate":"\u5B9A\u4F4D\u8BDD\u9898","sessions.locating":"\u53D1\u9001\u4E2D...","sessions.cooldown":"\u51B7\u5374 {seconds}s","sessions.openTerminal":"\u7EC8\u7AEF","sessions.close":"\u5173\u95ED\u4F1A\u8BDD","sessions.resume":"\u6062\u590D\u4F1A\u8BDD","sessions.dismiss":"\u5173\u95ED","sessions.closeConfirm":"\u5173\u95ED\u8FD9\u4E2A\u4F1A\u8BDD\uFF1F","sessions.resumeFailed":"\u6062\u590D\u5931\u8D25","sessions.closeBulkConfirm":"\u5173\u95ED\u9009\u4E2D\u7684 {count} \u4E2A\u4F1A\u8BDD\uFF1F","sessions.empty":"\u6CA1\u6709\u7B26\u5408\u6761\u4EF6\u7684\u4F1A\u8BDD\u3002","groups.title":"\u7FA4\u7EC4\u4E0E Bot","groups.subtitle":"\u67E5\u770B chat x bot \u8986\u76D6\u77E9\u9635\uFF0C\u7BA1\u7406\u62C9\u7FA4\u3001oncall\u3001\u9000\u7FA4\u548C\u89E3\u6563\u3002","groups.search":"\u641C\u7D22\u7FA4\u540D / ID / owner","groups.missingOnly":"\u4EC5\u7F3A bot","groups.refresh":"\u5237\u65B0","groups.create":"\u65B0\u5EFA\u7FA4","groups.chat":"\u7FA4\u804A","groups.actions":"\u64CD\u4F5C","groups.addBots":"\u6DFB\u52A0 bot","groups.manage":"\u7BA1\u7406","groups.empty":"\u6CA1\u6709\u7B26\u5408\u6761\u4EF6\u7684\u7FA4\u804A\u3002","groups.createTitle":"\u65B0\u5EFA\u7FA4\u804A","groups.createHelp":"\u9009\u62E9\u8981\u9080\u8BF7\u7684 bot\u3002dashboard \u4F1A\u81EA\u52A8\u9009\u62E9\u5728\u7EBF daemon \u4F5C\u4E3A\u521B\u5EFA\u8005\u3002","groups.name":"\u7FA4\u540D","groups.namePlaceholder":"\u4F8B\u5982 AI ChangeLog","groups.bindDir":"\u7ED1\u5B9A\u76EE\u5F55","groups.bindDirHelp":"\u65B0\u8BDD\u9898\u76F4\u63A5\u7528\u8BE5\u76EE\u5F55\u542F\u52A8 CLI\uFF0C\u8DF3\u8FC7 repo \u9009\u62E9\u3002","groups.botPicker":"Bot","groups.createSubmit":"\u521B\u5EFA","groups.cancel":"\u53D6\u6D88","groups.successTitle":"\u7FA4\u521B\u5EFA\u6210\u529F","groups.openGroup":"\u6253\u5F00\u65B0\u7FA4","groups.manageTitle":"\u7BA1\u7406 {name}","groups.owner":"\u7FA4\u4E3B","groups.oncall":"Oncall \u6A21\u5F0F","groups.oncallHelp":"\u5F00\u542F\u540E\uFF0C\u7FA4\u5185\u6210\u5458\u53EF @ \u673A\u5668\u4EBA\uFF1B\u65B0\u8BDD\u9898\u76F4\u63A5\u4F7F\u7528\u7ED1\u5B9A\u76EE\u5F55\u3002","groups.leaveTitle":"\u9009\u62E9\u673A\u5668\u4EBA\u9000\u51FA\u7FA4\u804A","groups.leaveSelected":"\u9009\u4E2D\u673A\u5668\u4EBA\u9000\u51FA\u7FA4\u804A","groups.disband":"\u89E3\u6563\u7FA4\u804A","groups.dangerHint":"\u89E3\u6563\u4EC5\u5F53\u5728\u7FA4\u673A\u5668\u4EBA\u662F\u7FA4\u4E3B\u65F6\u624D\u4F1A\u6210\u529F\uFF0C\u5426\u5219\u5EFA\u8BAE\u4F7F\u7528\u9000\u51FA\u7FA4\u804A\u3002","groups.save":"\u4FDD\u5B58","groups.needWorkingDir":"\u8BF7\u586B\u5DE5\u4F5C\u76EE\u5F55","groups.noBotsOnline":"\u6CA1\u6709\u5728\u7EBF bot\u3002\u8BF7\u5148\u91CD\u542F daemon\u3002","schedules.title":"\u5B9A\u65F6\u4EFB\u52A1","schedules.subtitle":"\u8DE8 daemon \u67E5\u770B\u3001\u6682\u505C\u3001\u6062\u590D\u548C\u7ACB\u5373\u89E6\u53D1\u5B9A\u65F6\u4EFB\u52A1\u3002","schedules.search":"\u641C\u7D22\u540D\u79F0 / prompt / \u5DE5\u4F5C\u76EE\u5F55","schedules.anyKind":"\u5168\u90E8\u7C7B\u578B","schedules.enabledOnly":"\u4EC5\u542F\u7528","schedules.name":"\u540D\u79F0","schedules.bot":"bot","schedules.schedule":"\u89C4\u5219","schedules.next":"\u4E0B\u6B21","schedules.last":"\u4E0A\u6B21","schedules.repeat":"\u91CD\u590D","schedules.enabled":"\u542F\u7528","schedules.actions":"\u64CD\u4F5C","schedules.runNow":"\u7ACB\u5373\u8FD0\u884C","schedules.pause":"\u6682\u505C","schedules.resume":"\u6062\u590D","schedules.empty":"\u6682\u65E0\u5B9A\u65F6\u4EFB\u52A1\u3002","botDefaults.title":"Bot \u9ED8\u8BA4\u914D\u7F6E","botDefaults.subtitle":"\u7BA1\u7406\u6BCF\u4E2A bot \u7684\u9ED8\u8BA4\u884C\u4E3A\uFF1A\u65B0\u7FA4 oncall \u81EA\u52A8\u7ED1\u5B9A\u3001\u5361\u7247\u7B7E\u540D\u7B49\u3002","botDefaults.search":"\u641C\u7D22 bot \u540D / app id","botDefaults.refresh":"\u5237\u65B0","botDefaults.sectionOncall":"\u65B0\u7FA4 Oncall","botDefaults.sectionBrand":"\u5361\u7247\u7B7E\u540D","botDefaults.warning":"\u5F00\u542F\u540E\uFF0C\u6CA1\u6709 oncall binding \u7684\u7FA4\u4F1A\u5728\u4E0B\u6B21\u5F00\u65B0\u8BDD\u9898\u65F6\u81EA\u52A8\u7ED1\u5B9A\u5230\u8BE5\u76EE\u5F55\uFF1B\u624B\u52A8\u7ED1\u5B9A\u6216\u624B\u52A8\u89E3\u7ED1\u8FC7\u7684\u7FA4\u4E0D\u4F1A\u88AB\u8986\u76D6\u3002","botDefaults.empty":"\u6CA1\u6709\u5728\u7EBF bot\u3002\u5148 botmux restart \u8BA9 daemon \u4E0A\u7EBF\u3002","botDefaults.defaultOncall":"\u9ED8\u8BA4\u8FDB\u5165 oncall \u6A21\u5F0F","botDefaults.defaultOncallHelp":"\u6240\u6709\u672A\u7ED1\u5B9A\u7684\u7FA4\u4E0B\u6B21\u5F00\u8BDD\u9898\u81EA\u52A8\u7ED1\u5B9A","botDefaults.workingDir":"\u9ED8\u8BA4\u5DE5\u4F5C\u76EE\u5F55","botDefaults.lastEnabled":"\u4E0A\u6B21\u542F\u7528\u65F6\u95F4","botDefaults.autobound":"\u5DF2\u81EA\u52A8\u7ED1\u5B9A {count} \u4E2A\u7FA4","botDefaults.save":"\u4FDD\u5B58","botDefaults.required":"\u5F00\u542F\u65F6\u5FC5\u987B\u586B\u5DE5\u4F5C\u76EE\u5F55","botDefaults.brandLabel":"\u4E2A\u6027\u7B7E\u540D\uFF08\u5361\u7247\u9875\u811A\uFF09","botDefaults.brandLabelHelp":"\u8BE5 bot \u53D1\u51FA\u7684\u5361\u7247\u9875\u811A\u7B7E\u540D\u3002\u7559\u7A7A\u4FDD\u5B58\uFF1D\u4E0D\u663E\u793A\uFF1B\u586B\u5199\uFF1D\u81EA\u5B9A\u4E49\uFF08\u652F\u6301 markdown\uFF0C\u5982 [Acme](https://\u2026)\uFF09\uFF1B\u6062\u590D\u9ED8\u8BA4\uFF1D\u663E\u793A botmux\u3002","botDefaults.brandLabelPlaceholder":"\u9ED8\u8BA4\uFF1Abotmux\uFF08\u7559\u7A7A\u5219\u4E0D\u663E\u793A\uFF09","botDefaults.brandSave":"\u4FDD\u5B58\u7B7E\u540D","botDefaults.brandReset":"\u6062\u590D\u9ED8\u8BA4","botDefaults.brandStateDefault":"\u5F53\u524D\uFF1A\u9ED8\u8BA4 botmux","botDefaults.brandStateOff":"\u5F53\u524D\uFF1A\u5DF2\u5173\u95ED","botDefaults.brandStateCustom":"\u5F53\u524D\uFF1A\u81EA\u5B9A\u4E49","botDefaults.sectionCard":"\u5361\u7247\u884C\u4E3A","botDefaults.disableStreaming":"\u5173\u95ED\u98DE\u4E66\u6D41\u5F0F\u5361\u7247","botDefaults.disableStreamingHelp":"\u4E0D\u518D\u53D1\u5B9E\u65F6\u5237\u65B0\u7684\u4F1A\u8BDD\u72B6\u6001\u5361\uFF08\u542B\u300C\u6253\u5F00\u7EC8\u7AEF\u300D\u5165\u53E3\uFF09\uFF1B\u4EFB\u52A1\u6700\u7EC8\u7ED3\u679C\u4ECD\u901A\u8FC7\u6D88\u606F\u9001\u8FBE\u3002\u9002\u5408\u5ACC\u6D41\u5F0F\u5361\u7247\u70E6\u7684\u573A\u666F\u3002","botDefaults.writableLink":"\u5361\u7247\u4E0A\u76F4\u63A5\u7ED9\u53EF\u64CD\u4F5C\uFF08\u53EF\u5199\uFF09\u7EC8\u7AEF\u94FE\u63A5","botDefaults.writableLinkHelp":"\u26A0\uFE0F \u5728\u6D41\u5F0F\u5361\u7247\u6B63\u6587\u76F4\u63A5\u8D34\u51FA\u53EF\u5199\u7EC8\u7AEF\u94FE\u63A5\uFF0C\u7FA4\u5185\u4EFB\u4F55\u4EBA\u90FD\u80FD\u70B9\u5F00\u5E76\u64CD\u63A7\u7EC8\u7AEF\u3002\u4E0D\u52FE\uFF1D\u4FDD\u6301\u73B0\u72B6\uFF08\u8D70\u300C\u83B7\u53D6\u64CD\u4F5C\u94FE\u63A5\u300D\u6309\u94AE\u79C1\u804A\u53D1\u7ED9\u70B9\u51FB\u8005\uFF09\u3002","botDefaults.writableLinkMoot":"\u5DF2\u5173\u95ED\u6D41\u5F0F\u5361\u7247\uFF0C\u672C\u9879\u4E0D\u751F\u6548","botDefaults.privateCard":"/card \u53D1\u79C1\u5BC6\u5361\u7247\uFF08\u4EC5\u6388\u6743\u4EBA\u53EF\u89C1\uFF09","botDefaults.privateCardHelp":"\u5F00\u542F\u540E /card \u6539\u7528\u300C\u4EC5\u7279\u5B9A\u4EBA\u53EF\u89C1\u300D\u7684\u4E34\u65F6\u5361\u7247\uFF1A\u53EA\u53D1\u7ED9 owner\uFF08allowedUsers\uFF09\uFF0C/grant \u6388\u6743\u5BF9\u8BDD\u7684\u4EBA\u548C\u7FA4\u91CC\u5176\u4ED6\u4EBA\u90FD\u770B\u4E0D\u5230\u3002\u4EE3\u4EF7\uFF1A\u662F\u9759\u6001\u5FEB\u7167\u3001\u4E0D\u4F1A\u5B9E\u65F6\u5237\u65B0\uFF1B\u4E14\u4EC5\u666E\u901A\u7FA4\u53EF\u7528\uFF08\u8BDD\u9898\u7FA4 / \u5355\u804A\u4F1A\u5931\u8D25\uFF0C\u4E0D\u964D\u7EA7\uFF09\u3002\u53EA\u5F71\u54CD /card \u547D\u4EE4\uFF0C\u81EA\u52A8\u6D41\u5F0F\u5361\u4E0D\u53D8\u3002","botDefaults.cardPrefSaved":"\u5DF2\u4FDD\u5B58","botDefaults.sectionRole":"\u9ED8\u8BA4\u89D2\u8272","botDefaults.roleHelp":"\u8BE5 bot \u7684\u9ED8\u8BA4\u4EBA\u8BBE\uFF08\u8DE8\u7FA4\u751F\u6548\uFF09\uFF0C\u4F1A\u6CE8\u5165\u5230\u8BE5 bot \u5728\u5404\u7FA4\u7684\u4F1A\u8BDD\u91CC\uFF1B\u5355\u4E2A\u7FA4\u53EF\u5728\u300C\u89D2\u8272\u300D\u9875\u5355\u72EC\u8986\u76D6\u3002\u7559\u7A7A\u4FDD\u5B58\uFF1D\u5220\u9664\u3002","botDefaults.rolePlaceholder":"\u4F8B\u5982\uFF1A\u4F60\u662F\u540E\u7AEF\u6392\u969C\u52A9\u624B\uFF0C\u56DE\u7B54\u7B80\u6D01\u3001\u4F18\u5148\u7ED9\u53EF\u6267\u884C\u547D\u4EE4\u2026","botDefaults.roleSave":"\u4FDD\u5B58\u89D2\u8272","botDefaults.roleDelete":"\u5220\u9664","botDefaults.roleSaved":"\u5DF2\u4FDD\u5B58","botDefaults.roleDeleted":"\u5DF2\u5220\u9664","botDefaults.roleLoadErr":"\u89D2\u8272\u52A0\u8F7D\u5931\u8D25","botDefaults.sectionAutoStart":"\u4E3B\u52A8\u5F00\u5DE5","botDefaults.autoStartJoin":"\u88AB\u62C9\u8FDB\u65B0\u7FA4\u81EA\u52A8\u5F00\u5DE5","botDefaults.autoStartJoinHelp":"\u5F00\u542F\u540E\uFF0C\u673A\u5668\u4EBA\u4E00\u88AB\u62C9\u8FDB\u65B0\u7FA4\uFF08\u7FA4\u91CC\u6709\u6388\u6743\u7528\u6237 allowedUsers \u65F6\uFF09\u5C31\u81EA\u52A8\u5F00\u4E00\u4E2A\u4F1A\u8BDD\u5F00\u59CB\u5DE5\u4F5C\uFF0C\u65E0\u9700 @\u3002\u5728\u673A\u5668\u4EBA\u7684\u9ED8\u8BA4\u5DE5\u4F5C\u76EE\u5F55\u542F\u52A8\uFF1B\u672A\u914D\u7F6E\u9ED8\u8BA4\u76EE\u5F55\u65F6\u5148\u5F39\u4ED3\u5E93\u9009\u62E9\u5361\u8BA9\u4F60\u9009\u3002\u524D\u63D0\uFF1A\u9700\u5728\u98DE\u4E66\u5F00\u653E\u5E73\u53F0\u4E3A\u672C\u5E94\u7528\u8BA2\u9605\u300C\u673A\u5668\u4EBA\u8FDB\u7FA4\u300D\u4E8B\u4EF6 im.chat.member.bot.added_v1\uFF0C\u5E76\u5F00\u901A\u7FA4\u6210\u5458\u8BFB\u53D6\u6743\u9650\u3002","botDefaults.autoStartJoinPrompt":"\u5165\u7FA4\u9996\u8F6E prompt\uFF08\u53EF\u9009\uFF09","botDefaults.autoStartJoinPromptPlaceholder":"\u586B\u4E86\uFF1D\u4F5C\u4E3A\u5165\u7FA4\u540E\u7684\u7B2C\u4E00\u6761\u4EFB\u52A1\uFF1B\u7559\u7A7A\uFF1D\u673A\u5668\u4EBA\u81EA\u5DF1\u770B\u7FA4\u91CC\u4FE1\u606F\u51B3\u5B9A\u505A\u4EC0\u4E48","botDefaults.autoStartJoinPromptSave":"\u4FDD\u5B58 prompt","botDefaults.autoStartTopic":"\u8BDD\u9898\u7FA4\u65B0\u8BDD\u9898\u81EA\u52A8\u5F00\u5DE5","botDefaults.autoStartTopicHelp":"\u5F00\u542F\u540E\uFF0C\u5728\u8BDD\u9898\u7FA4\u91CC\u6BCF\u5F53\u6709\u4EBA\u65B0\u5F00\u4E00\u4E2A\u8BDD\u9898\uFF0C\u673A\u5668\u4EBA\u5C31\u4F1A\u81EA\u52A8\u63A5\u5165\u8BE5\u8BDD\u9898\u3001\u628A\u9996\u6761\u6D88\u606F\u5F53\u4F5C\u4EFB\u52A1\u5F00\u59CB\u5904\u7406\uFF0C\u65E0\u9700 @\u3002\u4EC5\u5BF9\u8BDD\u9898\u7FA4\u751F\u6548\uFF0C\u666E\u901A\u7FA4\u4E0D\u53D7\u5F71\u54CD\u3002","nav.roles":"\u89D2\u8272\u7BA1\u7406","roles.title":"\u89D2\u8272\u7BA1\u7406","roles.subtitle":"\u4E3A\u6BCF\u4E2A\u7FA4\u7EC4\u7684\u6BCF\u4E2A Bot \u5355\u72EC\u8BBE\u7F6E\u89D2\u8272\u63D0\u793A\u8BCD\uFF0CBot \u5728\u8BE5\u7FA4\u4E2D\u4F1A\u4EE5\u6B64\u89D2\u8272\u884C\u4E8B\u3002","roles.search":"\u641C\u7D22\u7FA4\u540D/Bot/ID","roles.refresh":"\u5237\u65B0","roles.selectHint":"\u2190 \u5C55\u5F00\u7FA4\u7EC4\uFF0C\u9009\u62E9\u4E00\u4E2A Bot \u6765\u7F16\u8F91\u89D2\u8272","roles.editorPlaceholder":`\u8F93\u5165\u89D2\u8272\u63CF\u8FF0\uFF0C\u4F8B\u5982\uFF1A
|
|
1
|
+
"use strict";(()=>{var tt=class{sessions=new Map;schedules=new Map;online=!0;listeners=new Set;upsertSessions(t){for(let o of t)this.sessions.set(o.sessionId,o);this.emit()}upsertSchedules(t){for(let o of t)this.schedules.set(o.id,o);this.emit()}applySse(t,o){if(t==="session.spawned")this.sessions.set(o.session.sessionId,o.session);else if(t==="session.update"){let s=this.sessions.get(o.sessionId);s&&this.sessions.set(o.sessionId,{...s,...o.patch})}else if(t==="session.exited"){let s=this.sessions.get(o.sessionId);s&&this.sessions.set(o.sessionId,{...s,status:"closed"})}else if(t==="schedule.created")this.schedules.set(o.schedule.id,o.schedule);else if(t==="schedule.updated"){let s=this.schedules.get(o.id);s&&this.schedules.set(o.id,{...s,...o.patch})}else if(t==="schedule.deleted")this.schedules.delete(o.id);else return;this.emit()}setOnline(t){this.online!==t&&(this.online=t,this.emit())}on(t){return this.listeners.add(t),()=>this.listeners.delete(t)}emit(){for(let t of this.listeners)t()}},W=new tt;async function yt(){let[e,t]=await Promise.all([fetch("/api/sessions").then(a=>a.json()),fetch("/api/schedules").then(a=>a.json())]);W.upsertSessions(e.sessions??[]),W.upsertSchedules(t.schedules??[]);let o=new EventSource("/events"),s=["session.spawned","session.update","session.exited","schedule.created","schedule.updated","schedule.deleted","schedule.fired","heartbeat"];for(let a of s)o.addEventListener(a,r=>{try{let i=JSON.parse(r.data);W.applySse(a,i.body??i)}catch{}});o.onerror=()=>W.setOnline(!1),o.onopen=()=>W.setOnline(!0)}var nt="botmux.dashboard.locale",gn={"app.name":"botmux","app.subtitle":"\u98DE\u4E66 AI CLI \u63A7\u5236\u53F0","time.secondsAgo":"{value} \u79D2\u524D","time.minutesAgo":"{value} \u5206\u949F\u524D","time.hoursAgo":"{value} \u5C0F\u65F6\u524D","nav.overview":"\u603B\u89C8","nav.sessions":"\u4F1A\u8BDD","nav.groups":"\u7FA4\u7EC4","nav.schedules":"\u5B9A\u65F6","nav.botDefaults":"Bot \u914D\u7F6E","status.live":"\u5B9E\u65F6\u8FDE\u63A5","status.disconnected":"\u8FDE\u63A5\u65AD\u5F00","status.system":"\u7CFB\u7EDF","status.light":"\u6D45\u8272","status.dark":"\u6697\u9ED1","status.language":"\u8BED\u8A00","status.theme":"\u4E3B\u9898","botOnboarding.add":"\u6DFB\u52A0\u673A\u5668\u4EBA","botOnboarding.title":"\u626B\u7801\u6DFB\u52A0\u673A\u5668\u4EBA","botOnboarding.intro":"\u7528\u98DE\u4E66 App \u626B\u7801\u521B\u5EFA PersonalAgent \u5E94\u7528\uFF0C\u6210\u529F\u540E\u4F1A\u5199\u5165\u672C\u673A bots.json\u3002","botOnboarding.starting":"\u6B63\u5728\u751F\u6210\u4E8C\u7EF4\u7801...","botOnboarding.waiting":"\u8BF7\u7528\u98DE\u4E66 App \u626B\u7801\u786E\u8BA4\u3002","botOnboarding.verifying":"\u626B\u7801\u6210\u529F\uFF0C\u6B63\u5728\u6821\u9A8C\u51ED\u8BC1...","botOnboarding.completed":"\u673A\u5668\u4EBA\u5DF2\u6DFB\u52A0\u3002","botOnboarding.failed":"\u6DFB\u52A0\u5931\u8D25","botOnboarding.openLink":"\u6253\u4E0D\u5F00\u626B\u7801\uFF1F\u5728\u6D4F\u89C8\u5668\u4E2D\u6253\u5F00","botOnboarding.close":"\u5173\u95ED","botOnboarding.restartHint":"\u5DF2\u5199\u5165 bots.json\u3002\u6267\u884C pnpm daemon:restart \u540E\u65B0\u673A\u5668\u4EBA\u751F\u6548\u3002","botOnboarding.qrAlt":"\u98DE\u4E66\u626B\u7801\u6DFB\u52A0\u673A\u5668\u4EBA\u4E8C\u7EF4\u7801","overview.title":"\u63A7\u5236\u53F0\u603B\u89C8","overview.subtitle":"\u8DE8 bot\u3001\u7FA4\u804A\u3001\u4F1A\u8BDD\u548C\u5B9A\u65F6\u4EFB\u52A1\u7684\u5B9E\u65F6\u7BA1\u63A7\u9762\u3002","overview.openSessions":"\u6D3B\u8DC3\u4F1A\u8BDD","overview.workingSessions":"\u5DE5\u4F5C\u4E2D","overview.onlineBots":"\u5728\u7EBF Bot","overview.schedules":"\u5B9A\u65F6\u4EFB\u52A1","overview.groups":"\u7FA4\u804A\u8986\u76D6","overview.enabledSchedules":"\u5DF2\u542F\u7528","overview.total":"\u603B\u8BA1","overview.active":"\u6D3B\u8DC3","overview.daemonRegistry":"daemon \u6CE8\u518C\u8868","overview.chatMatrix":"\u7FA4\u804A\u77E9\u9635","overview.recentSessions":"\u6700\u8FD1\u4F1A\u8BDD","overview.nextSchedules":"\u5373\u5C06\u6267\u884C","overview.noSessions":"\u6682\u65E0\u4F1A\u8BDD\u3002","overview.noSchedules":"\u6682\u65E0\u5B9A\u65F6\u4EFB\u52A1\u3002","sessions.title":"\u4F1A\u8BDD\u63A7\u5236","sessions.subtitle":"\u5B9A\u4F4D\u98DE\u4E66\u8BDD\u9898\u3001\u6253\u5F00 Web Terminal\u3001\u5173\u95ED\u6216\u6062\u590D CLI \u4F1A\u8BDD\u3002","sessions.search":"\u641C\u7D22\u5DE5\u4F5C\u76EE\u5F55 / \u6807\u9898 / ID","sessions.anyStatus":"\u5168\u90E8\u72B6\u6001","sessions.adoptAny":"adopt: \u5168\u90E8","sessions.adoptYes":"adopt: \u662F","sessions.adoptNo":"adopt: \u5426","sessions.activeOnly":"\u4EC5\u6D3B\u8DC3","sessions.closeSelected":"\u5173\u95ED\u9009\u4E2D","sessions.clearSelection":"\u53D6\u6D88\u9009\u62E9","sessions.selectedCount":"\u5DF2\u9009 {count} \u4E2A\u4F1A\u8BDD","sessions.bot":"bot","sessions.cli":"CLI","sessions.status":"\u72B6\u6001","sessions.titleCol":"\u6807\u9898","sessions.workingDir":"\u5DE5\u4F5C\u76EE\u5F55","sessions.created":"\u521B\u5EFA","sessions.last":"\u6700\u8FD1","sessions.adopt":"\u63A5\u5165","sessions.actions":"\u64CD\u4F5C","sessions.details":"\u8BE6\u60C5","sessions.copy":"\u590D\u5236","sessions.copied":"\u5DF2\u590D\u5236","sessions.locate":"\u5B9A\u4F4D\u8BDD\u9898","sessions.locating":"\u53D1\u9001\u4E2D...","sessions.cooldown":"\u51B7\u5374 {seconds}s","sessions.openTerminal":"\u7EC8\u7AEF","sessions.close":"\u5173\u95ED\u4F1A\u8BDD","sessions.resume":"\u6062\u590D\u4F1A\u8BDD","sessions.dismiss":"\u5173\u95ED","sessions.closeConfirm":"\u5173\u95ED\u8FD9\u4E2A\u4F1A\u8BDD\uFF1F","sessions.resumeFailed":"\u6062\u590D\u5931\u8D25","sessions.closeBulkConfirm":"\u5173\u95ED\u9009\u4E2D\u7684 {count} \u4E2A\u4F1A\u8BDD\uFF1F","sessions.empty":"\u6CA1\u6709\u7B26\u5408\u6761\u4EF6\u7684\u4F1A\u8BDD\u3002","groups.title":"\u7FA4\u7EC4\u4E0E Bot","groups.subtitle":"\u67E5\u770B chat x bot \u8986\u76D6\u77E9\u9635\uFF0C\u7BA1\u7406\u62C9\u7FA4\u3001oncall\u3001\u9000\u7FA4\u548C\u89E3\u6563\u3002","groups.search":"\u641C\u7D22\u7FA4\u540D / ID / owner","groups.missingOnly":"\u4EC5\u7F3A bot","groups.refresh":"\u5237\u65B0","groups.create":"\u65B0\u5EFA\u7FA4","groups.chat":"\u7FA4\u804A","groups.actions":"\u64CD\u4F5C","groups.addBots":"\u6DFB\u52A0 bot","groups.manage":"\u7BA1\u7406","groups.empty":"\u6CA1\u6709\u7B26\u5408\u6761\u4EF6\u7684\u7FA4\u804A\u3002","groups.createTitle":"\u65B0\u5EFA\u7FA4\u804A","groups.createHelp":"\u9009\u62E9\u8981\u9080\u8BF7\u7684 bot\u3002dashboard \u4F1A\u81EA\u52A8\u9009\u62E9\u5728\u7EBF daemon \u4F5C\u4E3A\u521B\u5EFA\u8005\u3002","groups.name":"\u7FA4\u540D","groups.namePlaceholder":"\u4F8B\u5982 AI ChangeLog","groups.bindDir":"\u7ED1\u5B9A\u76EE\u5F55","groups.bindDirHelp":"\u65B0\u8BDD\u9898\u76F4\u63A5\u7528\u8BE5\u76EE\u5F55\u542F\u52A8 CLI\uFF0C\u8DF3\u8FC7 repo \u9009\u62E9\u3002","groups.botPicker":"Bot","groups.createSubmit":"\u521B\u5EFA","groups.cancel":"\u53D6\u6D88","groups.successTitle":"\u7FA4\u521B\u5EFA\u6210\u529F","groups.openGroup":"\u6253\u5F00\u65B0\u7FA4","groups.manageTitle":"\u7BA1\u7406 {name}","groups.owner":"\u7FA4\u4E3B","groups.oncall":"Oncall \u6A21\u5F0F","groups.oncallHelp":"\u5F00\u542F\u540E\uFF0C\u7FA4\u5185\u6210\u5458\u53EF @ \u673A\u5668\u4EBA\uFF1B\u65B0\u8BDD\u9898\u76F4\u63A5\u4F7F\u7528\u7ED1\u5B9A\u76EE\u5F55\u3002","groups.leaveTitle":"\u9009\u62E9\u673A\u5668\u4EBA\u9000\u51FA\u7FA4\u804A","groups.leaveSelected":"\u9009\u4E2D\u673A\u5668\u4EBA\u9000\u51FA\u7FA4\u804A","groups.disband":"\u89E3\u6563\u7FA4\u804A","groups.dangerHint":"\u89E3\u6563\u4EC5\u5F53\u5728\u7FA4\u673A\u5668\u4EBA\u662F\u7FA4\u4E3B\u65F6\u624D\u4F1A\u6210\u529F\uFF0C\u5426\u5219\u5EFA\u8BAE\u4F7F\u7528\u9000\u51FA\u7FA4\u804A\u3002","groups.save":"\u4FDD\u5B58","groups.needWorkingDir":"\u8BF7\u586B\u5DE5\u4F5C\u76EE\u5F55","groups.noBotsOnline":"\u6CA1\u6709\u5728\u7EBF bot\u3002\u8BF7\u5148\u91CD\u542F daemon\u3002","schedules.title":"\u5B9A\u65F6\u4EFB\u52A1","schedules.subtitle":"\u8DE8 daemon \u67E5\u770B\u3001\u6682\u505C\u3001\u6062\u590D\u548C\u7ACB\u5373\u89E6\u53D1\u5B9A\u65F6\u4EFB\u52A1\u3002","schedules.search":"\u641C\u7D22\u540D\u79F0 / prompt / \u5DE5\u4F5C\u76EE\u5F55","schedules.anyKind":"\u5168\u90E8\u7C7B\u578B","schedules.enabledOnly":"\u4EC5\u542F\u7528","schedules.name":"\u540D\u79F0","schedules.bot":"bot","schedules.schedule":"\u89C4\u5219","schedules.next":"\u4E0B\u6B21","schedules.last":"\u4E0A\u6B21","schedules.repeat":"\u91CD\u590D","schedules.enabled":"\u542F\u7528","schedules.actions":"\u64CD\u4F5C","schedules.runNow":"\u7ACB\u5373\u8FD0\u884C","schedules.pause":"\u6682\u505C","schedules.resume":"\u6062\u590D","schedules.empty":"\u6682\u65E0\u5B9A\u65F6\u4EFB\u52A1\u3002","botDefaults.title":"Bot \u9ED8\u8BA4\u914D\u7F6E","botDefaults.subtitle":"\u7BA1\u7406\u6BCF\u4E2A bot \u7684\u9ED8\u8BA4\u884C\u4E3A\uFF1A\u65B0\u7FA4 oncall \u81EA\u52A8\u7ED1\u5B9A\u3001\u5361\u7247\u7B7E\u540D\u7B49\u3002","botDefaults.search":"\u641C\u7D22 bot \u540D / app id","botDefaults.refresh":"\u5237\u65B0","botDefaults.sectionOncall":"\u65B0\u7FA4 Oncall","botDefaults.sectionBrand":"\u5361\u7247\u7B7E\u540D","botDefaults.warning":"\u5F00\u542F\u540E\uFF0C\u6CA1\u6709 oncall binding \u7684\u7FA4\u4F1A\u5728\u4E0B\u6B21\u5F00\u65B0\u8BDD\u9898\u65F6\u81EA\u52A8\u7ED1\u5B9A\u5230\u8BE5\u76EE\u5F55\uFF1B\u624B\u52A8\u7ED1\u5B9A\u6216\u624B\u52A8\u89E3\u7ED1\u8FC7\u7684\u7FA4\u4E0D\u4F1A\u88AB\u8986\u76D6\u3002","botDefaults.empty":"\u6CA1\u6709\u5728\u7EBF bot\u3002\u5148 botmux restart \u8BA9 daemon \u4E0A\u7EBF\u3002","botDefaults.defaultOncall":"\u9ED8\u8BA4\u8FDB\u5165 oncall \u6A21\u5F0F","botDefaults.defaultOncallHelp":"\u6240\u6709\u672A\u7ED1\u5B9A\u7684\u7FA4\u4E0B\u6B21\u5F00\u8BDD\u9898\u81EA\u52A8\u7ED1\u5B9A","botDefaults.workingDir":"\u9ED8\u8BA4\u5DE5\u4F5C\u76EE\u5F55","botDefaults.lastEnabled":"\u4E0A\u6B21\u542F\u7528\u65F6\u95F4","botDefaults.autobound":"\u5DF2\u81EA\u52A8\u7ED1\u5B9A {count} \u4E2A\u7FA4","botDefaults.save":"\u4FDD\u5B58","botDefaults.required":"\u5F00\u542F\u65F6\u5FC5\u987B\u586B\u5DE5\u4F5C\u76EE\u5F55","botDefaults.brandLabel":"\u4E2A\u6027\u7B7E\u540D\uFF08\u5361\u7247\u9875\u811A\uFF09","botDefaults.brandLabelHelp":"\u8BE5 bot \u53D1\u51FA\u7684\u5361\u7247\u9875\u811A\u7B7E\u540D\u3002\u7559\u7A7A\u4FDD\u5B58\uFF1D\u4E0D\u663E\u793A\uFF1B\u586B\u5199\uFF1D\u81EA\u5B9A\u4E49\uFF08\u652F\u6301 markdown\uFF0C\u5982 [Acme](https://\u2026)\uFF09\uFF1B\u6062\u590D\u9ED8\u8BA4\uFF1D\u663E\u793A botmux\u3002","botDefaults.brandLabelPlaceholder":"\u9ED8\u8BA4\uFF1Abotmux\uFF08\u7559\u7A7A\u5219\u4E0D\u663E\u793A\uFF09","botDefaults.brandSave":"\u4FDD\u5B58\u7B7E\u540D","botDefaults.brandReset":"\u6062\u590D\u9ED8\u8BA4","botDefaults.brandStateDefault":"\u5F53\u524D\uFF1A\u9ED8\u8BA4 botmux","botDefaults.brandStateOff":"\u5F53\u524D\uFF1A\u5DF2\u5173\u95ED","botDefaults.brandStateCustom":"\u5F53\u524D\uFF1A\u81EA\u5B9A\u4E49","botDefaults.sectionCard":"\u5361\u7247\u884C\u4E3A","botDefaults.disableStreaming":"\u5173\u95ED\u98DE\u4E66\u6D41\u5F0F\u5361\u7247","botDefaults.disableStreamingHelp":"\u4E0D\u518D\u53D1\u5B9E\u65F6\u5237\u65B0\u7684\u4F1A\u8BDD\u72B6\u6001\u5361\uFF08\u542B\u300C\u6253\u5F00\u7EC8\u7AEF\u300D\u5165\u53E3\uFF09\uFF1B\u4EFB\u52A1\u6700\u7EC8\u7ED3\u679C\u4ECD\u901A\u8FC7\u6D88\u606F\u9001\u8FBE\u3002\u9002\u5408\u5ACC\u6D41\u5F0F\u5361\u7247\u70E6\u7684\u573A\u666F\u3002","botDefaults.writableLink":"\u5361\u7247\u4E0A\u76F4\u63A5\u7ED9\u53EF\u64CD\u4F5C\uFF08\u53EF\u5199\uFF09\u7EC8\u7AEF\u94FE\u63A5","botDefaults.writableLinkHelp":"\u26A0\uFE0F \u5728\u6D41\u5F0F\u5361\u7247\u6B63\u6587\u76F4\u63A5\u8D34\u51FA\u53EF\u5199\u7EC8\u7AEF\u94FE\u63A5\uFF0C\u7FA4\u5185\u4EFB\u4F55\u4EBA\u90FD\u80FD\u70B9\u5F00\u5E76\u64CD\u63A7\u7EC8\u7AEF\u3002\u4E0D\u52FE\uFF1D\u4FDD\u6301\u73B0\u72B6\uFF08\u8D70\u300C\u83B7\u53D6\u64CD\u4F5C\u94FE\u63A5\u300D\u6309\u94AE\u79C1\u804A\u53D1\u7ED9\u70B9\u51FB\u8005\uFF09\u3002","botDefaults.writableLinkMoot":"\u5DF2\u5173\u95ED\u6D41\u5F0F\u5361\u7247\uFF0C\u672C\u9879\u4E0D\u751F\u6548","botDefaults.privateCard":"/card \u53D1\u79C1\u5BC6\u5361\u7247\uFF08\u4EC5\u6388\u6743\u4EBA\u53EF\u89C1\uFF09","botDefaults.privateCardHelp":"\u5F00\u542F\u540E /card \u6539\u7528\u300C\u4EC5\u7279\u5B9A\u4EBA\u53EF\u89C1\u300D\u7684\u4E34\u65F6\u5361\u7247\uFF1A\u53EA\u53D1\u7ED9 owner\uFF08allowedUsers\uFF09\uFF0C/grant \u6388\u6743\u5BF9\u8BDD\u7684\u4EBA\u548C\u7FA4\u91CC\u5176\u4ED6\u4EBA\u90FD\u770B\u4E0D\u5230\u3002\u4EE3\u4EF7\uFF1A\u662F\u9759\u6001\u5FEB\u7167\u3001\u4E0D\u4F1A\u5B9E\u65F6\u5237\u65B0\uFF1B\u4E14\u4EC5\u666E\u901A\u7FA4\u53EF\u7528\uFF08\u8BDD\u9898\u7FA4 / \u5355\u804A\u4F1A\u5931\u8D25\uFF0C\u4E0D\u964D\u7EA7\uFF09\u3002\u53EA\u5F71\u54CD /card \u547D\u4EE4\uFF0C\u81EA\u52A8\u6D41\u5F0F\u5361\u4E0D\u53D8\u3002","botDefaults.cardPrefSaved":"\u5DF2\u4FDD\u5B58","botDefaults.sectionRole":"\u9ED8\u8BA4\u89D2\u8272","botDefaults.roleHelp":"\u8BE5 bot \u7684\u9ED8\u8BA4\u4EBA\u8BBE\uFF08\u8DE8\u7FA4\u751F\u6548\uFF09\uFF0C\u4F1A\u6CE8\u5165\u5230\u8BE5 bot \u5728\u5404\u7FA4\u7684\u4F1A\u8BDD\u91CC\uFF1B\u5355\u4E2A\u7FA4\u53EF\u5728\u300C\u89D2\u8272\u300D\u9875\u5355\u72EC\u8986\u76D6\u3002\u7559\u7A7A\u4FDD\u5B58\uFF1D\u5220\u9664\u3002","botDefaults.rolePlaceholder":"\u4F8B\u5982\uFF1A\u4F60\u662F\u540E\u7AEF\u6392\u969C\u52A9\u624B\uFF0C\u56DE\u7B54\u7B80\u6D01\u3001\u4F18\u5148\u7ED9\u53EF\u6267\u884C\u547D\u4EE4\u2026","botDefaults.roleSave":"\u4FDD\u5B58\u89D2\u8272","botDefaults.roleDelete":"\u5220\u9664","botDefaults.roleSaved":"\u5DF2\u4FDD\u5B58","botDefaults.roleDeleted":"\u5DF2\u5220\u9664","botDefaults.roleLoadErr":"\u89D2\u8272\u52A0\u8F7D\u5931\u8D25","botDefaults.sectionAutoStart":"\u4E3B\u52A8\u5F00\u5DE5","botDefaults.autoStartJoin":"\u88AB\u62C9\u8FDB\u65B0\u7FA4\u81EA\u52A8\u5F00\u5DE5","botDefaults.autoStartJoinHelp":"\u5F00\u542F\u540E\uFF0C\u673A\u5668\u4EBA\u4E00\u88AB\u62C9\u8FDB\u65B0\u7FA4\uFF08\u7FA4\u91CC\u6709\u6388\u6743\u7528\u6237 allowedUsers \u65F6\uFF09\u5C31\u81EA\u52A8\u5F00\u4E00\u4E2A\u4F1A\u8BDD\u5F00\u59CB\u5DE5\u4F5C\uFF0C\u65E0\u9700 @\u3002\u5728\u673A\u5668\u4EBA\u7684\u9ED8\u8BA4\u5DE5\u4F5C\u76EE\u5F55\u542F\u52A8\uFF1B\u672A\u914D\u7F6E\u9ED8\u8BA4\u76EE\u5F55\u65F6\u5148\u5F39\u4ED3\u5E93\u9009\u62E9\u5361\u8BA9\u4F60\u9009\u3002\u524D\u63D0\uFF1A\u9700\u5728\u98DE\u4E66\u5F00\u653E\u5E73\u53F0\u4E3A\u672C\u5E94\u7528\u8BA2\u9605\u300C\u673A\u5668\u4EBA\u8FDB\u7FA4\u300D\u4E8B\u4EF6 im.chat.member.bot.added_v1\uFF0C\u5E76\u5F00\u901A\u7FA4\u6210\u5458\u8BFB\u53D6\u6743\u9650\u3002","botDefaults.autoStartJoinPrompt":"\u5165\u7FA4\u9996\u8F6E prompt\uFF08\u53EF\u9009\uFF09","botDefaults.autoStartJoinPromptPlaceholder":"\u586B\u4E86\uFF1D\u4F5C\u4E3A\u5165\u7FA4\u540E\u7684\u7B2C\u4E00\u6761\u4EFB\u52A1\uFF1B\u7559\u7A7A\uFF1D\u673A\u5668\u4EBA\u81EA\u5DF1\u770B\u7FA4\u91CC\u4FE1\u606F\u51B3\u5B9A\u505A\u4EC0\u4E48","botDefaults.autoStartJoinPromptSave":"\u4FDD\u5B58 prompt","botDefaults.autoStartTopic":"\u8BDD\u9898\u7FA4\u65B0\u8BDD\u9898\u81EA\u52A8\u5F00\u5DE5","botDefaults.autoStartTopicHelp":"\u5F00\u542F\u540E\uFF0C\u5728\u8BDD\u9898\u7FA4\u91CC\u6BCF\u5F53\u6709\u4EBA\u65B0\u5F00\u4E00\u4E2A\u8BDD\u9898\uFF0C\u673A\u5668\u4EBA\u5C31\u4F1A\u81EA\u52A8\u63A5\u5165\u8BE5\u8BDD\u9898\u3001\u628A\u9996\u6761\u6D88\u606F\u5F53\u4F5C\u4EFB\u52A1\u5F00\u59CB\u5904\u7406\uFF0C\u65E0\u9700 @\u3002\u4EC5\u5BF9\u8BDD\u9898\u7FA4\u751F\u6548\uFF0C\u666E\u901A\u7FA4\u4E0D\u53D7\u5F71\u54CD\u3002","botDefaults.sectionGrant":"\u6388\u6743\u4E0E\u989D\u5EA6","botDefaults.restrictGrant":"\u9650\u5236\u88AB\u6388\u6743\u4EBA\u53EA\u80FD\u7EAF\u5BF9\u8BDD","botDefaults.restrictGrantHelp":"\u5F00\u542F\u540E\uFF0C\u88AB /grant \u6388\u6743\u7684\u4EBA\uFF08owner \u81EA\u5DF1\u4E0D\u53D7\u9650\uFF09\u53EA\u80FD\u53D1\u666E\u901A\u5BF9\u8BDD\uFF0C\u6240\u6709 slash \u547D\u4EE4\u4E00\u5F8B\u62E6\u622A\uFF1Abotmux \u81EA\u5E26\u547D\u4EE4\u3001\u900F\u4F20\u547D\u4EE4\u3001/workflow\u3001/introduce\u3001/t \u4EE5\u53CA CLI \u539F\u751F\u547D\u4EE4\uFF08/help \u7B49\uFF09\u3002\u5F62\u5982 /path/to/file \u7684\u5185\u5BB9\u4E0D\u4F1A\u88AB\u8BEF\u5224\u3002","botDefaults.quotaDefault":"\u9ED8\u8BA4\u6D88\u606F\u989D\u5EA6","botDefaults.quotaPlaceholder":"\u7559\u7A7A\uFF1D\u4E0D\u914D\u7F6E\u9ED8\u8BA4\u989D\u5EA6","botDefaults.quotaHelp":"\u4E0D\u5E26\u6570\u5B57\u7684 /grant @x \u9ED8\u8BA4\u7ED9\u7684\u6D88\u606F\u6761\u6570\u3002\u7559\u7A7A\u6216\u70B9\u300C\u5173\u95ED\u300D\u53EA\u662F\u4E0D\u518D\u7ED9\u88F8 /grant \u5957\u9ED8\u8BA4\u989D\u5EA6\uFF0C\u4E0D\u4F1A\u6E05\u6389\u5DF2\u6709\u7684\u989D\u5EA6\u8BA1\u6570\uFF0C\u4E5F\u4E0D\u5F71\u54CD\u663E\u5F0F /grant @x N\u2014\u2014\u5B83\u4EEC\u7167\u5E38\u7EE7\u7EED enforce\u3002\u989D\u5EA6\u7528\u5C3D\u4F1A\u81EA\u52A8\u64A4\u9500\u8BE5\u4EBA\u6388\u6743\u3002","botDefaults.quotaSave":"\u4FDD\u5B58\u989D\u5EA6","botDefaults.quotaOff":"\u5173\u95ED","botDefaults.quotaInvalid":"\u989D\u5EA6\u5FC5\u987B\u662F\u6B63\u6574\u6570","botDefaults.quotaStateOff":"\u5F53\u524D\uFF1A\u672A\u914D\u7F6E\u9ED8\u8BA4\u989D\u5EA6","botDefaults.quotaStateOn":"\u5F53\u524D\uFF1A\u6BCF\u4EBA {count} \u6761","nav.roles":"\u89D2\u8272\u7BA1\u7406","roles.title":"\u89D2\u8272\u7BA1\u7406","roles.subtitle":"\u4E3A\u6BCF\u4E2A\u7FA4\u7EC4\u7684\u6BCF\u4E2A Bot \u5355\u72EC\u8BBE\u7F6E\u89D2\u8272\u63D0\u793A\u8BCD\uFF0CBot \u5728\u8BE5\u7FA4\u4E2D\u4F1A\u4EE5\u6B64\u89D2\u8272\u884C\u4E8B\u3002","roles.search":"\u641C\u7D22\u7FA4\u540D/Bot/ID","roles.refresh":"\u5237\u65B0","roles.selectHint":"\u2190 \u5C55\u5F00\u7FA4\u7EC4\uFF0C\u9009\u62E9\u4E00\u4E2A Bot \u6765\u7F16\u8F91\u89D2\u8272","roles.editorPlaceholder":`\u8F93\u5165\u89D2\u8272\u63CF\u8FF0\uFF0C\u4F8B\u5982\uFF1A
|
|
2
2
|
\u4F60\u662F\u672C\u7FA4\u7684\u6280\u672F\u987E\u95EE\uFF0C\u8D1F\u8D23\u56DE\u7B54\u6240\u6709\u6280\u672F\u95EE\u9898...`,"roles.configured":"\u5DF2\u914D\u7F6E","roles.unconfigured":"\u672A\u914D\u7F6E","roles.noChats":"\u6682\u65E0\u7FA4\u7EC4","roles.preview":"\u9884\u89C8","roles.previewEmpty":"\uFF08\u7A7A\u5185\u5BB9\uFF09","roles.saved":"\u5DF2\u4FDD\u5B58","roles.delete":"\u5220\u9664","roles.save":"\u4FDD\u5B58","roles.confirmDelete":"\u786E\u8BA4\u5220\u9664\u8BE5 Bot \u5728\u6B64\u7FA4\u7684\u89D2\u8272\u914D\u7F6E\uFF1F","roles.botsWithRoles":"\u4E2A Bot \u5DF2\u914D\u7F6E\u89D2\u8272","roles.emptyError":"\u89D2\u8272\u5185\u5BB9\u4E0D\u80FD\u4E3A\u7A7A\uFF0C\u8BF7\u5148\u8F93\u5165\u5185\u5BB9","roles.saveFailed":"\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5","common.none":"\u65E0","common.unknown":"\u672A\u77E5","common.now":"\u521A\u521A","common.never":"\u4ECE\u672A","nav.workflows":"\u5DE5\u4F5C\u6D41(beta)","nav.workflowCatalog":"\u76EE\u5F55","workflow.subnav.runs":"\u8FD0\u884C","workflow.subnav.catalog":"\u76EE\u5F55","workflow.searchPlaceholder":"\u641C\u7D22 runId / workflowId / chatId","workflow.filter.nonTerminal":"\u975E\u7EC8\u6001","workflow.filter.all":"\u5168\u90E8","workflow.status.pending":"\u5F85\u5F00\u59CB","workflow.status.running":"\u8FD0\u884C\u4E2D","workflow.status.waiting":"\u7B49\u5F85\u4E2D","workflow.status.effectAttempting":"\u526F\u4F5C\u7528\u4E2D","workflow.status.timedOut":"\u5DF2\u8D85\u65F6","workflow.status.succeeded":"\u6210\u529F","workflow.status.failed":"\u5931\u8D25","workflow.status.cancelled":"\u5DF2\u53D6\u6D88","workflow.table.run":"\u8FD0\u884C","workflow.table.workflow":"\u5DE5\u4F5C\u6D41","workflow.table.status":"\u72B6\u6001","workflow.table.lastSeq":"\u6700\u540E\u5E8F\u53F7","workflow.table.dangling":"\u60AC\u6302 dEf/dAct/dWait","workflow.table.updated":"\u66F4\u65B0\u65F6\u95F4","workflow.table.chatApp":"\u7FA4\u804A / \u5E94\u7528","workflow.list.failedLoad":"\u52A0\u8F7D\u5931\u8D25\uFF1A{error}","workflow.list.noRuns":"\u6CA1\u6709\u5339\u914D\u7684\u8FD0\u884C\u3002","workflow.list.noFilterMatch":"\u6CA1\u6709\u7B26\u5408\u7B5B\u9009\u6761\u4EF6\u7684\u8FD0\u884C\u3002","workflow.list.loaded":"{count} \u4E2A\u8FD0\u884C \xB7 \u5237\u65B0\u4E8E {time}","workflow.list.error":"\u9519\u8BEF\uFF1A{error}","workflow.detail.back":"\u8FD4\u56DE","workflow.detail.loading":"\u52A0\u8F7D\u4E2D...","workflow.detail.loadFailed":"\u52A0\u8F7D\u5931\u8D25","workflow.detail.cancel":"\u53D6\u6D88","workflow.detail.cliCancelOnly":"\u4EC5 CLI \u53EF\u53D6\u6D88","workflow.detail.cancelTitle":"\u53D6\u6D88\u8FD9\u4E2A\u5DE5\u4F5C\u6D41\u8FD0\u884C","workflow.detail.cliCancelTitle":"\u65E0\u6CD5\u5728\u9875\u9762\u53D6\u6D88\uFF1A\u8BF7\u4F7F\u7528 botmux workflow cancel {runId}","workflow.detail.nodes":"\u8282\u70B9 / Activity","workflow.detail.parallel":"\u5E76\u53D1\u6267\u884C","workflow.detail.parallelMeta":"{count} \u6B21\u5C1D\u8BD5 \xB7 \u6700\u9AD8\u5E76\u53D1 {max} \xB7 \u8FD0\u884C\u4E2D {running}","workflow.detail.noParallelData":"\u8FD8\u6CA1\u6709 attempt \u65F6\u95F4\u6570\u636E\u3002","workflow.detail.parallelNow":"\u73B0\u5728","workflow.detail.node":"\u8282\u70B9","workflow.detail.nodeStatus":"\u8282\u70B9\u72B6\u6001","workflow.detail.activity":"Activity","workflow.detail.activityStatus":"Activity \u72B6\u6001","workflow.detail.attempts":"\u5C1D\u8BD5\u6B21\u6570","workflow.detail.current":"\u5F53\u524D\u5C1D\u8BD5","workflow.detail.detail":"\u8BE6\u60C5","workflow.detail.nodeIO":"\u8282\u70B9\u8F93\u5165\u8F93\u51FA","workflow.detail.timeline":"\u65F6\u95F4\u7EBF","workflow.detail.loadOlder":"\u52A0\u8F7D\u66F4\u65E9\u4E8B\u4EF6","workflow.detail.seq":"\u5E8F\u53F7","workflow.detail.actor":"\u6267\u884C\u8005","workflow.detail.error":"\u9519\u8BEF","workflow.detail.event":"\u4E8B\u4EF6","workflow.detail.time":"\u65F6\u95F4","workflow.detail.refreshed":"\u5237\u65B0\u4E8E {time}","workflow.detail.unknownRun":"\u672A\u77E5\u8FD0\u884C","workflow.detail.snapshotHttp":"snapshot HTTP {status}","workflow.detail.eventsHttp":"events HTTP {status}","workflow.detail.cancelUnavailable":"\u65E0\u6CD5\u53D6\u6D88\uFF1A\u8BF7\u4F7F\u7528 botmux workflow cancel {runId}","workflow.detail.cancelConfirm":`\u786E\u8BA4\u53D6\u6D88\u5DE5\u4F5C\u6D41\u8FD0\u884C {runId}\uFF1F
|
|
3
3
|
|
|
4
4
|
{total} \u4E2A\u60AC\u6302\u9879\u4F1A\u7531 cancel recovery \u5904\u7406\u3002
|
|
5
5
|
effects={effects}, activities={activities}, waits={waits}, cancels={cancels}`,"workflow.detail.writeAccessCancel":"\u9700\u8981\u5199\u6743\u9650\uFF1A\u8BF7\u5728\u7EC8\u7AEF\u8FD0\u884C `botmux dashboard` \u83B7\u53D6\u4E00\u6B21\u6027 URL\uFF0C\u6253\u5F00\u540E\u5199\u5165 cookie\uFF0C\u518D\u56DE\u6765\u70B9\u51FB\u53D6\u6D88\u3002","workflow.detail.cancelHttp":"cancel HTTP {status}","workflow.detail.cancelPending":"\u53D6\u6D88\u5DF2\u63D0\u4EA4\uFF1B\u7B49\u5F85\u8FD0\u884C\u4E2D\u7684 activity \u6536\u655B","workflow.detail.writeAccessApproval":"\u9700\u8981\u5199\u6743\u9650\uFF1A\u8BF7\u5728\u7EC8\u7AEF\u8FD0\u884C `botmux dashboard` \u83B7\u53D6\u4E00\u6B21\u6027 URL\uFF0C\u6253\u5F00\u540E\u5199\u5165 cookie\uFF0C\u518D\u56DE\u6765\u5BA1\u6279\u3002","workflow.detail.actionHttp":"{action} HTTP {status}","workflow.detail.approved":"\u5DF2\u901A\u8FC7","workflow.detail.rejected":"\u5DF2\u62D2\u7EDD","workflow.detail.alreadyTerminal":"\u8FD0\u884C\u5DF2\u7EC8\u6001\uFF1B\u672A\u5E94\u7528\u201C{label}\u201D\u3002","workflow.detail.workflowContinue":"{label}\uFF1B\u7B49\u5F85\u5DE5\u4F5C\u6D41\u7EE7\u7EED\u6267\u884C\u3002","workflow.detail.workflowRefreshing":"{label}\uFF1B\u6B63\u5728\u5237\u65B0\u5DE5\u4F5C\u6D41\u72B6\u6001\u3002","workflow.detail.eventsLoaded":"\u5DF2\u52A0\u8F7D {loaded}/{total} \u4E2A\u4E8B\u4EF6","workflow.detail.dangling":"\u60AC\u6302\u9879","workflow.detail.noDangling":"\u6CA1\u6709\u60AC\u6302\u5DE5\u4F5C\u3002","workflow.detail.none":"\u65E0","workflow.detail.noNodes":"\u8FD8\u6CA1\u6709\u8282\u70B9\u3002","workflow.detail.idle":"\u7A7A\u95F2","workflow.detail.noNodeIO":"\u8FD8\u6CA1\u6709\u8282\u70B9\u8F93\u5165\u8F93\u51FA\u3002","workflow.detail.notDispatched":"\u5C1A\u672A\u6D3E\u53D1","workflow.detail.noAttempt":"\u8FD8\u6CA1\u6709\u5C1D\u8BD5","workflow.detail.attempt":"\u5C1D\u8BD5","workflow.detail.authoredInput":"\u539F\u59CB\u8F93\u5165","workflow.detail.resolvedInput":"\u89E3\u6790\u540E\u8F93\u5165","workflow.detail.output":"\u8F93\u51FA","workflow.detail.executionLog":"\u6267\u884C\u65E5\u5FD7","workflow.detail.liveTerminal":"\u5B9E\u65F6\u7EC8\u7AEF","workflow.detail.terminalLive":"\u5728\u7EBF","workflow.detail.terminalClosedShort":"\u5DF2\u5173\u95ED","workflow.detail.terminalClosed":"\u7EC8\u7AEF\u5DF2\u5173\u95ED\u3002\u8BF7\u67E5\u770B\u4E0B\u65B9\u6267\u884C\u65E5\u5FD7\u83B7\u53D6\u6700\u7EC8\u8BB0\u5F55\u3002","workflow.detail.openTerminalNewTab":"\u5728\u65B0\u6807\u7B7E\u9875\u6253\u5F00\u7EC8\u7AEF","workflow.detail.terminalReplay":"\u7EC8\u7AEF\u56DE\u653E","workflow.detail.openReplayNewTab":"\u5728\u65B0\u6807\u7B7E\u9875\u6253\u5F00\u56DE\u653E","workflow.detail.downloadFullLog":"\u4E0B\u8F7D\u5B8C\u6574\u65E5\u5FD7","workflow.detail.terminalResume":"\u8C03\u8BD5\u4F1A\u8BDD","workflow.detail.openResumeNewTab":"\u5728\u65B0\u6807\u7B7E\u9875\u6253\u5F00\u8C03\u8BD5\u4F1A\u8BDD","workflow.detail.resumeSession":"\u7EE7\u7EED\u4F1A\u8BDD","workflow.detail.resumeStarting":"\u6B63\u5728\u542F\u52A8\u2026","workflow.detail.endResumeSession":"\u7ED3\u675F\u8C03\u8BD5\u4F1A\u8BDD","workflow.detail.resumeEnding":"\u7ED3\u675F\u4E2D\u2026","workflow.detail.resumeUnsupportedCli":"{cliId} CLI \u4E0D\u652F\u6301 resume","workflow.detail.resumeMissingCliSession":"\u7F3A\u5C11 cliSessionId\uFF0C\u65E0\u6CD5 resume","workflow.detail.resumeStartFailed":"\u542F\u52A8\u8C03\u8BD5\u4F1A\u8BDD\u5931\u8D25 (HTTP {status})","workflow.detail.resumeEndFailed":"\u7ED3\u675F\u8C03\u8BD5\u4F1A\u8BDD\u5931\u8D25 (HTTP {status})","workflow.detail.writeAccessResume":"\u9700\u8981\u5199\u5165\u6743\u9650\u624D\u80FD resume \u4F1A\u8BDD\u3002","workflow.detail.waitPrompt":"\u7B49\u5F85\u63D0\u793A","workflow.detail.approvalComment":"\u5BA1\u6279\u5907\u6CE8","workflow.detail.optionalComment":"\u53EF\u9009\u5907\u6CE8","workflow.detail.approve":"\u901A\u8FC7","workflow.detail.reject":"\u62D2\u7EDD","workflow.detail.submitting":"\u63D0\u4EA4\u4E2D...","workflow.detail.empty":"\u7A7A","workflow.detail.truncated":"\u5DF2\u622A\u65AD","workflow.detail.noData":"\u6CA1\u6709\u6570\u636E\u3002","workflow.detail.noPreview":"\u6CA1\u6709\u9884\u89C8\u3002","workflow.detail.open":"\u6253\u5F00","workflow.detail.deadline":"\u622A\u6B62","workflow.detail.effect":"\u526F\u4F5C\u7528","workflow.detail.wait":"\u7B49\u5F85","workflow.detail.noEvents":"\u8FD8\u6CA1\u6709\u4E8B\u4EF6\u3002","workflow.summary.workflow":"\u5DE5\u4F5C\u6D41","workflow.summary.status":"\u72B6\u6001","workflow.summary.lastSeq":"\u6700\u540E\u5E8F\u53F7","workflow.summary.updated":"\u66F4\u65B0\u65F6\u95F4","workflow.summary.revision":"\u4FEE\u8BA2","workflow.summary.initiator":"\u53D1\u8D77\u4EBA","workflow.summary.failedNode":"\u5931\u8D25\u8282\u70B9","workflow.summary.cancelOrigin":"\u53D6\u6D88\u6765\u6E90","workflow.summary.chat":"\u7FA4\u804A","workflow.summary.app":"\u5E94\u7528","workflow.dangling.activities":"Activities","workflow.dangling.effects":"Effects","workflow.dangling.waits":"Waits","workflow.dangling.cancels":"Cancels","catalog.title":"\u5DE5\u4F5C\u6D41\u76EE\u5F55","catalog.subtitle":"\u4ECE\u5DF2\u4FDD\u5B58\u7684 workflow \u5B9A\u4E49\u521B\u5EFA\u4E00\u6B21\u8FD0\u884C\u3002","catalog.searchPlaceholder":"\u641C\u7D22 workflowId / \u8DEF\u5F84","catalog.refresh":"\u5237\u65B0","catalog.loading":"\u6B63\u5728\u52A0\u8F7D\u76EE\u5F55...","catalog.loadFailed":"\u76EE\u5F55\u52A0\u8F7D\u5931\u8D25\uFF1A{error}","catalog.noDefinitions":"\u6CA1\u6709\u627E\u5230 workflow \u5B9A\u4E49\u3002","catalog.noFilterMatch":"\u6CA1\u6709\u7B26\u5408\u7B5B\u9009\u6761\u4EF6\u7684\u5B9A\u4E49\u3002","catalog.table.workflow":"\u5DE5\u4F5C\u6D41","catalog.table.version":"\u7248\u672C","catalog.table.params":"\u53C2\u6570","catalog.table.nodes":"\u8282\u70B9","catalog.table.revision":"\u4FEE\u8BA2","catalog.table.path":"\u8DEF\u5F84","catalog.paramSummary":"{required}/{total} \u5FC5\u586B","catalog.back":"\u8FD4\u56DE\u76EE\u5F55","catalog.detailTitle":"\u5DE5\u4F5C\u6D41\u5B9A\u4E49","catalog.definitionLoadFailed":"\u5B9A\u4E49\u52A0\u8F7D\u5931\u8D25\uFF1A{error}","catalog.summary":"\u6458\u8981","catalog.paramsSchema":"\u53C2\u6570 Schema","catalog.definitionJson":"\u5B9A\u4E49 JSON","catalog.runPanel":"\u8FD0\u884C\u5DE5\u4F5C\u6D41","catalog.paramsJson":"\u53C2\u6570 JSON","catalog.paramsPlaceholder":`{
|
|
6
6
|
"city": "\u5317\u4EAC"
|
|
7
|
-
}`,"catalog.chatId":"\u7FA4\u804A ID","catalog.larkAppId":"\u98DE\u4E66\u5E94\u7528 ID","catalog.chatBindingHint":"\u5FC5\u586B\uFF0C\u7528\u4E8E\u786E\u5B9A humanGate \u5361\u7247\u53D1\u9001\u5230\u54EA\u4E2A\u98DE\u4E66\u7FA4\uFF0C\u4EE5\u53CA\u53D6\u6D88\u8DEF\u7531\u5F52\u5C5E\u3002","catalog.run":"\u8FD0\u884C","catalog.running":"\u542F\u52A8\u4E2D...","catalog.badParamsJson":"\u53C2\u6570\u5FC5\u987B\u662F JSON object\u3002","catalog.writeAccess":"\u9700\u8981\u5199\u6743\u9650\uFF1A\u8BF7\u5728\u7EC8\u7AEF\u8FD0\u884C `botmux dashboard` \u83B7\u53D6\u4E00\u6B21\u6027 URL\uFF0C\u6253\u5F00\u540E\u5199\u5165 cookie\uFF0C\u518D\u56DE\u6765\u8FD0\u884C\u3002","catalog.runHttp":"run HTTP {status}","catalog.runStarted":"\u8FD0\u884C\u5DF2\u542F\u52A8\uFF1B\u6B63\u5728\u6253\u5F00\u8BE6\u60C5\u9875...","catalog.invalidParams":"\u53C2\u6570\u65E0\u6548","catalog.issue":"{path}: {message}","catalog.noParams":"\u6CA1\u6709\u58F0\u660E\u53C2\u6570\u3002","catalog.required":"\u5FC5\u586B","catalog.optional":"\u53EF\u9009","catalog.default":"\u9ED8\u8BA4\u503C","catalog.description":"\u8BF4\u660E","catalog.path":"\u8DEF\u5F84","catalog.revision":"\u4FEE\u8BA2","catalog.nodeCount":"\u8282\u70B9\u6570"},gn={"app.name":"botmux","app.subtitle":"Feishu AI CLI Control","time.secondsAgo":"{value}s ago","time.minutesAgo":"{value}m ago","time.hoursAgo":"{value}h ago","nav.overview":"Overview","nav.sessions":"Sessions","nav.groups":"Groups","nav.schedules":"Schedules","nav.botDefaults":"Bot Defaults","status.live":"Live","status.disconnected":"Disconnected","status.system":"System","status.light":"Light","status.dark":"Dark","status.language":"Language","status.theme":"Theme","botOnboarding.add":"Add Bot","botOnboarding.title":"Scan to Add Bot","botOnboarding.intro":"Scan with the Feishu app to create a PersonalAgent app. The dashboard writes it to local bots.json after success.","botOnboarding.starting":"Generating QR code...","botOnboarding.waiting":"Scan with the Feishu app to continue.","botOnboarding.verifying":"Scan accepted. Verifying credentials...","botOnboarding.completed":"Bot added.","botOnboarding.failed":"Add failed","botOnboarding.openLink":"Open scan link in browser","botOnboarding.close":"Close","botOnboarding.restartHint":"bots.json has been updated. Run pnpm daemon:restart for the new bot to take effect.","botOnboarding.qrAlt":"Feishu bot onboarding QR code","overview.title":"Control Overview","overview.subtitle":"A realtime control plane across bots, chats, CLI sessions, and schedules.","overview.openSessions":"Active Sessions","overview.workingSessions":"Working","overview.onlineBots":"Online Bots","overview.schedules":"Schedules","overview.groups":"Groups Seen","overview.enabledSchedules":"Enabled","overview.total":"total","overview.active":"active","overview.daemonRegistry":"daemon registry","overview.chatMatrix":"chat matrix","overview.recentSessions":"Recent Sessions","overview.nextSchedules":"Next Runs","overview.noSessions":"No sessions yet.","overview.noSchedules":"No schedules yet.","sessions.title":"Session Control","sessions.subtitle":"Locate Feishu topics, open Web Terminal, close or resume CLI sessions.","sessions.search":"Search working dir / title / IDs","sessions.anyStatus":"Any status","sessions.adoptAny":"adopt: any","sessions.adoptYes":"adopt: yes","sessions.adoptNo":"adopt: no","sessions.activeOnly":"Active only","sessions.closeSelected":"Close selected","sessions.clearSelection":"Clear","sessions.selectedCount":"{count} sessions selected","sessions.bot":"Bot","sessions.cli":"CLI","sessions.status":"Status","sessions.titleCol":"Title","sessions.workingDir":"Working Dir","sessions.created":"Created","sessions.last":"Last","sessions.adopt":"Adopt","sessions.actions":"Actions","sessions.details":"Details","sessions.copy":"Copy","sessions.copied":"Copied","sessions.locate":"Locate Topic","sessions.locating":"Sending...","sessions.cooldown":"Cooldown {seconds}s","sessions.openTerminal":"Terminal","sessions.close":"Close Session","sessions.resume":"Resume Session","sessions.dismiss":"Close","sessions.closeConfirm":"Close this session?","sessions.resumeFailed":"Resume failed","sessions.closeBulkConfirm":"Close {count} selected sessions?","sessions.empty":"No sessions match the filters.","groups.title":"Groups & Bots","groups.subtitle":"Inspect the chat x bot matrix and manage group creation, oncall, leave, and disband flows.","groups.search":"Search chat name / ID / owner","groups.missingOnly":"Missing bot only","groups.refresh":"Refresh","groups.create":"New Group","groups.chat":"Chat","groups.actions":"Actions","groups.addBots":"Add Bots","groups.manage":"Manage","groups.empty":"No chats match the filters.","groups.createTitle":"Create New Group","groups.createHelp":"Pick bots to invite. The dashboard chooses an online daemon as creator.","groups.name":"Group Name","groups.namePlaceholder":"e.g. AI ChangeLog","groups.bindDir":"Bind Directory","groups.bindDirHelp":"New topics start the CLI here and skip repo selection.","groups.botPicker":"Bots","groups.createSubmit":"Create","groups.cancel":"Cancel","groups.successTitle":"Group Created","groups.openGroup":"Open Group","groups.manageTitle":"Manage {name}","groups.owner":"Owner","groups.oncall":"Oncall Mode","groups.oncallHelp":"When enabled, group members can @ the bot; new topics use the bound directory.","groups.leaveTitle":"Select Bots to Leave","groups.leaveSelected":"Selected Bots Leave","groups.disband":"Disband Group","groups.dangerHint":"Disband only works when an in-chat bot is the owner. Otherwise prefer leaving the chat.","groups.save":"Save","groups.needWorkingDir":"Working directory is required","groups.noBotsOnline":"No bots online. Restart the daemon first.","schedules.title":"Schedules","schedules.subtitle":"View, pause, resume, and run scheduled tasks across daemons.","schedules.search":"Search name / prompt / working dir","schedules.anyKind":"Any kind","schedules.enabledOnly":"Enabled only","schedules.name":"Name","schedules.bot":"Bot","schedules.schedule":"Schedule","schedules.next":"Next","schedules.last":"Last","schedules.repeat":"Repeat","schedules.enabled":"Enabled","schedules.actions":"Actions","schedules.runNow":"Run Now","schedules.pause":"Pause","schedules.resume":"Resume","schedules.empty":"No schedules.","botDefaults.title":"Bot Defaults","botDefaults.subtitle":"Manage each bot's defaults: new-chat oncall auto-binding, card signature, and more.","botDefaults.search":"Search bot name / app id","botDefaults.refresh":"Refresh","botDefaults.sectionOncall":"New-chat Oncall","botDefaults.sectionBrand":"Card Signature","botDefaults.warning":"When enabled, chats without an oncall binding auto-bind to this directory on their next new topic. Manually bound or unbound chats are preserved.","botDefaults.empty":"No bots online. Run botmux restart first.","botDefaults.defaultOncall":"Default to oncall mode","botDefaults.defaultOncallHelp":"Unbound chats auto-bind on the next new topic","botDefaults.workingDir":"Default Working Directory","botDefaults.lastEnabled":"Last Enabled","botDefaults.autobound":"{count} chats auto-bound","botDefaults.save":"Save","botDefaults.required":"Working directory is required when enabled","botDefaults.brandLabel":"Signature (card footer)","botDefaults.brandLabelHelp":"Footer signature on cards this bot sends. Save empty = hide; fill in = custom (markdown ok, e.g. [Acme](https://\u2026)); Reset = show botmux.","botDefaults.brandLabelPlaceholder":"Default: botmux (empty = hidden)","botDefaults.brandSave":"Save Signature","botDefaults.brandReset":"Reset to default","botDefaults.brandStateDefault":"Current: default botmux","botDefaults.brandStateOff":"Current: off","botDefaults.brandStateCustom":"Current: custom","botDefaults.sectionCard":"Card Behavior","botDefaults.disableStreaming":"Disable streaming card","botDefaults.disableStreamingHelp":"Stop posting the live session status card (and its Open Terminal entry). The task's final result still arrives as a message. For those who find the live card noisy.","botDefaults.writableLink":"Put a writable terminal link on the card","botDefaults.writableLinkHelp":'\u26A0\uFE0F Embeds a writable terminal link in the streaming card body \u2014 anyone in the chat can open and drive the terminal. Off = current behavior (private DM via the "Get Write Link" button).',"botDefaults.writableLinkMoot":"Streaming card disabled \u2014 this has no effect","botDefaults.privateCard":"/card sends a private card (authorized users only)","botDefaults.privateCardHelp":'Makes /card send an ephemeral "visible-to-specific-people" card: delivered only to the owner (allowedUsers); /grant-authorized talk users and everyone else in the chat cannot see it. Trade-off: it is a static snapshot (no live updates) and only works in regular group chats (topic groups / DMs fail, with no fallback). Affects only the /card command; the auto streaming card is unchanged.',"botDefaults.cardPrefSaved":"Saved","botDefaults.sectionRole":"Default Role","botDefaults.roleHelp":"This bot's default persona (applies across all chats), injected into the bot's sessions in every chat; a single group can override it on the Roles page. Save empty = delete.","botDefaults.rolePlaceholder":"e.g. You are a backend triage assistant; answer concisely, prefer runnable commands\u2026","botDefaults.roleSave":"Save role","botDefaults.roleDelete":"Delete","botDefaults.roleSaved":"Saved","botDefaults.roleDeleted":"Deleted","botDefaults.roleLoadErr":"Failed to load role","botDefaults.sectionAutoStart":"Proactive Start","botDefaults.autoStartJoin":"Auto-start when added to a new chat","botDefaults.autoStartJoinHelp":'When enabled, the bot auto-starts a session and gets to work the moment it is added to a new chat (when an authorized user / allowedUsers is a member), no @ needed. It launches in the bot default working dir; if none is configured it shows a repo-select card first. Prerequisite: subscribe the "bot joined chat" event im.chat.member.bot.added_v1 for this app and grant the member-read scope in the Feishu console.',"botDefaults.autoStartJoinPrompt":"First-turn prompt on join (optional)","botDefaults.autoStartJoinPromptPlaceholder":"Filled = used as the first task after joining; blank = the bot reads the chat and decides what to do","botDefaults.autoStartJoinPromptSave":"Save prompt","botDefaults.autoStartTopic":"Auto-start on new topics in topic groups","botDefaults.autoStartTopicHelp":"When enabled, in a topic group the bot automatically joins each newly opened topic and starts working on its first message, no @ needed. Topic groups only \u2014 regular groups are unaffected.","nav.roles":"Roles","roles.title":"Role Management","roles.subtitle":"Set per-bot role prompts for each group. Each bot adopts its own persona in the selected group.","roles.search":"Search group / bot / ID","roles.refresh":"Refresh","roles.selectHint":"\u2190 Expand a group and select a bot to edit its role","roles.editorPlaceholder":`Enter role description, e.g.:
|
|
7
|
+
}`,"catalog.chatId":"\u7FA4\u804A ID","catalog.larkAppId":"\u98DE\u4E66\u5E94\u7528 ID","catalog.chatBindingHint":"\u5FC5\u586B\uFF0C\u7528\u4E8E\u786E\u5B9A humanGate \u5361\u7247\u53D1\u9001\u5230\u54EA\u4E2A\u98DE\u4E66\u7FA4\uFF0C\u4EE5\u53CA\u53D6\u6D88\u8DEF\u7531\u5F52\u5C5E\u3002","catalog.run":"\u8FD0\u884C","catalog.running":"\u542F\u52A8\u4E2D...","catalog.badParamsJson":"\u53C2\u6570\u5FC5\u987B\u662F JSON object\u3002","catalog.writeAccess":"\u9700\u8981\u5199\u6743\u9650\uFF1A\u8BF7\u5728\u7EC8\u7AEF\u8FD0\u884C `botmux dashboard` \u83B7\u53D6\u4E00\u6B21\u6027 URL\uFF0C\u6253\u5F00\u540E\u5199\u5165 cookie\uFF0C\u518D\u56DE\u6765\u8FD0\u884C\u3002","catalog.runHttp":"run HTTP {status}","catalog.runStarted":"\u8FD0\u884C\u5DF2\u542F\u52A8\uFF1B\u6B63\u5728\u6253\u5F00\u8BE6\u60C5\u9875...","catalog.invalidParams":"\u53C2\u6570\u65E0\u6548","catalog.issue":"{path}: {message}","catalog.noParams":"\u6CA1\u6709\u58F0\u660E\u53C2\u6570\u3002","catalog.required":"\u5FC5\u586B","catalog.optional":"\u53EF\u9009","catalog.default":"\u9ED8\u8BA4\u503C","catalog.description":"\u8BF4\u660E","catalog.path":"\u8DEF\u5F84","catalog.revision":"\u4FEE\u8BA2","catalog.nodeCount":"\u8282\u70B9\u6570"},wn={"app.name":"botmux","app.subtitle":"Feishu AI CLI Control","time.secondsAgo":"{value}s ago","time.minutesAgo":"{value}m ago","time.hoursAgo":"{value}h ago","nav.overview":"Overview","nav.sessions":"Sessions","nav.groups":"Groups","nav.schedules":"Schedules","nav.botDefaults":"Bot Defaults","status.live":"Live","status.disconnected":"Disconnected","status.system":"System","status.light":"Light","status.dark":"Dark","status.language":"Language","status.theme":"Theme","botOnboarding.add":"Add Bot","botOnboarding.title":"Scan to Add Bot","botOnboarding.intro":"Scan with the Feishu app to create a PersonalAgent app. The dashboard writes it to local bots.json after success.","botOnboarding.starting":"Generating QR code...","botOnboarding.waiting":"Scan with the Feishu app to continue.","botOnboarding.verifying":"Scan accepted. Verifying credentials...","botOnboarding.completed":"Bot added.","botOnboarding.failed":"Add failed","botOnboarding.openLink":"Open scan link in browser","botOnboarding.close":"Close","botOnboarding.restartHint":"bots.json has been updated. Run pnpm daemon:restart for the new bot to take effect.","botOnboarding.qrAlt":"Feishu bot onboarding QR code","overview.title":"Control Overview","overview.subtitle":"A realtime control plane across bots, chats, CLI sessions, and schedules.","overview.openSessions":"Active Sessions","overview.workingSessions":"Working","overview.onlineBots":"Online Bots","overview.schedules":"Schedules","overview.groups":"Groups Seen","overview.enabledSchedules":"Enabled","overview.total":"total","overview.active":"active","overview.daemonRegistry":"daemon registry","overview.chatMatrix":"chat matrix","overview.recentSessions":"Recent Sessions","overview.nextSchedules":"Next Runs","overview.noSessions":"No sessions yet.","overview.noSchedules":"No schedules yet.","sessions.title":"Session Control","sessions.subtitle":"Locate Feishu topics, open Web Terminal, close or resume CLI sessions.","sessions.search":"Search working dir / title / IDs","sessions.anyStatus":"Any status","sessions.adoptAny":"adopt: any","sessions.adoptYes":"adopt: yes","sessions.adoptNo":"adopt: no","sessions.activeOnly":"Active only","sessions.closeSelected":"Close selected","sessions.clearSelection":"Clear","sessions.selectedCount":"{count} sessions selected","sessions.bot":"Bot","sessions.cli":"CLI","sessions.status":"Status","sessions.titleCol":"Title","sessions.workingDir":"Working Dir","sessions.created":"Created","sessions.last":"Last","sessions.adopt":"Adopt","sessions.actions":"Actions","sessions.details":"Details","sessions.copy":"Copy","sessions.copied":"Copied","sessions.locate":"Locate Topic","sessions.locating":"Sending...","sessions.cooldown":"Cooldown {seconds}s","sessions.openTerminal":"Terminal","sessions.close":"Close Session","sessions.resume":"Resume Session","sessions.dismiss":"Close","sessions.closeConfirm":"Close this session?","sessions.resumeFailed":"Resume failed","sessions.closeBulkConfirm":"Close {count} selected sessions?","sessions.empty":"No sessions match the filters.","groups.title":"Groups & Bots","groups.subtitle":"Inspect the chat x bot matrix and manage group creation, oncall, leave, and disband flows.","groups.search":"Search chat name / ID / owner","groups.missingOnly":"Missing bot only","groups.refresh":"Refresh","groups.create":"New Group","groups.chat":"Chat","groups.actions":"Actions","groups.addBots":"Add Bots","groups.manage":"Manage","groups.empty":"No chats match the filters.","groups.createTitle":"Create New Group","groups.createHelp":"Pick bots to invite. The dashboard chooses an online daemon as creator.","groups.name":"Group Name","groups.namePlaceholder":"e.g. AI ChangeLog","groups.bindDir":"Bind Directory","groups.bindDirHelp":"New topics start the CLI here and skip repo selection.","groups.botPicker":"Bots","groups.createSubmit":"Create","groups.cancel":"Cancel","groups.successTitle":"Group Created","groups.openGroup":"Open Group","groups.manageTitle":"Manage {name}","groups.owner":"Owner","groups.oncall":"Oncall Mode","groups.oncallHelp":"When enabled, group members can @ the bot; new topics use the bound directory.","groups.leaveTitle":"Select Bots to Leave","groups.leaveSelected":"Selected Bots Leave","groups.disband":"Disband Group","groups.dangerHint":"Disband only works when an in-chat bot is the owner. Otherwise prefer leaving the chat.","groups.save":"Save","groups.needWorkingDir":"Working directory is required","groups.noBotsOnline":"No bots online. Restart the daemon first.","schedules.title":"Schedules","schedules.subtitle":"View, pause, resume, and run scheduled tasks across daemons.","schedules.search":"Search name / prompt / working dir","schedules.anyKind":"Any kind","schedules.enabledOnly":"Enabled only","schedules.name":"Name","schedules.bot":"Bot","schedules.schedule":"Schedule","schedules.next":"Next","schedules.last":"Last","schedules.repeat":"Repeat","schedules.enabled":"Enabled","schedules.actions":"Actions","schedules.runNow":"Run Now","schedules.pause":"Pause","schedules.resume":"Resume","schedules.empty":"No schedules.","botDefaults.title":"Bot Defaults","botDefaults.subtitle":"Manage each bot's defaults: new-chat oncall auto-binding, card signature, and more.","botDefaults.search":"Search bot name / app id","botDefaults.refresh":"Refresh","botDefaults.sectionOncall":"New-chat Oncall","botDefaults.sectionBrand":"Card Signature","botDefaults.warning":"When enabled, chats without an oncall binding auto-bind to this directory on their next new topic. Manually bound or unbound chats are preserved.","botDefaults.empty":"No bots online. Run botmux restart first.","botDefaults.defaultOncall":"Default to oncall mode","botDefaults.defaultOncallHelp":"Unbound chats auto-bind on the next new topic","botDefaults.workingDir":"Default Working Directory","botDefaults.lastEnabled":"Last Enabled","botDefaults.autobound":"{count} chats auto-bound","botDefaults.save":"Save","botDefaults.required":"Working directory is required when enabled","botDefaults.brandLabel":"Signature (card footer)","botDefaults.brandLabelHelp":"Footer signature on cards this bot sends. Save empty = hide; fill in = custom (markdown ok, e.g. [Acme](https://\u2026)); Reset = show botmux.","botDefaults.brandLabelPlaceholder":"Default: botmux (empty = hidden)","botDefaults.brandSave":"Save Signature","botDefaults.brandReset":"Reset to default","botDefaults.brandStateDefault":"Current: default botmux","botDefaults.brandStateOff":"Current: off","botDefaults.brandStateCustom":"Current: custom","botDefaults.sectionCard":"Card Behavior","botDefaults.disableStreaming":"Disable streaming card","botDefaults.disableStreamingHelp":"Stop posting the live session status card (and its Open Terminal entry). The task's final result still arrives as a message. For those who find the live card noisy.","botDefaults.writableLink":"Put a writable terminal link on the card","botDefaults.writableLinkHelp":'\u26A0\uFE0F Embeds a writable terminal link in the streaming card body \u2014 anyone in the chat can open and drive the terminal. Off = current behavior (private DM via the "Get Write Link" button).',"botDefaults.writableLinkMoot":"Streaming card disabled \u2014 this has no effect","botDefaults.privateCard":"/card sends a private card (authorized users only)","botDefaults.privateCardHelp":'Makes /card send an ephemeral "visible-to-specific-people" card: delivered only to the owner (allowedUsers); /grant-authorized talk users and everyone else in the chat cannot see it. Trade-off: it is a static snapshot (no live updates) and only works in regular group chats (topic groups / DMs fail, with no fallback). Affects only the /card command; the auto streaming card is unchanged.',"botDefaults.cardPrefSaved":"Saved","botDefaults.sectionRole":"Default Role","botDefaults.roleHelp":"This bot's default persona (applies across all chats), injected into the bot's sessions in every chat; a single group can override it on the Roles page. Save empty = delete.","botDefaults.rolePlaceholder":"e.g. You are a backend triage assistant; answer concisely, prefer runnable commands\u2026","botDefaults.roleSave":"Save role","botDefaults.roleDelete":"Delete","botDefaults.roleSaved":"Saved","botDefaults.roleDeleted":"Deleted","botDefaults.roleLoadErr":"Failed to load role","botDefaults.sectionAutoStart":"Proactive Start","botDefaults.autoStartJoin":"Auto-start when added to a new chat","botDefaults.autoStartJoinHelp":'When enabled, the bot auto-starts a session and gets to work the moment it is added to a new chat (when an authorized user / allowedUsers is a member), no @ needed. It launches in the bot default working dir; if none is configured it shows a repo-select card first. Prerequisite: subscribe the "bot joined chat" event im.chat.member.bot.added_v1 for this app and grant the member-read scope in the Feishu console.',"botDefaults.autoStartJoinPrompt":"First-turn prompt on join (optional)","botDefaults.autoStartJoinPromptPlaceholder":"Filled = used as the first task after joining; blank = the bot reads the chat and decides what to do","botDefaults.autoStartJoinPromptSave":"Save prompt","botDefaults.autoStartTopic":"Auto-start on new topics in topic groups","botDefaults.autoStartTopicHelp":"When enabled, in a topic group the bot automatically joins each newly opened topic and starts working on its first message, no @ needed. Topic groups only \u2014 regular groups are unaffected.","botDefaults.sectionGrant":"Authorization & Quota","botDefaults.restrictGrant":"Restrict grantees to plain conversation","botDefaults.restrictGrantHelp":"When enabled, /grant-authorized users (the owner is exempt) can only send plain messages; every slash command is blocked: botmux built-in commands, passthrough commands, /workflow, /introduce, /t, and CLI-native commands (/help, etc.). Text like /path/to/file is not misclassified.","botDefaults.quotaDefault":"Default message quota","botDefaults.quotaPlaceholder":"Empty = no default quota","botDefaults.quotaHelp":'Message count a bare /grant @x (no number) hands out. Empty or "Turn off" merely stops applying a default to bare /grant \u2014 it does NOT clear existing quota counters, nor affect an explicit /grant @x N; those keep being enforced. Authorization is auto-revoked once a quota runs out.',"botDefaults.quotaSave":"Save quota","botDefaults.quotaOff":"Turn off","botDefaults.quotaInvalid":"Quota must be a positive integer","botDefaults.quotaStateOff":"Current: no default quota","botDefaults.quotaStateOn":"Current: {count} per grantee","nav.roles":"Roles","roles.title":"Role Management","roles.subtitle":"Set per-bot role prompts for each group. Each bot adopts its own persona in the selected group.","roles.search":"Search group / bot / ID","roles.refresh":"Refresh","roles.selectHint":"\u2190 Expand a group and select a bot to edit its role","roles.editorPlaceholder":`Enter role description, e.g.:
|
|
8
8
|
You are a code reviewer for this group...`,"roles.configured":"Configured","roles.unconfigured":"None","roles.noChats":"No groups","roles.preview":"Preview","roles.previewEmpty":"(empty)","roles.saved":"Saved","roles.delete":"Delete","roles.save":"Save","roles.confirmDelete":"Delete this bot's role config for this group?","roles.botsWithRoles":"bots configured","roles.emptyError":"Role content cannot be empty","roles.saveFailed":"Save failed, please retry","common.none":"None","common.unknown":"Unknown","common.now":"now","common.never":"never","nav.workflows":"Workflows (beta)","nav.workflowCatalog":"Catalog","workflow.subnav.runs":"Runs","workflow.subnav.catalog":"Catalog","workflow.searchPlaceholder":"search runId / workflowId / chatId","workflow.filter.nonTerminal":"non-terminal","workflow.filter.all":"all","workflow.status.pending":"pending","workflow.status.running":"running","workflow.status.waiting":"waiting","workflow.status.effectAttempting":"effect","workflow.status.timedOut":"timed out","workflow.status.succeeded":"succeeded","workflow.status.failed":"failed","workflow.status.cancelled":"cancelled","workflow.table.run":"run","workflow.table.workflow":"workflow","workflow.table.status":"status","workflow.table.lastSeq":"lastSeq","workflow.table.dangling":"dEf/dAct/dWait","workflow.table.updated":"updated","workflow.table.chatApp":"chat / app","workflow.list.failedLoad":"Failed to load: {error}","workflow.list.noRuns":"No runs match.","workflow.list.noFilterMatch":"No runs match this filter.","workflow.list.loaded":"{count} runs \xB7 refreshed {time}","workflow.list.error":"error: {error}","workflow.detail.back":"Back","workflow.detail.loading":"Loading...","workflow.detail.loadFailed":"Load failed","workflow.detail.cancel":"Cancel","workflow.detail.cliCancelOnly":"CLI cancel only","workflow.detail.cancelTitle":"Cancel this workflow run","workflow.detail.cliCancelTitle":"Cancel unavailable: use botmux workflow cancel {runId}","workflow.detail.nodes":"Nodes / Activities","workflow.detail.parallel":"Parallel execution","workflow.detail.parallelMeta":"{count} attempt(s) \xB7 max parallel {max} \xB7 running {running}","workflow.detail.noParallelData":"No attempt timing data yet.","workflow.detail.parallelNow":"now","workflow.detail.node":"node","workflow.detail.nodeStatus":"node status","workflow.detail.activity":"activity","workflow.detail.activityStatus":"activity status","workflow.detail.attempts":"attempts","workflow.detail.current":"current","workflow.detail.detail":"detail","workflow.detail.nodeIO":"Node I/O","workflow.detail.timeline":"Timeline","workflow.detail.loadOlder":"Load older","workflow.detail.seq":"seq","workflow.detail.actor":"actor","workflow.detail.error":"error","workflow.detail.event":"event","workflow.detail.time":"time","workflow.detail.refreshed":"refreshed {time}","workflow.detail.unknownRun":"unknown run","workflow.detail.snapshotHttp":"snapshot HTTP {status}","workflow.detail.eventsHttp":"events HTTP {status}","workflow.detail.cancelUnavailable":"cancel unavailable: use botmux workflow cancel {runId}","workflow.detail.cancelConfirm":`Cancel workflow run {runId}?
|
|
9
9
|
|
|
10
10
|
{total} dangling item(s) will be handled by cancel-driven recovery.
|
|
11
11
|
effects={effects}, activities={activities}, waits={waits}, cancels={cancels}`,"workflow.detail.writeAccessCancel":"write access required: run `botmux dashboard` in the terminal to get a one-time URL, open it once to set the cookie, then come back and click cancel again.","workflow.detail.cancelHttp":"cancel HTTP {status}","workflow.detail.cancelPending":"cancel pending; waiting for running activity to drain","workflow.detail.writeAccessApproval":"write access required: run `botmux dashboard` in the terminal to get a one-time URL, open it once to set the cookie, then come back and approve/reject again.","workflow.detail.actionHttp":"{action} HTTP {status}","workflow.detail.approved":"approved","workflow.detail.rejected":"rejected","workflow.detail.alreadyTerminal":"Run already terminal; {label} was not applied.","workflow.detail.workflowContinue":"{label}; waiting for workflow to continue.","workflow.detail.workflowRefreshing":"{label}; refreshing workflow state.","workflow.detail.eventsLoaded":"{loaded}/{total} events loaded","workflow.detail.dangling":"Dangling","workflow.detail.noDangling":"No dangling work.","workflow.detail.none":"none","workflow.detail.noNodes":"No nodes yet.","workflow.detail.idle":"idle","workflow.detail.noNodeIO":"No node I/O yet.","workflow.detail.notDispatched":"not dispatched","workflow.detail.noAttempt":"No attempt yet","workflow.detail.attempt":"attempt","workflow.detail.authoredInput":"Authored input","workflow.detail.resolvedInput":"Resolved input","workflow.detail.output":"Output","workflow.detail.executionLog":"Execution log","workflow.detail.liveTerminal":"Live terminal","workflow.detail.terminalLive":"live","workflow.detail.terminalClosedShort":"closed","workflow.detail.terminalClosed":"Terminal is closed. Use the execution log below for the final transcript.","workflow.detail.openTerminalNewTab":"Open terminal in new tab","workflow.detail.terminalReplay":"Terminal replay","workflow.detail.openReplayNewTab":"Open replay in new tab","workflow.detail.downloadFullLog":"Download full log","workflow.detail.terminalResume":"Debug session","workflow.detail.openResumeNewTab":"Open debug session in new tab","workflow.detail.resumeSession":"Resume session","workflow.detail.resumeStarting":"Starting\u2026","workflow.detail.endResumeSession":"End debug session","workflow.detail.resumeEnding":"Ending\u2026","workflow.detail.resumeUnsupportedCli":'CLI "{cliId}" does not support resume',"workflow.detail.resumeMissingCliSession":"Missing cliSessionId \u2014 cannot resume","workflow.detail.resumeStartFailed":"Failed to start debug session (HTTP {status})","workflow.detail.resumeEndFailed":"Failed to end debug session (HTTP {status})","workflow.detail.writeAccessResume":"Resume requires dashboard write access.","workflow.detail.waitPrompt":"Wait prompt","workflow.detail.approvalComment":"Approval comment","workflow.detail.optionalComment":"Optional comment","workflow.detail.approve":"Approve","workflow.detail.reject":"Reject","workflow.detail.submitting":"Submitting...","workflow.detail.empty":"empty","workflow.detail.truncated":"truncated","workflow.detail.noData":"No data.","workflow.detail.noPreview":"No preview.","workflow.detail.open":"open","workflow.detail.deadline":"deadline","workflow.detail.effect":"effect","workflow.detail.wait":"wait","workflow.detail.noEvents":"No events.","workflow.summary.workflow":"workflow","workflow.summary.status":"status","workflow.summary.lastSeq":"lastSeq","workflow.summary.updated":"updated","workflow.summary.revision":"revision","workflow.summary.initiator":"initiator","workflow.summary.failedNode":"failedNode","workflow.summary.cancelOrigin":"cancelOrigin","workflow.summary.chat":"chat","workflow.summary.app":"app","workflow.dangling.activities":"activities","workflow.dangling.effects":"effects","workflow.dangling.waits":"waits","workflow.dangling.cancels":"cancels","catalog.title":"Workflow catalog","catalog.subtitle":"Create a workflow run from a saved workflow definition.","catalog.searchPlaceholder":"search workflowId / path","catalog.refresh":"Refresh","catalog.loading":"Loading catalog...","catalog.loadFailed":"Failed to load catalog: {error}","catalog.noDefinitions":"No workflow definitions found.","catalog.noFilterMatch":"No definitions match this filter.","catalog.table.workflow":"workflow","catalog.table.version":"version","catalog.table.params":"params","catalog.table.nodes":"nodes","catalog.table.revision":"revision","catalog.table.path":"path","catalog.paramSummary":"{required}/{total} required","catalog.back":"Back to catalog","catalog.detailTitle":"Workflow definition","catalog.definitionLoadFailed":"Failed to load definition: {error}","catalog.summary":"Summary","catalog.paramsSchema":"Params schema","catalog.definitionJson":"Definition JSON","catalog.runPanel":"Run workflow","catalog.paramsJson":"Params JSON","catalog.paramsPlaceholder":`{
|
|
12
12
|
"city": "\u5317\u4EAC"
|
|
13
|
-
}`,"catalog.chatId":"Chat ID","catalog.larkAppId":"Lark app ID","catalog.chatBindingHint":"Required so humanGate cards and cancel routing know which Lark chat owns the run.","catalog.run":"Run","catalog.running":"Starting...","catalog.badParamsJson":"Params must be a JSON object.","catalog.writeAccess":"write access required: run `botmux dashboard` in the terminal to get a one-time URL, open it once to set the cookie, then come back and run again.","catalog.runHttp":"run HTTP {status}","catalog.runStarted":"Run started; opening detail page...","catalog.invalidParams":"Invalid params","catalog.issue":"{path}: {message}","catalog.noParams":"No params declared.","catalog.required":"required","catalog.optional":"optional","catalog.default":"default","catalog.description":"description","catalog.path":"path","catalog.revision":"revision","catalog.nodeCount":"nodes"},
|
|
13
|
+
}`,"catalog.chatId":"Chat ID","catalog.larkAppId":"Lark app ID","catalog.chatBindingHint":"Required so humanGate cards and cancel routing know which Lark chat owns the run.","catalog.run":"Run","catalog.running":"Starting...","catalog.badParamsJson":"Params must be a JSON object.","catalog.writeAccess":"write access required: run `botmux dashboard` in the terminal to get a one-time URL, open it once to set the cookie, then come back and run again.","catalog.runHttp":"run HTTP {status}","catalog.runStarted":"Run started; opening detail page...","catalog.invalidParams":"Invalid params","catalog.issue":"{path}: {message}","catalog.noParams":"No params declared.","catalog.required":"required","catalog.optional":"optional","catalog.default":"default","catalog.description":"description","catalog.path":"path","catalog.revision":"revision","catalog.nodeCount":"nodes"},vt={zh:gn,en:wn};function kt(e){if(typeof e!="string")return null;let t=e.trim().toLowerCase();return t==="zh"||t.startsWith("zh-")?"zh":t==="en"||t.startsWith("en-")?"en":null}function hn(e=[]){for(let t of e){let o=kt(t);if(o)return o}return"zh"}function je(e){return(t,o)=>{let s=vt[e][t]??vt.zh[t]??t;return o?s.replace(/\{(\w+)\}/g,(a,r)=>{let i=o[r];return i==null?`{${r}}`:String(i)}):s}}function $t(e,t){return(e?kt(e.getItem(nt)):null)??hn(t)}var ot="botmux.dashboard.theme";function bn(e){return e==="system"||e==="light"||e==="dark"?e:null}function St(e,t){return e==="system"?t?"dark":"light":e}function Tt(e){return bn(e?.getItem(ot))??"system"}var at=class{locale="zh";themeMode="system";resolvedTheme="light";listeners=new Set;translate=je(this.locale);mediaQuery=null;init(){let t=typeof window<"u"?window:void 0;this.locale=$t(t?.localStorage,yn()),this.translate=je(this.locale),this.themeMode=Tt(t?.localStorage),this.mediaQuery=t?.matchMedia?.("(prefers-color-scheme: dark)")??null,this.mediaQuery?.addEventListener("change",()=>{this.applyTheme(),this.emit()}),this.applyTheme(),this.applyLocale()}t(t,o){return this.translate(t,o)}setLocale(t){this.locale!==t&&(this.locale=t,this.translate=je(t),window.localStorage.setItem(nt,t),this.applyLocale(),this.emit())}setThemeMode(t){this.themeMode!==t&&(this.themeMode=t,window.localStorage.setItem(ot,t),this.applyTheme(),this.emit())}on(t){return this.listeners.add(t),()=>this.listeners.delete(t)}emit(){for(let t of this.listeners)t()}applyTheme(){this.resolvedTheme=St(this.themeMode,!!this.mediaQuery?.matches),document.documentElement.dataset.theme=this.resolvedTheme,document.documentElement.dataset.themeMode=this.themeMode}applyLocale(){document.documentElement.lang=this.locale==="zh"?"zh-CN":"en"}};function yn(){return typeof navigator>"u"?[]:navigator.languages?.length?navigator.languages:[navigator.language].filter(Boolean)}var ge=new at;function n(e,t){return ge.t(e,t)}function l(e){return e.replace(/[&<>"']/g,t=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[t])}function Ae(e){if(!e)return"-";let t=Date.now()-e;return t<6e4?n("common.now"):t<36e5?Math.floor(t/6e4)+"m":t<864e5?Math.floor(t/36e5)+"h":Math.floor(t/864e5)+"d"}var st={chats:[],bots:[]};async function vn(){try{let e=await fetch("/api/groups");if(!e.ok)return;st=await e.json()}catch{}}function kn(e){return`status status-${l(e||"unknown")}`}function $n(e){return`<li class="overview-list-row">
|
|
14
14
|
<div>
|
|
15
|
-
<strong>${
|
|
16
|
-
<span>${
|
|
15
|
+
<strong>${l(e.title??e.sessionId)}</strong>
|
|
16
|
+
<span>${l(e.botName??"")} \xB7 ${l(e.cliId??"unknown")}</span>
|
|
17
17
|
</div>
|
|
18
|
-
<span class="${kn(e.status)}">${
|
|
18
|
+
<span class="${kn(e.status)}">${l(e.status??"unknown")}</span>
|
|
19
19
|
</li>`}function Sn(e){let t=e.nextRunAt?new Date(e.nextRunAt).toLocaleString():"-";return`<li class="overview-list-row">
|
|
20
20
|
<div>
|
|
21
|
-
<strong>${
|
|
22
|
-
<span>${
|
|
21
|
+
<strong>${l(e.name??e.id)}</strong>
|
|
22
|
+
<span>${l(e.botName??e.larkAppId??"")} \xB7 ${l(e.parsed?.display??"")}</span>
|
|
23
23
|
</div>
|
|
24
|
-
<span>${
|
|
25
|
-
</li>`}async function
|
|
24
|
+
<span>${l(t)}</span>
|
|
25
|
+
</li>`}async function Lt(e){e.innerHTML=`<section class="page hero-page">
|
|
26
26
|
<div class="page-heading">
|
|
27
27
|
<div>
|
|
28
|
-
<p class="eyebrow">${
|
|
29
|
-
<h1>${
|
|
30
|
-
<p>${
|
|
28
|
+
<p class="eyebrow">${n("app.subtitle")}</p>
|
|
29
|
+
<h1>${n("overview.title")}</h1>
|
|
30
|
+
<p>${n("overview.subtitle")}</p>
|
|
31
31
|
</div>
|
|
32
32
|
</div>
|
|
33
33
|
<div class="metric-grid" id="overview-metrics"></div>
|
|
@@ -35,251 +35,251 @@ effects={effects}, activities={activities}, waits={waits}, cancels={cancels}`,"w
|
|
|
35
35
|
<section class="panel">
|
|
36
36
|
<header class="panel-header">
|
|
37
37
|
<div>
|
|
38
|
-
<h2>${
|
|
39
|
-
<p>${
|
|
38
|
+
<h2>${n("overview.recentSessions")}</h2>
|
|
39
|
+
<p>${n("sessions.subtitle")}</p>
|
|
40
40
|
</div>
|
|
41
|
-
<a class="btn-link" href="#/sessions">${
|
|
41
|
+
<a class="btn-link" href="#/sessions">${n("nav.sessions")}</a>
|
|
42
42
|
</header>
|
|
43
43
|
<ul class="overview-list" id="recent-sessions"></ul>
|
|
44
44
|
</section>
|
|
45
45
|
<section class="panel">
|
|
46
46
|
<header class="panel-header">
|
|
47
47
|
<div>
|
|
48
|
-
<h2>${
|
|
49
|
-
<p>${
|
|
48
|
+
<h2>${n("overview.nextSchedules")}</h2>
|
|
49
|
+
<p>${n("schedules.subtitle")}</p>
|
|
50
50
|
</div>
|
|
51
|
-
<a class="btn-link" href="#/schedules">${
|
|
51
|
+
<a class="btn-link" href="#/schedules">${n("nav.schedules")}</a>
|
|
52
52
|
</header>
|
|
53
53
|
<ul class="overview-list" id="next-schedules"></ul>
|
|
54
54
|
</section>
|
|
55
55
|
</div>
|
|
56
|
-
</section>`;let t=e.querySelector("#overview-metrics"),
|
|
57
|
-
<span>${
|
|
58
|
-
<strong>${
|
|
59
|
-
<small>${
|
|
60
|
-
</article>`).join("");let
|
|
61
|
-
<span class="filter-check-label">${
|
|
62
|
-
${
|
|
56
|
+
</section>`;let t=e.querySelector("#overview-metrics"),o=e.querySelector("#recent-sessions"),s=e.querySelector("#next-schedules");function a(){let r=[...W.sessions.values()],i=[...W.schedules.values()],m=r.filter(f=>f.status!=="closed"),$=r.filter(f=>f.status==="working"||f.status==="analyzing"||f.status==="starting"),T=i.filter(f=>f.enabled),k=st.bots?.length||new Set(r.map(f=>f.larkAppId).filter(Boolean)).size,y=[{label:n("overview.openSessions"),value:m.length,meta:`${r.length} ${n("overview.total")}`},{label:n("overview.workingSessions"),value:$.length,meta:`${m.length} ${n("overview.active")}`},{label:n("overview.onlineBots"),value:k,meta:n("overview.daemonRegistry")},{label:n("overview.schedules"),value:i.length,meta:`${T.length} ${n("overview.enabledSchedules")}`},{label:n("overview.groups"),value:st.chats?.length??0,meta:n("overview.chatMatrix")}];t.innerHTML=y.map(f=>`<article class="metric-card">
|
|
57
|
+
<span>${l(f.label)}</span>
|
|
58
|
+
<strong>${f.value}</strong>
|
|
59
|
+
<small>${l(f.meta)}</small>
|
|
60
|
+
</article>`).join("");let c=r.sort((f,g)=>Number(g.lastMessageAt??0)-Number(f.lastMessageAt??0)).slice(0,6);o.innerHTML=c.length?c.map(f=>$n({...f,title:f.title??`${Ae(f.lastMessageAt)} \xB7 ${f.sessionId}`})).join(""):`<li class="empty">${n("overview.noSessions")}</li>`;let I=i.filter(f=>f.nextRunAt).sort((f,g)=>Date.parse(f.nextRunAt)-Date.parse(g.nextRunAt)).slice(0,6);s.innerHTML=I.length?I.map(Sn).join(""):`<li class="empty">${n("overview.noSchedules")}</li>`}W.on(a),a(),vn().then(a)}function we(e,t){return`<th data-sort="${e}" data-label="${l(t)}">${l(t)}</th>`}var It=["claude-code","codex","codex-app","cursor","gemini","opencode","mtr","hermes","mira","aiden","coco","unknown"];function Tn(){return`<div class="filter-check-group" role="group" aria-label="${n("sessions.cli")}">
|
|
61
|
+
<span class="filter-check-label">${n("sessions.cli")}</span>
|
|
62
|
+
${It.map(e=>`
|
|
63
63
|
<label class="filter-check">
|
|
64
|
-
<input type="checkbox" name="cli" value="${
|
|
65
|
-
<span>${
|
|
64
|
+
<input type="checkbox" name="cli" value="${l(e)}" checked>
|
|
65
|
+
<span>${l(e)}</span>
|
|
66
66
|
</label>
|
|
67
67
|
`).join("")}
|
|
68
|
-
</div>`}function
|
|
68
|
+
</div>`}function Ln(){return`<section class="page">
|
|
69
69
|
<div class="page-heading">
|
|
70
70
|
<div>
|
|
71
|
-
<p class="eyebrow">${
|
|
72
|
-
<h1>${
|
|
73
|
-
<p>${
|
|
71
|
+
<p class="eyebrow">${n("nav.sessions")}</p>
|
|
72
|
+
<h1>${n("sessions.title")}</h1>
|
|
73
|
+
<p>${n("sessions.subtitle")}</p>
|
|
74
74
|
</div>
|
|
75
75
|
</div>
|
|
76
76
|
<form id="filters" class="filters sessions-filters">
|
|
77
|
-
<input type="search" name="q" placeholder="${
|
|
77
|
+
<input type="search" name="q" placeholder="${n("sessions.search")}" />
|
|
78
78
|
<select name="status">
|
|
79
|
-
<option value="">${
|
|
79
|
+
<option value="">${n("sessions.anyStatus")}</option>
|
|
80
80
|
<option>starting</option><option>working</option><option>idle</option>
|
|
81
81
|
<option>analyzing</option><option>active</option><option>closed</option>
|
|
82
82
|
</select>
|
|
83
83
|
<select name="adopt">
|
|
84
|
-
<option value="">${
|
|
85
|
-
<option value="yes">${
|
|
86
|
-
<option value="no">${
|
|
84
|
+
<option value="">${n("sessions.adoptAny")}</option>
|
|
85
|
+
<option value="yes">${n("sessions.adoptYes")}</option>
|
|
86
|
+
<option value="no">${n("sessions.adoptNo")}</option>
|
|
87
87
|
</select>
|
|
88
|
-
<label class="filter-toggle"><input type="checkbox" name="active" checked> ${
|
|
88
|
+
<label class="filter-toggle"><input type="checkbox" name="active" checked> ${n("sessions.activeOnly")}</label>
|
|
89
89
|
${Tn()}
|
|
90
90
|
</form>
|
|
91
91
|
<div id="bulk-bar" class="bulk-bar" hidden>
|
|
92
92
|
<span id="bulk-count"></span>
|
|
93
|
-
<button type="button" id="bulk-close" class="contrast">${
|
|
94
|
-
<button type="button" id="bulk-clear">${
|
|
93
|
+
<button type="button" id="bulk-close" class="contrast">${n("sessions.closeSelected")}</button>
|
|
94
|
+
<button type="button" id="bulk-clear">${n("sessions.clearSelection")}</button>
|
|
95
95
|
</div>
|
|
96
96
|
<table id="sessions-table">
|
|
97
97
|
<thead><tr>
|
|
98
|
-
<th><input type="checkbox" id="select-all" title="${
|
|
99
|
-
${
|
|
100
|
-
${
|
|
101
|
-
${
|
|
102
|
-
${
|
|
103
|
-
${
|
|
104
|
-
${
|
|
105
|
-
${
|
|
106
|
-
${
|
|
107
|
-
<th>${
|
|
98
|
+
<th><input type="checkbox" id="select-all" title="${n("sessions.activeOnly")}"></th>
|
|
99
|
+
${we("botName",n("sessions.bot"))}
|
|
100
|
+
${we("cliId",n("sessions.cli"))}
|
|
101
|
+
${we("status",n("sessions.status"))}
|
|
102
|
+
${we("title",n("sessions.titleCol"))}
|
|
103
|
+
${we("workingDir",n("sessions.workingDir"))}
|
|
104
|
+
${we("spawnedAt",n("sessions.created"))}
|
|
105
|
+
${we("lastMessageAt",n("sessions.last"))}
|
|
106
|
+
${we("adopt",n("sessions.adopt"))}
|
|
107
|
+
<th>${n("sessions.actions")}</th>
|
|
108
108
|
</tr></thead>
|
|
109
109
|
<tbody></tbody>
|
|
110
110
|
</table>
|
|
111
111
|
<dialog id="drawer"></dialog>
|
|
112
|
-
</section>`}function
|
|
113
|
-
<td><input type="checkbox" class="row-select" ${b} ${
|
|
114
|
-
<td>${
|
|
115
|
-
<td><span class="badge cli-${
|
|
116
|
-
<td><span class="status status-${
|
|
117
|
-
<td>${
|
|
118
|
-
<td title="${
|
|
119
|
-
<td>${
|
|
120
|
-
<td>${
|
|
112
|
+
</section>`}function Et(e){e.innerHTML=Ln();let t=e.querySelector("#sessions-table tbody"),o=e.querySelector("#filters"),s=e.querySelector("#drawer"),a=e.querySelector("#select-all"),r=e.querySelector("#bulk-bar"),i=e.querySelector("#bulk-count"),m=e.querySelector("#bulk-close"),$=e.querySelector("#bulk-clear"),T=e.querySelector("#sessions-table"),k=new Set,y="lastMessageAt",c="desc";function I(u){let w=u.status==="closed",b=k.has(u.sessionId)?"checked":"";return`<tr data-id="${l(u.sessionId)}">
|
|
113
|
+
<td><input type="checkbox" class="row-select" ${b} ${w?"disabled":""}></td>
|
|
114
|
+
<td>${l(u.botName??"")}</td>
|
|
115
|
+
<td><span class="badge cli-${l(u.cliId??"unknown")}">${l(u.cliId??"unknown")}</span></td>
|
|
116
|
+
<td><span class="status status-${l(u.status??"unknown")}">${l(u.status??"unknown")}</span></td>
|
|
117
|
+
<td>${l((u.title??"").slice(0,48))}</td>
|
|
118
|
+
<td title="${l(u.workingDir??"")}">${l((u.workingDir??"").slice(-34))}</td>
|
|
119
|
+
<td>${Ae(u.spawnedAt)}</td>
|
|
120
|
+
<td>${Ae(u.lastMessageAt)}</td>
|
|
121
121
|
<td>${u.adopt?'<span class="badge">adopt</span>':""}</td>
|
|
122
|
-
<td><button class="open" type="button">${
|
|
123
|
-
</tr>`}function
|
|
122
|
+
<td><button class="open" type="button">${n("sessions.details")}</button></td>
|
|
123
|
+
</tr>`}function f(){let u=new FormData(o),w=(u.get("q")??"").toLowerCase(),b=u.getAll("cli"),E=b.length>0&&b.length<It.length,H=u.get("status"),C=u.get("adopt"),U=!!u.get("active"),J=[...W.sessions.values()].filter(P=>!E||b.includes(P.cliId??"unknown")).filter(P=>!H||P.status===H).filter(P=>!C||C==="yes"==!!P.adopt).filter(P=>!U||P.status!=="closed").filter(P=>!w||JSON.stringify(P).toLowerCase().includes(w));return J.sort(p),J}function g(u,w){return w==="spawnedAt"||w==="lastMessageAt"?Number(u[w]??0):w==="adopt"?!!u.adopt:String(u[w]??"").toLowerCase()}function p(u,w){let b=g(u,y),E=g(w,y),H=0;return typeof b=="number"&&typeof E=="number"?H=b-E:typeof b=="boolean"&&typeof E=="boolean"?H=Number(b)-Number(E):H=String(b).localeCompare(String(E)),H===0&&(H=Number(u.lastMessageAt??0)-Number(w.lastMessageAt??0)),c==="asc"?H:-H}function S(){T.querySelectorAll("th[data-sort]").forEach(u=>{let w=u.dataset.sort===y;u.classList.toggle("sorted",w),u.setAttribute("aria-sort",w?c==="asc"?"ascending":"descending":"none");let b=u.dataset.label??u.textContent?.trim()??"";u.textContent=w?`${b} ${c==="asc"?"\u25B2":"\u25BC"}`:b})}function L(u){r.hidden=k.size===0,i.textContent=n("sessions.selectedCount",{count:k.size});let w=u.filter(E=>E.status!=="closed");if(w.length===0){a.checked=!1,a.indeterminate=!1,a.disabled=!0;return}a.disabled=!1;let b=w.filter(E=>k.has(E.sessionId)).length;a.checked=b===w.length,a.indeterminate=b>0&&b<w.length}function v(){let u=f();for(let w of[...k]){let b=W.sessions.get(w);(!b||b.status==="closed")&&k.delete(w)}t.innerHTML=u.length?u.map(I).join(""):`<tr><td colspan="10" class="empty">${n("sessions.empty")}</td></tr>`,S(),L(u)}function x(u){let w=u.status==="closed";s.innerHTML=`<article>
|
|
124
124
|
<header>
|
|
125
|
-
<h3>${
|
|
126
|
-
<span class="status status-${
|
|
127
|
-
<p><code>${
|
|
125
|
+
<h3>${l(u.title??u.sessionId)}</h3>
|
|
126
|
+
<span class="status status-${l(u.status??"unknown")}">${l(u.status??"unknown")}</span>
|
|
127
|
+
<p><code>${l(u.sessionId)}</code> <button data-copy="${l(u.sessionId)}">${n("sessions.copy")}</button></p>
|
|
128
128
|
</header>
|
|
129
|
-
<p><b>${
|
|
130
|
-
<p><b>chatId:</b> <code>${
|
|
131
|
-
<p><b>rootMessageId:</b> <code>${
|
|
132
|
-
${u.threadId?`<p><b>threadId:</b> <code>${
|
|
133
|
-
<p><b>${
|
|
129
|
+
<p><b>${n("sessions.bot")}:</b> ${l(u.botName??"-")} \xB7 <b>${n("sessions.cli")}:</b> ${l(u.cliId??"?")}</p>
|
|
130
|
+
<p><b>chatId:</b> <code>${l(u.chatId??"")}</code> <button data-copy="${l(u.chatId??"")}">${n("sessions.copy")}</button></p>
|
|
131
|
+
<p><b>rootMessageId:</b> <code>${l(u.rootMessageId??"")}</code> <button data-copy="${l(u.rootMessageId??"")}">${n("sessions.copy")}</button></p>
|
|
132
|
+
${u.threadId?`<p><b>threadId:</b> <code>${l(u.threadId)}</code></p>`:""}
|
|
133
|
+
<p><b>${n("sessions.workingDir")}:</b> ${l(u.workingDir??"-")}</p>
|
|
134
134
|
<div class="actions">
|
|
135
|
-
<button id="locate-btn" type="button">${
|
|
136
|
-
${u.webPort?`<a class="btn-link primary" href="http://${
|
|
137
|
-
${
|
|
138
|
-
${
|
|
135
|
+
<button id="locate-btn" type="button">${n("sessions.locate")}</button>
|
|
136
|
+
${u.webPort?`<a class="btn-link primary" href="http://${l(location.hostname)}:${u.proxyPort??u.webPort}${u.proxyPort?`/s/${encodeURIComponent(u.sessionId)}`:""}" target="_blank" rel="noopener">${n("sessions.openTerminal")}</a>`:""}
|
|
137
|
+
${w?`<button id="resume-btn" type="button" class="primary">${n("sessions.resume")}</button>`:""}
|
|
138
|
+
${w?"":`<button id="close-btn" type="button" class="contrast">${n("sessions.close")}</button>`}
|
|
139
139
|
</div>
|
|
140
|
-
<form method="dialog"><button>${
|
|
141
|
-
</article>`,s.querySelectorAll("[data-copy]").forEach(C=>{C.onclick=()=>{navigator.clipboard.writeText(C.dataset.copy??""),C.textContent=
|
|
140
|
+
<form method="dialog"><button>${n("sessions.dismiss")}</button></form>
|
|
141
|
+
</article>`,s.querySelectorAll("[data-copy]").forEach(C=>{C.onclick=()=>{navigator.clipboard.writeText(C.dataset.copy??""),C.textContent=n("sessions.copied"),setTimeout(()=>{C.textContent=n("sessions.copy")},800)}});let b=s.querySelector("#locate-btn");b&&(b.onclick=async()=>{b.disabled=!0,b.textContent=n("sessions.locating");try{let C=await fetch(`/api/sessions/${encodeURIComponent(u.sessionId)}/locate`,{method:"POST"}),U=await C.json();if(U.ok){let J=30;b.textContent=n("sessions.cooldown",{seconds:J});let P=setInterval(()=>{J-=1,J<=0?(clearInterval(P),b.disabled=!1,b.textContent=n("sessions.locate")):b.textContent=n("sessions.cooldown",{seconds:J})},1e3)}else alert(`Locate failed: ${U.error??C.status}`),b.disabled=!1,b.textContent=n("sessions.locate")}catch(C){alert(`Locate error: ${C}`),b.disabled=!1,b.textContent=n("sessions.locate")}});let E=s.querySelector("#resume-btn");E&&(E.onclick=async()=>{E.disabled=!0;try{let C=await fetch(`/api/sessions/${encodeURIComponent(u.sessionId)}/resume`,{method:"POST"}),U=await C.json().catch(()=>({}));if(!C.ok||U.ok===!1){alert(`${n("sessions.resumeFailed")}: ${U?.error??C.status}`),E.disabled=!1;return}s.close()}catch(C){alert(`${n("sessions.resumeFailed")}: ${C}`),E.disabled=!1}});let H=s.querySelector("#close-btn");H&&(H.onclick=async()=>{if(confirm(n("sessions.closeConfirm"))){H.disabled=!0;try{await fetch(`/api/sessions/${encodeURIComponent(u.sessionId)}/close`,{method:"POST"})}finally{s.close()}}}),s.showModal()}t.addEventListener("click",u=>{let w=u.target;if(w.classList.contains("row-select")){let C=w.closest("tr[data-id]");if(!C)return;w.checked?k.add(C.dataset.id):k.delete(C.dataset.id),L(f());return}let b=w.closest("td");if(b&&b.querySelector(".row-select"))return;let E=w.closest("tr[data-id]");if(!E)return;let H=W.sessions.get(E.dataset.id);H&&x(H)}),a.addEventListener("change",()=>{let u=f().filter(w=>w.status!=="closed");for(let w of u)a.checked?k.add(w.sessionId):k.delete(w.sessionId);v()}),$.addEventListener("click",()=>{k.clear(),v()}),m.addEventListener("click",async()=>{let u=[...k];if(u.length===0||!confirm(n("sessions.closeBulkConfirm",{count:u.length})))return;m.disabled=!0,$.disabled=!0;let w=m.textContent,b=0,E=0,H=[...u];m.textContent=`0/${u.length}`;async function C(){for(;H.length;){let U=H.shift();try{let J=await fetch(`/api/sessions/${encodeURIComponent(U)}/close`,{method:"POST"}),P=await J.json().catch(()=>({}));(!J.ok||P?.ok===!1)&&(E+=1)}catch{E+=1}finally{b+=1,m.textContent=`${b}/${u.length}`}}}await Promise.all(Array.from({length:Math.min(6,u.length)},()=>C())),m.textContent=w,m.disabled=!1,$.disabled=!1,k.clear(),v(),E>0&&alert(`Failed: ${E}/${u.length}`)}),T.querySelectorAll("th[data-sort]").forEach(u=>{u.addEventListener("click",()=>{let w=u.dataset.sort;y===w?c=c==="asc"?"desc":"asc":(y=w,c=w==="spawnedAt"||w==="lastMessageAt"?"desc":"asc"),v()})}),o.addEventListener("input",v),W.on(v),v()}function In(){return`<section class="page">
|
|
142
142
|
<div class="page-heading">
|
|
143
143
|
<div>
|
|
144
|
-
<p class="eyebrow">${
|
|
145
|
-
<h1>${
|
|
146
|
-
<p>${
|
|
144
|
+
<p class="eyebrow">${n("nav.schedules")}</p>
|
|
145
|
+
<h1>${n("schedules.title")}</h1>
|
|
146
|
+
<p>${n("schedules.subtitle")}</p>
|
|
147
147
|
</div>
|
|
148
148
|
</div>
|
|
149
149
|
<form id="sched-filters" class="filters">
|
|
150
|
-
<input type="search" name="q" placeholder="${
|
|
150
|
+
<input type="search" name="q" placeholder="${n("schedules.search")}" />
|
|
151
151
|
<select name="kind">
|
|
152
|
-
<option value="">${
|
|
152
|
+
<option value="">${n("schedules.anyKind")}</option>
|
|
153
153
|
<option>cron</option>
|
|
154
154
|
<option>interval</option>
|
|
155
155
|
<option>once</option>
|
|
156
156
|
</select>
|
|
157
|
-
<label><input type="checkbox" name="enabled"> ${
|
|
157
|
+
<label><input type="checkbox" name="enabled"> ${n("schedules.enabledOnly")}</label>
|
|
158
158
|
</form>
|
|
159
159
|
<table>
|
|
160
160
|
<thead><tr>
|
|
161
|
-
<th>${
|
|
162
|
-
<th>${
|
|
161
|
+
<th>${n("schedules.name")}</th><th>${n("schedules.bot")}</th><th>${n("schedules.schedule")}</th><th>${n("schedules.next")}</th><th>${n("schedules.last")}</th>
|
|
162
|
+
<th>${n("schedules.repeat")}</th><th>${n("schedules.enabled")}</th><th>${n("schedules.actions")}</th>
|
|
163
163
|
</tr></thead>
|
|
164
164
|
<tbody id="schedules-tbody"></tbody>
|
|
165
165
|
</table>
|
|
166
|
-
</section>`}function
|
|
167
|
-
<td>${
|
|
168
|
-
<td>${
|
|
169
|
-
<td><code>${
|
|
170
|
-
<td>${
|
|
171
|
-
<td>${
|
|
166
|
+
</section>`}function xt(e){if(!e)return"\u2014";try{return new Date(e).toLocaleString()}catch{return e}}function Mt(e){e.innerHTML=In();let t=e.querySelector("#schedules-tbody"),o=e.querySelector("#sched-filters");function s(){let r=new FormData(o),i=(r.get("q")??"").toLowerCase(),m=r.get("kind"),$=!!r.get("enabled");return[...W.schedules.values()].filter(T=>!m||T.parsed?.kind===m).filter(T=>!$||T.enabled).filter(T=>!i||JSON.stringify(T).toLowerCase().includes(i)).sort((T,k)=>{if(T.enabled!==k.enabled)return T.enabled?-1:1;let y=T.nextRunAt?Date.parse(T.nextRunAt):1/0,c=k.nextRunAt?Date.parse(k.nextRunAt):1/0;return y-c})}function a(){t.innerHTML=s().map(r=>`<tr data-id="${l(r.id)}">
|
|
167
|
+
<td>${l(r.name??r.id)}</td>
|
|
168
|
+
<td>${l(r.botName??r.larkAppId??"-")}</td>
|
|
169
|
+
<td><code>${l(r.parsed?.display??"?")}</code></td>
|
|
170
|
+
<td>${xt(r.nextRunAt)}</td>
|
|
171
|
+
<td>${xt(r.lastRunAt)} ${r.lastStatus==="error"?"\u26A0\uFE0F":""}</td>
|
|
172
172
|
<td>${r.repeat?`${r.repeat.completed}/${r.repeat.times??"\u221E"}`:"\u2014"}</td>
|
|
173
173
|
<td>${r.enabled?"\u2713":"\u2717"}</td>
|
|
174
174
|
<td class="actions-cell">
|
|
175
|
-
<button data-op="run" type="button">${
|
|
176
|
-
${r.enabled?`<button data-op="pause" type="button">${
|
|
175
|
+
<button data-op="run" type="button">${n("schedules.runNow")}</button>
|
|
176
|
+
${r.enabled?`<button data-op="pause" type="button">${n("schedules.pause")}</button>`:`<button data-op="resume" type="button">${n("schedules.resume")}</button>`}
|
|
177
177
|
</td>
|
|
178
|
-
</tr>`).join("")||`<tr><td colspan="8" class="empty">${
|
|
178
|
+
</tr>`).join("")||`<tr><td colspan="8" class="empty">${n("schedules.empty")}</td></tr>`}t.addEventListener("click",async r=>{let i=r.target.closest("button[data-op]");if(!i)return;let m=i.closest("tr[data-id]");if(!m)return;let $=m.dataset.id,T=i.dataset.op;i.disabled=!0;let k=i.textContent;i.textContent="...";try{let y=await fetch(`/api/schedules/${encodeURIComponent($)}/${T}`,{method:"POST"}),c=await y.json().catch(()=>({}));(!y.ok||c.ok===!1)&&alert(`Failed: ${y.status} ${c?.error??""}`.trim())}catch(y){alert("Network error: "+y)}finally{i.disabled=!1,i.textContent=k}}),o.addEventListener("input",a),W.on(a),a()}var Q={chats:[],bots:[]};function En(){return`<section class="page">
|
|
179
179
|
<div class="page-heading">
|
|
180
180
|
<div>
|
|
181
|
-
<p class="eyebrow">${
|
|
182
|
-
<h1>${
|
|
183
|
-
<p>${
|
|
181
|
+
<p class="eyebrow">${n("nav.groups")}</p>
|
|
182
|
+
<h1>${n("groups.title")}</h1>
|
|
183
|
+
<p>${n("groups.subtitle")}</p>
|
|
184
184
|
</div>
|
|
185
185
|
</div>
|
|
186
186
|
<form id="g-filters" class="filters">
|
|
187
|
-
<input type="search" name="q" placeholder="${
|
|
188
|
-
<label><input type="checkbox" name="missing"> ${
|
|
189
|
-
<button type="button" id="g-refresh">${
|
|
190
|
-
<button type="button" id="g-create" class="primary">${
|
|
187
|
+
<input type="search" name="q" placeholder="${n("groups.search")}" />
|
|
188
|
+
<label><input type="checkbox" name="missing"> ${n("groups.missingOnly")}</label>
|
|
189
|
+
<button type="button" id="g-refresh">${n("groups.refresh")}</button>
|
|
190
|
+
<button type="button" id="g-create" class="primary">${n("groups.create")}</button>
|
|
191
191
|
</form>
|
|
192
192
|
<table>
|
|
193
193
|
<thead id="g-head"></thead>
|
|
194
194
|
<tbody id="g-body"></tbody>
|
|
195
195
|
</table>
|
|
196
196
|
<dialog id="g-drawer"></dialog>
|
|
197
|
-
</section>`}async function
|
|
197
|
+
</section>`}async function xe(){Q=await(await fetch("/api/groups")).json()}async function xn(){return(await fetch("/api/groups")).json()}function Mn(e,t){if(t.size===0)return!0;let o=e?.memberBots??[];for(let s of t)if(!o.some(a=>a.larkAppId===s&&a.inChat))return!1;return!0}function Ht(e,t){return e.filter(o=>!t||!t.has(o.larkAppId)).map(o=>`
|
|
198
198
|
<label class="checkbox-row">
|
|
199
|
-
<input type="checkbox" name="bot" value="${
|
|
200
|
-
${
|
|
199
|
+
<input type="checkbox" name="bot" value="${l(o.larkAppId)}">
|
|
200
|
+
${l(o.botName??o.larkAppId)} <small>(${l(o.larkAppId)})</small>
|
|
201
201
|
</label>
|
|
202
|
-
`).join("")}async function
|
|
202
|
+
`).join("")}async function Ct(e){e.innerHTML=En();let t=e.querySelector("#g-head"),o=e.querySelector("#g-body"),s=e.querySelector("#g-filters"),a=e.querySelector("#g-refresh"),r=e.querySelector("#g-drawer");a.onclick=async()=>{a.disabled=!0;try{await xe(),k()}finally{a.disabled=!1}};let i=e.querySelector("#g-create");i.onclick=()=>m(),await xe();function m(){let c=Q.bots;if(c.length===0){alert(n("groups.noBotsOnline"));return}r.innerHTML=`
|
|
203
203
|
<article>
|
|
204
|
-
<header><h3>${
|
|
205
|
-
<p>${
|
|
204
|
+
<header><h3>${n("groups.createTitle")}</h3></header>
|
|
205
|
+
<p>${n("groups.createHelp")}</p>
|
|
206
206
|
<form id="g-createform">
|
|
207
207
|
<label class="form-row">
|
|
208
|
-
<span>${
|
|
209
|
-
<input type="text" name="name" placeholder="${
|
|
208
|
+
<span>${n("groups.name")}</span>
|
|
209
|
+
<input type="text" name="name" placeholder="${n("groups.namePlaceholder")}" maxlength="60">
|
|
210
210
|
</label>
|
|
211
211
|
<label class="form-row">
|
|
212
|
-
<span>${
|
|
212
|
+
<span>${n("groups.bindDir")}</span>
|
|
213
213
|
<input type="text" name="bindWorkingDir" placeholder="e.g. ~/projects/botmux">
|
|
214
|
-
<small>${
|
|
214
|
+
<small>${n("groups.bindDirHelp")}</small>
|
|
215
215
|
</label>
|
|
216
216
|
<fieldset>
|
|
217
|
-
<legend>${
|
|
218
|
-
${
|
|
217
|
+
<legend>${n("groups.botPicker")}</legend>
|
|
218
|
+
${Ht(c)}
|
|
219
219
|
</fieldset>
|
|
220
220
|
<div class="actions">
|
|
221
|
-
<button type="submit" class="primary">${
|
|
222
|
-
<button type="button" id="g-create-cancel">${
|
|
221
|
+
<button type="submit" class="primary">${n("groups.createSubmit")}</button>
|
|
222
|
+
<button type="button" id="g-create-cancel">${n("groups.cancel")}</button>
|
|
223
223
|
</div>
|
|
224
224
|
</form>
|
|
225
|
-
</article>`,r.showModal(),r.querySelector("#g-create-cancel").onclick=()=>r.close(),r.querySelector("#g-createform").onsubmit=async g=>{g.preventDefault();let p=new FormData(g.target)
|
|
225
|
+
</article>`,r.showModal(),r.querySelector("#g-create-cancel").onclick=()=>r.close(),r.querySelector("#g-createform").onsubmit=async g=>{g.preventDefault();let p=new FormData(g.target),S=(p.get("name")??"").trim(),L=(p.get("bindWorkingDir")??"").trim(),v=p.getAll("bot");if(v.length===0){alert("Pick at least one bot.");return}let x=g.target.querySelector("button[type=submit]");x&&(x.disabled=!0,x.textContent="Creating...");try{let u=await fetch("/api/groups/create",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({name:S||void 0,larkAppIds:v,bindWorkingDir:L||void 0})}),w=await u.json();if(w.ok&&w.chatId){$(w);let b=Array.isArray(w.invalidBotIds)?w.invalidBotIds:[],E=v.filter(C=>!b.includes(C)),H=new Set(E);typeof w.creator=="string"&&w.creator&&H.add(w.creator),I(w.chatId,S||w.chatId,E,w.creator),k(),f(w.chatId,H).catch(()=>{})}else alert(`Failed: ${w.error??u.status}`),r.close()}catch(u){alert("Network error: "+u),r.close()}};function I(g,p,S,L){let v=new Set(S);L&&v.add(L);let x=Q.bots.map(w=>({larkAppId:w.larkAppId,botName:w.botName,inChat:v.has(w.larkAppId),oncallChat:null})),u={chatId:g,name:p,ownerId:L??null,memberBots:x};Q.chats=[u,...Q.chats.filter(w=>w.chatId!==g)]}async function f(g,p){let S=[600,1200,1200,1200,1200,1200];for(let L of S){await new Promise(u=>setTimeout(u,L));let v;try{v=await xn()}catch{continue}let x=(v.chats??[]).find(u=>u.chatId===g);if(x&&Mn(x,p)){Q=v,k();return}}}}function $(c){let I=String(c.chatId),f=`https://applink.feishu.cn/client/chat/open?openChatId=${encodeURIComponent(I)}`,g=c.invalidBotIds??[],p=c.invalidUserIds??[],S=c.autoInvitedOpenId,L=!!c.autoInviteRejected,v=c.ownerTransferredTo,x=c.transferError,u=c.notifyMessageId,w=c.notifyError,b=Array.isArray(c.oncallBindings)?c.oncallBindings:[],E=b.filter(P=>P?.ok).length,H=b.filter(P=>!P?.ok),C=b.length>0?H.length===0?`<p class="hint-ok">\u5DF2\u7ED1\u5B9A\u76EE\u5F55\uFF1A<code>${l(c.bindResolvedPath??"")}</code>\uFF08${E}/${b.length} bots\uFF09</p>`:`<p class="hint-warn">\u76EE\u5F55\u7ED1\u5B9A\u90E8\u5206\u5931\u8D25\uFF1A\u6210\u529F ${E}/${b.length}\u3002${H.map(P=>`<br><code>${l(P.larkAppId??"?")}</code>: ${l(P.error??"unknown")}`).join("")}</p>`:"",U;if(S){let P=v?"<br><small>\u7FA4\u4E3B\u5DF2\u4ECE\u673A\u5668\u4EBA\u8F6C\u8BA9\u7ED9\u4F60\u3002</small>":x?`<br><small class="hint-warn-inline">\u26A0 \u81EA\u52A8\u8F6C\u8BA9\u7FA4\u4E3B\u5931\u8D25\uFF08${l(x)}\uFF09\uFF0C\u4F60\u73B0\u5728\u662F\u6210\u5458\u4F46\u7FA4\u4E3B\u4ECD\u662F\u673A\u5668\u4EBA\u3002</small>`:"",Ie=u?`<br><small>\u673A\u5668\u4EBA\u5DF2\u5728\u7FA4\u91CC @ \u4E86\u4F60\uFF08\u6D88\u606F id <code>${l(u)}</code>\uFF09\uFF0C\u770B\u98DE\u4E66\u901A\u77E5\u5C31\u80FD\u8FDB\u7FA4\u3002</small>`:w?`<br><small class="hint-warn-inline">\u26A0 \u81EA\u52A8 @ \u901A\u77E5\u5931\u8D25\uFF08${l(w)}\uFF09\uFF0C\u65B0\u7FA4\u53EF\u80FD\u4E0D\u4F1A\u4E3B\u52A8\u51FA\u73B0\u5728\u4F60\u4FA7\u8FB9\u680F\uFF0C\u5EFA\u8BAE\u4ECE\u4E0B\u9762\u6309\u94AE\u8DF3\u8FDB\u53BB\u3002</small>`:"";U=`<p class="hint-ok">\u5DF2\u81EA\u52A8\u9080\u8BF7\u4F60\uFF08<code>${l(S)}</code>\uFF09\u4F5C\u4E3A\u6210\u5458\u3002${P}${Ie}</p>`}else L?U='<p class="hint-warn">\u98DE\u4E66\u62D2\u7EDD\u4E86\u81EA\u52A8\u9080\u8BF7\uFF08\u4F60\u7684 open_id \u5728\u521B\u5EFA\u8005 bot \u7684 scope \u4E0B\u4E0D\u53EF\u7528\uFF09\u3002<strong>\u4F60\u76EE\u524D\u4E0D\u662F\u65B0\u7FA4\u6210\u5458</strong>\uFF0C\u9700\u8981\u8BA9\u7FA4\u91CC\u7684\u67D0\u4E2A\u673A\u5668\u4EBA\u624B\u52A8\u628A\u4F60\u52A0\u8FDB\u6765\u3002</p>':U='<p class="hint-warn">\u6CA1\u5728 dashboard \u7F13\u5B58\u91CC\u627E\u5230 ownerOpenId\uFF0C<strong>\u6CA1\u6709\u81EA\u52A8\u9080\u8BF7\u4F60</strong>\u3002\u70B9\u5F00\u4E0B\u9762\u94FE\u63A5\u524D\uFF0C\u5148\u8BA9\u7FA4\u91CC\u4EFB\u4E00\u673A\u5668\u4EBA\u624B\u52A8\u628A\u4F60\u52A0\u8FDB\u53BB\u3002</p>';let J=[g.length?`<li>\u65E0\u6548 bot id: <code>${g.map(l).join(", ")}</code></li>`:"",p.length?`<li>\u65E0\u6548\u7528\u6237 open_id: <code>${p.map(l).join(", ")}</code></li>`:""].filter(Boolean).join("");r.innerHTML=`
|
|
226
226
|
<article>
|
|
227
|
-
<header><h3>${
|
|
228
|
-
<p><b>chatId:</b> <code>${
|
|
229
|
-
<p><b>\u521B\u5EFA\u8005:</b> <code>${
|
|
230
|
-
${
|
|
227
|
+
<header><h3>${n("groups.successTitle")}</h3></header>
|
|
228
|
+
<p><b>chatId:</b> <code>${l(I)}</code> <button type="button" data-copy="${l(I)}">${n("sessions.copy")}</button></p>
|
|
229
|
+
<p><b>\u521B\u5EFA\u8005:</b> <code>${l(c.creator??"?")}</code></p>
|
|
230
|
+
${U}
|
|
231
231
|
${C}
|
|
232
232
|
${J?`<ul>${J}</ul>`:""}
|
|
233
233
|
<div class="actions">
|
|
234
|
-
<a class="btn-link primary" href="${
|
|
235
|
-
<button type="button" id="g-create-close">${
|
|
234
|
+
<a class="btn-link primary" href="${f}" target="_blank" rel="noopener">${n("groups.openGroup")}</a>
|
|
235
|
+
<button type="button" id="g-create-close">${n("sessions.dismiss")}</button>
|
|
236
236
|
</div>
|
|
237
|
-
</article>`,r.querySelectorAll("[data-copy]").forEach(
|
|
238
|
-
<th>${
|
|
239
|
-
${
|
|
240
|
-
<th>${
|
|
241
|
-
</tr>`}function
|
|
237
|
+
</article>`,r.querySelectorAll("[data-copy]").forEach(P=>{P.onclick=()=>{navigator.clipboard.writeText(P.dataset.copy??""),P.textContent=n("sessions.copied"),setTimeout(()=>{P.textContent=n("sessions.copy")},800)}}),r.querySelector("#g-create-close").onclick=()=>r.close()}function T(){t.innerHTML=`<tr>
|
|
238
|
+
<th>${n("groups.chat")}</th>
|
|
239
|
+
${Q.bots.map(c=>`<th>${l(c.botName??c.larkAppId)}</th>`).join("")}
|
|
240
|
+
<th>${n("groups.actions")}</th>
|
|
241
|
+
</tr>`}function k(){T();let c=new FormData(s),I=(c.get("q")??"").toLowerCase(),f=!!c.get("missing"),g=Q.chats.filter(p=>!I||(p.name??"").toLowerCase().includes(I)||p.chatId.toLowerCase().includes(I)||(p.ownerId??"").toLowerCase().includes(I)).filter(p=>!f||p.memberBots.some(S=>!S.inChat));if(g.length===0){o.innerHTML=`<tr><td colspan="${Q.bots.length+2}" class="empty">${n("groups.empty")}</td></tr>`;return}o.innerHTML=g.map(p=>`<tr data-chat="${l(p.chatId)}">
|
|
242
242
|
<td>
|
|
243
|
-
<strong>${
|
|
244
|
-
<small><code>${
|
|
243
|
+
<strong>${l(p.name??p.chatId)}</strong><br>
|
|
244
|
+
<small><code>${l(p.chatId)}</code></small>
|
|
245
245
|
</td>
|
|
246
|
-
${
|
|
246
|
+
${Q.bots.map(S=>{let L=p.memberBots.find(u=>u.larkAppId===S.larkAppId),v=L?L.error?"!":L.inChat?"\u2713":"\u2717":"?";return`<td class="${L?L.error?"cell-error":L.inChat?"cell-in":"cell-out":"cell-unknown"}" title="${l(L?.error??"")}">${v}</td>`}).join("")}
|
|
247
247
|
<td>
|
|
248
|
-
<button class="add-bots" type="button">${
|
|
249
|
-
<button class="manage-chat" type="button">${
|
|
248
|
+
<button class="add-bots" type="button">${n("groups.addBots")}</button>
|
|
249
|
+
<button class="manage-chat" type="button">${n("groups.manage")}</button>
|
|
250
250
|
</td>
|
|
251
|
-
</tr>`).join("")}
|
|
251
|
+
</tr>`).join("")}k(),o.addEventListener("click",async c=>{let I=c.target.closest("button.add-bots");if(!I)return;let g=I.closest("tr[data-chat]").dataset.chat,p=Q.chats.find(v=>v.chatId===g);if(!p)return;let S=new Set(p.memberBots.filter(v=>v.inChat).map(v=>v.larkAppId));if(!Q.bots.filter(v=>!S.has(v.larkAppId)).length){alert("All configured bots are already in this chat.");return}r.innerHTML=`
|
|
252
252
|
<article>
|
|
253
|
-
<header><h3>${
|
|
254
|
-
<p>${
|
|
253
|
+
<header><h3>${n("groups.addBots")} \xB7 ${l(p.name??p.chatId)}</h3></header>
|
|
254
|
+
<p>${n("groups.createHelp")}</p>
|
|
255
255
|
<form id="g-addform">
|
|
256
|
-
${
|
|
256
|
+
${Ht(Q.bots,S)}
|
|
257
257
|
<div class="actions">
|
|
258
|
-
<button type="submit" class="primary">${
|
|
259
|
-
<button type="button" id="g-cancel">${
|
|
258
|
+
<button type="submit" class="primary">${n("groups.addBots")}</button>
|
|
259
|
+
<button type="button" id="g-cancel">${n("groups.cancel")}</button>
|
|
260
260
|
</div>
|
|
261
261
|
</form>
|
|
262
|
-
</article>`,r.showModal(),r.querySelector("#g-cancel").onclick=()=>r.close(),r.querySelector("#g-addform").onsubmit=async
|
|
263
|
-
`);alert(E),await
|
|
262
|
+
</article>`,r.showModal(),r.querySelector("#g-cancel").onclick=()=>r.close(),r.querySelector("#g-addform").onsubmit=async v=>{v.preventDefault();let u=new FormData(v.target).getAll("bot");if(u.length===0){alert("Pick at least one bot.");return}try{let b=await(await fetch(`/api/groups/${encodeURIComponent(g)}/add-bots`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({larkAppIds:u})})).json();if(b.error==="no_proxy_bot")alert("No bot is currently in this chat \u2014 add one manually in Feishu first, then retry.");else if(b.result){let E=b.result.map(H=>`${H.id}: ${H.ok?"OK":`failed (${H.error??"unknown"})`}`).join(`
|
|
263
|
+
`);alert(E),await xe(),k()}else alert(`Unexpected response: ${JSON.stringify(b)}`)}catch(w){alert("Network error: "+w)}finally{r.close()}}}),o.addEventListener("click",async c=>{let I=c.target.closest("button.manage-chat");if(!I)return;let g=I.closest("tr[data-chat]").dataset.chat,p=Q.chats.find(S=>S.chatId===g);p&&y(p)});function y(c){let I=c.memberBots.filter(g=>g.inChat),f=typeof c.ownerId=="string"?c.ownerId:"";r.innerHTML=`
|
|
264
264
|
<article>
|
|
265
|
-
<header><h3>${
|
|
266
|
-
<p><b>chatId:</b> <code>${
|
|
267
|
-
<p><b>${
|
|
265
|
+
<header><h3>${n("groups.manageTitle",{name:c.name??c.chatId})}</h3></header>
|
|
266
|
+
<p><b>chatId:</b> <code>${l(c.chatId)}</code></p>
|
|
267
|
+
<p><b>${n("groups.owner")}:</b> <code>${l(c.ownerId??n("common.unknown"))}</code></p>
|
|
268
268
|
|
|
269
269
|
<fieldset>
|
|
270
|
-
<legend>${
|
|
271
|
-
<p><small>${
|
|
272
|
-
${
|
|
273
|
-
<div class="oncall-row" data-bot="${
|
|
270
|
+
<legend>${n("groups.oncall")}</legend>
|
|
271
|
+
<p><small>${n("groups.oncallHelp")}</small></p>
|
|
272
|
+
${I.length===0?'<p class="empty">\u6CA1\u6709\u673A\u5668\u4EBA\u5728\u7FA4\u91CC</p>':I.map(g=>{let p=!!g.oncallChat,S=g.oncallChat?.workingDir??"";return`
|
|
273
|
+
<div class="oncall-row" data-bot="${l(g.larkAppId)}">
|
|
274
274
|
<label class="checkbox-row">
|
|
275
275
|
<input type="checkbox" data-action="toggle" ${p?"checked":""}>
|
|
276
|
-
<strong>${
|
|
277
|
-
<small>(${
|
|
276
|
+
<strong>${l(g.botName??g.larkAppId)}</strong>
|
|
277
|
+
<small>(${l(g.larkAppId)})</small>
|
|
278
278
|
</label>
|
|
279
279
|
<div class="oncall-row-body">
|
|
280
280
|
<input type="text" data-input="workingDir" placeholder="e.g. /root/iserver/botmux"
|
|
281
|
-
value="${
|
|
282
|
-
<button type="button" data-action="save">${
|
|
281
|
+
value="${l(S)}" ${p?"":"disabled"}>
|
|
282
|
+
<button type="button" data-action="save">${n("groups.save")}</button>
|
|
283
283
|
<span class="oncall-status" data-status></span>
|
|
284
284
|
</div>
|
|
285
285
|
</div>
|
|
@@ -287,173 +287,196 @@ effects={effects}, activities={activities}, waits={waits}, cancels={cancels}`,"w
|
|
|
287
287
|
</fieldset>
|
|
288
288
|
|
|
289
289
|
<fieldset>
|
|
290
|
-
<legend>${
|
|
291
|
-
${
|
|
290
|
+
<legend>${n("groups.leaveTitle")}</legend>
|
|
291
|
+
${I.length===0?'<p class="empty">\u6CA1\u6709\u673A\u5668\u4EBA\u5728\u7FA4\u91CC</p>':I.map(g=>`
|
|
292
292
|
<label class="checkbox-row">
|
|
293
|
-
<input type="checkbox" name="leave-bot" value="${
|
|
294
|
-
${
|
|
295
|
-
<small>${g.larkAppId===
|
|
293
|
+
<input type="checkbox" name="leave-bot" value="${l(g.larkAppId)}">
|
|
294
|
+
${l(g.botName??g.larkAppId)}
|
|
295
|
+
<small>${g.larkAppId===f?"\xB7 \u7FA4\u4E3B":""}</small>
|
|
296
296
|
</label>
|
|
297
297
|
`).join("")}
|
|
298
298
|
</fieldset>
|
|
299
299
|
|
|
300
300
|
<div class="actions">
|
|
301
|
-
<button id="g-leave-btn" type="button" ${
|
|
302
|
-
<button id="g-disband-btn" type="button" class="contrast" ${
|
|
301
|
+
<button id="g-leave-btn" type="button" ${I.length===0?"disabled":""}>${n("groups.leaveSelected")}</button>
|
|
302
|
+
<button id="g-disband-btn" type="button" class="contrast" ${I.length===0?"disabled":""}>${n("groups.disband")}</button>
|
|
303
303
|
</div>
|
|
304
|
-
<p class="hint-warn"><small>${
|
|
305
|
-
<form method="dialog"><button>${
|
|
306
|
-
</article>`,r.showModal(),r.querySelectorAll(".oncall-row").forEach(g=>{let p=g.dataset.bot
|
|
307
|
-
`);alert(
|
|
308
|
-
\u5173\u95ED\u4E86 ${
|
|
309
|
-
\u5173\u95ED\u4E86 ${
|
|
304
|
+
<p class="hint-warn"><small>${n("groups.dangerHint")}</small></p>
|
|
305
|
+
<form method="dialog"><button>${n("sessions.dismiss")}</button></form>
|
|
306
|
+
</article>`,r.showModal(),r.querySelectorAll(".oncall-row").forEach(g=>{let p=g.dataset.bot,S=g.querySelector("input[data-action=toggle]"),L=g.querySelector("input[data-input=workingDir]"),v=g.querySelector("button[data-action=save]"),x=g.querySelector("[data-status]");S.addEventListener("change",()=>{L.disabled=!S.checked,S.checked&&L.focus()}),v.addEventListener("click",async()=>{x.textContent="",x.className="oncall-status";let u=S.checked,w=L.value.trim();if(u&&!w){x.textContent=n("groups.needWorkingDir"),x.classList.add("hint-warn-inline");return}v.disabled=!0;try{let b=`/api/groups/${encodeURIComponent(c.chatId)}/oncall/${encodeURIComponent(p)}`,E=u?await fetch(b,{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify({workingDir:w})}):await fetch(b,{method:"DELETE"}),H=await E.json().catch(()=>({}));if(E.ok&&H.ok){x.textContent=u?`\u2713 \u5DF2\u7ED1\u5B9A \u2192 ${H.resolvedPath??w}`:"\u2713 \u5DF2\u89E3\u7ED1",x.classList.add("hint-ok");try{await xe(),k()}catch{}}else x.textContent=`\u2717 ${H.error??E.status}`,x.classList.add("hint-warn-inline")}catch(b){x.textContent=`\u2717 ${b?.message??b}`,x.classList.add("hint-warn-inline")}finally{v.disabled=!1}})}),r.querySelector("#g-leave-btn").onclick=async()=>{let g=[...r.querySelectorAll("input[name=leave-bot]:checked")].map(p=>p.value);if(g.length===0){alert("\u81F3\u5C11\u9009\u4E00\u4E2A\u673A\u5668\u4EBA");return}if(confirm(`\u786E\u5B9A\u8BA9 ${g.length} \u4E2A\u673A\u5668\u4EBA\u9000\u51FA\u7FA4\u804A\uFF1F\u8BE5 bot \u5728\u6B64\u7FA4\u7684\u4F1A\u8BDD\u4F1A\u4E00\u5E76\u5173\u95ED\u3002`))try{let S=await(await fetch(`/api/groups/${encodeURIComponent(c.chatId)}/leave`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({larkAppIds:g})})).json(),L=(S.result??[]).map(v=>{if(!v.ok)return`${v.larkAppId}: \u5931\u8D25 (${v.error??"unknown"})`;let x=v.closedSessions??[],u=x.filter(E=>!E.ok).length,w=x.length-u,b=x.length===0?"":u===0?`\uFF08\u5173\u95ED ${w} \u4E2A\u4F1A\u8BDD\uFF09`:`\uFF08\u5173\u95ED ${w} \u4E2A\uFF0C${u} \u4E2A\u5931\u8D25\uFF09`;return`${v.larkAppId}: OK${b}`}).join(`
|
|
307
|
+
`);alert(L||`Unexpected: ${JSON.stringify(S)}`),await xe(),k()}catch(p){alert("Network error: "+p)}finally{r.close()}},r.querySelector("#g-disband-btn").onclick=async()=>{if(I.length===0||!confirm(`\u786E\u5B9A\u89E3\u6563\u7FA4\u804A\u300C${c.name??c.chatId}\u300D\uFF1F\u6B64\u64CD\u4F5C\u4E0D\u53EF\u6062\u590D\uFF0C\u672C\u7FA4\u6240\u6709\u673A\u5668\u4EBA\u4F1A\u8BDD\u4E5F\u4F1A\u4E00\u5E76\u5173\u95ED\u3002`))return;let g=[...I].sort((S,L)=>(L.larkAppId===f?1:0)-(S.larkAppId===f?1:0)),p=[];for(let S of g)try{let L=await fetch(`/api/groups/${encodeURIComponent(c.chatId)}/disband`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({larkAppId:S.larkAppId})}),v=await L.json();if(v.ok){let x=v.closedSessions??[],u=x.filter(E=>!E.ok).length,w=x.length-u,b=x.length===0?"":u===0?`
|
|
308
|
+
\u5173\u95ED\u4E86 ${w} \u4E2A\u4F1A\u8BDD\u3002`:`
|
|
309
|
+
\u5173\u95ED\u4E86 ${w} \u4E2A\u4F1A\u8BDD\uFF0C${u} \u4E2A\u4F1A\u8BDD\u5173\u95ED\u5931\u8D25\u3002`;alert(`\u5DF2\u89E3\u6563\uFF08\u7531 ${S.botName??S.larkAppId} \u6267\u884C\uFF09${b}`),await xe(),k(),r.close();return}p.push(`${S.botName??S.larkAppId}: ${v.error??L.status}`)}catch(L){p.push(`${S.botName??S.larkAppId}: ${L}`)}alert(`\u6240\u6709\u5728\u7FA4\u673A\u5668\u4EBA\u5747\u65E0\u6CD5\u89E3\u6563\uFF1A
|
|
310
310
|
${p.join(`
|
|
311
311
|
`)}
|
|
312
312
|
|
|
313
|
-
\u5EFA\u8BAE\u6539\u7528\u300C\u9000\u51FA\u7FA4\u804A\u300D\u3002`)}}s.addEventListener("input",
|
|
313
|
+
\u5EFA\u8BAE\u6539\u7528\u300C\u9000\u51FA\u7FA4\u804A\u300D\u3002`)}}s.addEventListener("input",k)}var ee={bots:[]},Me=null;function Hn(){return`<section class="page">
|
|
314
314
|
<div class="page-heading">
|
|
315
315
|
<div>
|
|
316
|
-
<p class="eyebrow">${
|
|
317
|
-
<h1>${
|
|
318
|
-
<p>${
|
|
316
|
+
<p class="eyebrow">${n("nav.botDefaults")}</p>
|
|
317
|
+
<h1>${n("botDefaults.title")}</h1>
|
|
318
|
+
<p>${n("botDefaults.subtitle")}</p>
|
|
319
319
|
</div>
|
|
320
320
|
</div>
|
|
321
321
|
<form id="bd-filters" class="filters">
|
|
322
|
-
<input type="search" name="q" placeholder="${
|
|
323
|
-
<button type="button" id="bd-refresh">${
|
|
322
|
+
<input type="search" name="q" placeholder="${n("botDefaults.search")}" />
|
|
323
|
+
<button type="button" id="bd-refresh">${n("botDefaults.refresh")}</button>
|
|
324
324
|
</form>
|
|
325
325
|
<div id="bd-list"></div>
|
|
326
|
-
</section>`}async function
|
|
327
|
-
<header><strong>${
|
|
328
|
-
<small>${
|
|
329
|
-
<p class="hint-warn-inline">\u67E5\u8BE2\u5931\u8D25\uFF1A${
|
|
330
|
-
</article>`;let
|
|
326
|
+
</section>`}async function At(){try{let e=await fetch("/api/bots"),t=await e.json().catch(()=>({}));if(!e.ok){Me=t?.error?`HTTP ${e.status}: ${t.error}${t.path?` (${t.path})`:""}`:`HTTP ${e.status}`,ee={bots:[]};return}if(!t||!Array.isArray(t.bots)){Me="unexpected response shape (no `bots` array)",ee={bots:[]};return}Me=null,ee=t}catch(e){Me=e?.message??String(e),ee={bots:[]}}}function Dt(e){if(!e)return"\u2014";let t=new Date(e);return Number.isNaN(t.getTime())?"\u2014":t.toLocaleString()}async function Rt(e){e.innerHTML=Hn();let t=e.querySelector("#bd-list"),o=e.querySelector("#bd-filters"),s=e.querySelector("#bd-refresh");s.onclick=async()=>{s.disabled=!0;try{await At(),a()}finally{s.disabled=!1}},await At();function a(){let g=(new FormData(o).get("q")??"").toLowerCase(),p=ee.bots.filter(S=>!g||(S.botName??"").toLowerCase().includes(g)||(S.larkAppId??"").toLowerCase().includes(g));if(Me){t.innerHTML=`<p class="hint-warn">\u65E0\u6CD5\u52A0\u8F7D bot \u5217\u8868\uFF1A${l(Me)}<br>\u5E38\u89C1\u539F\u56E0\uFF1Adashboard / daemon \u8FDB\u7A0B\u8FD8\u5728\u8DD1\u65E7\u4EE3\u7801\uFF0C\u6267\u884C <code>botmux restart</code> \u540E\u5237\u65B0\u3002</p>`;return}if(p.length===0){t.innerHTML=`<p class="empty">${n("botDefaults.empty")}</p>`;return}t.innerHTML=p.map(r).join(""),I()}function r(f){if(f.error)return`<article class="bd-card" data-appid="${l(f.larkAppId)}">
|
|
327
|
+
<header><strong>${l(f.botName??f.larkAppId)}</strong>
|
|
328
|
+
<small>${l(f.larkAppId)}</small></header>
|
|
329
|
+
<p class="hint-warn-inline">\u67E5\u8BE2\u5931\u8D25\uFF1A${l(f.error)}</p>
|
|
330
|
+
</article>`;let g=f.defaultOncall??{enabled:!1,workingDir:"",since:0},p=!!g.enabled;return`<article class="bd-card" data-appid="${l(f.larkAppId)}">
|
|
331
331
|
<header>
|
|
332
|
-
<strong>${
|
|
333
|
-
<small>${
|
|
332
|
+
<strong>${l(f.botName??f.larkAppId)}</strong>
|
|
333
|
+
<small>${l(f.larkAppId)}</small>
|
|
334
334
|
</header>
|
|
335
335
|
<div class="bd-body">
|
|
336
336
|
<section class="bd-section">
|
|
337
|
-
<h3 class="bd-section-title">${
|
|
337
|
+
<h3 class="bd-section-title">${n("botDefaults.sectionOncall")}</h3>
|
|
338
338
|
<label class="checkbox-row">
|
|
339
|
-
<input type="checkbox" data-action="toggle" ${
|
|
340
|
-
<strong>${
|
|
341
|
-
<small>${
|
|
339
|
+
<input type="checkbox" data-action="toggle" ${p?"checked":""}>
|
|
340
|
+
<strong>${n("botDefaults.defaultOncall")}</strong>
|
|
341
|
+
<small>${n("botDefaults.defaultOncallHelp")}</small>
|
|
342
342
|
</label>
|
|
343
343
|
<div class="bd-row">
|
|
344
344
|
<label>
|
|
345
|
-
<span>${
|
|
345
|
+
<span>${n("botDefaults.workingDir")}</span>
|
|
346
346
|
<input type="text" data-input="workingDir" placeholder="e.g. /root/iserver/botmux"
|
|
347
|
-
value="${
|
|
347
|
+
value="${l(g.workingDir??"")}" ${p?"":"disabled"}>
|
|
348
348
|
</label>
|
|
349
349
|
</div>
|
|
350
|
-
<p class="bd-section-note">${
|
|
350
|
+
<p class="bd-section-note">${n("botDefaults.warning")}</p>
|
|
351
351
|
<div class="bd-meta">
|
|
352
|
-
<small>${
|
|
353
|
-
<small>${
|
|
352
|
+
<small>${n("botDefaults.lastEnabled")}: ${l(Dt(g.since??0))}</small>
|
|
353
|
+
<small>${n("botDefaults.autobound",{count:f.autoboundChatCount??0})}</small>
|
|
354
354
|
</div>
|
|
355
355
|
<div class="actions">
|
|
356
|
-
<button type="button" data-action="save">${
|
|
356
|
+
<button type="button" data-action="save">${n("botDefaults.save")}</button>
|
|
357
357
|
<span class="oncall-status" data-status></span>
|
|
358
358
|
</div>
|
|
359
|
-
${
|
|
359
|
+
${c(f)}
|
|
360
360
|
</section>
|
|
361
|
-
${
|
|
362
|
-
${
|
|
363
|
-
${
|
|
361
|
+
${i(f)}
|
|
362
|
+
${$(f)}
|
|
363
|
+
${T(f)}
|
|
364
|
+
${y(f)}
|
|
364
365
|
</div>
|
|
365
|
-
</article>`}function
|
|
366
|
-
<h3 class="bd-section-title">${
|
|
367
|
-
<p class="bd-section-note">${
|
|
366
|
+
</article>`}function i(f){let g=typeof f.teamRole=="string";return`<section class="bd-section">
|
|
367
|
+
<h3 class="bd-section-title">${n("botDefaults.sectionRole")}</h3>
|
|
368
|
+
<p class="bd-section-note">${n("botDefaults.roleHelp")}</p>
|
|
368
369
|
<textarea data-input="teamRole" rows="6"
|
|
369
|
-
placeholder="${
|
|
370
|
-
style="width:100%;box-sizing:border-box;font:13px/1.5 ui-monospace,Menlo,monospace;padding:10px"${
|
|
370
|
+
placeholder="${l(n("botDefaults.rolePlaceholder"))}"
|
|
371
|
+
style="width:100%;box-sizing:border-box;font:13px/1.5 ui-monospace,Menlo,monospace;padding:10px"${g?"":" disabled"}>${g?l(f.teamRole):""}</textarea>
|
|
371
372
|
<div class="actions">
|
|
372
|
-
<button type="button" data-action="save-role"${
|
|
373
|
-
<button type="button" data-action="delete-role"${
|
|
373
|
+
<button type="button" data-action="save-role"${g?"":" disabled"}>${n("botDefaults.roleSave")}</button>
|
|
374
|
+
<button type="button" data-action="delete-role"${g?"":" disabled"}>${n("botDefaults.roleDelete")}</button>
|
|
374
375
|
<span class="oncall-status" data-role-status></span>
|
|
375
376
|
</div>
|
|
376
|
-
</section>`}function f
|
|
377
|
-
<h3 class="bd-section-title">${
|
|
377
|
+
</section>`}function m(f){return f==null?n("botDefaults.brandStateDefault"):f.trim()===""?n("botDefaults.brandStateOff"):n("botDefaults.brandStateCustom")}function $(f){let g=f.brandLabel??null;return`<section class="bd-section">
|
|
378
|
+
<h3 class="bd-section-title">${n("botDefaults.sectionBrand")}</h3>
|
|
378
379
|
<div class="bd-row bd-brand">
|
|
379
380
|
<label>
|
|
380
|
-
<span>${
|
|
381
|
+
<span>${n("botDefaults.brandLabel")}</span>
|
|
381
382
|
<input type="text" data-input="brandLabel"
|
|
382
|
-
placeholder="${
|
|
383
|
-
value="${
|
|
383
|
+
placeholder="${l(n("botDefaults.brandLabelPlaceholder"))}"
|
|
384
|
+
value="${l(g??"")}">
|
|
384
385
|
</label>
|
|
385
|
-
<small data-brand-state>${
|
|
386
|
-
<small>${
|
|
386
|
+
<small data-brand-state>${l(m(g))}</small>
|
|
387
|
+
<small>${n("botDefaults.brandLabelHelp")}</small>
|
|
387
388
|
<div class="actions">
|
|
388
|
-
<button type="button" data-action="save-brand">${
|
|
389
|
-
<button type="button" data-action="reset-brand">${
|
|
389
|
+
<button type="button" data-action="save-brand">${n("botDefaults.brandSave")}</button>
|
|
390
|
+
<button type="button" data-action="reset-brand">${n("botDefaults.brandReset")}</button>
|
|
390
391
|
<span class="oncall-status" data-brand-status></span>
|
|
391
392
|
</div>
|
|
392
393
|
</div>
|
|
393
|
-
</section>`}function
|
|
394
|
-
<h3 class="bd-section-title">${
|
|
394
|
+
</section>`}function T(f){let g=f.disableStreamingCard===!0,p=f.writableTerminalLinkInCard===!0,S=f.privateCard===!0;return`<section class="bd-section">
|
|
395
|
+
<h3 class="bd-section-title">${n("botDefaults.sectionCard")}</h3>
|
|
395
396
|
<label class="checkbox-row">
|
|
396
|
-
<input type="checkbox" data-action="toggle-disable-streaming" ${
|
|
397
|
-
<strong>${
|
|
398
|
-
<small>${
|
|
397
|
+
<input type="checkbox" data-action="toggle-disable-streaming" ${g?"checked":""}>
|
|
398
|
+
<strong>${n("botDefaults.disableStreaming")}</strong>
|
|
399
|
+
<small>${n("botDefaults.disableStreamingHelp")}</small>
|
|
399
400
|
</label>
|
|
400
401
|
<label class="checkbox-row">
|
|
401
|
-
<input type="checkbox" data-action="toggle-writable-link" ${
|
|
402
|
-
<strong>${
|
|
403
|
-
<small>${
|
|
402
|
+
<input type="checkbox" data-action="toggle-writable-link" ${p?"checked":""} ${g?"disabled":""}>
|
|
403
|
+
<strong>${n("botDefaults.writableLink")}</strong>
|
|
404
|
+
<small>${n("botDefaults.writableLinkHelp")}</small>
|
|
404
405
|
</label>
|
|
405
406
|
<label class="checkbox-row">
|
|
406
|
-
<input type="checkbox" data-action="toggle-private-card" ${
|
|
407
|
-
<strong>${
|
|
408
|
-
<small>${
|
|
407
|
+
<input type="checkbox" data-action="toggle-private-card" ${S?"checked":""}>
|
|
408
|
+
<strong>${n("botDefaults.privateCard")}</strong>
|
|
409
|
+
<small>${n("botDefaults.privateCardHelp")}</small>
|
|
409
410
|
</label>
|
|
410
411
|
<div class="actions">
|
|
411
|
-
<small data-card-pref-moot class="hint-warn-inline" ${
|
|
412
|
+
<small data-card-pref-moot class="hint-warn-inline" ${g?"":"hidden"}>${n("botDefaults.writableLinkMoot")}</small>
|
|
412
413
|
<span class="oncall-status" data-card-pref-status></span>
|
|
413
414
|
</div>
|
|
414
|
-
</section>`}function y(
|
|
415
|
-
<
|
|
415
|
+
</section>`}function k(f){return f==null?n("botDefaults.quotaStateOff"):n("botDefaults.quotaStateOn",{count:f})}function y(f){let g=f.restrictGrantCommands===!0,p=typeof f.messageQuotaDefaultLimit=="number"?f.messageQuotaDefaultLimit:null;return`<section class="bd-section">
|
|
416
|
+
<h3 class="bd-section-title">${n("botDefaults.sectionGrant")}</h3>
|
|
416
417
|
<label class="checkbox-row">
|
|
417
|
-
<input type="checkbox" data-action="toggle-
|
|
418
|
-
<strong>${
|
|
419
|
-
<small>${
|
|
418
|
+
<input type="checkbox" data-action="toggle-restrict-grant" ${g?"checked":""}>
|
|
419
|
+
<strong>${n("botDefaults.restrictGrant")}</strong>
|
|
420
|
+
<small>${n("botDefaults.restrictGrantHelp")}</small>
|
|
421
|
+
</label>
|
|
422
|
+
<div class="bd-row bd-quota">
|
|
423
|
+
<label>
|
|
424
|
+
<span>${n("botDefaults.quotaDefault")}</span>
|
|
425
|
+
<input type="number" min="1" step="1" data-input="quotaLimit"
|
|
426
|
+
placeholder="${l(n("botDefaults.quotaPlaceholder"))}"
|
|
427
|
+
value="${p??""}">
|
|
428
|
+
</label>
|
|
429
|
+
<small data-quota-state>${l(k(p))}</small>
|
|
430
|
+
<small>${n("botDefaults.quotaHelp")}</small>
|
|
431
|
+
<div class="actions">
|
|
432
|
+
<button type="button" data-action="save-quota">${n("botDefaults.quotaSave")}</button>
|
|
433
|
+
<button type="button" data-action="off-quota">${n("botDefaults.quotaOff")}</button>
|
|
434
|
+
<span class="oncall-status" data-grant-status></span>
|
|
435
|
+
</div>
|
|
436
|
+
</div>
|
|
437
|
+
</section>`}function c(f){let g=f.autoStartOnGroupJoin===!0,p=f.autoStartOnNewTopic===!0,S=typeof f.autoStartOnGroupJoinPrompt=="string"?f.autoStartOnGroupJoinPrompt:"";return`<div class="bd-subsection">
|
|
438
|
+
<h4 class="bd-subsection-title">${n("botDefaults.sectionAutoStart")}</h4>
|
|
439
|
+
<label class="checkbox-row">
|
|
440
|
+
<input type="checkbox" data-action="toggle-auto-join" ${g?"checked":""}>
|
|
441
|
+
<strong>${n("botDefaults.autoStartJoin")}</strong>
|
|
442
|
+
<small>${n("botDefaults.autoStartJoinHelp")}</small>
|
|
420
443
|
</label>
|
|
421
444
|
<div class="bd-row">
|
|
422
445
|
<label>
|
|
423
|
-
<span>${
|
|
446
|
+
<span>${n("botDefaults.autoStartJoinPrompt")}</span>
|
|
424
447
|
<textarea data-input="autoJoinPrompt" rows="3"
|
|
425
|
-
placeholder="${
|
|
448
|
+
placeholder="${l(n("botDefaults.autoStartJoinPromptPlaceholder"))}">${l(S)}</textarea>
|
|
426
449
|
</label>
|
|
427
450
|
<div class="actions">
|
|
428
|
-
<button type="button" data-action="save-auto-join-prompt">${
|
|
451
|
+
<button type="button" data-action="save-auto-join-prompt">${n("botDefaults.autoStartJoinPromptSave")}</button>
|
|
429
452
|
</div>
|
|
430
453
|
</div>
|
|
431
454
|
<label class="checkbox-row">
|
|
432
|
-
<input type="checkbox" data-action="toggle-auto-topic" ${
|
|
433
|
-
<strong>${
|
|
434
|
-
<small>${
|
|
455
|
+
<input type="checkbox" data-action="toggle-auto-topic" ${p?"checked":""}>
|
|
456
|
+
<strong>${n("botDefaults.autoStartTopic")}</strong>
|
|
457
|
+
<small>${n("botDefaults.autoStartTopicHelp")}</small>
|
|
435
458
|
</label>
|
|
436
459
|
<div class="actions">
|
|
437
460
|
<span class="oncall-status" data-auto-start-status></span>
|
|
438
461
|
</div>
|
|
439
|
-
</div>`}function
|
|
462
|
+
</div>`}function I(){t.querySelectorAll(".bd-card").forEach(f=>{let g=f.dataset.appid,p=f.querySelector("input[data-action=toggle]"),S=f.querySelector("input[data-input=workingDir]"),L=f.querySelector("button[data-action=save]"),v=f.querySelector("[data-status]");if(!p||!S||!L||!v)return;p.addEventListener("change",()=>{S.disabled=!p.checked,p.checked&&S.focus()}),L.addEventListener("click",async()=>{v.textContent="",v.className="oncall-status";let z=p.checked,F=S.value.trim();if(z&&!F){v.textContent=n("botDefaults.required"),v.classList.add("hint-warn-inline");return}L.disabled=!0;try{let O=await fetch(`/api/bots/${encodeURIComponent(g)}/default-oncall`,{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify({enabled:z,workingDir:F})}),A=await O.json().catch(()=>({}));if(O.ok&&A.ok){let R=A.resolvedPath?` \u2192 ${A.resolvedPath}`:"";v.textContent=z?`\u2713 \u5DF2\u5F00\u542F${R}\uFF08\u672A\u7ED1\u5B9A\u7684\u7FA4\u4E0B\u6B21\u5F00\u8BDD\u9898\u81EA\u52A8 oncall\uFF09`:"\u2713 \u5DF2\u5173\u95ED\uFF08\u5DF2\u7ED1\u5B9A\u7684\u7FA4\u4E0D\u52A8\uFF09",v.classList.add("hint-ok");let B=ee.bots.find(N=>N.larkAppId===g);B&&A.defaultOncall&&(B.defaultOncall=A.defaultOncall);let h=f.querySelector(".bd-meta small:first-child");h&&A.defaultOncall?.since!=null&&(h.textContent=`\u4E0A\u6B21\u542F\u7528\u65F6\u95F4\uFF1A${Dt(A.defaultOncall.since)}`)}else v.textContent=`\u2717 ${A.error??O.status}`,v.classList.add("hint-warn-inline")}catch(O){v.textContent=`\u2717 ${O?.message??O}`,v.classList.add("hint-warn-inline")}finally{L.disabled=!1}});let x=f.querySelector("input[data-input=brandLabel]"),u=f.querySelector("button[data-action=save-brand]"),w=f.querySelector("button[data-action=reset-brand]"),b=f.querySelector("[data-brand-status]"),E=f.querySelector("[data-brand-state]");async function H(z,F){if(b){b.textContent="",b.className="oncall-status",F.disabled=!0;try{let O=await fetch(`/api/bots/${encodeURIComponent(g)}/brand-label`,{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify({brandLabel:z})}),A=await O.json().catch(()=>({}));if(O.ok&&A.ok){let R=A.brandLabel??null;b.textContent="\u2713",b.classList.add("hint-ok"),x&&(x.value=R??""),E&&(E.textContent=m(R));let B=ee.bots.find(h=>h.larkAppId===g);B&&(B.brandLabel=R)}else b.textContent=`\u2717 ${A.error??O.status}`,b.classList.add("hint-warn-inline")}catch(O){b.textContent=`\u2717 ${O?.message??O}`,b.classList.add("hint-warn-inline")}finally{F.disabled=!1}}}x&&u&&u.addEventListener("click",()=>H(x.value,u)),w&&w.addEventListener("click",()=>H(null,w));let C=f.querySelector("input[data-action=toggle-disable-streaming]"),U=f.querySelector("input[data-action=toggle-writable-link]"),J=f.querySelector("input[data-action=toggle-private-card]"),P=f.querySelector("[data-card-pref-status]"),Ie=f.querySelector("[data-card-pref-moot]");async function ae(z,F,O=P){if(O){O.textContent="",O.className="oncall-status",F.disabled=!0;try{let A=await fetch(`/api/bots/${encodeURIComponent(g)}/card-prefs`,{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify(z)}),R=await A.json().catch(()=>({}));if(A.ok&&R.ok){O.textContent=`\u2713 ${n("botDefaults.cardPrefSaved")}`,O.classList.add("hint-ok");let B=ee.bots.find(h=>h.larkAppId===g);B&&(B.disableStreamingCard=R.disableStreamingCard,B.writableTerminalLinkInCard=R.writableTerminalLinkInCard,B.privateCard=R.privateCard,B.autoStartOnGroupJoin=R.autoStartOnGroupJoin,B.autoStartOnGroupJoinPrompt=R.autoStartOnGroupJoinPrompt,B.autoStartOnNewTopic=R.autoStartOnNewTopic)}else O.textContent=`\u2717 ${R.error??A.status}`,O.classList.add("hint-warn-inline")}catch(A){O.textContent=`\u2717 ${A?.message??A}`,O.classList.add("hint-warn-inline")}finally{F===U?F.disabled=!!C?.checked:F.disabled=!1}}}C&&C.addEventListener("change",()=>{let z=C.checked;U&&(U.disabled=z),Ie&&(Ie.hidden=!z),ae({disableStreamingCard:z},C)}),U&&U.addEventListener("change",()=>{ae({writableTerminalLinkInCard:U.checked},U)}),J&&J.addEventListener("change",()=>{ae({privateCard:J.checked},J)});let se=f.querySelector("input[data-action=toggle-auto-join]"),re=f.querySelector("input[data-action=toggle-auto-topic]"),ye=f.querySelector("textarea[data-input=autoJoinPrompt]"),Y=f.querySelector("button[data-action=save-auto-join-prompt]"),ie=f.querySelector("[data-auto-start-status]");se&&se.addEventListener("change",()=>{ae({autoStartOnGroupJoin:se.checked},se,ie)}),re&&re.addEventListener("change",()=>{ae({autoStartOnNewTopic:re.checked},re,ie)}),ye&&Y&&Y.addEventListener("click",()=>{ae({autoStartOnGroupJoinPrompt:ye.value},Y,ie)});let Ce=f.querySelector("textarea[data-input=teamRole]"),ue=f.querySelector("button[data-action=save-role]"),pe=f.querySelector("button[data-action=delete-role]"),_=f.querySelector("[data-role-status]");if(Ce&&ue&&pe&&_){let O=function(R){let B=t.querySelector(`.bd-card[data-appid="${CSS.escape(g)}"]`);if(!B)return;let h=B.querySelector("textarea[data-input=teamRole]"),N=B.querySelector("button[data-action=save-role]"),D=B.querySelector("button[data-action=delete-role]");h&&(h.value=R,h.disabled=!1),N&&(N.disabled=!1),D&&(D.disabled=!1)};var bt=O;let z=`/api/team/local-bots/${encodeURIComponent(g)}/role`,F=ee.bots.find(R=>R.larkAppId===g);F&&typeof F.teamRole!="string"&&!F.teamRoleLoading&&(F.teamRoleLoading=!0,(async()=>{try{let R=await fetch(z),B=await R.json().catch(()=>({}));R.ok&&B.ok?(F.teamRole=B.role??"",O(F.teamRole)):(_.textContent=`\u2717 ${n("botDefaults.roleLoadErr")}: ${B.error??R.status}`,_.classList.add("hint-warn-inline"))}catch(R){_.textContent=`\u2717 ${n("botDefaults.roleLoadErr")}: ${R?.message??R}`,_.classList.add("hint-warn-inline")}finally{F.teamRoleLoading=!1}})());async function A(R,B,h){if(_&&!(!F||typeof F.teamRole!="string")){_.textContent="",_.className="oncall-status",ue.disabled=!0,pe.disabled=!0;try{let N=await fetch(z,{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify({role:R})}),D=await N.json().catch(()=>({}));N.ok&&D.ok?(F&&(F.teamRole=R.trim()),_.textContent=`\u2713 ${h?n("botDefaults.roleDeleted"):n("botDefaults.roleSaved")}`,_.classList.add("hint-ok")):(_.textContent=`\u2717 ${D.error??N.status}`,_.classList.add("hint-warn-inline"))}catch(N){_.textContent=`\u2717 ${N?.message??N}`,_.classList.add("hint-warn-inline")}finally{ue.disabled=!1,pe.disabled=!1}}}ue.addEventListener("click",()=>A(Ce.value,ue,!1)),pe.addEventListener("click",()=>{Ce.value="",A("",pe,!0)})}let ve=f.querySelector("input[data-action=toggle-restrict-grant]"),le=f.querySelector("input[data-input=quotaLimit]"),de=f.querySelector("button[data-action=save-quota]"),fe=f.querySelector("button[data-action=off-quota]"),K=f.querySelector("[data-grant-status]"),ke=f.querySelector("[data-quota-state]");async function Ee(z,F){if(K){K.textContent="",K.className="oncall-status",F.disabled=!0;try{let O=await fetch(`/api/bots/${encodeURIComponent(g)}/grant-prefs`,{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify(z)}),A=await O.json().catch(()=>({}));if(O.ok&&A.ok){K.textContent=`\u2713 ${n("botDefaults.cardPrefSaved")}`,K.classList.add("hint-ok");let R=typeof A.messageQuotaDefaultLimit=="number"?A.messageQuotaDefaultLimit:null,B=ee.bots.find(h=>h.larkAppId===g);B&&(B.restrictGrantCommands=A.restrictGrantCommands===!0,B.messageQuotaDefaultLimit=R),ke&&(ke.textContent=k(R)),le&&"messageQuotaDefaultLimit"in z&&(le.value=R==null?"":String(R))}else K.textContent=`\u2717 ${A.error??O.status}`,K.classList.add("hint-warn-inline")}catch(O){K.textContent=`\u2717 ${O?.message??O}`,K.classList.add("hint-warn-inline")}finally{F.disabled=!1}}}ve&&ve.addEventListener("change",()=>{Ee({restrictGrantCommands:ve.checked},ve)}),le&&de&&de.addEventListener("click",()=>{let z=le.value.trim();if(z===""){Ee({messageQuotaDefaultLimit:null},de);return}if(!/^[1-9]\d*$/.test(z)){K&&(K.textContent=`\u2717 ${n("botDefaults.quotaInvalid")}`,K.className="oncall-status hint-warn-inline");return}Ee({messageQuotaDefaultLimit:Number(z)},de)}),le&&fe&&fe.addEventListener("click",()=>{le.value="",Ee({messageQuotaDefaultLimit:null},fe)})})}a(),o.addEventListener("input",a)}var rt=4096,Fe=[],te=null,ne=null,V="",He=new Set;function Cn(){return`<section class="page roles-page">
|
|
440
463
|
<div class="page-heading">
|
|
441
464
|
<div>
|
|
442
|
-
<p class="eyebrow">${
|
|
443
|
-
<h1>${
|
|
444
|
-
<p>${
|
|
465
|
+
<p class="eyebrow">${n("nav.roles")}</p>
|
|
466
|
+
<h1>${n("roles.title")}</h1>
|
|
467
|
+
<p>${n("roles.subtitle")}</p>
|
|
445
468
|
</div>
|
|
446
469
|
</div>
|
|
447
470
|
<div class="roles-layout">
|
|
448
471
|
<div class="roles-tree-panel">
|
|
449
472
|
<div class="roles-tree-header">
|
|
450
|
-
<input type="search" id="roles-search" placeholder="${
|
|
451
|
-
<button type="button" id="roles-refresh">${
|
|
473
|
+
<input type="search" id="roles-search" placeholder="${n("roles.search")}" />
|
|
474
|
+
<button type="button" id="roles-refresh">${n("roles.refresh")}</button>
|
|
452
475
|
</div>
|
|
453
476
|
<div id="roles-tree" class="roles-tree"></div>
|
|
454
477
|
</div>
|
|
455
478
|
<div class="roles-editor-panel">
|
|
456
|
-
<div id="roles-editor-empty" class="roles-editor-empty">${
|
|
479
|
+
<div id="roles-editor-empty" class="roles-editor-empty">${n("roles.selectHint")}</div>
|
|
457
480
|
<div id="roles-editor-form" class="roles-editor-form" style="display:none">
|
|
458
481
|
<div class="roles-editor-breadcrumb">
|
|
459
482
|
<span id="roles-editor-group-name"></span>
|
|
@@ -463,52 +486,52 @@ ${p.join(`
|
|
|
463
486
|
<div class="roles-editor-meta">
|
|
464
487
|
<span id="roles-editor-chat-id" class="roles-editor-meta-line"></span>
|
|
465
488
|
</div>
|
|
466
|
-
<textarea id="roles-editor-textarea" placeholder="${
|
|
489
|
+
<textarea id="roles-editor-textarea" placeholder="${n("roles.editorPlaceholder")}" rows="14"></textarea>
|
|
467
490
|
<div class="roles-editor-footer">
|
|
468
491
|
<span id="roles-editor-bytecount" class="roles-bytecount"></span>
|
|
469
492
|
<div class="roles-editor-actions">
|
|
470
|
-
<button type="button" id="roles-delete" class="danger">${
|
|
471
|
-
<button type="button" id="roles-save" class="primary">${
|
|
493
|
+
<button type="button" id="roles-delete" class="danger">${n("roles.delete")}</button>
|
|
494
|
+
<button type="button" id="roles-save" class="primary">${n("roles.save")}</button>
|
|
472
495
|
</div>
|
|
473
496
|
</div>
|
|
474
497
|
<div id="roles-preview" class="roles-preview"></div>
|
|
475
498
|
</div>
|
|
476
499
|
</div>
|
|
477
500
|
</div>
|
|
478
|
-
</section>`}async function
|
|
479
|
-
<div class="roles-bot-row ${
|
|
480
|
-
data-group-id="${
|
|
481
|
-
data-bot-id="${
|
|
501
|
+
</section>`}async function Ue(){Fe=((await(await fetch("/api/groups")).json()).chats??[]).map(o=>({chatId:o.chatId,name:o.name??o.chatId,memberBots:(o.memberBots??[]).map(s=>({larkAppId:s.larkAppId,botName:s.botName??s.larkAppId,inChat:s.inChat??!1,hasRole:s.hasRole??!1,oncallChat:s.oncallChat??null}))}))}async function Ot(e,t){return(await fetch(`/api/roles/${encodeURIComponent(e)}/${encodeURIComponent(t)}`)).json()}async function An(e,t,o){return(await fetch(`/api/roles/${encodeURIComponent(e)}/${encodeURIComponent(t)}`,{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify({content:o})})).ok}async function Dn(e,t){return(await fetch(`/api/roles/${encodeURIComponent(e)}/${encodeURIComponent(t)}`,{method:"DELETE"})).ok}function Pt(e){return e.memberBots.filter(t=>t.inChat&&t.hasRole).length}function Rn(e){return e.memberBots.filter(t=>t.inChat).length}function $e(e=""){let t=document.getElementById("roles-tree");if(!t)return;let o=e.toLowerCase(),s=Fe.filter(a=>{if(!o)return!0;let r=a.chatId.toLowerCase().includes(o)||(a.name??"").toLowerCase().includes(o),i=a.memberBots.some(m=>m.larkAppId.toLowerCase().includes(o)||(m.botName??"").toLowerCase().includes(o));return r||i});if(s.length===0){t.innerHTML=`<div class="roles-empty">${n("roles.noChats")}</div>`;return}t.innerHTML=s.map(a=>{let r=He.has(a.chatId),i=a.memberBots.filter(y=>y.inChat),m=r?"\u25BE":"\u25B8",$=Pt(a),T=Rn(a),k=r?i.map(y=>`
|
|
502
|
+
<div class="roles-bot-row ${te===a.chatId&&ne===y.larkAppId?"selected":""}"
|
|
503
|
+
data-group-id="${l(a.chatId)}"
|
|
504
|
+
data-bot-id="${l(y.larkAppId)}">
|
|
482
505
|
<span class="roles-bot-indent"></span>
|
|
483
506
|
<span class="roles-bot-icon">\u{1F916}</span>
|
|
484
507
|
<div class="roles-bot-info">
|
|
485
|
-
<div class="roles-bot-name">${
|
|
486
|
-
<div class="roles-bot-id">${
|
|
508
|
+
<div class="roles-bot-name">${l(y.botName)}</div>
|
|
509
|
+
<div class="roles-bot-id">${l(y.larkAppId)}</div>
|
|
487
510
|
</div>
|
|
488
|
-
<span class="roles-badge ${
|
|
489
|
-
${
|
|
511
|
+
<span class="roles-badge ${y.hasRole?"has-role":"no-role"}">
|
|
512
|
+
${y.hasRole?n("roles.configured"):n("roles.unconfigured")}
|
|
490
513
|
</span>
|
|
491
514
|
</div>`).join(""):"";return`
|
|
492
515
|
<div class="roles-group-section">
|
|
493
|
-
<div class="roles-group-row ${r?"expanded":""} ${
|
|
494
|
-
data-group-id="${
|
|
495
|
-
<span class="roles-group-arrow">${
|
|
516
|
+
<div class="roles-group-row ${r?"expanded":""} ${te===a.chatId&&!ne?"selected":""}"
|
|
517
|
+
data-group-id="${l(a.chatId)}">
|
|
518
|
+
<span class="roles-group-arrow">${m}</span>
|
|
496
519
|
<span class="roles-group-icon">\u{1F4C1}</span>
|
|
497
520
|
<div class="roles-group-info">
|
|
498
|
-
<div class="roles-group-name">${
|
|
521
|
+
<div class="roles-group-name">${l(a.name??a.chatId)}</div>
|
|
499
522
|
<div class="roles-group-meta">
|
|
500
|
-
${
|
|
523
|
+
${$}/${T} ${n("roles.botsWithRoles")}
|
|
501
524
|
</div>
|
|
502
525
|
</div>
|
|
503
526
|
<span class="roles-group-chevron"></span>
|
|
504
527
|
</div>
|
|
505
|
-
<div class="roles-bot-list">${
|
|
506
|
-
</div>`}).join(""),t.querySelectorAll(".roles-group-row").forEach(a=>{a.addEventListener("click",()=>{let r=a.dataset.groupId;r&&(
|
|
528
|
+
<div class="roles-bot-list">${k}</div>
|
|
529
|
+
</div>`}).join(""),t.querySelectorAll(".roles-group-row").forEach(a=>{a.addEventListener("click",()=>{let r=a.dataset.groupId;r&&(He.has(r)?He.delete(r):He.add(r),$e(document.getElementById("roles-search")?.value??""))})}),t.querySelectorAll(".roles-bot-row").forEach(a=>{a.addEventListener("click",r=>{r.stopPropagation();let i=a.dataset.groupId,m=a.dataset.botId;i&&m&&qn(i,m)})})}async function qn(e,t){te=e,ne=t;let o=await Ot(t,e),s=document.getElementById("roles-editor-empty"),a=document.getElementById("roles-editor-form"),r=document.getElementById("roles-editor-textarea"),i=document.getElementById("roles-editor-group-name"),m=document.getElementById("roles-editor-bot-name"),$=document.getElementById("roles-editor-chat-id");s&&(s.style.display="none"),a&&(a.style.display="");let T=Fe.find(c=>c.chatId===e),k=T?.memberBots.find(c=>c.larkAppId===t);i&&(i.textContent=T?.name??e),m&&(m.textContent=k?.botName??t),$&&($.textContent=`${e} \xB7 ${t}`),V=o.content??"",r&&(r.value=V,r.focus()),it(),lt(),$e(document.getElementById("roles-search")?.value??"");let y=document.getElementById("roles-delete");y&&(y.style.display=o.hasRole?"":"none")}function it(){let e=document.getElementById("roles-editor-bytecount");if(!e)return;let t=new TextEncoder().encode(V).length;e.textContent=`${t} / ${rt} bytes`,e.className=`roles-bytecount ${t>3800?"warn":""} ${t>rt?"over":""}`,On(t)}function On(e){let t=document.getElementById("roles-save");if(!t)return;let o=e??new TextEncoder().encode(V).length;t.disabled=o>rt||V.trim().length===0}function lt(){let e=document.getElementById("roles-preview");e&&(V.trim()?e.innerHTML=`<strong>${n("roles.preview")}</strong><pre>${l(V)}</pre>`:e.innerHTML=`<small>${n("roles.previewEmpty")}</small>`)}function qt(){te=null,ne=null,V="";let e=document.getElementById("roles-editor-empty"),t=document.getElementById("roles-editor-form"),o=document.getElementById("roles-editor-textarea"),s=document.getElementById("roles-delete");e&&(e.style.display=""),t&&(t.style.display="none"),o&&(o.value=""),s&&(s.style.display="none")}async function Bt(e){e.innerHTML=Cn(),He.clear(),qt(),await Ue();for(let t of Fe)Pt(t)>0&&He.add(t.chatId);$e(),document.getElementById("roles-search")?.addEventListener("input",t=>{$e(t.target.value)}),document.getElementById("roles-refresh")?.addEventListener("click",async()=>{if(await Ue(),$e(document.getElementById("roles-search")?.value??""),te&&ne){let t=await Ot(ne,te),o=document.getElementById("roles-editor-textarea");o&&(o.value=t.content??""),V=t.content??"",it(),lt();let s=document.getElementById("roles-delete");s&&(s.style.display=t.hasRole?"":"none")}}),document.getElementById("roles-save")?.addEventListener("click",async function(){if(!(!te||!ne)){this.disabled=!0,this.textContent="...";try{if(await An(ne,te,V)){await Ue(),$e(document.getElementById("roles-search")?.value??"");let o=document.getElementById("roles-delete");o&&(o.style.display="");let s=document.createElement("span");s.className="roles-saved-flash",s.textContent=` ${n("roles.saved")}`,document.querySelector(".roles-editor-footer")?.appendChild(s),setTimeout(()=>s.remove(),2e3)}else{let o=document.createElement("span");o.className="roles-saved-flash roles-save-error",o.textContent=V.trim().length===0?` ${n("roles.emptyError")}`:` ${n("roles.saveFailed")}`,document.querySelector(".roles-editor-footer")?.appendChild(o),setTimeout(()=>o.remove(),3e3)}}finally{this.disabled=!1,this.textContent=n("roles.save")}}}),document.getElementById("roles-delete")?.addEventListener("click",async function(){if(!(!te||!ne)&&confirm(n("roles.confirmDelete"))){this.disabled=!0,this.textContent="...";try{await Dn(ne,te)&&(await Ue(),qt(),$e(document.getElementById("roles-search")?.value??""))}finally{this.disabled=!1,this.textContent=n("roles.delete")}}}),document.getElementById("roles-editor-textarea")?.addEventListener("input",t=>{V=t.target.value,it(),lt()})}async function ze(e){let t=await fetch(e);return{status:t.status,body:await t.json().catch(()=>({}))}}async function We(e,t,o){let s=await fetch(t,{method:e,headers:{"content-type":"application/json"},body:o?JSON.stringify(o):void 0});return{status:s.status,body:await s.json().catch(()=>({}))}}var Te=(e,t)=>We("POST",e,t),Pn=(e,t)=>We("PUT",e,t),dt=[],Nt=[],jt="",_e="",ct=new Map,Je=new Map,De=new Set,Re=new Set;function j(e){return document.getElementById(e)}function Ge(){return[...dt,...Nt]}function Se(e){let t=ct.get(e);return t||(t=new Set,ct.set(e,t)),t}function Bn(e){return Ge().find(t=>t.key===e)}function Ut(e){let t=(o,s,a)=>`<a href="${o}" style="padding:6px 14px;border-radius:8px;text-decoration:none;font-size:14px;${a?"background:var(--accent,#3370ff);color:#fff":"color:var(--text,#1f2329)"}">${s}</a>`;return`<div style="display:flex;gap:8px;margin-bottom:14px">${t("#/team","\u6211\u7684\u56E2\u961F",e==="home")}${t("#/team/manage","\u56E2\u961F\u7BA1\u7406",e==="manage")}</div>`}function Nn(){return`<section class="page">
|
|
507
530
|
<div class="page-heading"><div>
|
|
508
531
|
<p class="eyebrow">\u56E2\u961F</p><h1>\u56E2\u961F\u534F\u4F5C\uFF08\u8DE8\u90E8\u7F72\uFF09</h1>
|
|
509
532
|
<p>\u628A\u522B\u7684\u90E8\u7F72\uFF08\u540C\u4E8B\u81EA\u5DF1\u8DD1\u7684 botmux\uFF09\u9080\u8BF7\u8FDB\u540C\u4E00\u4E2A\u56E2\u961F\uFF0C\u4E92\u76F8\u53D1\u73B0\u673A\u5668\u4EBA\u3001\u534F\u4F5C\u62C9\u7FA4\u3002</p>
|
|
510
533
|
</div></div>
|
|
511
|
-
${
|
|
534
|
+
${Ut("home")}
|
|
512
535
|
<div class="card" style="margin-bottom:16px">
|
|
513
536
|
<h2 style="margin-top:0">\u672C\u90E8\u7F72</h2>
|
|
514
537
|
<p>\u6211\u7684\u98DE\u4E66\u8EAB\u4EFD\uFF1A<b id="tf-owner">\u672A\u7ED1\u5B9A</b>
|
|
@@ -537,12 +560,12 @@ ${qt("home")}
|
|
|
537
560
|
</div>
|
|
538
561
|
</div>
|
|
539
562
|
</div>
|
|
540
|
-
</section>`}function jn(e){let t=(B("tf-search").value||"").trim().toLowerCase();if(t&&!((e.name||"")+" "+(e.cliId||"")+" "+(e.capability||"")).toLowerCase().includes(t))return!1;let n=B("tf-cli").value;return!(n&&e.cliId!==n||B("tf-fcap").checked&&!e.capability||B("tf-frole").checked&&!e.hasTeamRole)}function Un(e,t){let n=[...e.deployments].sort((a,r)=>a.local===r.local?0:a.local?-1:1),s="";for(let a of n){let r=t.filter(i=>i.deployment.id===a.id);if(!r.length)continue;let l=a.id===Bt,f=l?"\u672C\u90E8\u7F72":a.stale?"\u8FDC\u7AEF\xB7\u79BB\u7EBF\uFF1F":"\u8FDC\u7AEF",k=e.kind==="local"&&!l?` <button class="tf-rmmember ghost" data-team="${d(e.teamId)}" data-dep="${d(a.id)}" data-name="${d(a.name)}" style="font-size:12px">\u79FB\u9664</button>`:"",S=`${e.key}::${a.id}`,y=xe.has(S),h=r.filter(i=>ke(e.key).has(i.larkAppId)).length;if(s+=`<div class="tf-dep-h" data-dk="${d(S)}" style="cursor:pointer;margin:10px 0 2px"><b>${y?"\u25BE":"\u25B8"} ${d(a.name)}</b> <span class="muted" style="font-size:12px">\uFF08${f}\uFF09\xB7 ${r.length} \u4E2A${h?`\uFF0C\u5DF2\u9009 ${h}`:""}</span>${k}</div>`,!!y){s+='<table style="width:100%;border-collapse:collapse;font-size:14px"><tbody>';for(let i of r){let w=d(i.larkAppId),L=ke(e.key).has(i.larkAppId)?" checked":"",g=i.deployment.stale?"opacity:.55":"",p=l?`<input class="tf-cap" data-app="${w}" value="${d(i.capability||"")}" placeholder="\u80FD\u529B\u6807\u7B7E\u2026" style="width:92%;padding:3px 6px">`:i.capability?d(i.capability):'<span class="muted">\u2014</span>',$=i.hasTeamRole?l?`<button class="tf-role" data-app="${w}" data-name="${d(i.name)}">\u67E5\u770B</button>`:"\u6709\u89D2\u8272":'<span class="muted">\u2014</span>';s+=`<tr style="${g}"><td style="padding:4px 8px"><input type="checkbox" class="tf-pick" data-tk="${d(e.key)}" data-app="${w}"${L}></td><td style="padding:4px 8px">${d(i.name)}</td><td style="padding:4px 8px" class="muted">${d(i.cliId)}</td><td style="padding:4px 8px">${p}</td><td style="padding:4px 8px">${$}</td></tr>`}s+="</tbody></table>"}}return s||(s='<p class="muted" style="margin:8px 0 0">\u6CA1\u6709\u7B26\u5408\u6761\u4EF6\u7684\u673A\u5668\u4EBA\u3002</p>'),s+=`<div style="margin-top:12px;display:flex;gap:8px;flex-wrap:wrap;align-items:center"><input class="tf-gname" data-tk="${d(e.key)}" value="${d(Ne.get(e.key)||"")}" placeholder="\u7FA4\u540D\uFF08\u5982\uFF1A\u8DE8\u56E2\u961F\u6392\u969C\uFF09" style="min-width:200px"><button class="tf-grp primary" data-tk="${d(e.key)}">\u628A\u52FE\u9009\u7684\u673A\u5668\u4EBA\u62C9\u4E00\u4E2A\u7FA4</button><span class="muted" style="font-size:13px">\u52FE\u9009\u673A\u5668\u4EBA \u2192 \u62C9\u5230\u4E00\u4E2A\u98DE\u4E66\u7FA4\uFF08\u81EA\u52A8\u542B owner\uFF09</span><span class="tf-gout" data-tk="${d(e.key)}" style="font-size:13px;display:block;flex-basis:100%"></span></div>`,s}function me(){let e=B("tf-teams"),t=Fe();if(!t.length){e.innerHTML='<p class="muted">\u8FD8\u6CA1\u6709\u56E2\u961F\u3002\u53BB\u300C\u56E2\u961F\u7BA1\u7406\u300D\u751F\u6210\u9080\u8BF7\u7801\u8BA9\u522B\u4EBA\u52A0\u5165\u4F60\uFF0C\u6216\u52A0\u5165\u522B\u4EBA\u7684\u56E2\u961F\u3002</p>',B("tf-count").textContent="";return}let n="",s=new Set,a=new Set;for(let l of t){let f=l.bots.filter(jn);f.forEach(h=>s.add(h.larkAppId)),l.bots.forEach(h=>a.add(h.larkAppId));let k=new Set(f.map(h=>h.larkAppId));[...ke(l.key)].forEach(h=>{k.has(h)||ke(l.key).delete(h)});let S=!Me.has(l.key),y=l.kind==="remote"?l.ok?' <span class="ok" style="font-size:12px">\u5DF2\u8FDE\u63A5</span>':` <span class="err" style="font-size:12px">\u8FDE\u63A5\u5931\u8D25\uFF1A${d(l.error||"")}</span>`:' <span class="muted" style="font-size:12px">\u6211\u6258\u7BA1</span>';n+=`<div class="card" style="margin:0 0 12px;padding:12px 14px;background:var(--bg-soft,#f6f7f9)"><div class="tf-team-h" data-tk="${d(l.key)}" style="cursor:pointer;display:flex;align-items:center;gap:8px;flex-wrap:wrap"><b style="font-size:15px">${S?"\u25B8":"\u25BE"} ${d(l.label)}</b>`+(l.sub?` <span class="muted" style="font-size:12px">${d(l.sub)}</span>`:"")+y+` <span class="muted" style="font-size:12px">\xB7 ${l.deployments.length} \u4E2A\u90E8\u7F72 \xB7 ${l.bots.length} \u4E2A\u673A\u5668\u4EBA</span></div>`,S||(n+=l.kind==="remote"&&!l.ok?'<p class="muted" style="margin:8px 0 0">\u65E0\u6CD5\u83B7\u53D6\u8BE5\u56E2\u961F\u82B1\u540D\u518C\u3002</p>':Un(l,f)),n+="</div>"}e.innerHTML=n;let r=t.length>1?`\uFF08\u8DE8 ${t.length} \u4E2A\u56E2\u961F\uFF0C\u53BB\u91CD\uFF09`:"";B("tf-count").textContent=`\xB7 ${s.size===a.size?`${a.size}`:`${s.size} / ${a.size}`} \u4E2A\u673A\u5668\u4EBA${r}`,Fn()}function Fn(){let e=B("tf-teams");e.querySelectorAll(".tf-team-h").forEach(t=>{t.onclick=()=>{let n=t.dataset.tk;Me.has(n)?Me.delete(n):Me.add(n),me()}}),e.querySelectorAll(".tf-dep-h").forEach(t=>{t.onclick=()=>{let n=t.dataset.dk;xe.has(n)?xe.delete(n):xe.add(n),me()}}),e.querySelectorAll(".tf-pick").forEach(t=>{t.onchange=()=>{let n=ke(t.dataset.tk);t.checked?n.add(t.dataset.app):n.delete(t.dataset.app)}}),e.querySelectorAll(".tf-gname").forEach(t=>{t.oninput=()=>{Ne.set(t.dataset.tk,t.value)}}),e.querySelectorAll(".tf-cap").forEach(t=>{t.onchange=async()=>{let n=t.dataset.app,s=t.value;await Bn("/api/team/local-bots/"+encodeURIComponent(n)+"/capability",{capability:s}),Fe().forEach(a=>{let r=a.bots.find(l=>l.larkAppId===n);r&&(r.capability=s.trim()||null)})}}),e.querySelectorAll(".tf-role").forEach(t=>{t.onclick=()=>Jn(t.dataset.app,t.dataset.name||"")}),e.querySelectorAll(".tf-rmmember").forEach(t=>{t.onclick=async n=>{n.stopPropagation(),confirm(`\u628A\u300C${t.dataset.name}\u300D\u79FB\u51FA\u8FD9\u4E2A\u56E2\u961F\uFF1F\u5B83\u7684\u673A\u5668\u4EBA\u5C06\u4ECE\u672C\u56E2\u961F\u82B1\u540D\u518C\u6D88\u5931\uFF08\u4E0D\u5F71\u54CD\u5BF9\u65B9\u81EA\u5DF1\u7684\u90E8\u7F72\uFF09\u3002`)&&(await Ue("DELETE",`/api/team/hosted/${encodeURIComponent(t.dataset.team)}/members/${encodeURIComponent(t.dataset.dep)}`),He())}}),e.querySelectorAll(".tf-grp").forEach(t=>{t.onclick=async()=>{let n=t.dataset.tk,s=qn(n);if(!s)return;let a=[...ke(n)],r=e.querySelector(`.tf-gout[data-tk="${CSS.escape(n)}"]`);if(!a.length){r.innerHTML='<span class="err">\u8BF7\u5148\u52FE\u9009\u81F3\u5C11\u4E00\u4E2A\u673A\u5668\u4EBA</span>';return}let l=(e.querySelector(`.tf-gname[data-tk="${CSS.escape(n)}"]`)?.value||"").trim()||"\u534F\u4F5C\u7FA4";r.innerHTML='<span class="muted">\u5EFA\u7FA4\u4E2D\u2026</span>';let f=s.kind==="local"?await $e("/api/team/federated-group",{name:l,larkAppIds:a,teamId:s.teamId}):await $e("/api/team/remote-group",{hubUrl:s.hubUrl,teamId:s.teamId,name:l,larkAppIds:a});if(_n(r,f.body,f.status),f.body?.ok){ke(n).clear(),Ne.delete(n);let k=r.innerHTML,S=()=>{let y=e.querySelector(`.tf-gout[data-tk="${CSS.escape(n)}"]`);y&&(y.innerHTML=k)};s.kind==="local"?He().then(S):(me(),S())}}})}function _n(e,t,n){if(t?.ok&&t.chatId){let s=t.shareLink||"https://applink.feishu.cn/client/chat/open?openChatId="+encodeURIComponent(t.chatId),a=(t.invalidBotIds||[]).length?`<span class="err"> \xB7 \u672A\u52A0\u5165\u7684\u673A\u5668\u4EBA\uFF1A${d((t.invalidBotIds||[]).join(", "))}</span>`:"",r=(t.invalidOwnerUnionIds||[]).length?`<span class="err"> \xB7 ${(t.invalidOwnerUnionIds||[]).length} \u4E2A owner \u672A\u80FD\u62C9\u8FDB</span>`:"",l=t.missingOperatorIdentity?'<span class="err"> \xB7 \u4F60\u672A\u7ED1\u5B9A\u98DE\u4E66\u8EAB\u4EFD\uFF0C\u6CA1\u628A\u4F60\u81EA\u5DF1\u62C9\u8FDB\u7FA4\uFF08\u53BB\u300C\u6211\u7684\u56E2\u961F\u300D\u7ED1\u5B9A\uFF09</span>':"",f=(t.skippedNoOwner||[]).length?`<span class="err"> \xB7 ${(t.skippedNoOwner||[]).length} \u4E2A\u673A\u5668\u4EBA\u56E0\u8D1F\u8D23\u4EBA\u672A\u7ED1\u5B9A\u8EAB\u4EFD\u88AB\u8DF3\u8FC7\uFF08\u672A\u52A0\u5165\u7FA4\uFF09</span>`:"",k=t.delegatedTo?`\uFF08\u7531\u300C${d(t.delegatedTo)}\u300D\u5EFA\u7FA4\uFF09`:"";e.innerHTML=`<span class="ok">\u7FA4\u5DF2\u521B\u5EFA</span>${k} \xB7 <a href="${d(s)}" target="_blank">\u5728\u98DE\u4E66\u6253\u5F00</a>${a}${r}${l}${f}`}else{let s=t?.error||n,a=s==="no_local_online_bot"?"\u8BF7\u81F3\u5C11\u52FE\u9009\u4E00\u4E2A\u4F60\u81EA\u5DF1\uFF08\u672C\u90E8\u7F72\uFF09\u7684\u5728\u7EBF\u673A\u5668\u4EBA\u2014\u2014\u7FA4\u8981\u7531\u5B83\u521B\u5EFA\u5E76\u628A\u4F60\uFF08\u53D1\u8D77\u4EBA\uFF09\u62C9\u8FDB\u7FA4\u3002":s==="all_bots_skipped_no_owner"?"\u6240\u9009\u673A\u5668\u4EBA\u7684\u8D1F\u8D23\u4EBA\u90FD\u6CA1\u7ED1\u5B9A\u8EAB\u4EFD\uFF0C\u65E0\u6CD5\u62C9\u7FA4\uFF08\u673A\u5668\u4EBA\u4E0D\u80FD\u8FDB\u4E00\u4E2A owner \u4E0D\u5728\u7684\u7FA4\uFF09\u3002\u8BF7\u8BA9\u5BF9\u5E94\u90E8\u7F72\u5148\u7ED1\u5B9A\u8EAB\u4EFD\u3002":s==="no_creator_available"?"\u6CA1\u6709\u53EF\u7528\u7684\u5EFA\u7FA4\u53D1\u8D77\u65B9\uFF08\u76F8\u5173\u90E8\u7F72\u90FD\u6CA1\u6709\u5728\u7EBF\u673A\u5668\u4EBA\uFF0C\u6216\u4E0D\u53EF\u8FBE\uFF09":s==="delegation_timeout"?"\u59D4\u6258\u5BF9\u65B9\u90E8\u7F72\u5EFA\u7FA4\u8D85\u65F6\uFF08\u53EF\u80FD\u5DF2\u5EFA\uFF0C\u53BB\u98DE\u4E66\u786E\u8BA4\uFF0C\u52FF\u91CD\u590D\u70B9\uFF09":`\u5EFA\u7FA4\u5931\u8D25\uFF1A${s}`;e.innerHTML=`<span class="err">${d(String(a))}</span>`}}async function Jn(e,t){let n=await je("/api/team/local-bots/"+encodeURIComponent(e)+"/role");B("tf-modal-title").textContent="\u9ED8\u8BA4\u89D2\u8272 \xB7 "+t,B("tf-modal-text").value=n.body?.role||"",B("tf-modal").dataset.app=e,B("tf-modal").style.display="flex"}function Nt(){let e=Array.from(new Set(Fe().flatMap(s=>s.bots.map(a=>a.cliId)).filter(Boolean))).sort(),t=B("tf-cli"),n=t.value;t.innerHTML='<option value="">\u5168\u90E8 CLI</option>'+e.map(s=>`<option value="${d(s)}">${d(s)}</option>`).join(""),t.value=n}async function He(){let t=(await je("/api/team/hosted")).body;if(!t?.ok){rt=[],me();return}Bt=t.deployment.deploymentId,qe=t.suggestedHubUrl||"",B("tf-owner").textContent=t.deployment.ownerName||(t.deployment.ownerUnionId?"\u5DF2\u7ED1\u5B9A":"\u672A\u7ED1\u5B9A"),rt=(t.teams||[]).map(n=>({kind:"local",key:`local:${n.teamId}`,teamId:n.teamId,label:n.isDefault?"\u6211\u6258\u7BA1\u7684\u56E2\u961F":n.name,sub:"",ok:!0,deployments:n.deployments||[],bots:n.bots||[]})),Nt(),me()}async function zn(){Ot=((await je("/api/team/remote-roster")).body?.memberships||[]).map(n=>{let s=n.roster?.deployments||[],a=s.find(l=>l.local),r=a?.name?`${a.name} \u7684\u56E2\u961F`:n.teamName||n.teamId;return{kind:"remote",key:`${n.hubUrl}::${n.teamId}`,teamId:n.teamId,label:r,sub:n.hubUrl,ok:!!n.ok,error:n.error,hubUrl:n.hubUrl,deployments:s,bots:n.roster?.bots||[]}}),Nt(),me()}function jt(e){e.innerHTML=Nn(),it.clear(),Ne.clear(),Me.clear(),xe.clear(),["tf-search","tf-cli","tf-fcap","tf-frole"].forEach(t=>{let n=B(t);n.oninput=me,n.onchange=me}),B("tf-modal-cancel").onclick=()=>{B("tf-modal").style.display="none"},Gn(),He(),zn()}function Wn(){return`<section class="page">
|
|
563
|
+
</section>`}function jn(e){let t=(j("tf-search").value||"").trim().toLowerCase();if(t&&!((e.name||"")+" "+(e.cliId||"")+" "+(e.capability||"")).toLowerCase().includes(t))return!1;let o=j("tf-cli").value;return!(o&&e.cliId!==o||j("tf-fcap").checked&&!e.capability||j("tf-frole").checked&&!e.hasTeamRole)}function Un(e,t){let o=[...e.deployments].sort((a,r)=>a.local===r.local?0:a.local?-1:1),s="";for(let a of o){let r=t.filter(c=>c.deployment.id===a.id);if(!r.length)continue;let i=a.id===jt,m=i?"\u672C\u90E8\u7F72":a.stale?"\u8FDC\u7AEF\xB7\u79BB\u7EBF\uFF1F":"\u8FDC\u7AEF",$=e.kind==="local"&&!i?` <button class="tf-rmmember ghost" data-team="${l(e.teamId)}" data-dep="${l(a.id)}" data-name="${l(a.name)}" style="font-size:12px">\u79FB\u9664</button>`:"",T=`${e.key}::${a.id}`,k=Re.has(T),y=r.filter(c=>Se(e.key).has(c.larkAppId)).length;if(s+=`<div class="tf-dep-h" data-dk="${l(T)}" style="cursor:pointer;margin:10px 0 2px"><b>${k?"\u25BE":"\u25B8"} ${l(a.name)}</b> <span class="muted" style="font-size:12px">\uFF08${m}\uFF09\xB7 ${r.length} \u4E2A${y?`\uFF0C\u5DF2\u9009 ${y}`:""}</span>${$}</div>`,!!k){s+='<table style="width:100%;border-collapse:collapse;font-size:14px"><tbody>';for(let c of r){let I=l(c.larkAppId),f=Se(e.key).has(c.larkAppId)?" checked":"",g=c.deployment.stale?"opacity:.55":"",p=i?`<input class="tf-cap" data-app="${I}" value="${l(c.capability||"")}" placeholder="\u80FD\u529B\u6807\u7B7E\u2026" style="width:92%;padding:3px 6px">`:c.capability?l(c.capability):'<span class="muted">\u2014</span>',S=c.hasTeamRole?i?`<button class="tf-role" data-app="${I}" data-name="${l(c.name)}">\u67E5\u770B</button>`:"\u6709\u89D2\u8272":'<span class="muted">\u2014</span>';s+=`<tr style="${g}"><td style="padding:4px 8px"><input type="checkbox" class="tf-pick" data-tk="${l(e.key)}" data-app="${I}"${f}></td><td style="padding:4px 8px">${l(c.name)}</td><td style="padding:4px 8px" class="muted">${l(c.cliId)}</td><td style="padding:4px 8px">${p}</td><td style="padding:4px 8px">${S}</td></tr>`}s+="</tbody></table>"}}return s||(s='<p class="muted" style="margin:8px 0 0">\u6CA1\u6709\u7B26\u5408\u6761\u4EF6\u7684\u673A\u5668\u4EBA\u3002</p>'),s+=`<div style="margin-top:12px;display:flex;gap:8px;flex-wrap:wrap;align-items:center"><input class="tf-gname" data-tk="${l(e.key)}" value="${l(Je.get(e.key)||"")}" placeholder="\u7FA4\u540D\uFF08\u5982\uFF1A\u8DE8\u56E2\u961F\u6392\u969C\uFF09" style="min-width:200px"><button class="tf-grp primary" data-tk="${l(e.key)}">\u628A\u52FE\u9009\u7684\u673A\u5668\u4EBA\u62C9\u4E00\u4E2A\u7FA4</button><span class="muted" style="font-size:13px">\u52FE\u9009\u673A\u5668\u4EBA \u2192 \u62C9\u5230\u4E00\u4E2A\u98DE\u4E66\u7FA4\uFF08\u81EA\u52A8\u542B owner\uFF09</span><span class="tf-gout" data-tk="${l(e.key)}" style="font-size:13px;display:block;flex-basis:100%"></span></div>`,s}function he(){let e=j("tf-teams"),t=Ge();if(!t.length){e.innerHTML='<p class="muted">\u8FD8\u6CA1\u6709\u56E2\u961F\u3002\u53BB\u300C\u56E2\u961F\u7BA1\u7406\u300D\u751F\u6210\u9080\u8BF7\u7801\u8BA9\u522B\u4EBA\u52A0\u5165\u4F60\uFF0C\u6216\u52A0\u5165\u522B\u4EBA\u7684\u56E2\u961F\u3002</p>',j("tf-count").textContent="";return}let o="",s=new Set,a=new Set;for(let i of t){let m=i.bots.filter(jn);m.forEach(y=>s.add(y.larkAppId)),i.bots.forEach(y=>a.add(y.larkAppId));let $=new Set(m.map(y=>y.larkAppId));[...Se(i.key)].forEach(y=>{$.has(y)||Se(i.key).delete(y)});let T=!De.has(i.key),k=i.kind==="remote"?i.ok?' <span class="ok" style="font-size:12px">\u5DF2\u8FDE\u63A5</span>':` <span class="err" style="font-size:12px">\u8FDE\u63A5\u5931\u8D25\uFF1A${l(i.error||"")}</span>`:' <span class="muted" style="font-size:12px">\u6211\u6258\u7BA1</span>';o+=`<div class="card" style="margin:0 0 12px;padding:12px 14px;background:var(--bg-soft,#f6f7f9)"><div class="tf-team-h" data-tk="${l(i.key)}" style="cursor:pointer;display:flex;align-items:center;gap:8px;flex-wrap:wrap"><b style="font-size:15px">${T?"\u25B8":"\u25BE"} ${l(i.label)}</b>`+(i.sub?` <span class="muted" style="font-size:12px">${l(i.sub)}</span>`:"")+k+` <span class="muted" style="font-size:12px">\xB7 ${i.deployments.length} \u4E2A\u90E8\u7F72 \xB7 ${i.bots.length} \u4E2A\u673A\u5668\u4EBA</span></div>`,T||(o+=i.kind==="remote"&&!i.ok?'<p class="muted" style="margin:8px 0 0">\u65E0\u6CD5\u83B7\u53D6\u8BE5\u56E2\u961F\u82B1\u540D\u518C\u3002</p>':Un(i,m)),o+="</div>"}e.innerHTML=o;let r=t.length>1?`\uFF08\u8DE8 ${t.length} \u4E2A\u56E2\u961F\uFF0C\u53BB\u91CD\uFF09`:"";j("tf-count").textContent=`\xB7 ${s.size===a.size?`${a.size}`:`${s.size} / ${a.size}`} \u4E2A\u673A\u5668\u4EBA${r}`,Fn()}function Fn(){let e=j("tf-teams");e.querySelectorAll(".tf-team-h").forEach(t=>{t.onclick=()=>{let o=t.dataset.tk;De.has(o)?De.delete(o):De.add(o),he()}}),e.querySelectorAll(".tf-dep-h").forEach(t=>{t.onclick=()=>{let o=t.dataset.dk;Re.has(o)?Re.delete(o):Re.add(o),he()}}),e.querySelectorAll(".tf-pick").forEach(t=>{t.onchange=()=>{let o=Se(t.dataset.tk);t.checked?o.add(t.dataset.app):o.delete(t.dataset.app)}}),e.querySelectorAll(".tf-gname").forEach(t=>{t.oninput=()=>{Je.set(t.dataset.tk,t.value)}}),e.querySelectorAll(".tf-cap").forEach(t=>{t.onchange=async()=>{let o=t.dataset.app,s=t.value;await Pn("/api/team/local-bots/"+encodeURIComponent(o)+"/capability",{capability:s}),Ge().forEach(a=>{let r=a.bots.find(i=>i.larkAppId===o);r&&(r.capability=s.trim()||null)})}}),e.querySelectorAll(".tf-role").forEach(t=>{t.onclick=()=>Jn(t.dataset.app,t.dataset.name||"")}),e.querySelectorAll(".tf-rmmember").forEach(t=>{t.onclick=async o=>{o.stopPropagation(),confirm(`\u628A\u300C${t.dataset.name}\u300D\u79FB\u51FA\u8FD9\u4E2A\u56E2\u961F\uFF1F\u5B83\u7684\u673A\u5668\u4EBA\u5C06\u4ECE\u672C\u56E2\u961F\u82B1\u540D\u518C\u6D88\u5931\uFF08\u4E0D\u5F71\u54CD\u5BF9\u65B9\u81EA\u5DF1\u7684\u90E8\u7F72\uFF09\u3002`)&&(await We("DELETE",`/api/team/hosted/${encodeURIComponent(t.dataset.team)}/members/${encodeURIComponent(t.dataset.dep)}`),qe())}}),e.querySelectorAll(".tf-grp").forEach(t=>{t.onclick=async()=>{let o=t.dataset.tk,s=Bn(o);if(!s)return;let a=[...Se(o)],r=e.querySelector(`.tf-gout[data-tk="${CSS.escape(o)}"]`);if(!a.length){r.innerHTML='<span class="err">\u8BF7\u5148\u52FE\u9009\u81F3\u5C11\u4E00\u4E2A\u673A\u5668\u4EBA</span>';return}let i=(e.querySelector(`.tf-gname[data-tk="${CSS.escape(o)}"]`)?.value||"").trim()||"\u534F\u4F5C\u7FA4";r.innerHTML='<span class="muted">\u5EFA\u7FA4\u4E2D\u2026</span>';let m=s.kind==="local"?await Te("/api/team/federated-group",{name:i,larkAppIds:a,teamId:s.teamId}):await Te("/api/team/remote-group",{hubUrl:s.hubUrl,teamId:s.teamId,name:i,larkAppIds:a});if(_n(r,m.body,m.status),m.body?.ok){Se(o).clear(),Je.delete(o);let $=r.innerHTML,T=()=>{let k=e.querySelector(`.tf-gout[data-tk="${CSS.escape(o)}"]`);k&&(k.innerHTML=$)};s.kind==="local"?qe().then(T):(he(),T())}}})}function _n(e,t,o){if(t?.ok&&t.chatId){let s=t.shareLink||"https://applink.feishu.cn/client/chat/open?openChatId="+encodeURIComponent(t.chatId),a=(t.invalidBotIds||[]).length?`<span class="err"> \xB7 \u672A\u52A0\u5165\u7684\u673A\u5668\u4EBA\uFF1A${l((t.invalidBotIds||[]).join(", "))}</span>`:"",r=(t.invalidOwnerUnionIds||[]).length?`<span class="err"> \xB7 ${(t.invalidOwnerUnionIds||[]).length} \u4E2A owner \u672A\u80FD\u62C9\u8FDB</span>`:"",i=t.missingOperatorIdentity?'<span class="err"> \xB7 \u4F60\u672A\u7ED1\u5B9A\u98DE\u4E66\u8EAB\u4EFD\uFF0C\u6CA1\u628A\u4F60\u81EA\u5DF1\u62C9\u8FDB\u7FA4\uFF08\u53BB\u300C\u6211\u7684\u56E2\u961F\u300D\u7ED1\u5B9A\uFF09</span>':"",m=(t.skippedNoOwner||[]).length?`<span class="err"> \xB7 ${(t.skippedNoOwner||[]).length} \u4E2A\u673A\u5668\u4EBA\u56E0\u8D1F\u8D23\u4EBA\u672A\u7ED1\u5B9A\u8EAB\u4EFD\u88AB\u8DF3\u8FC7\uFF08\u672A\u52A0\u5165\u7FA4\uFF09</span>`:"",$=t.delegatedTo?`\uFF08\u7531\u300C${l(t.delegatedTo)}\u300D\u5EFA\u7FA4\uFF09`:"";e.innerHTML=`<span class="ok">\u7FA4\u5DF2\u521B\u5EFA</span>${$} \xB7 <a href="${l(s)}" target="_blank">\u5728\u98DE\u4E66\u6253\u5F00</a>${a}${r}${i}${m}`}else{let s=t?.error||o,a=s==="no_local_online_bot"?"\u8BF7\u81F3\u5C11\u52FE\u9009\u4E00\u4E2A\u4F60\u81EA\u5DF1\uFF08\u672C\u90E8\u7F72\uFF09\u7684\u5728\u7EBF\u673A\u5668\u4EBA\u2014\u2014\u7FA4\u8981\u7531\u5B83\u521B\u5EFA\u5E76\u628A\u4F60\uFF08\u53D1\u8D77\u4EBA\uFF09\u62C9\u8FDB\u7FA4\u3002":s==="all_bots_skipped_no_owner"?"\u6240\u9009\u673A\u5668\u4EBA\u7684\u8D1F\u8D23\u4EBA\u90FD\u6CA1\u7ED1\u5B9A\u8EAB\u4EFD\uFF0C\u65E0\u6CD5\u62C9\u7FA4\uFF08\u673A\u5668\u4EBA\u4E0D\u80FD\u8FDB\u4E00\u4E2A owner \u4E0D\u5728\u7684\u7FA4\uFF09\u3002\u8BF7\u8BA9\u5BF9\u5E94\u90E8\u7F72\u5148\u7ED1\u5B9A\u8EAB\u4EFD\u3002":s==="no_creator_available"?"\u6CA1\u6709\u53EF\u7528\u7684\u5EFA\u7FA4\u53D1\u8D77\u65B9\uFF08\u76F8\u5173\u90E8\u7F72\u90FD\u6CA1\u6709\u5728\u7EBF\u673A\u5668\u4EBA\uFF0C\u6216\u4E0D\u53EF\u8FBE\uFF09":s==="delegation_timeout"?"\u59D4\u6258\u5BF9\u65B9\u90E8\u7F72\u5EFA\u7FA4\u8D85\u65F6\uFF08\u53EF\u80FD\u5DF2\u5EFA\uFF0C\u53BB\u98DE\u4E66\u786E\u8BA4\uFF0C\u52FF\u91CD\u590D\u70B9\uFF09":`\u5EFA\u7FA4\u5931\u8D25\uFF1A${s}`;e.innerHTML=`<span class="err">${l(String(a))}</span>`}}async function Jn(e,t){let o=await ze("/api/team/local-bots/"+encodeURIComponent(e)+"/role");j("tf-modal-title").textContent="\u9ED8\u8BA4\u89D2\u8272 \xB7 "+t,j("tf-modal-text").value=o.body?.role||"",j("tf-modal").dataset.app=e,j("tf-modal").style.display="flex"}function Ft(){let e=Array.from(new Set(Ge().flatMap(s=>s.bots.map(a=>a.cliId)).filter(Boolean))).sort(),t=j("tf-cli"),o=t.value;t.innerHTML='<option value="">\u5168\u90E8 CLI</option>'+e.map(s=>`<option value="${l(s)}">${l(s)}</option>`).join(""),t.value=o}async function qe(){let t=(await ze("/api/team/hosted")).body;if(!t?.ok){dt=[],he();return}jt=t.deployment.deploymentId,_e=t.suggestedHubUrl||"",j("tf-owner").textContent=t.deployment.ownerName||(t.deployment.ownerUnionId?"\u5DF2\u7ED1\u5B9A":"\u672A\u7ED1\u5B9A"),dt=(t.teams||[]).map(o=>({kind:"local",key:`local:${o.teamId}`,teamId:o.teamId,label:o.isDefault?"\u6211\u6258\u7BA1\u7684\u56E2\u961F":o.name,sub:"",ok:!0,deployments:o.deployments||[],bots:o.bots||[]})),Ft(),he()}async function zn(){Nt=((await ze("/api/team/remote-roster")).body?.memberships||[]).map(o=>{let s=o.roster?.deployments||[],a=s.find(i=>i.local),r=a?.name?`${a.name} \u7684\u56E2\u961F`:o.teamName||o.teamId;return{kind:"remote",key:`${o.hubUrl}::${o.teamId}`,teamId:o.teamId,label:r,sub:o.hubUrl,ok:!!o.ok,error:o.error,hubUrl:o.hubUrl,deployments:s,bots:o.roster?.bots||[]}}),Ft(),he()}function _t(e){e.innerHTML=Nn(),ct.clear(),Je.clear(),De.clear(),Re.clear(),["tf-search","tf-cli","tf-fcap","tf-frole"].forEach(t=>{let o=j(t);o.oninput=he,o.onchange=he}),j("tf-modal-cancel").onclick=()=>{j("tf-modal").style.display="none"},Gn(),qe(),zn()}function Wn(){return`<section class="page">
|
|
541
564
|
<div class="page-heading"><div>
|
|
542
565
|
<p class="eyebrow">\u56E2\u961F</p><h1>\u56E2\u961F\u7BA1\u7406</h1>
|
|
543
566
|
<p>\u521B\u5EFA\u591A\u4E2A\u56E2\u961F\u3001\u7ED9\u6BCF\u4E2A\u56E2\u961F\u751F\u6210\u9080\u8BF7\u7801\u3001\u6216\u52A0\u5165\u522B\u4EBA\u7684\u56E2\u961F\u3002\u4E00\u4E2A\u56E2\u961F = \u4F60\u672C\u90E8\u7F72\u7684\u673A\u5668\u4EBA + \u52A0\u5165\u8BE5\u56E2\u961F\u7684\u5176\u5B83\u90E8\u7F72\u3002</p>
|
|
544
567
|
</div></div>
|
|
545
|
-
${
|
|
568
|
+
${Ut("manage")}
|
|
546
569
|
<div class="card" style="margin-bottom:16px">
|
|
547
570
|
<h2 style="margin-top:0">\u6211\u6258\u7BA1\u7684\u56E2\u961F</h2>
|
|
548
571
|
<p style="display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin-bottom:6px">
|
|
@@ -561,16 +584,16 @@ ${qt("manage")}
|
|
|
561
584
|
</p>
|
|
562
585
|
<div id="tm-join-out" style="display:none;margin-top:6px"></div>
|
|
563
586
|
</div>
|
|
564
|
-
</section>`}async function
|
|
587
|
+
</section>`}async function ut(){let t=(await ze("/api/team/hosted")).body,o=j("tm-list");_e=t?.suggestedHubUrl||_e;let s=t?.teams||[];if(!s.length){o.innerHTML='<p class="muted">\u8FD8\u6CA1\u6709\u56E2\u961F\u3002</p>';return}o.innerHTML=s.map(a=>{let r=(a.deployments||[]).filter(i=>!i.local).length;return`<div class="card" style="margin:0 0 8px;padding:10px 14px;background:var(--bg-soft,#f6f7f9)">
|
|
565
588
|
<div style="display:flex;align-items:center;gap:8px;flex-wrap:wrap">
|
|
566
|
-
<b>${
|
|
589
|
+
<b>${l(a.name)}</b>${a.isDefault?' <span class="muted" style="font-size:12px">\u9ED8\u8BA4</span>':""}
|
|
567
590
|
<span class="muted" style="font-size:12px">\xB7 ${(a.deployments||[]).length} \u4E2A\u90E8\u7F72${r?`\uFF08\u542B ${r} \u8FDC\u7AEF\uFF09`:""} \xB7 ${(a.bots||[]).length} \u4E2A\u673A\u5668\u4EBA</span>
|
|
568
591
|
<span style="margin-left:auto;display:flex;gap:6px">
|
|
569
|
-
<button class="tm-invite ghost" data-team="${
|
|
570
|
-
${a.isDefault?"":`<button class="tm-del ghost" data-team="${
|
|
592
|
+
<button class="tm-invite ghost" data-team="${l(a.teamId)}" style="font-size:12px">\u751F\u6210\u9080\u8BF7\u7801</button>
|
|
593
|
+
${a.isDefault?"":`<button class="tm-del ghost" data-team="${l(a.teamId)}" data-name="${l(a.name)}" style="font-size:12px">\u5220\u9664</button>`}
|
|
571
594
|
</span>
|
|
572
595
|
</div>
|
|
573
|
-
<div class="tm-inv-out" data-team="${
|
|
596
|
+
<div class="tm-inv-out" data-team="${l(a.teamId)}" style="display:none;margin-top:6px;font-size:13px"></div></div>`}).join(""),o.querySelectorAll(".tm-invite").forEach(a=>{a.onclick=async()=>{let r=a.dataset.team,i=o.querySelector(`.tm-inv-out[data-team="${CSS.escape(r)}"]`);i.style.display="",i.innerHTML='<span class="muted">\u751F\u6210\u4E2D\u2026</span>';let m=await Te("/api/team/local-invite",{teamId:r});m.body?.code?i.innerHTML=`\u628A\u4E0B\u9762\u4E24\u9879\u53D1\u7ED9<b>\u522B\u7684\u90E8\u7F72</b>\u7684\u4EBA\uFF0824 \u5C0F\u65F6\u5185\u3001\u5355\u6B21\u6709\u6548\uFF09\uFF1A<br>Hub \u5730\u5740\uFF1A<code>${l(_e)}</code><br>\u9080\u8BF7\u7801\uFF1A<code style="font-size:15px">${l(m.body.code)}</code>`:i.innerHTML='<span class="err">\u751F\u6210\u5931\u8D25\u3002</span>'}}),o.querySelectorAll(".tm-del").forEach(a=>{a.onclick=async()=>{confirm(`\u5220\u9664\u56E2\u961F\u300C${a.dataset.name}\u300D\uFF1F\u5DF2\u52A0\u5165\u5B83\u7684\u90E8\u7F72\u4F1A\u88AB\u79FB\u9664\uFF08\u4E0D\u5F71\u54CD\u4ED6\u4EEC\u81EA\u5DF1\u7684\u90E8\u7F72\uFF09\u3002`)&&(await We("DELETE","/api/team/hosted/"+encodeURIComponent(a.dataset.team)),ut())}})}function Jt(e){e.innerHTML=Wn(),j("tm-create").onclick=async()=>{let t=j("tm-newname").value.trim(),o=e.querySelector(".tm-cout");if(!t){o.innerHTML='<span class="err">\u8BF7\u586B\u56E2\u961F\u540D\u79F0</span>';return}o.innerHTML='<span class="muted">\u521B\u5EFA\u4E2D\u2026</span>';let s=await Te("/api/team/hosted",{name:t});s.body?.ok?(o.innerHTML='<span class="ok">\u5DF2\u521B\u5EFA</span>',j("tm-newname").value="",ut()):o.innerHTML=`<span class="err">\u521B\u5EFA\u5931\u8D25\uFF1A${l(String(s.body?.error||s.status))}</span>`},j("tm-join").onclick=async()=>{let t=j("tm-hub").value.trim(),o=j("tm-code").value.trim(),s=j("tm-join-out");if(s.style.display="",!t||!o){s.innerHTML='<span class="err">\u8BF7\u586B Hub \u5730\u5740\u548C\u9080\u8BF7\u7801\u3002</span>';return}s.innerHTML='<span class="muted">\u52A0\u5165\u4E2D\u2026</span>';let a=await Te("/api/team/join-remote",{hubUrl:t,inviteCode:o});if(a.body?.ok)s.innerHTML=`<span class="ok">\u5DF2\u52A0\u5165\u300C${l(a.body.teamName||"")}\u300D\uFF0C\u53BB\u300C\u6211\u7684\u56E2\u961F\u300D\u67E5\u770B\u3002</span>`,j("tm-code").value="";else{let r=a.body?.error||a.status,i=r==="cannot_join_self"?"\u8FD9\u662F\u4F60\u81EA\u5DF1\u7684\u90E8\u7F72\uFF0C\u4E0D\u80FD\u52A0\u5165\u81EA\u5DF1\uFF08\u9080\u8BF7\u7801\u8981\u53D1\u7ED9\u522B\u7684\u90E8\u7F72\u7684\u4EBA\u7528\uFF09":r==="deployment_already_joined"?"\u4F60\u7684\u90E8\u7F72\u5DF2\u7ECF\u52A0\u5165\u8FC7\u8FD9\u4E2A\u56E2\u961F\u4E86":r==="hub_unreachable"?"\u8FDE\u4E0D\u4E0A\u5BF9\u65B9 Hub\uFF08\u68C0\u67E5\u5730\u5740/\u7F51\u7EDC\uFF09":r==="hub_timeout"?"\u5BF9\u65B9 Hub \u54CD\u5E94\u8D85\u65F6":`\u52A0\u5165\u5931\u8D25\uFF1A${r}`;s.innerHTML=`<span class="err">${l(String(i))}</span>`}},ut()}function Gn(){j("tf-autobind").onclick=async()=>{let e=j("tf-bind-out");e.style.display="",e.innerHTML='<span class="muted">\u8BC6\u522B\u4E2D\u2026</span>';let o=(await Te("/api/team/identity/auto-bind")).body;if(o?.ok&&o.owner){e.innerHTML=`<span class="ok">\u5DF2\u7ED1\u5B9A\uFF1A${l(o.owner.name||o.owner.unionId)}</span>`,qe();return}if(o?.ok&&o.needChoice&&Array.isArray(o.candidates)){let s=o.candidates.map(a=>`<button class="tf-pickowner ghost" data-union="${l(a.unionId)}" style="margin:2px">${l(a.name||a.unionId)}</button>`).join(" ");e.innerHTML=`\u8BC6\u522B\u5230\u591A\u4E2A\u5019\u9009\uFF0C\u70B9\u4F60\u81EA\u5DF1\uFF1A<br>${s}`,e.querySelectorAll(".tf-pickowner").forEach(a=>{a.onclick=async()=>{e.innerHTML='<span class="muted">\u7ED1\u5B9A\u4E2D\u2026</span>';let i=(await Te("/api/team/identity/auto-bind",{unionId:a.dataset.union})).body;i?.ok&&i.owner?(e.innerHTML=`<span class="ok">\u5DF2\u7ED1\u5B9A\uFF1A${l(i.owner.name||i.owner.unionId)}</span>`,qe()):e.innerHTML=`<span class="err">\u7ED1\u5B9A\u5931\u8D25\uFF1A${l(String(i?.error||"unknown"))}</span>`}});return}if(o?.error==="no_candidates"){e.innerHTML='<span class="err">\u6CA1\u8BC6\u522B\u5230\u8EAB\u4EFD\uFF1A\u8BF7\u786E\u8BA4\u673A\u5668\u4EBA\u914D\u7F6E\u4E86 allowedUsers\uFF08\u5141\u8BB8\u4F7F\u7528\u8005\uFF09\uFF0C\u4E14\u673A\u5668\u4EBA\u6709\u901A\u8BAF\u5F55\u6743\u9650\u3002</span>';return}e.innerHTML=`<span class="err">\u7ED1\u5B9A\u5931\u8D25\uFF1A${l(String(o?.error||"unknown"))}</span>`}}async function zt(e){let t=await fetch(e);return{status:t.status,body:await t.json().catch(()=>({}))}}async function ft(e,t,o){let s=await fetch(t,{method:e,headers:{"content-type":"application/json"},body:o?JSON.stringify(o):void 0});return{status:s.status,body:await s.json().catch(()=>({}))}}function G(e){return document.getElementById(e)}function X(e){return(G(e).value||"").trim()}var mt=[];function Kn(){return`<section class="page">
|
|
574
597
|
<div class="page-heading">
|
|
575
598
|
<div>
|
|
576
599
|
<p class="eyebrow">\u63A5\u5165\u70B9 \xB7 beta</p>
|
|
@@ -609,59 +632,59 @@ ${qt("manage")}
|
|
|
609
632
|
<h2 style="margin-top:0">\u5DF2\u6709\u63A5\u5165\u70B9 <span class="muted" id="cn-count" style="font-size:13px"></span></h2>
|
|
610
633
|
<div id="cn-list">\u52A0\u8F7D\u4E2D\u2026</div>
|
|
611
634
|
</div>
|
|
612
|
-
</section>`}function
|
|
635
|
+
</section>`}function pt(){let e=G("cn-kind").value,t=G("cn-mode").value;document.querySelectorAll(".cn-wf").forEach(o=>{o.style.display=e==="workflow"?"":"none"}),document.querySelectorAll(".cn-fixed").forEach(o=>{o.style.display=t==="fixed"?"":"none"}),document.querySelectorAll(".cn-allow").forEach(o=>{o.style.display=t==="fixed"?"none":""}),document.querySelectorAll(".cn-life").forEach(o=>{o.style.display=t==="new-group"?"":"none"})}function Wt(e){return`${location.origin}/webhook/${encodeURIComponent(e)}`}function Qn(e){return e==="fixed"?"\u56FA\u5B9A\u7FA4":e==="new-group"?"\u6BCF\u6B21\u65B0\u5EFA\u7FA4":"\u8BF7\u6C42\u6307\u5B9A\u7FA4"}function Vn(e){return e==="workflow"?"\u5DE5\u4F5C\u6D41":"\u5355\u8F6E"}function Yn(e){let t=G("cn-list");if(G("cn-count").textContent=e.length?`\xB7 ${e.length} \u4E2A`:"",!e.length){t.innerHTML='<p class="muted">\u8FD8\u6CA1\u6709\u63A5\u5165\u70B9\u3002\u7528\u4E0A\u9762\u7684\u8868\u5355\u521B\u5EFA\u4E00\u4E2A\u3002</p>';return}t.innerHTML=e.map(o=>{let s=mt.find(r=>r.larkAppId===o.target.botId),a=Wt(o.id);return`<div class="card" style="margin:0 0 10px;padding:12px 14px;background:var(--bg-soft,#f6f7f9)">
|
|
613
636
|
<div style="display:flex;align-items:center;gap:8px;flex-wrap:wrap">
|
|
614
|
-
<b style="font-size:15px">${
|
|
615
|
-
<span class="${
|
|
616
|
-
<span class="muted" style="font-size:12px">\xB7 ${
|
|
637
|
+
<b style="font-size:15px">${l(o.name)}</b>
|
|
638
|
+
<span class="${o.enabled?"ok":"muted"}" style="font-size:12px">${o.enabled?"\u5DF2\u542F\u7528":"\u5DF2\u505C\u7528"}</span>
|
|
639
|
+
<span class="muted" style="font-size:12px">\xB7 ${l(s?.botName||o.target.botId)} \xB7 ${Vn(o.target.kind)} \xB7 ${Qn(o.target.mode)}</span>
|
|
617
640
|
<span style="margin-left:auto;display:flex;gap:6px">
|
|
618
|
-
<button class="cn-toggle ghost" data-id="${
|
|
619
|
-
<button class="cn-del ghost" data-id="${
|
|
641
|
+
<button class="cn-toggle ghost" data-id="${l(o.id)}" data-on="${o.enabled}" style="font-size:12px">${o.enabled?"\u505C\u7528":"\u542F\u7528"}</button>
|
|
642
|
+
<button class="cn-del ghost" data-id="${l(o.id)}" style="font-size:12px">\u5220\u9664</button>
|
|
620
643
|
</span>
|
|
621
644
|
</div>
|
|
622
645
|
<div style="margin-top:6px;font-size:13px;display:flex;align-items:center;gap:8px;flex-wrap:wrap">
|
|
623
|
-
<span class="muted">Webhook URL\uFF1A</span><code style="font-size:12px;word-break:break-all">${
|
|
624
|
-
<button class="cn-copy ghost" data-url="${
|
|
625
|
-
</div></div>`}).join(""),t.querySelectorAll(".cn-copy").forEach(
|
|
626
|
-
<p class="ok" style="margin:0 0 6px">\u5DF2\u521B\u5EFA\u300C${
|
|
627
|
-
<p style="margin:4px 0;font-size:13px"><span class="muted">Webhook URL\uFF1A</span><code style="word-break:break-all">${
|
|
628
|
-
${
|
|
629
|
-
<p class="muted" style="font-size:12px;margin:6px 0 0">\u5916\u90E8\u7CFB\u7EDF\u7528\u6B64 URL + \u5BC6\u94A5\uFF08HMAC-SHA256 \u7B7E\u540D\uFF09\u8C03\u7528\u5373\u53EF\u89E6\u53D1\u3002</p></div>`,["cn-name","cn-wf","cn-chat","cn-allow","cn-dedup","cn-status","cn-secret"].forEach(
|
|
646
|
+
<span class="muted">Webhook URL\uFF1A</span><code style="font-size:12px;word-break:break-all">${l(a)}</code>
|
|
647
|
+
<button class="cn-copy ghost" data-url="${l(a)}" style="font-size:12px">\u590D\u5236</button>
|
|
648
|
+
</div></div>`}).join(""),t.querySelectorAll(".cn-copy").forEach(o=>{o.onclick=()=>{navigator.clipboard?.writeText(o.dataset.url),o.textContent="\u5DF2\u590D\u5236",setTimeout(()=>o.textContent="\u590D\u5236",1200)}}),t.querySelectorAll(".cn-toggle").forEach(o=>{o.onclick=async()=>{await ft("PATCH","/api/connectors/"+encodeURIComponent(o.dataset.id),{enabled:o.dataset.on!=="true"}),Ke()}}),t.querySelectorAll(".cn-del").forEach(o=>{o.onclick=async()=>{confirm("\u5220\u9664\u8FD9\u4E2A\u63A5\u5165\u70B9\uFF1F\u5B83\u7684 webhook URL \u4F1A\u7ACB\u5373\u5931\u6548\u3002")&&(await ft("DELETE","/api/connectors/"+encodeURIComponent(o.dataset.id)),Ke())}})}async function Ke(){let[e,t]=await Promise.all([zt("/api/bots"),zt("/api/connectors")]);mt=(e.body?.bots||[]).map(a=>({larkAppId:a.larkAppId,botName:a.botName||a.larkAppId}));let o=G("cn-bot"),s=o.value;o.innerHTML=mt.map(a=>`<option value="${l(a.larkAppId)}">${l(a.botName)}</option>`).join("")||'<option value="">\uFF08\u6CA1\u6709\u5728\u7EBF\u673A\u5668\u4EBA\uFF09</option>',s&&(o.value=s),Yn(t.body?.connectors||[])}function Gt(e){e.innerHTML=Kn(),G("cn-kind").onchange=pt,G("cn-mode").onchange=pt,pt(),G("cn-create").onclick=async()=>{let t=G("cn-create-out"),o=X("cn-name"),s=G("cn-bot").value;if(!o){t.innerHTML='<span class="err">\u8BF7\u586B\u540D\u79F0</span>';return}if(!s){t.innerHTML='<span class="err">\u8BF7\u9009\u673A\u5668\u4EBA</span>';return}let a=G("cn-kind").value,r=G("cn-mode").value,i={name:o,enabled:!0,target:{kind:a,mode:r,botId:s},promptEnvelope:{sourceName:o}};if(a==="workflow"){if(!X("cn-wf")){t.innerHTML='<span class="err">\u8BF7\u586B\u5DE5\u4F5C\u6D41 ID</span>';return}i.target.workflowId=X("cn-wf")}if(r==="fixed"){if(!X("cn-chat")){t.innerHTML='<span class="err">\u56FA\u5B9A\u7FA4\u9700\u8981\u586B\u7FA4 ID</span>';return}i.target.chatId=X("cn-chat")}else{let T=X("cn-allow");T&&(i.target.allowChats=T.split(",").map(k=>k.trim()).filter(Boolean))}if(r==="new-group"){if(!X("cn-dedup")||!X("cn-status")){t.innerHTML='<span class="err">\u300C\u6BCF\u6B21\u65B0\u5EFA\u7FA4\u300D\u9700\u8981\u586B\u53BB\u91CD\u5B57\u6BB5\u548C\u72B6\u6001\u5B57\u6BB5</span>';return}i.lifecycleExtractors={dedupKey:X("cn-dedup"),status:X("cn-status")}}let m=X("cn-secret");m&&(i.secret=m),t.innerHTML='<span class="muted">\u521B\u5EFA\u4E2D\u2026</span>';let $=await ft("POST","/api/connectors",i);if($.status===201&&$.body?.ok){t.innerHTML="";let T=G("cn-created");T.style.display="";let k=$.body.webhookUrl||Wt($.body.connector.id),y=$.body.secret;T.innerHTML=`<div class="card" style="padding:12px 14px;background:var(--bg-soft,#f6f7f9)">
|
|
649
|
+
<p class="ok" style="margin:0 0 6px">\u5DF2\u521B\u5EFA\u300C${l(o)}\u300D</p>
|
|
650
|
+
<p style="margin:4px 0;font-size:13px"><span class="muted">Webhook URL\uFF1A</span><code style="word-break:break-all">${l(k)}</code></p>
|
|
651
|
+
${y?`<p style="margin:4px 0;font-size:13px"><span class="muted">\u7B7E\u540D\u5BC6\u94A5\uFF08\u53EA\u663E\u793A\u8FD9\u4E00\u6B21\uFF0C\u8BF7\u4FDD\u5B58\uFF09\uFF1A</span><code>${l(y)}</code></p>`:""}
|
|
652
|
+
<p class="muted" style="font-size:12px;margin:6px 0 0">\u5916\u90E8\u7CFB\u7EDF\u7528\u6B64 URL + \u5BC6\u94A5\uFF08HMAC-SHA256 \u7B7E\u540D\uFF09\u8C03\u7528\u5373\u53EF\u89E6\u53D1\u3002</p></div>`,["cn-name","cn-wf","cn-chat","cn-allow","cn-dedup","cn-status","cn-secret"].forEach(c=>{G(c).value=""}),Ke()}else{let T=$.body?.error||$.status;t.innerHTML=`<span class="err">\u521B\u5EFA\u5931\u8D25\uFF1A${l(String(T))}</span>`}},Ke()}function Xn(){let e=[["",n("workflow.filter.nonTerminal")],["all",n("workflow.filter.all")],["pending",be("pending")],["running",be("running")],["waiting",be("waiting")],["succeeded",be("succeeded")],["failed",be("failed")],["cancelled",be("cancelled")]];return`
|
|
630
653
|
<nav class="wf-subnav">
|
|
631
|
-
<a href="#/workflows" class="active" data-i18n="workflow.subnav.runs">${
|
|
632
|
-
<a href="#/workflows/catalog" data-i18n="workflow.subnav.catalog">${
|
|
654
|
+
<a href="#/workflows" class="active" data-i18n="workflow.subnav.runs">${d(n("workflow.subnav.runs"))}</a>
|
|
655
|
+
<a href="#/workflows/catalog" data-i18n="workflow.subnav.catalog">${d(n("workflow.subnav.catalog"))}</a>
|
|
633
656
|
</nav>
|
|
634
657
|
<form id="wf-filters" class="filters">
|
|
635
|
-
<input type="search" name="q" placeholder="${
|
|
658
|
+
<input type="search" name="q" placeholder="${d(n("workflow.searchPlaceholder"))}" />
|
|
636
659
|
<select name="status">
|
|
637
|
-
${e.map(([t,
|
|
660
|
+
${e.map(([t,o])=>`<option value="${d(t)}">${d(o)}</option>`).join("")}
|
|
638
661
|
</select>
|
|
639
662
|
<span id="wf-last-load" class="muted"></span>
|
|
640
663
|
</form>
|
|
641
664
|
<table>
|
|
642
665
|
<thead><tr>
|
|
643
|
-
<th>${
|
|
644
|
-
<th>${
|
|
645
|
-
<th>${
|
|
666
|
+
<th>${d(n("workflow.table.run"))}</th><th>${d(n("workflow.table.workflow"))}</th><th>${d(n("workflow.table.status"))}</th>
|
|
667
|
+
<th>${d(n("workflow.table.lastSeq"))}</th><th>${d(n("workflow.table.dangling"))}</th><th>${d(n("workflow.table.updated"))}</th>
|
|
668
|
+
<th>${d(n("workflow.table.chatApp"))}</th>
|
|
646
669
|
</tr></thead>
|
|
647
670
|
<tbody id="wf-tbody"></tbody>
|
|
648
671
|
</table>
|
|
649
|
-
`}var Zn=5e3,eo=2e3,
|
|
650
|
-
<td><a href="#/workflows/${encodeURIComponent(p.runId)}"><code>${
|
|
651
|
-
<td>${
|
|
652
|
-
<td>${
|
|
672
|
+
`}var Zn=5e3,eo=2e3,Oe=new Set(["succeeded","failed","cancelled"]);function d(e){return e.replace(/[&<>"']/g,t=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[t])}function to(e){let t=new Date(e),s=Date.now()-e;return s<6e4?n("time.secondsAgo",{value:Math.max(1,Math.floor(s/1e3))}):s<36e5?n("time.minutesAgo",{value:Math.floor(s/6e4)}):s<864e5?n("time.hoursAgo",{value:Math.floor(s/36e5)}):t.toISOString().slice(0,19).replace("T"," ")}function ce(e){return`<span class="${Oe.has(e)?"wf-status terminal":"wf-status live"} wf-status-${d(e)}">${d(be(e))}</span>`}function be(e){let t=`workflow.status.${e}`,o=n(t);return o===t?e:o}function Yt(e){let t=location.hash.match(/^#\/workflows\/([^?#]+)(?:\?([^#]*))?$/);if(t){let o=new URLSearchParams(t[2]??"");return oo(e,decodeURIComponent(t[1]),{focusAttemptId:o.get("attempt")??void 0})}return no(e)}function no(e){e.innerHTML=Xn();let t=e.querySelector("#wf-tbody"),o=e.querySelector("#wf-filters"),s=e.querySelector("#wf-last-load"),a=[],r=null,i=!1,m=null,$=!1;function T(g){let S=(new FormData(o).get("q")??"").trim().toLowerCase();return S?g.filter(L=>L.runId.toLowerCase().includes(S)||L.workflowId.toLowerCase().includes(S)||(L.chatId??"").toLowerCase().includes(S)):g}function k(){let g=T(a);if(g.length===0){t.innerHTML=`<tr><td colspan="7" class="empty">${m?d(n("workflow.list.failedLoad",{error:m})):a.length===0?d(n("workflow.list.noRuns")):d(n("workflow.list.noFilterMatch"))}</td></tr>`;return}t.innerHTML=g.map(p=>{let S=`${p.dEf}/${p.dAct}/${p.dWait}`,L=p.dEf+p.dAct+p.dWait>0?"wf-dangling has":"wf-dangling none",v=[];p.chatId&&v.push(d(p.chatId)),p.larkAppId&&v.push(`<span class="muted">${d(p.larkAppId)}</span>`);let x=v.length>0?v.join("<br/>"):"\u2014",u=so(p);return`<tr data-runid="${d(p.runId)}">
|
|
673
|
+
<td><a href="#/workflows/${encodeURIComponent(p.runId)}"><code>${d(p.runId)}</code></a></td>
|
|
674
|
+
<td>${d(p.workflowId)}</td>
|
|
675
|
+
<td>${ce(p.status)}${p.failedNodeId?` <span class="muted">(${d(p.failedNodeId)})</span>`:""}${u}</td>
|
|
653
676
|
<td>${p.lastSeq}</td>
|
|
654
|
-
<td class="${
|
|
655
|
-
<td title="${
|
|
656
|
-
<td>${
|
|
657
|
-
</tr>`}).join("")}function
|
|
677
|
+
<td class="${L}">${S}</td>
|
|
678
|
+
<td title="${d(new Date(p.updatedAt).toISOString())}">${to(p.updatedAt)}</td>
|
|
679
|
+
<td>${x}</td>
|
|
680
|
+
</tr>`}).join("")}function y(){m?(s.textContent=n("workflow.list.error",{error:m}),s.classList.add("error")):(s.textContent=n("workflow.list.loaded",{count:a.length,time:new Date().toLocaleTimeString()}),s.classList.remove("error"))}async function c(){if(!($||i)&&!document.hidden){i=!0;try{let g=o.elements.namedItem("status")?.value??"",p=new URLSearchParams;g==="all"?p.set("all","1"):g&&p.set("status",g);let S="/api/workflows/runs"+(p.toString()?`?${p}`:""),L=await fetch(S);L.ok?(a=(await L.json()).runs??[],m=null):(m=`HTTP ${L.status}`,a=[])}catch(g){m=g?.message??String(g),a=[]}finally{i=!1,$||(k(),y())}}}function I(){r!==null&&window.clearTimeout(r),r=window.setTimeout(async()=>{await c(),$||I()},Zn)}function f(){document.hidden||c()}return o.addEventListener("input",()=>{k()}),o.addEventListener("change",g=>{g.target.getAttribute("name")==="status"&&c()}),document.addEventListener("visibilitychange",f),c().then(()=>{$||I()}),()=>{$=!0,r!==null&&window.clearTimeout(r),document.removeEventListener("visibilitychange",f)}}function oo(e,t,o={}){e.innerHTML=`
|
|
658
681
|
<div class="wf-detail-head">
|
|
659
|
-
<a class="btn-link" href="#/workflows">${
|
|
682
|
+
<a class="btn-link" href="#/workflows">${d(n("workflow.detail.back"))}</a>
|
|
660
683
|
<div>
|
|
661
|
-
<h2><code>${
|
|
662
|
-
<div id="wf-detail-subtitle" class="muted">${
|
|
684
|
+
<h2><code>${d(t)}</code></h2>
|
|
685
|
+
<div id="wf-detail-subtitle" class="muted">${d(n("workflow.detail.loading"))}</div>
|
|
663
686
|
</div>
|
|
664
|
-
<button id="wf-cancel-run" type="button" class="contrast" hidden>${
|
|
687
|
+
<button id="wf-cancel-run" type="button" class="contrast" hidden>${d(n("workflow.detail.cancel"))}</button>
|
|
665
688
|
<span id="wf-detail-refresh" class="muted"></span>
|
|
666
689
|
</div>
|
|
667
690
|
<section id="wf-detail-error" class="hint-warn" hidden></section>
|
|
@@ -670,20 +693,20 @@ ${qt("manage")}
|
|
|
670
693
|
<section id="wf-dangling-panel"></section>
|
|
671
694
|
<section class="wf-panel">
|
|
672
695
|
<div class="wf-panel-title">
|
|
673
|
-
<h3>${
|
|
696
|
+
<h3>${d(n("workflow.detail.parallel"))}</h3>
|
|
674
697
|
<span id="wf-parallel-meta" class="muted"></span>
|
|
675
698
|
</div>
|
|
676
699
|
<div id="wf-parallel-view"></div>
|
|
677
700
|
</section>
|
|
678
701
|
<section class="wf-panel">
|
|
679
702
|
<div class="wf-panel-title">
|
|
680
|
-
<h3>${
|
|
703
|
+
<h3>${d(n("workflow.detail.nodes"))}</h3>
|
|
681
704
|
</div>
|
|
682
705
|
<div class="wf-table-scroll">
|
|
683
706
|
<table>
|
|
684
707
|
<thead><tr>
|
|
685
|
-
<th>${
|
|
686
|
-
<th>${
|
|
708
|
+
<th>${d(n("workflow.detail.node"))}</th><th>${d(n("workflow.detail.nodeStatus"))}</th><th>${d(n("workflow.detail.activity"))}</th><th>${d(n("workflow.detail.activityStatus"))}</th>
|
|
709
|
+
<th>${d(n("workflow.detail.attempts"))}</th><th>${d(n("workflow.detail.current"))}</th><th>${d(n("workflow.detail.detail"))}</th>
|
|
687
710
|
</tr></thead>
|
|
688
711
|
<tbody id="wf-node-tbody"></tbody>
|
|
689
712
|
</table>
|
|
@@ -691,237 +714,237 @@ ${qt("manage")}
|
|
|
691
714
|
</section>
|
|
692
715
|
<section class="wf-panel">
|
|
693
716
|
<div class="wf-panel-title">
|
|
694
|
-
<h3>${
|
|
717
|
+
<h3>${d(n("workflow.detail.nodeIO"))}</h3>
|
|
695
718
|
</div>
|
|
696
719
|
<div id="wf-io-list" class="wf-io-list"></div>
|
|
697
720
|
</section>
|
|
698
721
|
<section class="wf-panel">
|
|
699
722
|
<div class="wf-panel-title">
|
|
700
|
-
<h3>${
|
|
701
|
-
<button id="wf-load-older" type="button" hidden>${
|
|
723
|
+
<h3>${d(n("workflow.detail.timeline"))}</h3>
|
|
724
|
+
<button id="wf-load-older" type="button" hidden>${d(n("workflow.detail.loadOlder"))}</button>
|
|
702
725
|
</div>
|
|
703
726
|
<div class="wf-table-scroll wf-timeline-scroll">
|
|
704
727
|
<table>
|
|
705
728
|
<thead><tr>
|
|
706
|
-
<th>${
|
|
729
|
+
<th>${d(n("workflow.detail.seq"))}</th><th>${d(n("workflow.detail.event"))}</th><th>${d(n("workflow.detail.actor"))}</th><th>${d(n("workflow.detail.node"))}</th><th>${d(n("workflow.detail.activity"))}</th><th>${d(n("workflow.detail.error"))}</th><th>${d(n("workflow.detail.time"))}</th>
|
|
707
730
|
</tr></thead>
|
|
708
731
|
<tbody id="wf-event-tbody"></tbody>
|
|
709
732
|
</table>
|
|
710
733
|
</div>
|
|
711
734
|
<div id="wf-event-meta" class="muted"></div>
|
|
712
735
|
</section>
|
|
713
|
-
`;let s=e.querySelector("#wf-detail-subtitle"),a=e.querySelector("#wf-detail-refresh"),r=e.querySelector("#wf-detail-error"),
|
|
714
|
-
<span class="muted error">${
|
|
715
|
-
</div>`}function ro(e){let t=e.dangling;return{total:new Set([...t.activities,...t.effectAttempted,...t.waits,...t.cancels]).size,effects:t.effectAttempted.length,activities:t.activities.length,waits:t.waits.length,cancels:t.cancels.length}}function io(e,t){let
|
|
736
|
+
`;let s=e.querySelector("#wf-detail-subtitle"),a=e.querySelector("#wf-detail-refresh"),r=e.querySelector("#wf-detail-error"),i=e.querySelector("#wf-cancel-status"),m=e.querySelector("#wf-summary"),$=e.querySelector("#wf-dangling-panel"),T=e.querySelector("#wf-parallel-view"),k=e.querySelector("#wf-parallel-meta"),y=e.querySelector("#wf-node-tbody"),c=e.querySelector("#wf-io-list"),I=e.querySelector(".wf-timeline-scroll"),f=e.querySelector("#wf-event-tbody"),g=e.querySelector("#wf-event-meta"),p=e.querySelector("#wf-cancel-run"),S=e.querySelector("#wf-load-older"),L=null,v=[],x=new Set,u=null,w=null,b=!1,E=0,H=null,C=!1,U=!1,J=!1,P=new Set,Ie=new Map,ae=new Map,se=new Map,re=new Set,ye=new Map,Y=new Set,ie=new Map,Ce=new Map,ue=0,pe=o.focusAttemptId;function _(h){if(!h){r.hidden=!0,r.textContent="";return}r.hidden=!1,r.textContent=h}function ve(h){if(!h){i.hidden=!0,i.textContent="";return}i.hidden=!1,i.textContent=h}async function le(){let h=await fetch(`/api/workflows/runs/${encodeURIComponent(t)}/snapshot`);if(h.status===404)throw new Error(n("workflow.detail.unknownRun"));if(!h.ok)throw new Error(n("workflow.detail.snapshotHttp",{status:h.status}));L=await h.json()}async function de(h){let N=await fetch(`/api/workflows/runs/${encodeURIComponent(t)}/events?${h}`);if(N.status===404)throw new Error(n("workflow.detail.unknownRun"));if(!N.ok)throw new Error(n("workflow.detail.eventsHttp",{status:N.status}));return await N.json()}function fe(h,N){let D=h.filter(q=>x.has(q.eventId)?!1:(x.add(q.eventId),!0));D.length!==0&&(v=N==="prepend"?[...D,...v]:[...v,...D],v.sort((q,me)=>Pe(q.eventId)-Pe(me.eventId)))}async function K(){await le();let h=await de(new URLSearchParams({tail:"100"}));v=[],x=new Set,fe(h.events,"append"),u=h.oldestSeq,w=h.newestSeq,b=h.hasOlder,E=h.totalCount,A()}async function ke(){if(!(C||U||document.hidden)){U=!0;try{if(await le(),w!==null){let h=await de(new URLSearchParams({afterSeq:String(w),limit:"200"}));fe(h.events,"append"),h.newestSeq!==null&&(w=h.newestSeq),u===null&&h.oldestSeq!==null&&(u=h.oldestSeq),E=h.totalCount}else{let h=await de(new URLSearchParams({tail:"1"}));fe(h.events,"append"),u=h.oldestSeq,w=h.newestSeq,b=h.hasOlder,E=h.totalCount}_(null),A()}catch(h){_(h?.message??String(h))}finally{U=!1}}}async function Ee(){if(!(u===null||!b)){S.disabled=!0;try{let h=await de(new URLSearchParams({beforeSeq:String(u),limit:"100"}));fe(h.events,"prepend"),h.oldestSeq!==null&&(u=h.oldestSeq),b=h.hasOlder,E=h.totalCount,_(null),A()}catch(h){_(h?.message??String(h))}finally{S.disabled=!1}}}async function bt(){if(!L||Oe.has(L.run.status)||J)return;if(!L.chatBinding?.larkAppId){_(n("workflow.detail.cancelUnavailable",{runId:t}));return}let h=ro(L),N=n("workflow.detail.cancelConfirm",{runId:t,...h});if(window.confirm(N)){J=!0,p.disabled=!0;try{let D=await fetch(`/api/workflows/runs/${encodeURIComponent(t)}/cancel`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({reason:"cancelled via dashboard"})});if(D.status===401)throw new Error(n("workflow.detail.writeAccessCancel"));let q=await D.json().catch(()=>({}));if(!D.ok||!q.ok)throw new Error(q.hint??q.error??n("workflow.detail.cancelHttp",{status:D.status}));ve(q.pending?n("workflow.detail.cancelPending"):null),_(null),await ke()}catch(D){_(D?.message??String(D))}finally{J=!1,p.disabled=!1,A()}}}async function z(h,N){if(!Y.has(h)){Y.add(h),ie.delete(h),A();try{let D=await fetch(`/api/workflows/runs/${encodeURIComponent(t)}/attempts/${encodeURIComponent(N)}/${encodeURIComponent(h)}/resume`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({})});if(D.status===401)throw new Error(n("workflow.detail.writeAccessResume"));let q=await D.json().catch(()=>({}));if(!D.ok||!q.ok||!q.resumeId||!q.url)throw new Error(q.hint??q.message??q.error??n("workflow.detail.resumeStartFailed",{status:D.status}));ye.set(h,{resumeId:q.resumeId,url:q.url})}catch(D){let q=D?.message??String(D);ie.set(h,q)}finally{Y.delete(h),A()}}}async function F(h,N){if(!Y.has(h)){Y.add(h),ie.delete(h),A();try{let D=await fetch(`/api/workflows/runs/${encodeURIComponent(t)}/attempts/${encodeURIComponent(N)}/${encodeURIComponent(h)}/resume/end`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({reason:"ended_by_dashboard"})});if(D.status===401)throw new Error(n("workflow.detail.writeAccessResume"));let q=await D.json().catch(()=>({}));if(!D.ok||!q.ok)if(q.error==="resume_not_running")ye.delete(h);else throw new Error(q.hint??q.message??q.error??n("workflow.detail.resumeEndFailed",{status:D.status}));else ye.delete(h)}catch(D){let q=D?.message??String(D);ie.set(h,q)}finally{Y.delete(h),A()}}}async function O(h,N){if(!re.has(h)){re.add(h),se.delete(h),A();try{let D=ae.get(h)?.trim()||void 0,q=await fetch(`/api/workflows/runs/${encodeURIComponent(t)}/${N}`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({comment:D})});if(q.status===401)throw new Error(n("workflow.detail.writeAccessApproval"));let me=await q.json().catch(()=>({}));if(!q.ok||!me.ok)throw new Error(me.hint??me.message??me.error??n("workflow.detail.actionHttp",{action:N,status:q.status}));let et=N==="approve"?n("workflow.detail.approved"):n("workflow.detail.rejected");se.set(h,{kind:"ok",text:me.alreadyTerminal?n("workflow.detail.alreadyTerminal",{label:et}):me.pending?n("workflow.detail.workflowContinue",{label:et}):n("workflow.detail.workflowRefreshing",{label:et})}),_(null),await ke()}catch(D){let q=D?.message??String(D);se.set(h,{kind:"error",text:q}),_(q)}finally{re.delete(h),A()}}}function A(){if(!L)return;ue=I.scrollTop;let h=L.run;Oe.has(h.status)&&ve(null),s.innerHTML=`${d(h.workflowId??"?")} \xB7 ${ce(h.status)} \xB7 lastSeq ${L.lastSeq}`,a.textContent=n("workflow.detail.refreshed",{time:new Date().toLocaleTimeString()}),p.hidden=Oe.has(h.status),p.disabled=J||!L.chatBinding?.larkAppId,p.textContent=L.chatBinding?.larkAppId?n("workflow.detail.cancel"):n("workflow.detail.cliCancelOnly"),p.title=L.chatBinding?.larkAppId?n("workflow.detail.cancelTitle"):n("workflow.detail.cliCancelTitle",{runId:t}),ao(m,L),io($,L),lo(T,k,L,v),go(y,L),wo(c,L,P,Ie,{comments:ae,statuses:se,resolving:re,onResolve:O},{sessions:ye,pending:Y,errors:ie,onStart:z,onEnd:F},pe,Ce)&&(pe=void 0),Fo(f,v),I.scrollTop=ue,S.hidden=!b,g.textContent=n("workflow.detail.eventsLoaded",{loaded:v.length,total:E})}function R(){if(H!==null&&window.clearTimeout(H),L&&Oe.has(L.run.status)){H=null;return}H=window.setTimeout(async()=>{await ke(),C||R()},eo)}function B(){document.hidden||ke().then(()=>{!C&&H===null&&R()})}return S.addEventListener("click",()=>{Ee()}),p.addEventListener("click",()=>{bt()}),document.addEventListener("visibilitychange",B),K().then(()=>{_(null),C||R()}).catch(h=>{_(h?.message??String(h)),s.textContent=n("workflow.detail.loadFailed")}),()=>{C=!0,H!==null&&window.clearTimeout(H),document.removeEventListener("visibilitychange",B)}}function ao(e,t){let o=t.run,s=[[n("workflow.summary.workflow"),d(o.workflowId??"?")],[n("workflow.summary.status"),ce(o.status)],[n("workflow.summary.lastSeq"),String(t.lastSeq)],[n("workflow.summary.updated"),d(new Date(t.updatedAt).toLocaleString())],[n("workflow.summary.revision"),d(Ye(o.revisionId))],[n("workflow.summary.initiator"),d(o.initiator??"-")]];o.failedNodeId&&s.push([n("workflow.summary.failedNode"),d(o.failedNodeId)]),o.cancelOriginEventId&&s.push([n("workflow.summary.cancelOrigin"),d(o.cancelOriginEventId)]),t.chatBinding&&(s.push([n("workflow.summary.chat"),`<code>${d(t.chatBinding.chatId)}</code>`]),s.push([n("workflow.summary.app"),`<code>${d(t.chatBinding.larkAppId)}</code>`])),e.innerHTML=s.map(([a,r])=>`<div class="wf-summary-item"><span>${a}</span><strong>${r}</strong></div>`).join("")}function so(e){if(!e.errorCode)return"";let t=e.errorMessage?` \u2014 ${Wo(e.errorMessage,96)}`:"";return`<div class="wf-run-error">
|
|
737
|
+
<span class="muted error">${d(e.errorCode)}</span>${d(t)}
|
|
738
|
+
</div>`}function ro(e){let t=e.dangling;return{total:new Set([...t.activities,...t.effectAttempted,...t.waits,...t.cancels]).size,effects:t.effectAttempted.length,activities:t.activities.length,waits:t.waits.length,cancels:t.cancels.length}}function io(e,t){let o=t.dangling,s=[[n("workflow.dangling.activities"),o.activities],[n("workflow.dangling.effects"),o.effectAttempted],[n("workflow.dangling.waits"),o.waits],[n("workflow.dangling.cancels"),o.cancels]],a=new Set(s.flatMap(([,r])=>r)).size;if(e.className=a>0?"wf-panel wf-dangling-panel has":"wf-panel wf-dangling-panel",a===0){e.innerHTML=`<div class="wf-panel-title"><h3>${d(n("workflow.detail.dangling"))}</h3></div><div class="muted">${d(n("workflow.detail.noDangling"))}</div>`;return}e.innerHTML=`<div class="wf-panel-title"><h3>${d(n("workflow.detail.dangling"))}</h3><span class="wf-dangling has">${a}</span></div>
|
|
716
739
|
<div class="wf-dangling-grid">
|
|
717
|
-
${s.map(([r,
|
|
718
|
-
</div>`}function lo(e,t,
|
|
719
|
-
<span title="${
|
|
720
|
-
<span title="${
|
|
740
|
+
${s.map(([r,i])=>`<div><strong>${r}</strong>${i.length===0?`<div class="muted">${d(n("workflow.detail.none"))}</div>`:`<ul>${i.map(m=>`<li><code>${d(m)}</code></li>`).join("")}</ul>`}</div>`).join("")}
|
|
741
|
+
</div>`}function lo(e,t,o,s){let a=co(s,o);if(a.length===0){t.textContent="",e.innerHTML=`<div class="empty">${d(n("workflow.detail.noParallelData"))}</div>`;return}let r=Date.now(),i=Math.min(...a.map(c=>c.startedAt)),m=Math.max(...a.map(c=>c.endedAt??r),i+1e3),$=Math.max(1,m-i),T=po(a,r),k=a.filter(c=>!c.endedAt&&(c.status==="running"||c.status==="effectAttempting")).length;t.textContent=n("workflow.detail.parallelMeta",{count:a.length,max:T,running:k});let y=a.sort((c,I)=>c.startedAt-I.startedAt||c.activityId.localeCompare(I.activityId)).map(c=>uo(c,i,$,r)).join("");e.innerHTML=`<div class="wf-parallel-axis">
|
|
742
|
+
<span title="${d(new Date(i).toISOString())}">${d(Ve(i))}</span>
|
|
743
|
+
<span title="${d(new Date(m).toISOString())}">${d(Ve(m))}</span>
|
|
721
744
|
</div>
|
|
722
|
-
<div class="wf-parallel-list">${
|
|
745
|
+
<div class="wf-parallel-list">${y}</div>`}function co(e,t){let o=new Map,s=new Map(t.activities.map(a=>[a.activityId,a.ownerNodeId]));for(let a of[...e].sort((r,i)=>Pe(r.eventId)-Pe(i.eventId))){let r=zo(a);if(!r)continue;let i=typeof r.activityId=="string"?r.activityId:void 0,m=typeof r.attemptId=="string"?r.attemptId:void 0;if(!i||!m)continue;let $=o.get(m);if(a.type==="attemptCreated"){let T=typeof r.attemptNumber=="number"?r.attemptNumber:void 0;$={nodeId:typeof r.nodeId=="string"?r.nodeId:s.get(i),activityId:i,attemptId:m,attemptNumber:T,status:"pending",startedAt:a.timestamp},o.set(m,$);continue}$||($={nodeId:s.get(i),activityId:i,attemptId:m,status:"pending",startedAt:a.timestamp},o.set(m,$)),a.type==="activityRunning"?($.status="running",$.runningAt=a.timestamp):a.type==="effectAttempted"?$.status="effectAttempting":a.type==="activityWaiting"||a.type==="waitCreated"?$.status="waiting":fo(a.type)&&($.status=mo(a.type),$.endedAt=a.timestamp,$.endType=a.type)}return[...o.values()]}function uo(e,t,o,s){let a=e.endedAt??s,r=Vt((e.startedAt-t)/o*100,0,100),i=Vt((Math.max(a,e.startedAt+1)-e.startedAt)/o*100,.7,100-r),m=e.nodeId??e.activityId,$=e.attemptNumber!==void 0?`#${e.attemptNumber}`:Ye(e.attemptId),T=[`${m} ${e.status}`,`${new Date(e.startedAt).toISOString()} \u2192 ${e.endedAt?new Date(e.endedAt).toISOString():n("workflow.detail.parallelNow")}`,e.endType?`end: ${e.endType}`:void 0].filter(Boolean).join(`
|
|
723
746
|
`);return`<div class="wf-parallel-row">
|
|
724
747
|
<div class="wf-parallel-label">
|
|
725
|
-
<code>${
|
|
726
|
-
<span class="muted">${
|
|
748
|
+
<code>${d(m)}</code>
|
|
749
|
+
<span class="muted">${d(e.activityId)} \xB7 ${d($)}</span>
|
|
727
750
|
</div>
|
|
728
751
|
<div class="wf-parallel-track">
|
|
729
|
-
<div class="wf-parallel-bar wf-parallel-${
|
|
730
|
-
<span>${
|
|
752
|
+
<div class="wf-parallel-bar wf-parallel-${d(e.status)}" style="left:${r.toFixed(3)}%;width:${i.toFixed(3)}%;" title="${d(T)}">
|
|
753
|
+
<span>${d(be(e.status))}</span>
|
|
731
754
|
</div>
|
|
732
755
|
</div>
|
|
733
|
-
</div>`}function po(e,t){let
|
|
734
|
-
<td>${e?`<code>${
|
|
735
|
-
<td>${e?
|
|
736
|
-
<td>${t?`<code>${
|
|
737
|
-
<td>${t?
|
|
756
|
+
</div>`}function po(e,t){let o=[];for(let r of e)o.push({time:r.startedAt,delta:1}),o.push({time:r.endedAt??t,delta:-1});o.sort((r,i)=>r.time-i.time||i.delta-r.delta);let s=0,a=0;for(let r of o)s+=r.delta,a=Math.max(a,s);return a}function fo(e){return e==="activitySucceeded"||e==="activityFailed"||e==="activityTimedOut"||e==="activityCanceled"}function mo(e){return e==="activitySucceeded"?"succeeded":e==="activityCanceled"?"cancelled":e==="activityTimedOut"?"timedOut":"failed"}function go(e,t){let o=new Map(t.activities.map(r=>[r.activityId,r])),s=new Set,a=[];for(let r of t.nodes){let i=(r.activityId?o.get(r.activityId):void 0)??t.activities.find(m=>m.ownerNodeId===r.nodeId);i&&s.add(i.activityId),a.push(Kt(r,i))}for(let r of t.activities)s.has(r.activityId)||a.push(Kt(void 0,r));e.innerHTML=a.length>0?a.join(""):`<tr><td colspan="7" class="empty">${d(n("workflow.detail.noNodes"))}</td></tr>`}function Kt(e,t){let o=t?.attempts[t.attempts.length-1];return`<tr>
|
|
757
|
+
<td>${e?`<code>${d(e.nodeId)}</code>`:'<span class="muted">-</span>'}</td>
|
|
758
|
+
<td>${e?ce(e.status):'<span class="muted">-</span>'}</td>
|
|
759
|
+
<td>${t?`<code>${d(t.activityId)}</code>`:'<span class="muted">-</span>'}</td>
|
|
760
|
+
<td>${t?ce(t.status):'<span class="muted">-</span>'}</td>
|
|
738
761
|
<td>${t?.attempts.length??0}</td>
|
|
739
|
-
<td>${
|
|
740
|
-
<td>${
|
|
741
|
-
</tr>`}function
|
|
762
|
+
<td>${o?`<code>${d(o.attemptId)}</code>`:'<span class="muted">-</span>'}</td>
|
|
763
|
+
<td>${o?Uo(o):`<span class="muted">${d(n("workflow.detail.idle"))}</span>`}</td>
|
|
764
|
+
</tr>`}function wo(e,t,o,s,a,r,i,m){qo(e,o,s),Co(e,a.comments);let $=!!(i&&t.attemptIO?.[i]?.terminal);$&&i&&o.add(gt(i,n("workflow.detail.liveTerminal")));let T=ho(t),k=new Set;if(m){for(let c of T){k.add(c.key);let I=m.get(c.key);I||(I=bo(c.key),m.set(c.key,I),e.appendChild(I.article)),yo(I,c,o,a,r,i)}for(let[c,I]of Array.from(m))k.has(c)||(I.article.remove(),m.delete(c));if(T.length===0){if(!e.querySelector(".wf-io-empty-placeholder")){let c=document.createElement("div");c.className="empty wf-io-empty-placeholder",c.textContent=n("workflow.detail.noNodeIO"),e.appendChild(c)}}else e.querySelector(".wf-io-empty-placeholder")?.remove()}else{let c=[];for(let I of T)c.push(To(I,o,a,r,i));e.innerHTML=c.length>0?c.join(""):`<div class="empty">${d(n("workflow.detail.noNodeIO"))}</div>`}Po(e,s);let y=Ro(e,i);return Oo(e,o),Bo(e,s),Do(e,a),rn(e,r),y&&$}function ho(e){let t=new Map(e.activities.map(a=>[a.activityId,a])),o=new Set,s=[];for(let a of e.nodes){let r=(a.activityId?t.get(a.activityId):void 0)??e.activities.find(i=>i.ownerNodeId===a.nodeId);if(!r){s.push({key:`node:${a.nodeId}`,node:a});continue}o.add(r.activityId),s.push({key:`activity:${r.activityId}`,node:a,activity:r,io:e.attemptIO?.[Qe(r)?.attemptId??""]})}for(let a of e.activities)o.has(a.activityId)||s.push({key:`activity:${a.activityId}`,activity:a,io:e.attemptIO?.[Qe(a)?.attemptId??""]});return s}function bo(e){let t=document.createElement("article");t.className="wf-io-card",t.dataset.wfCardKey=e;let o=document.createElement("div");o.className="wf-io-card-head";let s=document.createElement("div");s.className="wf-io-terminal-slot";let a=document.createElement("div");return a.className="wf-io-grid",t.appendChild(o),t.appendChild(s),t.appendChild(a),{article:t,head:o,terminalSlot:s,grid:a,currentTerminalUrl:null}}function yo(e,t,o,s,a,r){let i=Qe(t.activity),m=t.node?.nodeId??t.activity?.ownerNodeId??t.activity?.activityId??"unknown",$=!!(i&&i.attemptId===r);e.article.classList.toggle("is-focused",$),i?e.article.dataset.wfAttemptCard=i.attemptId:delete e.article.dataset.wfAttemptCard;let T=sn(i,s);e.head.innerHTML=`
|
|
742
765
|
<header>
|
|
743
766
|
<div>
|
|
744
|
-
<strong><code>${
|
|
745
|
-
<span class="muted">${t.activity?
|
|
767
|
+
<strong><code>${d(m)}</code></strong>
|
|
768
|
+
<span class="muted">${t.activity?d(t.activity.activityId):d(n("workflow.detail.notDispatched"))}</span>
|
|
746
769
|
</div>
|
|
747
|
-
<div>${t.node?
|
|
770
|
+
<div>${t.node?ce(t.node.status):""} ${t.activity?ce(t.activity.status):""}</div>
|
|
748
771
|
</header>
|
|
749
772
|
<div class="wf-io-meta">
|
|
750
|
-
${
|
|
773
|
+
${i?`${d(n("workflow.detail.attempt"))} <code>${d(i.attemptId)}</code>`:d(n("workflow.detail.noAttempt"))}
|
|
751
774
|
</div>
|
|
752
|
-
${
|
|
753
|
-
`;let
|
|
754
|
-
${
|
|
755
|
-
${
|
|
756
|
-
${
|
|
757
|
-
${
|
|
758
|
-
${t.io?.waitPrompt?
|
|
759
|
-
`}function
|
|
760
|
-
<summary>${
|
|
775
|
+
${T}
|
|
776
|
+
`;let k=Xt(i,t.activity,t.io?.terminal,a),y=k?.url??null;if(y!==e.currentTerminalUrl)k===null?e.terminalSlot.innerHTML="":e.terminalSlot.innerHTML=Zt(t.key,i,t.activity,t.io?.terminal,k,o,a),e.currentTerminalUrl=y;else if(k!==null&&t.io?.terminal){let I=e.terminalSlot.querySelector("details.wf-terminal-block > summary");if(I){let f=en(k.kind);I.innerHTML=`${d(f)} ${an(i,t.io.terminal)}`}i&&Ao(e.terminalSlot,i,t.activity,t.io.terminal,k,a)}let c=i?.attemptId??t.activity?.activityId??t.node?.nodeId??"unknown";e.grid.innerHTML=`
|
|
777
|
+
${oe(c,n("workflow.detail.authoredInput"),t.io?.input,o)}
|
|
778
|
+
${oe(c,n("workflow.detail.resolvedInput"),t.io?.resolvedInput,o)}
|
|
779
|
+
${oe(c,n("workflow.detail.output"),t.io?.output,o)}
|
|
780
|
+
${oe(c,n("workflow.detail.executionLog"),t.io?.log,o)}
|
|
781
|
+
${t.io?.waitPrompt?oe(c,n("workflow.detail.waitPrompt"),t.io.waitPrompt,o):""}
|
|
782
|
+
`}function Xt(e,t,o,s){if(!o||o.error)return null;if(Lo(e,o))return{kind:"live",url:Eo(o)};if(!e||!t||!Io(e,o))return null;let a=Mo();if(!a)return null;let r=s?.sessions.get(e.attemptId);return r?{kind:"resume",url:r.url,resumeId:r.resumeId,downloadUrl:Qt(a,t.activityId,e.attemptId)}:{kind:"replay",url:xo(a,t.activityId,e.attemptId,!!o.hasPtyLog),downloadUrl:Qt(a,t.activityId,e.attemptId)}}function Zt(e,t,o,s,a,r,i){if(!s)return"";let m=en(a.kind),$=gt(e,m),T=an(t,s),k=vo(a.kind),y=a.kind==="replay"||a.kind==="resume"?`<a class="btn-link" href="${d(a.downloadUrl)}" download>${d(n("workflow.detail.downloadFullLog"))}</a>`:"",c=t?nn(t,o,s,a,i):"",I=t?on(t.attemptId,i):"";return`<details class="wf-io-block wf-terminal-block" data-io-key="${d($)}"${r.has($)?" open":""}>
|
|
783
|
+
<summary>${d(m)} ${T}</summary>
|
|
761
784
|
<div class="wf-terminal-actions">
|
|
762
|
-
<a class="btn-link" href="${
|
|
763
|
-
${
|
|
764
|
-
${
|
|
785
|
+
<a class="btn-link" href="${d(a.url)}" target="_blank" rel="noreferrer">${d(k)}</a>
|
|
786
|
+
${y}
|
|
787
|
+
${c}
|
|
765
788
|
</div>
|
|
766
|
-
${
|
|
767
|
-
<iframe class="wf-terminal-frame" src="${
|
|
768
|
-
</details>`}function
|
|
789
|
+
${I}
|
|
790
|
+
<iframe class="wf-terminal-frame" src="${d(a.url)}" title="${d(m)}" loading="lazy"></iframe>
|
|
791
|
+
</details>`}function en(e){return e==="live"?n("workflow.detail.liveTerminal"):e==="resume"?n("workflow.detail.terminalResume"):n("workflow.detail.terminalReplay")}function vo(e){return e==="live"?n("workflow.detail.openTerminalNewTab"):e==="resume"?n("workflow.detail.openResumeNewTab"):n("workflow.detail.openReplayNewTab")}var tn=new Set(["antigravity","codex-app","cursor","mira"]),ko=new Set(["aiden","coco","claude-code","codex","mtr","hermes"]);function $o(e){return!!e&&(ko.has(e)||tn.has(e))}function So(e){return!!e&&tn.has(e)}function nn(e,t,o,s,a){if(!a||s.kind==="live"||!t)return"";let r=s.kind==="resume",i=a.pending.has(e.attemptId),m=`data-wf-resume-attempt="${d(e.attemptId)}" data-wf-resume-activity="${d(t.activityId)}"`;return r?`<button type="button" class="btn-link" data-wf-resume-button="1" data-wf-resume-action="end" ${m}${i?" disabled":""}>${d(i?n("workflow.detail.resumeEnding"):n("workflow.detail.endResumeSession"))}</button>`:$o(o.cliId)?So(o.cliId)&&!o.cliSessionId?`<button type="button" class="btn-link" data-wf-resume-button="1" disabled title="${d(n("workflow.detail.resumeMissingCliSession"))}">${d(n("workflow.detail.resumeSession"))}</button>`:`<button type="button" class="btn-link" data-wf-resume-button="1" data-wf-resume-action="start" ${m}${i?" disabled":""}>${d(i?n("workflow.detail.resumeStarting"):n("workflow.detail.resumeSession"))}</button>`:`<button type="button" class="btn-link" data-wf-resume-button="1" disabled title="${d(n("workflow.detail.resumeUnsupportedCli",{cliId:o.cliId??"?"}))}">${d(n("workflow.detail.resumeSession"))}</button>`}function on(e,t){if(!t)return"";let o=t.errors.get(e);return o?`<div class="hint-warn wf-resume-status" data-wf-resume-status="${d(e)}">${d(o)}</div>`:""}function To(e,t,o,s,a){let r=Qe(e.activity),i=e.node?.nodeId??e.activity?.ownerNodeId??e.activity?.activityId??"unknown",m=r?.attemptId??e.activity?.activityId??e.node?.nodeId??"unknown",$=sn(r,o),T=r?.attemptId===a?" is-focused":"",k=r?` data-wf-attempt-card="${d(r.attemptId)}"`:"",y=Xt(r,e.activity,e.io?.terminal,s),c=y?Zt(m,r,e.activity,e.io?.terminal,y,t,s):"";return`<article class="wf-io-card${T}" data-wf-card-key="${d(e.key)}"${k}>
|
|
769
792
|
<div class="wf-io-card-head">
|
|
770
793
|
<header>
|
|
771
794
|
<div>
|
|
772
|
-
<strong><code>${
|
|
773
|
-
<span class="muted">${e.activity?
|
|
795
|
+
<strong><code>${d(i)}</code></strong>
|
|
796
|
+
<span class="muted">${e.activity?d(e.activity.activityId):d(n("workflow.detail.notDispatched"))}</span>
|
|
774
797
|
</div>
|
|
775
|
-
<div>${e.node?
|
|
798
|
+
<div>${e.node?ce(e.node.status):""} ${e.activity?ce(e.activity.status):""}</div>
|
|
776
799
|
</header>
|
|
777
800
|
<div class="wf-io-meta">
|
|
778
|
-
${r?`${
|
|
801
|
+
${r?`${d(n("workflow.detail.attempt"))} <code>${d(r.attemptId)}</code>`:d(n("workflow.detail.noAttempt"))}
|
|
779
802
|
</div>
|
|
780
|
-
${
|
|
803
|
+
${$}
|
|
781
804
|
</div>
|
|
782
|
-
<div class="wf-io-terminal-slot">${
|
|
805
|
+
<div class="wf-io-terminal-slot">${c}</div>
|
|
783
806
|
<div class="wf-io-grid">
|
|
784
|
-
${
|
|
785
|
-
${
|
|
786
|
-
${
|
|
787
|
-
${
|
|
788
|
-
${e.io?.waitPrompt?
|
|
807
|
+
${oe(m,n("workflow.detail.authoredInput"),e.io?.input,t)}
|
|
808
|
+
${oe(m,n("workflow.detail.resolvedInput"),e.io?.resolvedInput,t)}
|
|
809
|
+
${oe(m,n("workflow.detail.output"),e.io?.output,t)}
|
|
810
|
+
${oe(m,n("workflow.detail.executionLog"),e.io?.log,t)}
|
|
811
|
+
${e.io?.waitPrompt?oe(m,n("workflow.detail.waitPrompt"),e.io.waitPrompt,t):""}
|
|
789
812
|
</div>
|
|
790
|
-
</article>`}function
|
|
813
|
+
</article>`}function Qe(e){return e?.attempts[e.attempts.length-1]}function an(e,t){let o=[];return t.error?o.push(n("workflow.detail.error")):o.push(t.status==="live"?n("workflow.detail.terminalLive"):n("workflow.detail.terminalClosedShort")),e?.status&&o.push(e.status),t.webPort>0&&o.push(`:${t.webPort}`),`<span class="muted">${d(o.join(" \xB7 "))}</span>`}function Lo(e,t){return t.status==="live"&&t.webPort>0&&(e?.status==="pending"||e?.status==="running"||e?.status==="effectAttempting")}function Io(e,t){return e.status==="succeeded"||e.status==="failed"||e.status==="cancelled"||e.status==="timedOut"?!!(t.sessionId||t.startedAt):!1}function Eo(e){return`http://${window.location.hostname||"127.0.0.1"}:${e.webPort}`}function xo(e,t,o,s){let a=new URLSearchParams({runId:e,activityId:t,attemptId:o});return s&&a.set("hasPtyLog","1"),`/assets/terminal-replay.html?${a.toString()}`}function Qt(e,t,o){return`/api/workflows/runs/${encodeURIComponent(e)}/attempts/${encodeURIComponent(t)}/${encodeURIComponent(o)}/terminal-log/raw?download=1`}function Mo(){let e=window.location.hash.match(/^#\/workflows\/([^/?#]+)/);if(!e)return null;try{return decodeURIComponent(e[1])}catch{return null}}function sn(e,t){if(!Ho(e))return"";let o=e.attemptId,s=t.comments.get(o)??"",a=t.resolving.has(o),r=t.statuses.get(o),i=r?.kind==="error"?"hint-warn":"hint-ok";return`<div class="wf-approval-box" data-wf-approval="${d(o)}">
|
|
791
814
|
<label>
|
|
792
|
-
<span>${
|
|
793
|
-
<textarea class="wf-approval-comment" data-wf-approval-comment="${
|
|
815
|
+
<span>${d(n("workflow.detail.approvalComment"))}</span>
|
|
816
|
+
<textarea class="wf-approval-comment" data-wf-approval-comment="${d(o)}" rows="2" placeholder="${d(n("workflow.detail.optionalComment"))}"${a?" disabled":""}>${d(s)}</textarea>
|
|
794
817
|
</label>
|
|
795
818
|
<div class="wf-approval-actions">
|
|
796
|
-
<button type="button" class="primary" data-wf-approval-action="approve" data-wf-attempt-id="${
|
|
797
|
-
<button type="button" data-wf-approval-action="reject" data-wf-attempt-id="${
|
|
798
|
-
${a?`<span class="muted">${
|
|
819
|
+
<button type="button" class="primary" data-wf-approval-action="approve" data-wf-attempt-id="${d(o)}"${a?" disabled":""}>${d(n("workflow.detail.approve"))}</button>
|
|
820
|
+
<button type="button" data-wf-approval-action="reject" data-wf-attempt-id="${d(o)}"${a?" disabled":""}>${d(n("workflow.detail.reject"))}</button>
|
|
821
|
+
${a?`<span class="muted">${d(n("workflow.detail.submitting"))}</span>`:""}
|
|
799
822
|
</div>
|
|
800
|
-
${r?`<div class="${
|
|
801
|
-
</div>`}function Ho(e){return!!e&&e.status==="waiting"&&e.wait?.waitKind==="human-gate"&&!e.wait.resolution}function Co(e,t){e.querySelectorAll("textarea[data-wf-approval-comment]").forEach(
|
|
802
|
-
<summary>${
|
|
803
|
-
${jo(
|
|
804
|
-
</details>`}function
|
|
805
|
-
<td>${
|
|
806
|
-
<td><code>${
|
|
807
|
-
<td>${
|
|
808
|
-
<td>${t.nodeId?`<code>${
|
|
809
|
-
<td>${t.activityId?`<code>${
|
|
810
|
-
<td>${t.errorCode?`<span class="muted error">${
|
|
811
|
-
<td title="${
|
|
812
|
-
</tr>`}function
|
|
823
|
+
${r?`<div class="${i} wf-approval-status">${d(r.text)}</div>`:""}
|
|
824
|
+
</div>`}function Ho(e){return!!e&&e.status==="waiting"&&e.wait?.waitKind==="human-gate"&&!e.wait.resolution}function Co(e,t){e.querySelectorAll("textarea[data-wf-approval-comment]").forEach(o=>{let s=o.dataset.wfApprovalComment;s&&t.set(s,o.value)})}function rn(e,t){e.querySelectorAll("button[data-wf-resume-action][data-wf-resume-attempt][data-wf-resume-activity]").forEach(o=>{o.dataset.wfResumeBound!=="1"&&(o.dataset.wfResumeBound="1",o.addEventListener("click",()=>{let s=o.dataset.wfResumeAttempt,a=o.dataset.wfResumeActivity,r=o.dataset.wfResumeAction;!s||!a||(r==="start"?t.onStart(s,a):r==="end"&&t.onEnd(s,a))}))})}function Ao(e,t,o,s,a,r){let i=e.querySelector(".wf-terminal-actions");if(!i)return;let m=i.querySelector('button[data-wf-resume-button="1"]'),$=nn(t,o,s,a,r);m?m.outerHTML=$:$&&i.insertAdjacentHTML("beforeend",$);let T=e.querySelector("details.wf-terminal-block");if(T){let k=T.querySelector(".wf-resume-status"),y=on(t.attemptId,r);k?k.outerHTML=y:y&&i.insertAdjacentHTML("afterend",y)}rn(e,r)}function Do(e,t){e.querySelectorAll("textarea[data-wf-approval-comment]").forEach(o=>{let s=o.dataset.wfApprovalComment;s&&o.addEventListener("input",()=>{t.comments.set(s,o.value)})}),e.querySelectorAll("button[data-wf-approval-action][data-wf-attempt-id]").forEach(o=>{o.addEventListener("click",()=>{let s=o.dataset.wfAttemptId,a=o.dataset.wfApprovalAction;!s||a!=="approve"&&a!=="reject"||t.onResolve(s,a)})})}function oe(e,t,o,s){let a=gt(e,t);return`<details class="wf-io-block" data-io-key="${d(a)}"${s.has(a)?" open":""}>
|
|
825
|
+
<summary>${d(t)} ${No(o)}</summary>
|
|
826
|
+
${jo(o)}
|
|
827
|
+
</details>`}function gt(e,t){return`${e}:${t}`}function Ro(e,t){if(!t)return!1;for(let o of e.querySelectorAll("[data-wf-attempt-card]"))if(o.dataset.wfAttemptCard===t)return o.scrollIntoView({block:"center"}),!0;return!1}function qo(e,t,o){e.querySelectorAll("details.wf-io-block[data-io-key]").forEach(s=>{let a=s.dataset.ioKey;if(!a)return;s.open?t.add(a):t.delete(a);let r=s.querySelector(".wf-io-pre");r&&o.set(a,r.scrollTop)})}function Oo(e,t){e.querySelectorAll("details.wf-io-block[data-io-key]").forEach(o=>{o.dataset.ioToggleBound!=="1"&&(o.dataset.ioToggleBound="1",o.addEventListener("toggle",()=>{let s=o.dataset.ioKey;s&&(o.open?t.add(s):t.delete(s))}))})}function Po(e,t){e.querySelectorAll("details.wf-io-block[data-io-key]").forEach(o=>{let s=o.dataset.ioKey;if(!s)return;let a=t.get(s);if(a===void 0)return;let r=o.querySelector(".wf-io-pre");r&&(r.scrollTop=a)})}function Bo(e,t){e.querySelectorAll("details.wf-io-block[data-io-key]").forEach(o=>{let s=o.dataset.ioKey;if(!s)return;let a=o.querySelector(".wf-io-pre");a&&a.dataset.ioScrollBound!=="1"&&(a.dataset.ioScrollBound="1",a.addEventListener("scroll",()=>{t.set(s,a.scrollTop)}))})}function No(e){if(!e)return`<span class="muted">${d(n("workflow.detail.empty"))}</span>`;let t=[];return e.outputBytes!==void 0&&t.push(`${e.outputBytes}B`),e.truncated&&t.push(n("workflow.detail.truncated")),e.error&&t.push(n("workflow.detail.error")),e.outputHash&&t.push(Ye(e.outputHash)),t.length?`<span class="muted">${d(t.join(" \xB7 "))}</span>`:""}function jo(e){if(!e)return`<div class="muted wf-io-empty">${d(n("workflow.detail.noData"))}</div>`;let t=e.value!==void 0?JSON.stringify(e.value,null,2):e.text??"",o=e.error?`<div class="muted error">${d(e.error)}</div>`:"";return t?`${o}<pre class="wf-io-pre">${d(t)}</pre>`:`${o}<div class="muted wf-io-empty">${d(n("workflow.detail.noPreview"))}</div>`}function Uo(e){let t=[];if(e.effectAttempted&&t.push(`${d(n("workflow.detail.effect"))} ${d(e.effectAttempted.provider)}`),e.wait){let o=e.wait.resolution?`${e.wait.resolution.kind}${e.wait.resolution.resolution?":"+e.wait.resolution.resolution:""}`:n("workflow.detail.open");t.push(`${d(n("workflow.detail.wait"))} ${d(e.wait.waitKind)} ${d(o)}`),e.wait.deadlineAt!==void 0&&t.push(`${d(n("workflow.detail.deadline"))} ${d(Ve(e.wait.deadlineAt))}`)}if(e.error){let o=`${e.error.errorCode}${e.error.errorClass?` \xB7 ${e.error.errorClass}`:""}`;t.push(`<span class="muted error">${d(o)}</span>`),e.error.errorMessage&&t.push(`<span class="error wf-error-msg">${d(e.error.errorMessage)}</span>`)}return e.output&&t.push(`${d(n("workflow.detail.output"))} ${d(Ye(e.output.outputHash))}`),e.runningMs!==void 0&&t.push(`${e.runningMs}ms`),t.length>0?t.join("<br/>"):'<span class="muted">-</span>'}function Fo(e,t){e.innerHTML=t.length>0?t.map(_o).join(""):`<tr><td colspan="7" class="empty">${d(n("workflow.detail.noEvents"))}</td></tr>`}function _o(e){let t=Jo(e.payload);return`<tr>
|
|
828
|
+
<td>${Pe(e.eventId)}</td>
|
|
829
|
+
<td><code>${d(e.type)}</code></td>
|
|
830
|
+
<td>${d(e.actor)}</td>
|
|
831
|
+
<td>${t.nodeId?`<code>${d(t.nodeId)}</code>`:"-"}</td>
|
|
832
|
+
<td>${t.activityId?`<code>${d(t.activityId)}</code>`:"-"}</td>
|
|
833
|
+
<td>${t.errorCode?`<span class="muted error">${d(t.errorCode)}</span>`:"-"}</td>
|
|
834
|
+
<td title="${d(new Date(e.timestamp).toISOString())}">${d(Ve(e.timestamp))}</td>
|
|
835
|
+
</tr>`}function Pe(e){let t=e.lastIndexOf("-");if(t<0)return 0;let o=Number(e.slice(t+1));return Number.isFinite(o)?o:0}function Jo(e){if(!e||typeof e!="object"||"ref"in e)return{};let t=e,o={};typeof t.nodeId=="string"&&(o.nodeId=t.nodeId),typeof t.activityId=="string"&&(o.activityId=t.activityId),typeof t.failedNodeId=="string"&&(o.nodeId=t.failedNodeId);let s=t.error;return s&&typeof s=="object"&&"errorCode"in s&&(o.errorCode=String(s.errorCode)),o}function zo(e){return!e.payload||typeof e.payload!="object"||"ref"in e.payload?null:e.payload}function Vt(e,t,o){return Math.min(o,Math.max(t,e))}function Ye(e){return e?e.length>18?e.slice(0,10)+"..."+e.slice(-6):e:"-"}function Wo(e,t){return e.length>t?e.slice(0,t-1)+"\u2026":e}function Ve(e){return new Date(e).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit",second:"2-digit"})}function M(e){return e.replace(/[&<>"']/g,t=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[t])}function ln(e){return e?e.length>18?e.slice(0,10)+"..."+e.slice(-6):e:"-"}function dn(e){let t=location.hash.match(/^#\/(?:workflows\/catalog|workflows-catalog)\/([^/?#]+)$/);return t?Ko(e,decodeURIComponent(t[1])):Go(e)}function Go(e){e.innerHTML=`
|
|
813
836
|
<nav class="wf-subnav">
|
|
814
|
-
<a href="#/workflows" data-i18n="workflow.subnav.runs">${
|
|
815
|
-
<a href="#/workflows/catalog" class="active" data-i18n="workflow.subnav.catalog">${
|
|
837
|
+
<a href="#/workflows" data-i18n="workflow.subnav.runs">${M(n("workflow.subnav.runs"))}</a>
|
|
838
|
+
<a href="#/workflows/catalog" class="active" data-i18n="workflow.subnav.catalog">${M(n("workflow.subnav.catalog"))}</a>
|
|
816
839
|
</nav>
|
|
817
840
|
<section class="catalog-head">
|
|
818
841
|
<div>
|
|
819
|
-
<h2>${
|
|
820
|
-
<p class="muted">${
|
|
842
|
+
<h2>${M(n("catalog.title"))}</h2>
|
|
843
|
+
<p class="muted">${M(n("catalog.subtitle"))}</p>
|
|
821
844
|
</div>
|
|
822
|
-
<button id="catalog-refresh" type="button">${
|
|
845
|
+
<button id="catalog-refresh" type="button">${M(n("catalog.refresh"))}</button>
|
|
823
846
|
</section>
|
|
824
847
|
<form id="catalog-filters" class="filters">
|
|
825
|
-
<input type="search" name="q" placeholder="${
|
|
848
|
+
<input type="search" name="q" placeholder="${M(n("catalog.searchPlaceholder"))}" />
|
|
826
849
|
<span id="catalog-status" class="muted"></span>
|
|
827
850
|
</form>
|
|
828
851
|
<div class="wf-table-scroll">
|
|
829
852
|
<table>
|
|
830
853
|
<thead><tr>
|
|
831
|
-
<th>${
|
|
832
|
-
<th>${
|
|
833
|
-
<th>${
|
|
834
|
-
<th>${
|
|
835
|
-
<th>${
|
|
836
|
-
<th>${
|
|
854
|
+
<th>${M(n("catalog.table.workflow"))}</th>
|
|
855
|
+
<th>${M(n("catalog.table.version"))}</th>
|
|
856
|
+
<th>${M(n("catalog.table.params"))}</th>
|
|
857
|
+
<th>${M(n("catalog.table.nodes"))}</th>
|
|
858
|
+
<th>${M(n("catalog.table.revision"))}</th>
|
|
859
|
+
<th>${M(n("catalog.table.path"))}</th>
|
|
837
860
|
</tr></thead>
|
|
838
861
|
<tbody id="catalog-tbody"></tbody>
|
|
839
862
|
</table>
|
|
840
863
|
</div>
|
|
841
|
-
`;let t=e.querySelector("#catalog-tbody"),
|
|
864
|
+
`;let t=e.querySelector("#catalog-tbody"),o=e.querySelector("#catalog-status"),s=e.querySelector("#catalog-filters"),a=e.querySelector("#catalog-refresh"),r=[],i=null,m=!1;function $(){let y=(new FormData(s).get("q")??"").trim().toLowerCase();return y?r.filter(c=>c.workflowId.toLowerCase().includes(y)||c.path.toLowerCase().includes(y)):r}function T(){i?(o.textContent=n("catalog.loadFailed",{error:i}),o.classList.add("error")):(o.textContent=`${r.length}`,o.classList.remove("error"));let y=$();if(y.length===0){t.innerHTML=`<tr><td colspan="6" class="empty">${r.length===0?M(n("catalog.noDefinitions")):M(n("catalog.noFilterMatch"))}</td></tr>`;return}t.innerHTML=y.map(c=>`
|
|
842
865
|
<tr>
|
|
843
|
-
<td><a href="#/workflows/catalog/${encodeURIComponent(
|
|
844
|
-
<td>${
|
|
845
|
-
<td>${
|
|
846
|
-
<td>${
|
|
847
|
-
<td><code>${
|
|
848
|
-
<td><code>${
|
|
866
|
+
<td><a href="#/workflows/catalog/${encodeURIComponent(c.workflowId)}"><code>${M(c.workflowId)}</code></a></td>
|
|
867
|
+
<td>${c.version}</td>
|
|
868
|
+
<td>${M(n("catalog.paramSummary",{required:c.requiredParamCount,total:c.paramCount}))}</td>
|
|
869
|
+
<td>${c.nodeCount}</td>
|
|
870
|
+
<td><code>${M(ln(c.revisionId))}</code></td>
|
|
871
|
+
<td><code>${M(c.path)}</code></td>
|
|
849
872
|
</tr>
|
|
850
|
-
`).join("")}async function
|
|
873
|
+
`).join("")}async function k(){a.disabled=!0,o.textContent=n("catalog.loading");try{let y=await fetch("/api/workflows/definitions");if(!y.ok)throw new Error(`HTTP ${y.status}`);r=(await y.json()).definitions??[],i=null}catch(y){i=y?.message??String(y),r=[]}finally{a.disabled=!1,m||T()}}return s.addEventListener("input",T),a.addEventListener("click",()=>{k()}),k(),()=>{m=!0}}function Ko(e,t){e.innerHTML=`
|
|
851
874
|
<div class="catalog-detail-head">
|
|
852
|
-
<a class="btn-link" href="#/workflows/catalog">${
|
|
875
|
+
<a class="btn-link" href="#/workflows/catalog">${M(n("catalog.back"))}</a>
|
|
853
876
|
<div>
|
|
854
|
-
<h2><code>${
|
|
855
|
-
<div id="catalog-detail-subtitle" class="muted">${
|
|
877
|
+
<h2><code>${M(t)}</code></h2>
|
|
878
|
+
<div id="catalog-detail-subtitle" class="muted">${M(n("workflow.detail.loading"))}</div>
|
|
856
879
|
</div>
|
|
857
880
|
</div>
|
|
858
881
|
<section id="catalog-error" class="hint-warn" hidden></section>
|
|
859
882
|
<section id="catalog-run-status" class="hint-ok" hidden></section>
|
|
860
883
|
<div id="catalog-detail-body"></div>
|
|
861
|
-
`;let
|
|
884
|
+
`;let o=e.querySelector("#catalog-detail-subtitle"),s=e.querySelector("#catalog-error"),a=e.querySelector("#catalog-run-status"),r=e.querySelector("#catalog-detail-body"),i=null,m=!1,$=!1;function T(p){s.hidden=!p,s.textContent=p??""}function k(p){a.hidden=!p,a.textContent=p??""}function y(p){let S={};for(let[L,v]of Object.entries(p??{}))"default"in v&&(S[L]=v.default);return S}function c(){if(!i)return;let p=i.definition;o.textContent=`${n("catalog.revision")} ${ln(i.revisionId)} \xB7 ${i.path}`;let S=JSON.stringify(y(p.params),null,2);r.innerHTML=`
|
|
862
885
|
<section class="wf-panel">
|
|
863
|
-
<div class="wf-panel-title"><h3>${
|
|
886
|
+
<div class="wf-panel-title"><h3>${M(n("catalog.summary"))}</h3></div>
|
|
864
887
|
<div class="wf-summary-grid">
|
|
865
|
-
<div class="wf-summary-item"><span>${
|
|
866
|
-
<div class="wf-summary-item"><span>${
|
|
867
|
-
<div class="wf-summary-item"><span>${
|
|
868
|
-
<div class="wf-summary-item"><span>${
|
|
888
|
+
<div class="wf-summary-item"><span>${M(n("catalog.table.workflow"))}</span><strong><code>${M(p.workflowId)}</code></strong></div>
|
|
889
|
+
<div class="wf-summary-item"><span>${M(n("catalog.table.version"))}</span><strong>${p.version}</strong></div>
|
|
890
|
+
<div class="wf-summary-item"><span>${M(n("catalog.nodeCount"))}</span><strong>${Object.keys(p.nodes).length}</strong></div>
|
|
891
|
+
<div class="wf-summary-item"><span>${M(n("catalog.path"))}</span><strong><code>${M(i.path)}</code></strong></div>
|
|
869
892
|
</div>
|
|
870
893
|
</section>
|
|
871
894
|
|
|
872
895
|
<section class="wf-panel">
|
|
873
|
-
<div class="wf-panel-title"><h3>${
|
|
896
|
+
<div class="wf-panel-title"><h3>${M(n("catalog.runPanel"))}</h3></div>
|
|
874
897
|
<form id="catalog-run-form" class="catalog-run-form">
|
|
875
898
|
<label>
|
|
876
|
-
<span>${
|
|
877
|
-
<textarea id="catalog-params" rows="8" spellcheck="false" placeholder="${
|
|
899
|
+
<span>${M(n("catalog.paramsJson"))}</span>
|
|
900
|
+
<textarea id="catalog-params" rows="8" spellcheck="false" placeholder="${M(n("catalog.paramsPlaceholder"))}">${M(S)}</textarea>
|
|
878
901
|
</label>
|
|
879
902
|
<div class="catalog-chat-grid">
|
|
880
903
|
<label>
|
|
881
|
-
<span>${
|
|
904
|
+
<span>${M(n("catalog.chatId"))}</span>
|
|
882
905
|
<input id="catalog-chat-id" type="text" autocomplete="off" />
|
|
883
906
|
</label>
|
|
884
907
|
<label>
|
|
885
|
-
<span>${
|
|
908
|
+
<span>${M(n("catalog.larkAppId"))}</span>
|
|
886
909
|
<input id="catalog-lark-app-id" type="text" autocomplete="off" />
|
|
887
910
|
</label>
|
|
888
911
|
</div>
|
|
889
|
-
<div class="muted">${
|
|
912
|
+
<div class="muted">${M(n("catalog.chatBindingHint"))}</div>
|
|
890
913
|
<div id="catalog-param-errors" class="catalog-param-errors" hidden></div>
|
|
891
|
-
<button id="catalog-run-btn" type="submit" class="primary">${
|
|
914
|
+
<button id="catalog-run-btn" type="submit" class="primary">${M(n("catalog.run"))}</button>
|
|
892
915
|
</form>
|
|
893
916
|
</section>
|
|
894
917
|
|
|
895
918
|
<section class="wf-panel">
|
|
896
|
-
<div class="wf-panel-title"><h3>${
|
|
897
|
-
${
|
|
919
|
+
<div class="wf-panel-title"><h3>${M(n("catalog.paramsSchema"))}</h3></div>
|
|
920
|
+
${Qo(p.params)}
|
|
898
921
|
</section>
|
|
899
922
|
|
|
900
923
|
<section class="wf-panel">
|
|
901
|
-
<div class="wf-panel-title"><h3>${
|
|
902
|
-
<pre class="wf-io-pre">${
|
|
924
|
+
<div class="wf-panel-title"><h3>${M(n("catalog.definitionJson"))}</h3></div>
|
|
925
|
+
<pre class="wf-io-pre">${M(JSON.stringify(p,null,2))}</pre>
|
|
903
926
|
</section>
|
|
904
|
-
`,
|
|
927
|
+
`,f()}async function I(){if(!i||$)return;let p=r.querySelector("#catalog-params"),S=r.querySelector("#catalog-chat-id"),L=r.querySelector("#catalog-lark-app-id"),v=r.querySelector("#catalog-run-btn"),x=r.querySelector("#catalog-param-errors"),u;try{if(u=JSON.parse(p.value||"{}"),!u||typeof u!="object"||Array.isArray(u))throw new Error(n("catalog.badParamsJson"))}catch(w){x.hidden=!1,x.innerHTML=`<div class="muted error">${M(w?.message??String(w))}</div>`;return}$=!0,v.disabled=!0,v.textContent=n("catalog.running"),x.hidden=!0,T(null),k(null);try{let w=await fetch(`/api/workflows/definitions/${encodeURIComponent(i.definition.workflowId)}/run`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({params:u,chatBinding:{chatId:S.value.trim(),larkAppId:L.value.trim()}})});if(w.status===401)throw new Error(n("catalog.writeAccess"));let b=await w.json().catch(()=>({}));if(!w.ok||!b.ok)throw b.issues?.length&&(x.hidden=!1,x.innerHTML=`<strong>${M(n("catalog.invalidParams"))}</strong><ul>${b.issues.map(E=>`<li>${M(n("catalog.issue",{path:E.path.length?E.path.join("."):"(root)",message:E.message}))}</li>`).join("")}</ul>`),new Error(b.hint??b.message??b.error??n("catalog.runHttp",{status:w.status}));k(n("catalog.runStarted")),b.runId&&(location.hash=`#/workflows/${encodeURIComponent(b.runId)}`)}catch(w){T(w?.message??String(w))}finally{$=!1,v.disabled=!1,v.textContent=n("catalog.run")}}function f(){r.querySelector("#catalog-run-form")?.addEventListener("submit",S=>{S.preventDefault(),I()})}async function g(){try{let p=await fetch(`/api/workflows/definitions/${encodeURIComponent(t)}`);if(p.status===404)throw new Error("unknown_workflow");if(!p.ok)throw new Error(`HTTP ${p.status}`);i=await p.json(),T(null),c()}catch(p){T(n("catalog.definitionLoadFailed",{error:p?.message??String(p)})),o.textContent=n("workflow.detail.loadFailed")}}return g().then(()=>{}),()=>{m=!0}}function Qo(e){let t=Object.entries(e??{});return t.length===0?`<div class="muted">${M(n("catalog.noParams"))}</div>`:`<div class="catalog-param-list">${t.map(([o,s])=>`
|
|
905
928
|
<article class="catalog-param">
|
|
906
929
|
<header>
|
|
907
|
-
<code>${
|
|
908
|
-
<span class="wf-status">${
|
|
909
|
-
<span class="muted">${
|
|
930
|
+
<code>${M(o)}</code>
|
|
931
|
+
<span class="wf-status">${M(s.required?n("catalog.required"):n("catalog.optional"))}</span>
|
|
932
|
+
<span class="muted">${M(s.type)}${s.format?` \xB7 ${M(s.format)}`:""}</span>
|
|
910
933
|
</header>
|
|
911
|
-
${s.description?`<div class="muted">${
|
|
912
|
-
${"default"in s?`<pre class="wf-io-pre">${
|
|
934
|
+
${s.description?`<div class="muted">${M(n("catalog.description"))}: ${M(s.description)}</div>`:""}
|
|
935
|
+
${"default"in s?`<pre class="wf-io-pre">${M(`${n("catalog.default")}: ${JSON.stringify(s.default,null,2)}`)}</pre>`:""}
|
|
913
936
|
</article>
|
|
914
|
-
`).join("")}</div>`}var
|
|
915
|
-
<img class="qr-image" src="${e.qrDataUrl}" alt="${
|
|
916
|
-
${e.qrUrl?`<a class="onboarding-link" href="${e.qrUrl}" target="_blank" rel="noopener">${
|
|
917
|
-
</div>`:"",s=e.appId?`<p><b>App ID:</b> <code>${e.appId}</code></p>`:"",a=e.status==="completed"?`<p class="hint-ok">${
|
|
937
|
+
`).join("")}</div>`}var Le=null,Xe=null;function Ze(){Xe!==null&&(window.clearInterval(Xe),Xe=null)}function cn(){return Le||(Le=document.createElement("dialog"),Le.className="onboarding-dialog",document.body.appendChild(Le),Le.addEventListener("close",Ze),Le)}function Vo(e){return e.status==="waiting_for_scan"?n("botOnboarding.waiting"):e.status==="verifying"?n("botOnboarding.verifying"):e.status==="completed"?n("botOnboarding.completed"):e.status==="failed"?`${n("botOnboarding.failed")}: ${e.message??e.error??"unknown"}`:n("botOnboarding.starting")}function Be(e){let t=cn(),o=e.qrDataUrl?`<div class="qr-card">
|
|
938
|
+
<img class="qr-image" src="${e.qrDataUrl}" alt="${n("botOnboarding.qrAlt")}">
|
|
939
|
+
${e.qrUrl?`<a class="onboarding-link" href="${e.qrUrl}" target="_blank" rel="noopener">${n("botOnboarding.openLink")}</a>`:""}
|
|
940
|
+
</div>`:"",s=e.appId?`<p><b>App ID:</b> <code>${e.appId}</code></p>`:"",a=e.status==="completed"?`<p class="hint-ok">${n("botOnboarding.restartHint")}</p>`:"";t.innerHTML=`<article>
|
|
918
941
|
<header>
|
|
919
|
-
<h3>${
|
|
920
|
-
<p>${
|
|
942
|
+
<h3>${n("botOnboarding.title")}</h3>
|
|
943
|
+
<p>${n("botOnboarding.intro")}</p>
|
|
921
944
|
</header>
|
|
922
|
-
<p class="onboarding-status status-${e.status}">${
|
|
923
|
-
${
|
|
945
|
+
<p class="onboarding-status status-${e.status}">${Vo(e)}</p>
|
|
946
|
+
${o}
|
|
924
947
|
${s}
|
|
925
948
|
${a}
|
|
926
|
-
<form method="dialog"><button>${
|
|
927
|
-
</article>`}async function
|
|
949
|
+
<form method="dialog"><button>${n("botOnboarding.close")}</button></form>
|
|
950
|
+
</article>`}async function Yo(e){let t=await fetch(`/api/bot-onboarding/${encodeURIComponent(e)}`),o=await t.json();if(!t.ok||!o?.job)throw new Error(o?.error??`http_${t.status}`);Be(o.job),(o.job.status==="completed"||o.job.status==="failed")&&Ze()}async function Xo(){Ze(),Be({id:"",status:"starting"});let e=cn();e.open||e.showModal();try{let t=await fetch("/api/bot-onboarding/start",{method:"POST"}),o=await t.json();if(!t.ok||!o?.job?.id)throw new Error(o?.error??`http_${t.status}`);Be(o.job),Xe=window.setInterval(()=>{Yo(o.job.id).catch(s=>{Ze(),Be({id:o.job.id,status:"failed",message:s instanceof Error?s.message:String(s)})})},1200)}catch(t){Be({id:"",status:"failed",message:t instanceof Error?t.message:String(t)})}}function un(){let e=document.getElementById("add-bot-btn");e&&(e.onclick=()=>{Xo()})}var Z=document.getElementById("root"),pn=!1;function Zo(){if(pn)return;pn=!0;let e=document.createElement("div");e.id="auth-expired-overlay",e.style.cssText="position:fixed;inset:0;background:rgba(0,0,0,.65);display:flex;align-items:center;justify-content:center;z-index:9999",e.innerHTML='<div style="background:var(--card,#fff);color:var(--text,#1f2329);border-radius:12px;padding:36px 40px;max-width:460px;width:90vw;text-align:center;box-shadow:0 12px 40px rgba(0,0,0,.35)"><h2 style="margin:0 0 14px;font-size:19px">\u8BBF\u95EE\u94FE\u63A5\u5DF2\u5931\u6548</h2><p style="margin:0 0 24px;line-height:1.7;color:var(--muted,#8f959e);font-size:14px">\u5F53\u524D\u94FE\u63A5/\u8BBF\u95EE\u5DF2\u5931\u6548\uFF0C\u8BF7\u4F7F\u7528\u6700\u65B0\u6388\u6743\u94FE\u63A5\u91CD\u65B0\u8FDB\u5165\u3002<br>\u6700\u597D\u5173\u95ED\u5F53\u524D\u9875\u3002</p><button onclick="window.close()" style="padding:8px 22px;background:var(--accent,#3370ff);color:#fff;border:none;border-radius:8px;cursor:pointer;font-size:14px">\u5173\u95ED\u6B64\u9875</button></div>',document.body.appendChild(e)}var ea=window.fetch.bind(window);window.fetch=async function(...t){let o=await ea(...t);return o.status===401&&Zo(),o};var Ne=null;function wt(){Ne&&(Ne(),Ne=null);let e=location.hash||"#/";e.startsWith("#/workflows/catalog")||e.startsWith("#/workflows-catalog")?Ne=dn(Z):e.startsWith("#/workflows")?Ne=Yt(Z):e.startsWith("#/groups")?Ct(Z):e.startsWith("#/bot-defaults")?Rt(Z):e.startsWith("#/connectors")?Gt(Z):e.startsWith("#/team/manage")?Jt(Z):e.startsWith("#/team")?_t(Z):e.startsWith("#/roles")?Bt(Z):e.startsWith("#/schedules")?Mt(Z):e.startsWith("#/sessions")?Et(Z):Lt(Z);for(let t of document.querySelectorAll(".sidebar-nav a")){let o=t.getAttribute("href")??"#/";t.classList.toggle("active",o===(e||"#/"))}}var ht=document.getElementById("status");function mn(){ht&&(ht.textContent=W.online?n("status.live"):n("status.disconnected"),ht.className="connection-status "+(W.online?"online":"offline"))}W.on(mn);function fn(){document.querySelectorAll("[data-i18n]").forEach(e=>{e.textContent=n(e.dataset.i18n??"")}),document.querySelectorAll("[data-locale]").forEach(e=>{e.classList.toggle("active",e.dataset.locale===ge.locale)}),document.querySelectorAll("[data-theme-mode]").forEach(e=>{e.classList.toggle("active",e.dataset.themeMode===ge.themeMode)}),mn()}function ta(){document.querySelectorAll("[data-locale]").forEach(e=>{e.onclick=()=>ge.setLocale(e.dataset.locale)}),document.querySelectorAll("[data-theme-mode]").forEach(e=>{e.onclick=()=>ge.setThemeMode(e.dataset.themeMode)})}(async()=>{ge.init(),ta(),un(),ge.on(()=>{fn(),wt()}),fn();try{await yt()}catch(e){console.error("botmux dashboard bootstrap failed",e),W.setOnline(!1)}window.addEventListener("hashchange",wt),wt()})();})();
|