botmux 2.43.0 → 2.45.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 +1 -2
- package/README.md +1 -2
- package/dist/bot-registry.d.ts +11 -0
- package/dist/bot-registry.d.ts.map +1 -1
- package/dist/bot-registry.js +1 -0
- package/dist/bot-registry.js.map +1 -1
- package/dist/cli/bots-list-output.d.ts +8 -0
- package/dist/cli/bots-list-output.d.ts.map +1 -1
- package/dist/cli/bots-list-output.js +9 -0
- package/dist/cli/bots-list-output.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +6 -0
- package/dist/cli.js.map +1 -1
- package/dist/core/command-handler.d.ts.map +1 -1
- package/dist/core/command-handler.js +169 -51
- package/dist/core/command-handler.js.map +1 -1
- package/dist/core/dashboard-ipc-server.d.ts +2 -0
- package/dist/core/dashboard-ipc-server.d.ts.map +1 -1
- package/dist/core/dashboard-ipc-server.js +61 -2
- package/dist/core/dashboard-ipc-server.js.map +1 -1
- package/dist/core/role-resolver.d.ts +17 -1
- package/dist/core/role-resolver.d.ts.map +1 -1
- package/dist/core/role-resolver.js +64 -10
- package/dist/core/role-resolver.js.map +1 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +14 -9
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/trigger-session.d.ts +9 -0
- package/dist/core/trigger-session.d.ts.map +1 -0
- package/dist/core/trigger-session.js +158 -0
- package/dist/core/trigger-session.js.map +1 -0
- package/dist/core/worker-pool.d.ts +22 -0
- package/dist/core/worker-pool.d.ts.map +1 -1
- package/dist/core/worker-pool.js +54 -2
- package/dist/core/worker-pool.js.map +1 -1
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +73 -44
- package/dist/daemon.js.map +1 -1
- package/dist/dashboard/connector-api.d.ts +3 -0
- package/dist/dashboard/connector-api.d.ts.map +1 -0
- package/dist/dashboard/connector-api.js +351 -0
- package/dist/dashboard/connector-api.js.map +1 -0
- package/dist/dashboard/federated-group-core.d.ts +54 -0
- package/dist/dashboard/federated-group-core.d.ts.map +1 -0
- package/dist/dashboard/federated-group-core.js +165 -0
- package/dist/dashboard/federated-group-core.js.map +1 -0
- package/dist/dashboard/federation-api.d.ts +42 -0
- package/dist/dashboard/federation-api.d.ts.map +1 -0
- package/dist/dashboard/federation-api.js +408 -0
- package/dist/dashboard/federation-api.js.map +1 -0
- package/dist/dashboard/federation-spoke-api.d.ts +76 -0
- package/dist/dashboard/federation-spoke-api.d.ts.map +1 -0
- package/dist/dashboard/federation-spoke-api.js +618 -0
- package/dist/dashboard/federation-spoke-api.js.map +1 -0
- package/dist/dashboard/team-group.d.ts +18 -0
- package/dist/dashboard/team-group.d.ts.map +1 -0
- package/dist/dashboard/team-group.js +7 -0
- package/dist/dashboard/team-group.js.map +1 -0
- package/dist/dashboard/trigger-api.d.ts +13 -0
- package/dist/dashboard/trigger-api.d.ts.map +1 -0
- package/dist/dashboard/trigger-api.js +77 -0
- package/dist/dashboard/trigger-api.js.map +1 -0
- package/dist/dashboard/web/app.js +8 -0
- package/dist/dashboard/web/app.js.map +1 -1
- package/dist/dashboard/web/bot-defaults.d.ts.map +1 -1
- package/dist/dashboard/web/bot-defaults.js +13 -0
- package/dist/dashboard/web/bot-defaults.js.map +1 -1
- package/dist/dashboard/web/connectors.d.ts +2 -0
- package/dist/dashboard/web/connectors.d.ts.map +1 -0
- package/dist/dashboard/web/connectors.js +187 -0
- package/dist/dashboard/web/connectors.js.map +1 -0
- package/dist/dashboard/web/i18n.d.ts.map +1 -1
- package/dist/dashboard/web/i18n.js +4 -0
- package/dist/dashboard/web/i18n.js.map +1 -1
- package/dist/dashboard/web/team-federation.d.ts +3 -0
- package/dist/dashboard/web/team-federation.d.ts.map +1 -0
- package/dist/dashboard/web/team-federation.js +472 -0
- package/dist/dashboard/web/team-federation.js.map +1 -0
- package/dist/dashboard/webhook-routes.d.ts +19 -0
- package/dist/dashboard/webhook-routes.d.ts.map +1 -0
- package/dist/dashboard/webhook-routes.js +321 -0
- package/dist/dashboard/webhook-routes.js.map +1 -0
- package/dist/dashboard-web/app.js +512 -384
- package/dist/dashboard-web/index.html +2 -0
- package/dist/dashboard.js +153 -1
- package/dist/dashboard.js.map +1 -1
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +31 -9
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/zh.d.ts.map +1 -1
- package/dist/i18n/zh.js +31 -9
- package/dist/i18n/zh.js.map +1 -1
- package/dist/im/lark/card-builder.d.ts +25 -2
- package/dist/im/lark/card-builder.d.ts.map +1 -1
- package/dist/im/lark/card-builder.js +141 -43
- 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 +20 -4
- package/dist/im/lark/card-handler.js.map +1 -1
- package/dist/im/lark/client.d.ts +41 -0
- package/dist/im/lark/client.d.ts.map +1 -1
- package/dist/im/lark/client.js +174 -48
- package/dist/im/lark/client.js.map +1 -1
- package/dist/services/bot-owner-store.d.ts +28 -0
- package/dist/services/bot-owner-store.d.ts.map +1 -0
- package/dist/services/bot-owner-store.js +82 -0
- package/dist/services/bot-owner-store.js.map +1 -0
- package/dist/services/bot-profile-store.d.ts +16 -0
- package/dist/services/bot-profile-store.d.ts.map +1 -0
- package/dist/services/bot-profile-store.js +98 -0
- package/dist/services/bot-profile-store.js.map +1 -0
- package/dist/services/card-prefs-store.d.ts +2 -1
- package/dist/services/card-prefs-store.d.ts.map +1 -1
- package/dist/services/card-prefs-store.js +13 -4
- package/dist/services/card-prefs-store.js.map +1 -1
- package/dist/services/connector-store.d.ts +58 -0
- package/dist/services/connector-store.d.ts.map +1 -0
- package/dist/services/connector-store.js +79 -0
- package/dist/services/connector-store.js.map +1 -0
- package/dist/services/deployment-identity.d.ts +22 -0
- package/dist/services/deployment-identity.d.ts.map +1 -0
- package/dist/services/deployment-identity.js +67 -0
- package/dist/services/deployment-identity.js.map +1 -0
- package/dist/services/federation-membership-store.d.ts +23 -0
- package/dist/services/federation-membership-store.d.ts.map +1 -0
- package/dist/services/federation-membership-store.js +66 -0
- package/dist/services/federation-membership-store.js.map +1 -0
- package/dist/services/federation-roster.d.ts +54 -0
- package/dist/services/federation-roster.d.ts.map +1 -0
- package/dist/services/federation-roster.js +51 -0
- package/dist/services/federation-roster.js.map +1 -0
- package/dist/services/federation-store.d.ts +76 -0
- package/dist/services/federation-store.d.ts.map +1 -0
- package/dist/services/federation-store.js +133 -0
- package/dist/services/federation-store.js.map +1 -0
- package/dist/services/group-creator.d.ts +6 -0
- package/dist/services/group-creator.d.ts.map +1 -1
- package/dist/services/group-creator.js +10 -1
- package/dist/services/group-creator.js.map +1 -1
- package/dist/services/groups-store.d.ts +11 -0
- package/dist/services/groups-store.d.ts.map +1 -1
- package/dist/services/groups-store.js +42 -0
- package/dist/services/groups-store.js.map +1 -1
- package/dist/services/invite-store.d.ts +28 -0
- package/dist/services/invite-store.d.ts.map +1 -0
- package/dist/services/invite-store.js +85 -0
- package/dist/services/invite-store.js.map +1 -0
- package/dist/services/pairing-store.d.ts +47 -0
- package/dist/services/pairing-store.d.ts.map +1 -0
- package/dist/services/pairing-store.js +132 -0
- package/dist/services/pairing-store.js.map +1 -0
- package/dist/services/team-roster.d.ts +38 -0
- package/dist/services/team-roster.d.ts.map +1 -0
- package/dist/services/team-roster.js +82 -0
- package/dist/services/team-roster.js.map +1 -0
- package/dist/services/team-store.d.ts +54 -0
- package/dist/services/team-store.d.ts.map +1 -0
- package/dist/services/team-store.js +156 -0
- package/dist/services/team-store.js.map +1 -0
- package/dist/services/trigger-log-store.d.ts +46 -0
- package/dist/services/trigger-log-store.d.ts.map +1 -0
- package/dist/services/trigger-log-store.js +132 -0
- package/dist/services/trigger-log-store.js.map +1 -0
- package/dist/services/trigger-types.d.ts +57 -0
- package/dist/services/trigger-types.d.ts.map +1 -0
- package/dist/services/trigger-types.js +28 -0
- package/dist/services/trigger-types.js.map +1 -0
- package/dist/services/webhook-key.d.ts +16 -0
- package/dist/services/webhook-key.d.ts.map +1 -0
- package/dist/services/webhook-key.js +123 -0
- package/dist/services/webhook-key.js.map +1 -0
- package/dist/services/webhook-lifecycle-extractors.d.ts +15 -0
- package/dist/services/webhook-lifecycle-extractors.d.ts.map +1 -0
- package/dist/services/webhook-lifecycle-extractors.js +59 -0
- package/dist/services/webhook-lifecycle-extractors.js.map +1 -0
- package/dist/services/webhook-lifecycle-store.d.ts +45 -0
- package/dist/services/webhook-lifecycle-store.d.ts.map +1 -0
- package/dist/services/webhook-lifecycle-store.js +159 -0
- package/dist/services/webhook-lifecycle-store.js.map +1 -0
- package/dist/setup/verify-permissions.d.ts.map +1 -1
- package/dist/setup/verify-permissions.js +4 -0
- package/dist/setup/verify-permissions.js.map +1 -1
- package/dist/skills/definitions.d.ts.map +1 -1
- package/dist/skills/definitions.js +64 -10
- package/dist/skills/definitions.js.map +1 -1
- package/dist/workflows/events/payloads.d.ts +2 -2
- package/dist/workflows/events/schema.d.ts +8 -8
- package/dist/workflows/trigger-from-envelope.d.ts +13 -0
- package/dist/workflows/trigger-from-envelope.d.ts.map +1 -0
- package/dist/workflows/trigger-from-envelope.js +67 -0
- package/dist/workflows/trigger-from-envelope.js.map +1 -0
- package/package.json +1 -1
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
"use strict";(()=>{var Me=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 r=this.sessions.get(o.sessionId);r&&this.sessions.set(o.sessionId,{...r,...o.patch})}else if(t==="session.exited"){let r=this.sessions.get(o.sessionId);r&&this.sessions.set(o.sessionId,{...r,status:"closed"})}else if(t==="schedule.created")this.schedules.set(o.schedule.id,o.schedule);else if(t==="schedule.updated"){let r=this.schedules.get(o.id);r&&this.schedules.set(o.id,{...r,...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()}},q=new Me;async function Je(){let[e,t]=await Promise.all([fetch("/api/sessions").then(a=>a.json()),fetch("/api/schedules").then(a=>a.json())]);q.upsertSessions(e.sessions??[]),q.upsertSchedules(t.schedules??[]);let o=new EventSource("/events"),r=["session.spawned","session.update","session.exited","schedule.created","schedule.updated","schedule.deleted","schedule.fired","heartbeat"];for(let a of r)o.addEventListener(a,s=>{try{let d=JSON.parse(s.data);q.applySse(a,d.body??d)}catch{}});o.onerror=()=>q.setOnline(!1),o.onopen=()=>q.setOnline(!0)}var Ae="botmux.dashboard.locale",qt={"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.cardPrefSaved":"\u5DF2\u4FDD\u5B58","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 _e=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()}},N=new _e;async function ut(){let[e,t]=await Promise.all([fetch("/api/sessions").then(a=>a.json()),fetch("/api/schedules").then(a=>a.json())]);N.upsertSessions(e.sessions??[]),N.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 i=JSON.parse(r.data);N.applySse(a,i.body??i)}catch{}});n.onerror=()=>N.setOnline(!1),n.onopen=()=>N.setOnline(!0)}var We="botmux.dashboard.locale",mn={"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","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"},
|
|
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","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"},pt={zh:mn,en:wn};function ft(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 gn(e=[]){for(let t of e){let n=ft(t);if(n)return n}return"zh"}function Me(e){return(t,n)=>{let s=pt[e][t]??pt.zh[t]??t;return n?s.replace(/\{(\w+)\}/g,(a,r)=>{let i=n[r];return i==null?`{${r}}`:String(i)}):s}}function mt(e,t){return(e?ft(e.getItem(We)):null)??gn(t)}var ze="botmux.dashboard.theme";function hn(e){return e==="system"||e==="light"||e==="dark"?e:null}function wt(e,t){return e==="system"?t?"dark":"light":e}function gt(e){return hn(e?.getItem(ze))??"system"}var Je=class{locale="zh";themeMode="system";resolvedTheme="light";listeners=new Set;translate=Me(this.locale);mediaQuery=null;init(){let t=typeof window<"u"?window:void 0;this.locale=mt(t?.localStorage,bn()),this.translate=Me(this.locale),this.themeMode=gt(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,n){return this.translate(t,n)}setLocale(t){this.locale!==t&&(this.locale=t,this.translate=Me(t),window.localStorage.setItem(We,t),this.applyLocale(),this.emit())}setThemeMode(t){this.themeMode!==t&&(this.themeMode=t,window.localStorage.setItem(ze,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=wt(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 bn(){return typeof navigator>"u"?[]:navigator.languages?.length?navigator.languages:[navigator.language].filter(Boolean)}var ae=new Je;function o(e,t){return ae.t(e,t)}function d(e){return e.replace(/[&<>"']/g,t=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[t])}function be(e){if(!e)return"-";let t=Date.now()-e;return t<6e4?o("common.now"):t<36e5?Math.floor(t/6e4)+"m":t<864e5?Math.floor(t/36e5)+"h":Math.floor(t/864e5)+"d"}var Ge={chats:[],bots:[]};async function yn(){try{let e=await fetch("/api/groups");if(!e.ok)return;Ge=await e.json()}catch{}}function vn(e){return`status status-${d(e||"unknown")}`}function kn(e){return`<li class="overview-list-row">
|
|
14
14
|
<div>
|
|
15
|
-
<strong>${
|
|
16
|
-
<span>${
|
|
15
|
+
<strong>${d(e.title??e.sessionId)}</strong>
|
|
16
|
+
<span>${d(e.botName??"")} \xB7 ${d(e.cliId??"unknown")}</span>
|
|
17
17
|
</div>
|
|
18
|
-
<span class="${
|
|
19
|
-
</li>`}function
|
|
18
|
+
<span class="${vn(e.status)}">${d(e.status??"unknown")}</span>
|
|
19
|
+
</li>`}function $n(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>${d(e.name??e.id)}</strong>
|
|
22
|
+
<span>${d(e.botName??e.larkAppId??"")} \xB7 ${d(e.parsed?.display??"")}</span>
|
|
23
23
|
</div>
|
|
24
|
-
<span>${
|
|
25
|
-
</li>`}async function
|
|
24
|
+
<span>${d(t)}</span>
|
|
25
|
+
</li>`}async function ht(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">${o("app.subtitle")}</p>
|
|
29
|
+
<h1>${o("overview.title")}</h1>
|
|
30
|
+
<p>${o("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>${o("overview.recentSessions")}</h2>
|
|
39
|
+
<p>${o("sessions.subtitle")}</p>
|
|
40
40
|
</div>
|
|
41
|
-
<a class="btn-link" href="#/sessions">${
|
|
41
|
+
<a class="btn-link" href="#/sessions">${o("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>${o("overview.nextSchedules")}</h2>
|
|
49
|
+
<p>${o("schedules.subtitle")}</p>
|
|
50
50
|
</div>
|
|
51
|
-
<a class="btn-link" href="#/schedules">${
|
|
51
|
+
<a class="btn-link" href="#/schedules">${o("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 l=
|
|
61
|
-
<span class="filter-check-label">${
|
|
62
|
-
${
|
|
56
|
+
</section>`;let t=e.querySelector("#overview-metrics"),n=e.querySelector("#recent-sessions"),s=e.querySelector("#next-schedules");function a(){let r=[...N.sessions.values()],i=[...N.schedules.values()],m=r.filter(L=>L.status!=="closed"),k=r.filter(L=>L.status==="working"||L.status==="analyzing"||L.status==="starting"),$=i.filter(L=>L.enabled),p=Ge.bots?.length||new Set(r.map(L=>L.larkAppId).filter(Boolean)).size,g=[{label:o("overview.openSessions"),value:m.length,meta:`${r.length} ${o("overview.total")}`},{label:o("overview.workingSessions"),value:k.length,meta:`${m.length} ${o("overview.active")}`},{label:o("overview.onlineBots"),value:p,meta:o("overview.daemonRegistry")},{label:o("overview.schedules"),value:i.length,meta:`${$.length} ${o("overview.enabledSchedules")}`},{label:o("overview.groups"),value:Ge.chats?.length??0,meta:o("overview.chatMatrix")}];t.innerHTML=g.map(L=>`<article class="metric-card">
|
|
57
|
+
<span>${d(L.label)}</span>
|
|
58
|
+
<strong>${L.value}</strong>
|
|
59
|
+
<small>${d(L.meta)}</small>
|
|
60
|
+
</article>`).join("");let l=r.sort((L,b)=>Number(b.lastMessageAt??0)-Number(L.lastMessageAt??0)).slice(0,6);n.innerHTML=l.length?l.map(L=>kn({...L,title:L.title??`${be(L.lastMessageAt)} \xB7 ${L.sessionId}`})).join(""):`<li class="empty">${o("overview.noSessions")}</li>`;let I=i.filter(L=>L.nextRunAt).sort((L,b)=>Date.parse(L.nextRunAt)-Date.parse(b.nextRunAt)).slice(0,6);s.innerHTML=I.length?I.map($n).join(""):`<li class="empty">${o("overview.noSchedules")}</li>`}N.on(a),a(),yn().then(a)}function se(e,t){return`<th data-sort="${e}" data-label="${d(t)}">${d(t)}</th>`}var bt=["claude-code","codex","codex-app","cursor","gemini","opencode","mtr","hermes","mira","aiden","coco","unknown"];function Sn(){return`<div class="filter-check-group" role="group" aria-label="${o("sessions.cli")}">
|
|
61
|
+
<span class="filter-check-label">${o("sessions.cli")}</span>
|
|
62
|
+
${bt.map(e=>`
|
|
63
63
|
<label class="filter-check">
|
|
64
|
-
<input type="checkbox" name="cli" value="${
|
|
65
|
-
<span>${
|
|
64
|
+
<input type="checkbox" name="cli" value="${d(e)}" checked>
|
|
65
|
+
<span>${d(e)}</span>
|
|
66
66
|
</label>
|
|
67
67
|
`).join("")}
|
|
68
|
-
</div>`}function
|
|
68
|
+
</div>`}function In(){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">${o("nav.sessions")}</p>
|
|
72
|
+
<h1>${o("sessions.title")}</h1>
|
|
73
|
+
<p>${o("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="${o("sessions.search")}" />
|
|
78
78
|
<select name="status">
|
|
79
|
-
<option value="">${
|
|
79
|
+
<option value="">${o("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="">${o("sessions.adoptAny")}</option>
|
|
85
|
+
<option value="yes">${o("sessions.adoptYes")}</option>
|
|
86
|
+
<option value="no">${o("sessions.adoptNo")}</option>
|
|
87
87
|
</select>
|
|
88
|
-
<label class="filter-toggle"><input type="checkbox" name="active" checked> ${
|
|
89
|
-
${
|
|
88
|
+
<label class="filter-toggle"><input type="checkbox" name="active" checked> ${o("sessions.activeOnly")}</label>
|
|
89
|
+
${Sn()}
|
|
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">${o("sessions.closeSelected")}</button>
|
|
94
|
+
<button type="button" id="bulk-clear">${o("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="${o("sessions.activeOnly")}"></th>
|
|
99
|
+
${se("botName",o("sessions.bot"))}
|
|
100
|
+
${se("cliId",o("sessions.cli"))}
|
|
101
|
+
${se("status",o("sessions.status"))}
|
|
102
|
+
${se("title",o("sessions.titleCol"))}
|
|
103
|
+
${se("workingDir",o("sessions.workingDir"))}
|
|
104
|
+
${se("spawnedAt",o("sessions.created"))}
|
|
105
|
+
${se("lastMessageAt",o("sessions.last"))}
|
|
106
|
+
${se("adopt",o("sessions.adopt"))}
|
|
107
|
+
<th>${o("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" ${h} ${
|
|
114
|
-
<td>${
|
|
115
|
-
<td><span class="badge cli-${
|
|
116
|
-
<td><span class="status status-${
|
|
117
|
-
<td>${
|
|
118
|
-
<td title="${
|
|
119
|
-
<td>${
|
|
120
|
-
<td>${
|
|
121
|
-
<td>${
|
|
122
|
-
<td><button class="open" type="button">${
|
|
123
|
-
</tr>`}function
|
|
112
|
+
</section>`}function yt(e){e.innerHTML=In();let t=e.querySelector("#sessions-table tbody"),n=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"),k=e.querySelector("#bulk-clear"),$=e.querySelector("#sessions-table"),p=new Set,g="lastMessageAt",l="desc";function I(u){let w=u.status==="closed",h=p.has(u.sessionId)?"checked":"";return`<tr data-id="${d(u.sessionId)}">
|
|
113
|
+
<td><input type="checkbox" class="row-select" ${h} ${w?"disabled":""}></td>
|
|
114
|
+
<td>${d(u.botName??"")}</td>
|
|
115
|
+
<td><span class="badge cli-${d(u.cliId??"unknown")}">${d(u.cliId??"unknown")}</span></td>
|
|
116
|
+
<td><span class="status status-${d(u.status??"unknown")}">${d(u.status??"unknown")}</span></td>
|
|
117
|
+
<td>${d((u.title??"").slice(0,48))}</td>
|
|
118
|
+
<td title="${d(u.workingDir??"")}">${d((u.workingDir??"").slice(-34))}</td>
|
|
119
|
+
<td>${be(u.spawnedAt)}</td>
|
|
120
|
+
<td>${be(u.lastMessageAt)}</td>
|
|
121
|
+
<td>${u.adopt?'<span class="badge">adopt</span>':""}</td>
|
|
122
|
+
<td><button class="open" type="button">${o("sessions.details")}</button></td>
|
|
123
|
+
</tr>`}function L(){let u=new FormData(n),w=(u.get("q")??"").toLowerCase(),h=u.getAll("cli"),E=h.length>0&&h.length<bt.length,M=u.get("status"),A=u.get("adopt"),P=!!u.get("active"),O=[...N.sessions.values()].filter(H=>!E||h.includes(H.cliId??"unknown")).filter(H=>!M||H.status===M).filter(H=>!A||A==="yes"==!!H.adopt).filter(H=>!P||H.status!=="closed").filter(H=>!w||JSON.stringify(H).toLowerCase().includes(w));return O.sort(f),O}function b(u,w){return w==="spawnedAt"||w==="lastMessageAt"?Number(u[w]??0):w==="adopt"?!!u.adopt:String(u[w]??"").toLowerCase()}function f(u,w){let h=b(u,g),E=b(w,g),M=0;return typeof h=="number"&&typeof E=="number"?M=h-E:typeof h=="boolean"&&typeof E=="boolean"?M=Number(h)-Number(E):M=String(h).localeCompare(String(E)),M===0&&(M=Number(u.lastMessageAt??0)-Number(w.lastMessageAt??0)),l==="asc"?M:-M}function T(){$.querySelectorAll("th[data-sort]").forEach(u=>{let w=u.dataset.sort===g;u.classList.toggle("sorted",w),u.setAttribute("aria-sort",w?l==="asc"?"ascending":"descending":"none");let h=u.dataset.label??u.textContent?.trim()??"";u.textContent=w?`${h} ${l==="asc"?"\u25B2":"\u25BC"}`:h})}function S(u){r.hidden=p.size===0,i.textContent=o("sessions.selectedCount",{count:p.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 h=w.filter(E=>p.has(E.sessionId)).length;a.checked=h===w.length,a.indeterminate=h>0&&h<w.length}function y(){let u=L();for(let w of[...p]){let h=N.sessions.get(w);(!h||h.status==="closed")&&p.delete(w)}t.innerHTML=u.length?u.map(I).join(""):`<tr><td colspan="10" class="empty">${o("sessions.empty")}</td></tr>`,T(),S(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>${d(u.title??u.sessionId)}</h3>
|
|
126
|
+
<span class="status status-${d(u.status??"unknown")}">${d(u.status??"unknown")}</span>
|
|
127
|
+
<p><code>${d(u.sessionId)}</code> <button data-copy="${d(u.sessionId)}">${o("sessions.copy")}</button></p>
|
|
128
128
|
</header>
|
|
129
|
-
<p><b>${
|
|
130
|
-
<p><b>chatId:</b> <code>${
|
|
131
|
-
<p><b>rootMessageId:</b> <code>${
|
|
132
|
-
${
|
|
133
|
-
<p><b>${
|
|
129
|
+
<p><b>${o("sessions.bot")}:</b> ${d(u.botName??"-")} \xB7 <b>${o("sessions.cli")}:</b> ${d(u.cliId??"?")}</p>
|
|
130
|
+
<p><b>chatId:</b> <code>${d(u.chatId??"")}</code> <button data-copy="${d(u.chatId??"")}">${o("sessions.copy")}</button></p>
|
|
131
|
+
<p><b>rootMessageId:</b> <code>${d(u.rootMessageId??"")}</code> <button data-copy="${d(u.rootMessageId??"")}">${o("sessions.copy")}</button></p>
|
|
132
|
+
${u.threadId?`<p><b>threadId:</b> <code>${d(u.threadId)}</code></p>`:""}
|
|
133
|
+
<p><b>${o("sessions.workingDir")}:</b> ${d(u.workingDir??"-")}</p>
|
|
134
134
|
<div class="actions">
|
|
135
|
-
<button id="locate-btn" type="button">${
|
|
136
|
-
${
|
|
137
|
-
${
|
|
138
|
-
${
|
|
135
|
+
<button id="locate-btn" type="button">${o("sessions.locate")}</button>
|
|
136
|
+
${u.webPort?`<a class="btn-link primary" href="http://${d(location.hostname)}:${u.webPort}" target="_blank" rel="noopener">${o("sessions.openTerminal")}</a>`:""}
|
|
137
|
+
${w?`<button id="resume-btn" type="button" class="primary">${o("sessions.resume")}</button>`:""}
|
|
138
|
+
${w?"":`<button id="close-btn" type="button" class="contrast">${o("sessions.close")}</button>`}
|
|
139
139
|
</div>
|
|
140
|
-
<form method="dialog"><button>${
|
|
141
|
-
</article>`,
|
|
140
|
+
<form method="dialog"><button>${o("sessions.dismiss")}</button></form>
|
|
141
|
+
</article>`,s.querySelectorAll("[data-copy]").forEach(A=>{A.onclick=()=>{navigator.clipboard.writeText(A.dataset.copy??""),A.textContent=o("sessions.copied"),setTimeout(()=>{A.textContent=o("sessions.copy")},800)}});let h=s.querySelector("#locate-btn");h&&(h.onclick=async()=>{h.disabled=!0,h.textContent=o("sessions.locating");try{let A=await fetch(`/api/sessions/${encodeURIComponent(u.sessionId)}/locate`,{method:"POST"}),P=await A.json();if(P.ok){let O=30;h.textContent=o("sessions.cooldown",{seconds:O});let H=setInterval(()=>{O-=1,O<=0?(clearInterval(H),h.disabled=!1,h.textContent=o("sessions.locate")):h.textContent=o("sessions.cooldown",{seconds:O})},1e3)}else alert(`Locate failed: ${P.error??A.status}`),h.disabled=!1,h.textContent=o("sessions.locate")}catch(A){alert(`Locate error: ${A}`),h.disabled=!1,h.textContent=o("sessions.locate")}});let E=s.querySelector("#resume-btn");E&&(E.onclick=async()=>{E.disabled=!0;try{let A=await fetch(`/api/sessions/${encodeURIComponent(u.sessionId)}/resume`,{method:"POST"}),P=await A.json().catch(()=>({}));if(!A.ok||P.ok===!1){alert(`${o("sessions.resumeFailed")}: ${P?.error??A.status}`),E.disabled=!1;return}s.close()}catch(A){alert(`${o("sessions.resumeFailed")}: ${A}`),E.disabled=!1}});let M=s.querySelector("#close-btn");M&&(M.onclick=async()=>{if(confirm(o("sessions.closeConfirm"))){M.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 A=w.closest("tr[data-id]");if(!A)return;w.checked?p.add(A.dataset.id):p.delete(A.dataset.id),S(L());return}let h=w.closest("td");if(h&&h.querySelector(".row-select"))return;let E=w.closest("tr[data-id]");if(!E)return;let M=N.sessions.get(E.dataset.id);M&&x(M)}),a.addEventListener("change",()=>{let u=L().filter(w=>w.status!=="closed");for(let w of u)a.checked?p.add(w.sessionId):p.delete(w.sessionId);y()}),k.addEventListener("click",()=>{p.clear(),y()}),m.addEventListener("click",async()=>{let u=[...p];if(u.length===0||!confirm(o("sessions.closeBulkConfirm",{count:u.length})))return;m.disabled=!0,k.disabled=!0;let w=m.textContent,h=0,E=0,M=[...u];m.textContent=`0/${u.length}`;async function A(){for(;M.length;){let P=M.shift();try{let O=await fetch(`/api/sessions/${encodeURIComponent(P)}/close`,{method:"POST"}),H=await O.json().catch(()=>({}));(!O.ok||H?.ok===!1)&&(E+=1)}catch{E+=1}finally{h+=1,m.textContent=`${h}/${u.length}`}}}await Promise.all(Array.from({length:Math.min(6,u.length)},()=>A())),m.textContent=w,m.disabled=!1,k.disabled=!1,p.clear(),y(),E>0&&alert(`Failed: ${E}/${u.length}`)}),$.querySelectorAll("th[data-sort]").forEach(u=>{u.addEventListener("click",()=>{let w=u.dataset.sort;g===w?l=l==="asc"?"desc":"asc":(g=w,l=w==="spawnedAt"||w==="lastMessageAt"?"desc":"asc"),y()})}),n.addEventListener("input",y),N.on(y),y()}function Tn(){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">${o("nav.schedules")}</p>
|
|
145
|
+
<h1>${o("schedules.title")}</h1>
|
|
146
|
+
<p>${o("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="${o("schedules.search")}" />
|
|
151
151
|
<select name="kind">
|
|
152
|
-
<option value="">${
|
|
152
|
+
<option value="">${o("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"> ${o("schedules.enabledOnly")}</label>
|
|
158
158
|
</form>
|
|
159
159
|
<table>
|
|
160
160
|
<thead><tr>
|
|
161
|
-
<th>${
|
|
162
|
-
<th>${
|
|
161
|
+
<th>${o("schedules.name")}</th><th>${o("schedules.bot")}</th><th>${o("schedules.schedule")}</th><th>${o("schedules.next")}</th><th>${o("schedules.last")}</th>
|
|
162
|
+
<th>${o("schedules.repeat")}</th><th>${o("schedules.enabled")}</th><th>${o("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>${
|
|
172
|
-
<td>${
|
|
173
|
-
<td>${
|
|
166
|
+
</section>`}function vt(e){if(!e)return"\u2014";try{return new Date(e).toLocaleString()}catch{return e}}function kt(e){e.innerHTML=Tn();let t=e.querySelector("#schedules-tbody"),n=e.querySelector("#sched-filters");function s(){let r=new FormData(n),i=(r.get("q")??"").toLowerCase(),m=r.get("kind"),k=!!r.get("enabled");return[...N.schedules.values()].filter($=>!m||$.parsed?.kind===m).filter($=>!k||$.enabled).filter($=>!i||JSON.stringify($).toLowerCase().includes(i)).sort(($,p)=>{if($.enabled!==p.enabled)return $.enabled?-1:1;let g=$.nextRunAt?Date.parse($.nextRunAt):1/0,l=p.nextRunAt?Date.parse(p.nextRunAt):1/0;return g-l})}function a(){t.innerHTML=s().map(r=>`<tr data-id="${d(r.id)}">
|
|
167
|
+
<td>${d(r.name??r.id)}</td>
|
|
168
|
+
<td>${d(r.botName??r.larkAppId??"-")}</td>
|
|
169
|
+
<td><code>${d(r.parsed?.display??"?")}</code></td>
|
|
170
|
+
<td>${vt(r.nextRunAt)}</td>
|
|
171
|
+
<td>${vt(r.lastRunAt)} ${r.lastStatus==="error"?"\u26A0\uFE0F":""}</td>
|
|
172
|
+
<td>${r.repeat?`${r.repeat.completed}/${r.repeat.times??"\u221E"}`:"\u2014"}</td>
|
|
173
|
+
<td>${r.enabled?"\u2713":"\u2717"}</td>
|
|
174
174
|
<td class="actions-cell">
|
|
175
|
-
<button data-op="run" type="button">${
|
|
176
|
-
${
|
|
175
|
+
<button data-op="run" type="button">${o("schedules.runNow")}</button>
|
|
176
|
+
${r.enabled?`<button data-op="pause" type="button">${o("schedules.pause")}</button>`:`<button data-op="resume" type="button">${o("schedules.resume")}</button>`}
|
|
177
177
|
</td>
|
|
178
|
-
</tr>`).join("")||`<tr><td colspan="8" class="empty">${
|
|
178
|
+
</tr>`).join("")||`<tr><td colspan="8" class="empty">${o("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 k=m.dataset.id,$=i.dataset.op;i.disabled=!0;let p=i.textContent;i.textContent="...";try{let g=await fetch(`/api/schedules/${encodeURIComponent(k)}/${$}`,{method:"POST"}),l=await g.json().catch(()=>({}));(!g.ok||l.ok===!1)&&alert(`Failed: ${g.status} ${l?.error??""}`.trim())}catch(g){alert("Network error: "+g)}finally{i.disabled=!1,i.textContent=p}}),n.addEventListener("input",a),N.on(a),a()}var W={chats:[],bots:[]};function Ln(){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">${o("nav.groups")}</p>
|
|
182
|
+
<h1>${o("groups.title")}</h1>
|
|
183
|
+
<p>${o("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="${o("groups.search")}" />
|
|
188
|
+
<label><input type="checkbox" name="missing"> ${o("groups.missingOnly")}</label>
|
|
189
|
+
<button type="button" id="g-refresh">${o("groups.refresh")}</button>
|
|
190
|
+
<button type="button" id="g-create" class="primary">${o("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 fe(){W=await(await fetch("/api/groups")).json()}async function En(){return(await fetch("/api/groups")).json()}function Mn(e,t){if(t.size===0)return!0;let n=e?.memberBots??[];for(let s of t)if(!n.some(a=>a.larkAppId===s&&a.inChat))return!1;return!0}function $t(e,t){return e.filter(n=>!t||!t.has(n.larkAppId)).map(n=>`
|
|
198
198
|
<label class="checkbox-row">
|
|
199
|
-
<input type="checkbox" name="bot" value="${
|
|
200
|
-
${
|
|
199
|
+
<input type="checkbox" name="bot" value="${d(n.larkAppId)}">
|
|
200
|
+
${d(n.botName??n.larkAppId)} <small>(${d(n.larkAppId)})</small>
|
|
201
201
|
</label>
|
|
202
|
-
`).join("")}async function
|
|
202
|
+
`).join("")}async function St(e){e.innerHTML=Ln();let t=e.querySelector("#g-head"),n=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 fe(),p()}finally{a.disabled=!1}};let i=e.querySelector("#g-create");i.onclick=()=>m(),await fe();function m(){let l=W.bots;if(l.length===0){alert(o("groups.noBotsOnline"));return}r.innerHTML=`
|
|
203
203
|
<article>
|
|
204
|
-
<header><h3>${
|
|
205
|
-
<p>${
|
|
204
|
+
<header><h3>${o("groups.createTitle")}</h3></header>
|
|
205
|
+
<p>${o("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>${o("groups.name")}</span>
|
|
209
|
+
<input type="text" name="name" placeholder="${o("groups.namePlaceholder")}" maxlength="60">
|
|
210
210
|
</label>
|
|
211
211
|
<label class="form-row">
|
|
212
|
-
<span>${
|
|
212
|
+
<span>${o("groups.bindDir")}</span>
|
|
213
213
|
<input type="text" name="bindWorkingDir" placeholder="e.g. ~/projects/botmux">
|
|
214
|
-
<small>${
|
|
214
|
+
<small>${o("groups.bindDirHelp")}</small>
|
|
215
215
|
</label>
|
|
216
216
|
<fieldset>
|
|
217
|
-
<legend>${
|
|
218
|
-
${
|
|
217
|
+
<legend>${o("groups.botPicker")}</legend>
|
|
218
|
+
${$t(l)}
|
|
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">${o("groups.createSubmit")}</button>
|
|
222
|
+
<button type="button" id="g-create-cancel">${o("groups.cancel")}</button>
|
|
223
223
|
</div>
|
|
224
224
|
</form>
|
|
225
|
-
</article>`,
|
|
225
|
+
</article>`,r.showModal(),r.querySelector("#g-create-cancel").onclick=()=>r.close(),r.querySelector("#g-createform").onsubmit=async b=>{b.preventDefault();let f=new FormData(b.target),T=(f.get("name")??"").trim(),S=(f.get("bindWorkingDir")??"").trim(),y=f.getAll("bot");if(y.length===0){alert("Pick at least one bot.");return}let x=b.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:T||void 0,larkAppIds:y,bindWorkingDir:S||void 0})}),w=await u.json();if(w.ok&&w.chatId){k(w);let h=Array.isArray(w.invalidBotIds)?w.invalidBotIds:[],E=y.filter(A=>!h.includes(A)),M=new Set(E);typeof w.creator=="string"&&w.creator&&M.add(w.creator),I(w.chatId,T||w.chatId,E,w.creator),p(),L(w.chatId,M).catch(()=>{})}else alert(`Failed: ${w.error??u.status}`),r.close()}catch(u){alert("Network error: "+u),r.close()}};function I(b,f,T,S){let y=new Set(T);S&&y.add(S);let x=W.bots.map(w=>({larkAppId:w.larkAppId,botName:w.botName,inChat:y.has(w.larkAppId),oncallChat:null})),u={chatId:b,name:f,ownerId:S??null,memberBots:x};W.chats=[u,...W.chats.filter(w=>w.chatId!==b)]}async function L(b,f){let T=[600,1200,1200,1200,1200,1200];for(let S of T){await new Promise(u=>setTimeout(u,S));let y;try{y=await En()}catch{continue}let x=(y.chats??[]).find(u=>u.chatId===b);if(x&&Mn(x,f)){W=y,p();return}}}}function k(l){let I=String(l.chatId),L=`https://applink.feishu.cn/client/chat/open?openChatId=${encodeURIComponent(I)}`,b=l.invalidBotIds??[],f=l.invalidUserIds??[],T=l.autoInvitedOpenId,S=!!l.autoInviteRejected,y=l.ownerTransferredTo,x=l.transferError,u=l.notifyMessageId,w=l.notifyError,h=Array.isArray(l.oncallBindings)?l.oncallBindings:[],E=h.filter(H=>H?.ok).length,M=h.filter(H=>!H?.ok),A=h.length>0?M.length===0?`<p class="hint-ok">\u5DF2\u7ED1\u5B9A\u76EE\u5F55\uFF1A<code>${d(l.bindResolvedPath??"")}</code>\uFF08${E}/${h.length} bots\uFF09</p>`:`<p class="hint-warn">\u76EE\u5F55\u7ED1\u5B9A\u90E8\u5206\u5931\u8D25\uFF1A\u6210\u529F ${E}/${h.length}\u3002${M.map(H=>`<br><code>${d(H.larkAppId??"?")}</code>: ${d(H.error??"unknown")}`).join("")}</p>`:"",P;if(T){let H=y?"<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${d(x)}\uFF09\uFF0C\u4F60\u73B0\u5728\u662F\u6210\u5458\u4F46\u7FA4\u4E3B\u4ECD\u662F\u673A\u5668\u4EBA\u3002</small>`:"",q=u?`<br><small>\u673A\u5668\u4EBA\u5DF2\u5728\u7FA4\u91CC @ \u4E86\u4F60\uFF08\u6D88\u606F id <code>${d(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${d(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>`:"";P=`<p class="hint-ok">\u5DF2\u81EA\u52A8\u9080\u8BF7\u4F60\uFF08<code>${d(T)}</code>\uFF09\u4F5C\u4E3A\u6210\u5458\u3002${H}${q}</p>`}else S?P='<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>':P='<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 O=[b.length?`<li>\u65E0\u6548 bot id: <code>${b.map(d).join(", ")}</code></li>`:"",f.length?`<li>\u65E0\u6548\u7528\u6237 open_id: <code>${f.map(d).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
|
-
${
|
|
231
|
-
${
|
|
232
|
-
${
|
|
227
|
+
<header><h3>${o("groups.successTitle")}</h3></header>
|
|
228
|
+
<p><b>chatId:</b> <code>${d(I)}</code> <button type="button" data-copy="${d(I)}">${o("sessions.copy")}</button></p>
|
|
229
|
+
<p><b>\u521B\u5EFA\u8005:</b> <code>${d(l.creator??"?")}</code></p>
|
|
230
|
+
${P}
|
|
231
|
+
${A}
|
|
232
|
+
${O?`<ul>${O}</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="${L}" target="_blank" rel="noopener">${o("groups.openGroup")}</a>
|
|
235
|
+
<button type="button" id="g-create-close">${o("sessions.dismiss")}</button>
|
|
236
236
|
</div>
|
|
237
|
-
</article>`,
|
|
238
|
-
<th>${
|
|
239
|
-
${
|
|
240
|
-
<th>${
|
|
241
|
-
</tr>`}function
|
|
237
|
+
</article>`,r.querySelectorAll("[data-copy]").forEach(H=>{H.onclick=()=>{navigator.clipboard.writeText(H.dataset.copy??""),H.textContent=o("sessions.copied"),setTimeout(()=>{H.textContent=o("sessions.copy")},800)}}),r.querySelector("#g-create-close").onclick=()=>r.close()}function $(){t.innerHTML=`<tr>
|
|
238
|
+
<th>${o("groups.chat")}</th>
|
|
239
|
+
${W.bots.map(l=>`<th>${d(l.botName??l.larkAppId)}</th>`).join("")}
|
|
240
|
+
<th>${o("groups.actions")}</th>
|
|
241
|
+
</tr>`}function p(){$();let l=new FormData(s),I=(l.get("q")??"").toLowerCase(),L=!!l.get("missing"),b=W.chats.filter(f=>!I||(f.name??"").toLowerCase().includes(I)||f.chatId.toLowerCase().includes(I)||(f.ownerId??"").toLowerCase().includes(I)).filter(f=>!L||f.memberBots.some(T=>!T.inChat));if(b.length===0){n.innerHTML=`<tr><td colspan="${W.bots.length+2}" class="empty">${o("groups.empty")}</td></tr>`;return}n.innerHTML=b.map(f=>`<tr data-chat="${d(f.chatId)}">
|
|
242
242
|
<td>
|
|
243
|
-
<strong>${
|
|
244
|
-
<small><code>${
|
|
243
|
+
<strong>${d(f.name??f.chatId)}</strong><br>
|
|
244
|
+
<small><code>${d(f.chatId)}</code></small>
|
|
245
245
|
</td>
|
|
246
|
-
${
|
|
246
|
+
${W.bots.map(T=>{let S=f.memberBots.find(u=>u.larkAppId===T.larkAppId),y=S?S.error?"!":S.inChat?"\u2713":"\u2717":"?";return`<td class="${S?S.error?"cell-error":S.inChat?"cell-in":"cell-out":"cell-unknown"}" title="${d(S?.error??"")}">${y}</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">${o("groups.addBots")}</button>
|
|
249
|
+
<button class="manage-chat" type="button">${o("groups.manage")}</button>
|
|
250
250
|
</td>
|
|
251
|
-
</tr>`).join("")}
|
|
251
|
+
</tr>`).join("")}p(),n.addEventListener("click",async l=>{let I=l.target.closest("button.add-bots");if(!I)return;let b=I.closest("tr[data-chat]").dataset.chat,f=W.chats.find(y=>y.chatId===b);if(!f)return;let T=new Set(f.memberBots.filter(y=>y.inChat).map(y=>y.larkAppId));if(!W.bots.filter(y=>!T.has(y.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>${o("groups.addBots")} \xB7 ${d(f.name??f.chatId)}</h3></header>
|
|
254
|
+
<p>${o("groups.createHelp")}</p>
|
|
255
255
|
<form id="g-addform">
|
|
256
|
-
${
|
|
256
|
+
${$t(W.bots,T)}
|
|
257
257
|
<div class="actions">
|
|
258
|
-
<button type="submit" class="primary">${
|
|
259
|
-
<button type="button" id="g-cancel">${
|
|
258
|
+
<button type="submit" class="primary">${o("groups.addBots")}</button>
|
|
259
|
+
<button type="button" id="g-cancel">${o("groups.cancel")}</button>
|
|
260
260
|
</div>
|
|
261
261
|
</form>
|
|
262
|
-
</article>`,
|
|
263
|
-
`);alert(
|
|
262
|
+
</article>`,r.showModal(),r.querySelector("#g-cancel").onclick=()=>r.close(),r.querySelector("#g-addform").onsubmit=async y=>{y.preventDefault();let u=new FormData(y.target).getAll("bot");if(u.length===0){alert("Pick at least one bot.");return}try{let h=await(await fetch(`/api/groups/${encodeURIComponent(b)}/add-bots`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({larkAppIds:u})})).json();if(h.error==="no_proxy_bot")alert("No bot is currently in this chat \u2014 add one manually in Feishu first, then retry.");else if(h.result){let E=h.result.map(M=>`${M.id}: ${M.ok?"OK":`failed (${M.error??"unknown"})`}`).join(`
|
|
263
|
+
`);alert(E),await fe(),p()}else alert(`Unexpected response: ${JSON.stringify(h)}`)}catch(w){alert("Network error: "+w)}finally{r.close()}}}),n.addEventListener("click",async l=>{let I=l.target.closest("button.manage-chat");if(!I)return;let b=I.closest("tr[data-chat]").dataset.chat,f=W.chats.find(T=>T.chatId===b);f&&g(f)});function g(l){let I=l.memberBots.filter(b=>b.inChat),L=typeof l.ownerId=="string"?l.ownerId:"";r.innerHTML=`
|
|
264
264
|
<article>
|
|
265
|
-
<header><h3>${
|
|
266
|
-
<p><b>chatId:</b> <code>${
|
|
267
|
-
<p><b>${
|
|
265
|
+
<header><h3>${o("groups.manageTitle",{name:l.name??l.chatId})}</h3></header>
|
|
266
|
+
<p><b>chatId:</b> <code>${d(l.chatId)}</code></p>
|
|
267
|
+
<p><b>${o("groups.owner")}:</b> <code>${d(l.ownerId??o("common.unknown"))}</code></p>
|
|
268
268
|
|
|
269
269
|
<fieldset>
|
|
270
|
-
<legend>${
|
|
271
|
-
<p><small>${
|
|
272
|
-
${
|
|
273
|
-
<div class="oncall-row" data-bot="${
|
|
270
|
+
<legend>${o("groups.oncall")}</legend>
|
|
271
|
+
<p><small>${o("groups.oncallHelp")}</small></p>
|
|
272
|
+
${I.length===0?'<p class="empty">\u6CA1\u6709\u673A\u5668\u4EBA\u5728\u7FA4\u91CC</p>':I.map(b=>{let f=!!b.oncallChat,T=b.oncallChat?.workingDir??"";return`
|
|
273
|
+
<div class="oncall-row" data-bot="${d(b.larkAppId)}">
|
|
274
274
|
<label class="checkbox-row">
|
|
275
|
-
<input type="checkbox" data-action="toggle" ${
|
|
276
|
-
<strong>${
|
|
277
|
-
<small>(${
|
|
275
|
+
<input type="checkbox" data-action="toggle" ${f?"checked":""}>
|
|
276
|
+
<strong>${d(b.botName??b.larkAppId)}</strong>
|
|
277
|
+
<small>(${d(b.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="${d(T)}" ${f?"":"disabled"}>
|
|
282
|
+
<button type="button" data-action="save">${o("groups.save")}</button>
|
|
283
283
|
<span class="oncall-status" data-status></span>
|
|
284
284
|
</div>
|
|
285
285
|
</div>
|
|
@@ -287,130 +287,135 @@ effects={effects}, activities={activities}, waits={waits}, cancels={cancels}`,"w
|
|
|
287
287
|
</fieldset>
|
|
288
288
|
|
|
289
289
|
<fieldset>
|
|
290
|
-
<legend>${
|
|
291
|
-
${
|
|
290
|
+
<legend>${o("groups.leaveTitle")}</legend>
|
|
291
|
+
${I.length===0?'<p class="empty">\u6CA1\u6709\u673A\u5668\u4EBA\u5728\u7FA4\u91CC</p>':I.map(b=>`
|
|
292
292
|
<label class="checkbox-row">
|
|
293
|
-
<input type="checkbox" name="leave-bot" value="${
|
|
294
|
-
${
|
|
295
|
-
<small>${b.larkAppId===
|
|
293
|
+
<input type="checkbox" name="leave-bot" value="${d(b.larkAppId)}">
|
|
294
|
+
${d(b.botName??b.larkAppId)}
|
|
295
|
+
<small>${b.larkAppId===L?"\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":""}>${o("groups.leaveSelected")}</button>
|
|
302
|
+
<button id="g-disband-btn" type="button" class="contrast" ${I.length===0?"disabled":""}>${o("groups.disband")}</button>
|
|
303
303
|
</div>
|
|
304
|
-
<p class="hint-warn"><small>${
|
|
305
|
-
<form method="dialog"><button>${
|
|
306
|
-
</article>`,
|
|
307
|
-
`);alert(
|
|
308
|
-
\u5173\u95ED\u4E86 ${
|
|
309
|
-
\u5173\u95ED\u4E86 ${
|
|
310
|
-
${
|
|
304
|
+
<p class="hint-warn"><small>${o("groups.dangerHint")}</small></p>
|
|
305
|
+
<form method="dialog"><button>${o("sessions.dismiss")}</button></form>
|
|
306
|
+
</article>`,r.showModal(),r.querySelectorAll(".oncall-row").forEach(b=>{let f=b.dataset.bot,T=b.querySelector("input[data-action=toggle]"),S=b.querySelector("input[data-input=workingDir]"),y=b.querySelector("button[data-action=save]"),x=b.querySelector("[data-status]");T.addEventListener("change",()=>{S.disabled=!T.checked,T.checked&&S.focus()}),y.addEventListener("click",async()=>{x.textContent="",x.className="oncall-status";let u=T.checked,w=S.value.trim();if(u&&!w){x.textContent=o("groups.needWorkingDir"),x.classList.add("hint-warn-inline");return}y.disabled=!0;try{let h=`/api/groups/${encodeURIComponent(l.chatId)}/oncall/${encodeURIComponent(f)}`,E=u?await fetch(h,{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify({workingDir:w})}):await fetch(h,{method:"DELETE"}),M=await E.json().catch(()=>({}));if(E.ok&&M.ok){x.textContent=u?`\u2713 \u5DF2\u7ED1\u5B9A \u2192 ${M.resolvedPath??w}`:"\u2713 \u5DF2\u89E3\u7ED1",x.classList.add("hint-ok");try{await fe(),p()}catch{}}else x.textContent=`\u2717 ${M.error??E.status}`,x.classList.add("hint-warn-inline")}catch(h){x.textContent=`\u2717 ${h?.message??h}`,x.classList.add("hint-warn-inline")}finally{y.disabled=!1}})}),r.querySelector("#g-leave-btn").onclick=async()=>{let b=[...r.querySelectorAll("input[name=leave-bot]:checked")].map(f=>f.value);if(b.length===0){alert("\u81F3\u5C11\u9009\u4E00\u4E2A\u673A\u5668\u4EBA");return}if(confirm(`\u786E\u5B9A\u8BA9 ${b.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 T=await(await fetch(`/api/groups/${encodeURIComponent(l.chatId)}/leave`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({larkAppIds:b})})).json(),S=(T.result??[]).map(y=>{if(!y.ok)return`${y.larkAppId}: \u5931\u8D25 (${y.error??"unknown"})`;let x=y.closedSessions??[],u=x.filter(E=>!E.ok).length,w=x.length-u,h=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`${y.larkAppId}: OK${h}`}).join(`
|
|
307
|
+
`);alert(S||`Unexpected: ${JSON.stringify(T)}`),await fe(),p()}catch(f){alert("Network error: "+f)}finally{r.close()}},r.querySelector("#g-disband-btn").onclick=async()=>{if(I.length===0||!confirm(`\u786E\u5B9A\u89E3\u6563\u7FA4\u804A\u300C${l.name??l.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 b=[...I].sort((T,S)=>(S.larkAppId===L?1:0)-(T.larkAppId===L?1:0)),f=[];for(let T of b)try{let S=await fetch(`/api/groups/${encodeURIComponent(l.chatId)}/disband`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({larkAppId:T.larkAppId})}),y=await S.json();if(y.ok){let x=y.closedSessions??[],u=x.filter(E=>!E.ok).length,w=x.length-u,h=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 ${T.botName??T.larkAppId} \u6267\u884C\uFF09${h}`),await fe(),p(),r.close();return}f.push(`${T.botName??T.larkAppId}: ${y.error??S.status}`)}catch(S){f.push(`${T.botName??T.larkAppId}: ${S}`)}alert(`\u6240\u6709\u5728\u7FA4\u673A\u5668\u4EBA\u5747\u65E0\u6CD5\u89E3\u6563\uFF1A
|
|
310
|
+
${f.join(`
|
|
311
311
|
`)}
|
|
312
312
|
|
|
313
|
-
\u5EFA\u8BAE\u6539\u7528\u300C\u9000\u51FA\u7FA4\u804A\u300D\u3002`)}}
|
|
313
|
+
\u5EFA\u8BAE\u6539\u7528\u300C\u9000\u51FA\u7FA4\u804A\u300D\u3002`)}}s.addEventListener("input",p)}var re={bots:[]},me=null;function Cn(){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">${o("nav.botDefaults")}</p>
|
|
317
|
+
<h1>${o("botDefaults.title")}</h1>
|
|
318
|
+
<p>${o("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="${o("botDefaults.search")}" />
|
|
323
|
+
<button type="button" id="bd-refresh">${o("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 g=
|
|
326
|
+
</section>`}async function It(){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}`,re={bots:[]};return}if(!t||!Array.isArray(t.bots)){me="unexpected response shape (no `bots` array)",re={bots:[]};return}me=null,re=t}catch(e){me=e?.message??String(e),re={bots:[]}}}function Tt(e){if(!e)return"\u2014";let t=new Date(e);return Number.isNaN(t.getTime())?"\u2014":t.toLocaleString()}async function Lt(e){e.innerHTML=Cn();let t=e.querySelector("#bd-list"),n=e.querySelector("#bd-filters"),s=e.querySelector("#bd-refresh");s.onclick=async()=>{s.disabled=!0;try{await It(),a()}finally{s.disabled=!1}},await It();function a(){let g=(new FormData(n).get("q")??"").toLowerCase(),l=re.bots.filter(I=>!g||(I.botName??"").toLowerCase().includes(g)||(I.larkAppId??"").toLowerCase().includes(g));if(me){t.innerHTML=`<p class="hint-warn">\u65E0\u6CD5\u52A0\u8F7D bot \u5217\u8868\uFF1A${d(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(l.length===0){t.innerHTML=`<p class="empty">${o("botDefaults.empty")}</p>`;return}t.innerHTML=l.map(r).join(""),$()}function r(p){if(p.error)return`<article class="bd-card" data-appid="${d(p.larkAppId)}">
|
|
327
|
+
<header><strong>${d(p.botName??p.larkAppId)}</strong>
|
|
328
|
+
<small>${d(p.larkAppId)}</small></header>
|
|
329
|
+
<p class="hint-warn-inline">\u67E5\u8BE2\u5931\u8D25\uFF1A${d(p.error)}</p>
|
|
330
|
+
</article>`;let g=p.defaultOncall??{enabled:!1,workingDir:"",since:0},l=!!g.enabled;return`<article class="bd-card" data-appid="${d(p.larkAppId)}">
|
|
331
331
|
<header>
|
|
332
|
-
<strong>${
|
|
333
|
-
<small>${
|
|
332
|
+
<strong>${d(p.botName??p.larkAppId)}</strong>
|
|
333
|
+
<small>${d(p.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">${o("botDefaults.sectionOncall")}</h3>
|
|
338
338
|
<label class="checkbox-row">
|
|
339
339
|
<input type="checkbox" data-action="toggle" ${l?"checked":""}>
|
|
340
|
-
<strong>${
|
|
341
|
-
<small>${
|
|
340
|
+
<strong>${o("botDefaults.defaultOncall")}</strong>
|
|
341
|
+
<small>${o("botDefaults.defaultOncallHelp")}</small>
|
|
342
342
|
</label>
|
|
343
343
|
<div class="bd-row">
|
|
344
344
|
<label>
|
|
345
|
-
<span>${
|
|
345
|
+
<span>${o("botDefaults.workingDir")}</span>
|
|
346
346
|
<input type="text" data-input="workingDir" placeholder="e.g. /root/iserver/botmux"
|
|
347
|
-
value="${
|
|
347
|
+
value="${d(g.workingDir??"")}" ${l?"":"disabled"}>
|
|
348
348
|
</label>
|
|
349
349
|
</div>
|
|
350
|
-
<p class="bd-section-note">${
|
|
350
|
+
<p class="bd-section-note">${o("botDefaults.warning")}</p>
|
|
351
351
|
<div class="bd-meta">
|
|
352
|
-
<small>${
|
|
353
|
-
<small>${
|
|
352
|
+
<small>${o("botDefaults.lastEnabled")}: ${d(Tt(g.since??0))}</small>
|
|
353
|
+
<small>${o("botDefaults.autobound",{count:p.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">${o("botDefaults.save")}</button>
|
|
357
357
|
<span class="oncall-status" data-status></span>
|
|
358
358
|
</div>
|
|
359
359
|
</section>
|
|
360
|
-
${
|
|
361
|
-
${
|
|
360
|
+
${m(p)}
|
|
361
|
+
${k(p)}
|
|
362
362
|
</div>
|
|
363
|
-
</article>`}function
|
|
364
|
-
<h3 class="bd-section-title">${
|
|
363
|
+
</article>`}function i(p){return p==null?o("botDefaults.brandStateDefault"):p.trim()===""?o("botDefaults.brandStateOff"):o("botDefaults.brandStateCustom")}function m(p){let g=p.brandLabel??null;return`<section class="bd-section">
|
|
364
|
+
<h3 class="bd-section-title">${o("botDefaults.sectionBrand")}</h3>
|
|
365
365
|
<div class="bd-row bd-brand">
|
|
366
366
|
<label>
|
|
367
|
-
<span>${
|
|
367
|
+
<span>${o("botDefaults.brandLabel")}</span>
|
|
368
368
|
<input type="text" data-input="brandLabel"
|
|
369
|
-
placeholder="${
|
|
370
|
-
value="${
|
|
369
|
+
placeholder="${d(o("botDefaults.brandLabelPlaceholder"))}"
|
|
370
|
+
value="${d(g??"")}">
|
|
371
371
|
</label>
|
|
372
|
-
<small data-brand-state>${
|
|
373
|
-
<small>${
|
|
372
|
+
<small data-brand-state>${d(i(g))}</small>
|
|
373
|
+
<small>${o("botDefaults.brandLabelHelp")}</small>
|
|
374
374
|
<div class="actions">
|
|
375
|
-
<button type="button" data-action="save-brand">${
|
|
376
|
-
<button type="button" data-action="reset-brand">${
|
|
375
|
+
<button type="button" data-action="save-brand">${o("botDefaults.brandSave")}</button>
|
|
376
|
+
<button type="button" data-action="reset-brand">${o("botDefaults.brandReset")}</button>
|
|
377
377
|
<span class="oncall-status" data-brand-status></span>
|
|
378
378
|
</div>
|
|
379
379
|
</div>
|
|
380
|
-
</section>`}function
|
|
381
|
-
<h3 class="bd-section-title">${
|
|
380
|
+
</section>`}function k(p){let g=p.disableStreamingCard===!0,l=p.writableTerminalLinkInCard===!0,I=p.privateCard===!0;return`<section class="bd-section">
|
|
381
|
+
<h3 class="bd-section-title">${o("botDefaults.sectionCard")}</h3>
|
|
382
382
|
<label class="checkbox-row">
|
|
383
383
|
<input type="checkbox" data-action="toggle-disable-streaming" ${g?"checked":""}>
|
|
384
|
-
<strong>${
|
|
385
|
-
<small>${
|
|
384
|
+
<strong>${o("botDefaults.disableStreaming")}</strong>
|
|
385
|
+
<small>${o("botDefaults.disableStreamingHelp")}</small>
|
|
386
386
|
</label>
|
|
387
387
|
<label class="checkbox-row">
|
|
388
388
|
<input type="checkbox" data-action="toggle-writable-link" ${l?"checked":""} ${g?"disabled":""}>
|
|
389
|
-
<strong>${
|
|
390
|
-
<small>${
|
|
389
|
+
<strong>${o("botDefaults.writableLink")}</strong>
|
|
390
|
+
<small>${o("botDefaults.writableLinkHelp")}</small>
|
|
391
|
+
</label>
|
|
392
|
+
<label class="checkbox-row">
|
|
393
|
+
<input type="checkbox" data-action="toggle-private-card" ${I?"checked":""}>
|
|
394
|
+
<strong>${o("botDefaults.privateCard")}</strong>
|
|
395
|
+
<small>${o("botDefaults.privateCardHelp")}</small>
|
|
391
396
|
</label>
|
|
392
397
|
<div class="actions">
|
|
393
|
-
<small data-card-pref-moot class="hint-warn-inline" ${g?"":"hidden"}>${
|
|
398
|
+
<small data-card-pref-moot class="hint-warn-inline" ${g?"":"hidden"}>${o("botDefaults.writableLinkMoot")}</small>
|
|
394
399
|
<span class="oncall-status" data-card-pref-status></span>
|
|
395
400
|
</div>
|
|
396
|
-
</section>`}function
|
|
401
|
+
</section>`}function $(){t.querySelectorAll(".bd-card").forEach(p=>{let g=p.dataset.appid,l=p.querySelector("input[data-action=toggle]"),I=p.querySelector("input[data-input=workingDir]"),L=p.querySelector("button[data-action=save]"),b=p.querySelector("[data-status]");if(!l||!I||!L||!b)return;l.addEventListener("change",()=>{I.disabled=!l.checked,l.checked&&I.focus()}),L.addEventListener("click",async()=>{b.textContent="",b.className="oncall-status";let O=l.checked,H=I.value.trim();if(O&&!H){b.textContent=o("botDefaults.required"),b.classList.add("hint-warn-inline");return}L.disabled=!0;try{let q=await fetch(`/api/bots/${encodeURIComponent(g)}/default-oncall`,{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify({enabled:O,workingDir:H})}),j=await q.json().catch(()=>({}));if(q.ok&&j.ok){let F=j.resolvedPath?` \u2192 ${j.resolvedPath}`:"";b.textContent=O?`\u2713 \u5DF2\u5F00\u542F${F}\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",b.classList.add("hint-ok");let z=re.bots.find(te=>te.larkAppId===g);z&&j.defaultOncall&&(z.defaultOncall=j.defaultOncall);let ee=p.querySelector(".bd-meta small:first-child");ee&&j.defaultOncall?.since!=null&&(ee.textContent=`\u4E0A\u6B21\u542F\u7528\u65F6\u95F4\uFF1A${Tt(j.defaultOncall.since)}`)}else b.textContent=`\u2717 ${j.error??q.status}`,b.classList.add("hint-warn-inline")}catch(q){b.textContent=`\u2717 ${q?.message??q}`,b.classList.add("hint-warn-inline")}finally{L.disabled=!1}});let f=p.querySelector("input[data-input=brandLabel]"),T=p.querySelector("button[data-action=save-brand]"),S=p.querySelector("button[data-action=reset-brand]"),y=p.querySelector("[data-brand-status]"),x=p.querySelector("[data-brand-state]");async function u(O,H){if(y){y.textContent="",y.className="oncall-status",H.disabled=!0;try{let q=await fetch(`/api/bots/${encodeURIComponent(g)}/brand-label`,{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify({brandLabel:O})}),j=await q.json().catch(()=>({}));if(q.ok&&j.ok){let F=j.brandLabel??null;y.textContent="\u2713",y.classList.add("hint-ok"),f&&(f.value=F??""),x&&(x.textContent=i(F));let z=re.bots.find(ee=>ee.larkAppId===g);z&&(z.brandLabel=F)}else y.textContent=`\u2717 ${j.error??q.status}`,y.classList.add("hint-warn-inline")}catch(q){y.textContent=`\u2717 ${q?.message??q}`,y.classList.add("hint-warn-inline")}finally{H.disabled=!1}}}f&&T&&T.addEventListener("click",()=>u(f.value,T)),S&&S.addEventListener("click",()=>u(null,S));let w=p.querySelector("input[data-action=toggle-disable-streaming]"),h=p.querySelector("input[data-action=toggle-writable-link]"),E=p.querySelector("input[data-action=toggle-private-card]"),M=p.querySelector("[data-card-pref-status]"),A=p.querySelector("[data-card-pref-moot]");async function P(O,H){if(M){M.textContent="",M.className="oncall-status",H.disabled=!0;try{let q=await fetch(`/api/bots/${encodeURIComponent(g)}/card-prefs`,{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify(O)}),j=await q.json().catch(()=>({}));if(q.ok&&j.ok){M.textContent=`\u2713 ${o("botDefaults.cardPrefSaved")}`,M.classList.add("hint-ok");let F=re.bots.find(z=>z.larkAppId===g);F&&(F.disableStreamingCard=j.disableStreamingCard,F.writableTerminalLinkInCard=j.writableTerminalLinkInCard,F.privateCard=j.privateCard)}else M.textContent=`\u2717 ${j.error??q.status}`,M.classList.add("hint-warn-inline")}catch(q){M.textContent=`\u2717 ${q?.message??q}`,M.classList.add("hint-warn-inline")}finally{H===h?H.disabled=!!w?.checked:H.disabled=!1}}}w&&w.addEventListener("change",()=>{let O=w.checked;h&&(h.disabled=O),A&&(A.hidden=!O),P({disableStreamingCard:O},w)}),h&&h.addEventListener("change",()=>{P({writableTerminalLinkInCard:h.checked},h)}),E&&E.addEventListener("change",()=>{P({privateCard:E.checked},E)})})}a(),n.addEventListener("input",a)}var Ke=4096,xe=[],Q=null,X=null,J="",we=new Set;function xn(){return`<section class="page roles-page">
|
|
397
402
|
<div class="page-heading">
|
|
398
403
|
<div>
|
|
399
|
-
<p class="eyebrow">${
|
|
400
|
-
<h1>${
|
|
401
|
-
<p>${
|
|
404
|
+
<p class="eyebrow">${o("nav.roles")}</p>
|
|
405
|
+
<h1>${o("roles.title")}</h1>
|
|
406
|
+
<p>${o("roles.subtitle")}</p>
|
|
402
407
|
</div>
|
|
403
408
|
</div>
|
|
404
409
|
<div class="roles-layout">
|
|
405
410
|
<div class="roles-tree-panel">
|
|
406
411
|
<div class="roles-tree-header">
|
|
407
|
-
<input type="search" id="roles-search" placeholder="${
|
|
408
|
-
<button type="button" id="roles-refresh">${
|
|
412
|
+
<input type="search" id="roles-search" placeholder="${o("roles.search")}" />
|
|
413
|
+
<button type="button" id="roles-refresh">${o("roles.refresh")}</button>
|
|
409
414
|
</div>
|
|
410
415
|
<div id="roles-tree" class="roles-tree"></div>
|
|
411
416
|
</div>
|
|
412
417
|
<div class="roles-editor-panel">
|
|
413
|
-
<div id="roles-editor-empty" class="roles-editor-empty">${
|
|
418
|
+
<div id="roles-editor-empty" class="roles-editor-empty">${o("roles.selectHint")}</div>
|
|
414
419
|
<div id="roles-editor-form" class="roles-editor-form" style="display:none">
|
|
415
420
|
<div class="roles-editor-breadcrumb">
|
|
416
421
|
<span id="roles-editor-group-name"></span>
|
|
@@ -420,82 +425,205 @@ ${u.join(`
|
|
|
420
425
|
<div class="roles-editor-meta">
|
|
421
426
|
<span id="roles-editor-chat-id" class="roles-editor-meta-line"></span>
|
|
422
427
|
</div>
|
|
423
|
-
<textarea id="roles-editor-textarea" placeholder="${
|
|
428
|
+
<textarea id="roles-editor-textarea" placeholder="${o("roles.editorPlaceholder")}" rows="14"></textarea>
|
|
424
429
|
<div class="roles-editor-footer">
|
|
425
430
|
<span id="roles-editor-bytecount" class="roles-bytecount"></span>
|
|
426
431
|
<div class="roles-editor-actions">
|
|
427
|
-
<button type="button" id="roles-delete" class="danger">${
|
|
428
|
-
<button type="button" id="roles-save" class="primary">${
|
|
432
|
+
<button type="button" id="roles-delete" class="danger">${o("roles.delete")}</button>
|
|
433
|
+
<button type="button" id="roles-save" class="primary">${o("roles.save")}</button>
|
|
429
434
|
</div>
|
|
430
435
|
</div>
|
|
431
436
|
<div id="roles-preview" class="roles-preview"></div>
|
|
432
437
|
</div>
|
|
433
438
|
</div>
|
|
434
439
|
</div>
|
|
435
|
-
</section>`}async function
|
|
436
|
-
<div class="roles-bot-row ${
|
|
437
|
-
data-group-id="${
|
|
438
|
-
data-bot-id="${
|
|
440
|
+
</section>`}async function Ce(){xe=((await(await fetch("/api/groups")).json()).chats??[]).map(n=>({chatId:n.chatId,name:n.name??n.chatId,memberBots:(n.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 Mt(e,t){return(await fetch(`/api/roles/${encodeURIComponent(e)}/${encodeURIComponent(t)}`)).json()}async function Hn(e,t,n){return(await fetch(`/api/roles/${encodeURIComponent(e)}/${encodeURIComponent(t)}`,{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify({content:n})})).ok}async function An(e,t){return(await fetch(`/api/roles/${encodeURIComponent(e)}/${encodeURIComponent(t)}`,{method:"DELETE"})).ok}function Ct(e){return e.memberBots.filter(t=>t.inChat&&t.hasRole).length}function Rn(e){return e.memberBots.filter(t=>t.inChat).length}function le(e=""){let t=document.getElementById("roles-tree");if(!t)return;let n=e.toLowerCase(),s=xe.filter(a=>{if(!n)return!0;let r=a.chatId.toLowerCase().includes(n)||(a.name??"").toLowerCase().includes(n),i=a.memberBots.some(m=>m.larkAppId.toLowerCase().includes(n)||(m.botName??"").toLowerCase().includes(n));return r||i});if(s.length===0){t.innerHTML=`<div class="roles-empty">${o("roles.noChats")}</div>`;return}t.innerHTML=s.map(a=>{let r=we.has(a.chatId),i=a.memberBots.filter(g=>g.inChat),m=r?"\u25BE":"\u25B8",k=Ct(a),$=Rn(a),p=r?i.map(g=>`
|
|
441
|
+
<div class="roles-bot-row ${Q===a.chatId&&X===g.larkAppId?"selected":""}"
|
|
442
|
+
data-group-id="${d(a.chatId)}"
|
|
443
|
+
data-bot-id="${d(g.larkAppId)}">
|
|
439
444
|
<span class="roles-bot-indent"></span>
|
|
440
445
|
<span class="roles-bot-icon">\u{1F916}</span>
|
|
441
446
|
<div class="roles-bot-info">
|
|
442
|
-
<div class="roles-bot-name">${
|
|
443
|
-
<div class="roles-bot-id">${
|
|
447
|
+
<div class="roles-bot-name">${d(g.botName)}</div>
|
|
448
|
+
<div class="roles-bot-id">${d(g.larkAppId)}</div>
|
|
444
449
|
</div>
|
|
445
450
|
<span class="roles-badge ${g.hasRole?"has-role":"no-role"}">
|
|
446
|
-
${g.hasRole?
|
|
451
|
+
${g.hasRole?o("roles.configured"):o("roles.unconfigured")}
|
|
447
452
|
</span>
|
|
448
453
|
</div>`).join(""):"";return`
|
|
449
454
|
<div class="roles-group-section">
|
|
450
|
-
<div class="roles-group-row ${
|
|
451
|
-
data-group-id="${
|
|
452
|
-
<span class="roles-group-arrow">${
|
|
455
|
+
<div class="roles-group-row ${r?"expanded":""} ${Q===a.chatId&&!X?"selected":""}"
|
|
456
|
+
data-group-id="${d(a.chatId)}">
|
|
457
|
+
<span class="roles-group-arrow">${m}</span>
|
|
453
458
|
<span class="roles-group-icon">\u{1F4C1}</span>
|
|
454
459
|
<div class="roles-group-info">
|
|
455
|
-
<div class="roles-group-name">${
|
|
460
|
+
<div class="roles-group-name">${d(a.name??a.chatId)}</div>
|
|
456
461
|
<div class="roles-group-meta">
|
|
457
|
-
${
|
|
462
|
+
${k}/${$} ${o("roles.botsWithRoles")}
|
|
458
463
|
</div>
|
|
459
464
|
</div>
|
|
460
465
|
<span class="roles-group-chevron"></span>
|
|
461
466
|
</div>
|
|
462
|
-
<div class="roles-bot-list">${
|
|
463
|
-
</div>`}).join(""),t.querySelectorAll(".roles-group-row").forEach(a=>{a.addEventListener("click",()=>{let
|
|
467
|
+
<div class="roles-bot-list">${p}</div>
|
|
468
|
+
</div>`}).join(""),t.querySelectorAll(".roles-group-row").forEach(a=>{a.addEventListener("click",()=>{let r=a.dataset.groupId;r&&(we.has(r)?we.delete(r):we.add(r),le(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&&Dn(i,m)})})}async function Dn(e,t){Q=e,X=t;let n=await Mt(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"),k=document.getElementById("roles-editor-chat-id");s&&(s.style.display="none"),a&&(a.style.display="");let $=xe.find(l=>l.chatId===e),p=$?.memberBots.find(l=>l.larkAppId===t);i&&(i.textContent=$?.name??e),m&&(m.textContent=p?.botName??t),k&&(k.textContent=`${e} \xB7 ${t}`),J=n.content??"",r&&(r.value=J,r.focus()),Ve(),Ye(),le(document.getElementById("roles-search")?.value??"");let g=document.getElementById("roles-delete");g&&(g.style.display=n.hasRole?"":"none")}function Ve(){let e=document.getElementById("roles-editor-bytecount");if(!e)return;let t=new TextEncoder().encode(J).length;e.textContent=`${t} / ${Ke} bytes`,e.className=`roles-bytecount ${t>3800?"warn":""} ${t>Ke?"over":""}`,Bn(t)}function Bn(e){let t=document.getElementById("roles-save");if(!t)return;let n=e??new TextEncoder().encode(J).length;t.disabled=n>Ke||J.trim().length===0}function Ye(){let e=document.getElementById("roles-preview");e&&(J.trim()?e.innerHTML=`<strong>${o("roles.preview")}</strong><pre>${d(J)}</pre>`:e.innerHTML=`<small>${o("roles.previewEmpty")}</small>`)}function Et(){Q=null,X=null,J="";let e=document.getElementById("roles-editor-empty"),t=document.getElementById("roles-editor-form"),n=document.getElementById("roles-editor-textarea"),s=document.getElementById("roles-delete");e&&(e.style.display=""),t&&(t.style.display="none"),n&&(n.value=""),s&&(s.style.display="none")}async function xt(e){e.innerHTML=xn(),we.clear(),Et(),await Ce();for(let t of xe)Ct(t)>0&&we.add(t.chatId);le(),document.getElementById("roles-search")?.addEventListener("input",t=>{le(t.target.value)}),document.getElementById("roles-refresh")?.addEventListener("click",async()=>{if(await Ce(),le(document.getElementById("roles-search")?.value??""),Q&&X){let t=await Mt(X,Q),n=document.getElementById("roles-editor-textarea");n&&(n.value=t.content??""),J=t.content??"",Ve(),Ye();let s=document.getElementById("roles-delete");s&&(s.style.display=t.hasRole?"":"none")}}),document.getElementById("roles-save")?.addEventListener("click",async function(){if(!(!Q||!X)){this.disabled=!0,this.textContent="...";try{if(await Hn(X,Q,J)){await Ce(),le(document.getElementById("roles-search")?.value??"");let n=document.getElementById("roles-delete");n&&(n.style.display="");let s=document.createElement("span");s.className="roles-saved-flash",s.textContent=` ${o("roles.saved")}`,document.querySelector(".roles-editor-footer")?.appendChild(s),setTimeout(()=>s.remove(),2e3)}else{let n=document.createElement("span");n.className="roles-saved-flash roles-save-error",n.textContent=J.trim().length===0?` ${o("roles.emptyError")}`:` ${o("roles.saveFailed")}`,document.querySelector(".roles-editor-footer")?.appendChild(n),setTimeout(()=>n.remove(),3e3)}}finally{this.disabled=!1,this.textContent=o("roles.save")}}}),document.getElementById("roles-delete")?.addEventListener("click",async function(){if(!(!Q||!X)&&confirm(o("roles.confirmDelete"))){this.disabled=!0,this.textContent="...";try{await An(X,Q)&&(await Ce(),Et(),le(document.getElementById("roles-search")?.value??""))}finally{this.disabled=!1,this.textContent=o("roles.delete")}}}),document.getElementById("roles-editor-textarea")?.addEventListener("input",t=>{J=t.target.value,Ve(),Ye()})}async function Ae(e){let t=await fetch(e);return{status:t.status,body:await t.json().catch(()=>({}))}}async function Re(e,t,n){let s=await fetch(t,{method:e,headers:{"content-type":"application/json"},body:n?JSON.stringify(n):void 0});return{status:s.status,body:await s.json().catch(()=>({}))}}var ce=(e,t)=>Re("POST",e,t),Ht=(e,t)=>Re("PUT",e,t),Qe=[],At=[],Rt="",He="",Xe=new Map,ye=new Set,ve=new Set;function B(e){return document.getElementById(e)}function De(){return[...Qe,...At]}function de(e){let t=Xe.get(e);return t||(t=new Set,Xe.set(e,t)),t}function On(e){return De().find(t=>t.key===e)}function Dt(e){let t=(n,s,a)=>`<a href="${n}" 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 qn(){return`<section class="page">
|
|
469
|
+
<div class="page-heading"><div>
|
|
470
|
+
<p class="eyebrow">\u56E2\u961F</p><h1>\u56E2\u961F\u534F\u4F5C\uFF08\u8DE8\u90E8\u7F72\uFF09</h1>
|
|
471
|
+
<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>
|
|
472
|
+
</div></div>
|
|
473
|
+
${Dt("home")}
|
|
474
|
+
<div class="card" style="margin-bottom:16px">
|
|
475
|
+
<h2 style="margin-top:0">\u672C\u90E8\u7F72</h2>
|
|
476
|
+
<p>\u6211\u7684\u98DE\u4E66\u8EAB\u4EFD\uFF1A<b id="tf-owner">\u672A\u7ED1\u5B9A</b>
|
|
477
|
+
<button id="tf-autobind" class="primary" style="margin-left:8px">\u7ED1\u5B9A</button>
|
|
478
|
+
<span class="muted" style="font-size:13px">\uFF08\u7528\u673A\u5668\u4EBA\u51ED\u8BC1\u81EA\u52A8\u8BC6\u522B\u4F60\uFF1B\u7ED1\u5B9A\u540E\u62C9\u7FA4\u4F1A\u628A\u4F60\u62C9\u8FDB\u7FA4\u3001\u673A\u5668\u4EBA\u4E5F\u5F52\u5230\u4F60\u540D\u4E0B\uFF09</span></p>
|
|
479
|
+
<div id="tf-bind-out" style="display:none;margin-top:6px"></div>
|
|
480
|
+
</div>
|
|
481
|
+
<div class="card">
|
|
482
|
+
<h2 style="margin-top:0">\u6211\u7684\u56E2\u961F <span class="muted" id="tf-count" style="font-size:13px"></span></h2>
|
|
483
|
+
<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin-bottom:8px;font-size:13px">
|
|
484
|
+
<input id="tf-search" placeholder="\u641C\u7D22 \u540D\u79F0/\u80FD\u529B/CLI\u2026" style="padding:5px 9px;min-width:180px">
|
|
485
|
+
<select id="tf-cli" style="padding:5px"><option value="">\u5168\u90E8 CLI</option></select>
|
|
486
|
+
<label><input type="checkbox" id="tf-fcap"> \u6709\u80FD\u529B\u6807\u7B7E</label>
|
|
487
|
+
<label><input type="checkbox" id="tf-frole"> \u6709\u56E2\u961F\u89D2\u8272</label>
|
|
488
|
+
</div>
|
|
489
|
+
<p class="muted" style="font-size:13px;margin:0 0 4px">\u6BCF\u4E2A\u56E2\u961F\u91CC\u52FE\u9009\u673A\u5668\u4EBA\u5373\u53EF\u5355\u72EC\u62C9\u7FA4\uFF08\u81EA\u52A8\u5E26\u4E0A\u5404\u81EA\u8D1F\u8D23\u4EBA\uFF09\u3002\u8981\u65B0\u5EFA\u56E2\u961F / \u751F\u6210\u9080\u8BF7\u7801 / \u52A0\u5165\u522B\u4EBA\u7684\u56E2\u961F\uFF0C\u53BB\u300C\u56E2\u961F\u7BA1\u7406\u300D\u3002</p>
|
|
490
|
+
<div id="tf-teams">\u52A0\u8F7D\u4E2D\u2026</div>
|
|
491
|
+
</div>
|
|
492
|
+
<div id="tf-modal" style="display:none;position:fixed;inset:0;background:rgba(0,0,0,.5);align-items:center;justify-content:center;z-index:50">
|
|
493
|
+
<div style="background:var(--card,#fff);color:var(--text,#1f2329);border-radius:10px;padding:18px 20px;width:min(560px,92vw)">
|
|
494
|
+
<h2 id="tf-modal-title" style="margin-top:0">\u56E2\u961F\u89D2\u8272</h2>
|
|
495
|
+
<p class="muted" style="font-size:13px">\u56E2\u961F\u7EA7\u89D2\u8272\uFF08\u8BE5\u673A\u5668\u4EBA\u8DE8\u7FA4\u7684\u9ED8\u8BA4\u4EBA\u8BBE\uFF09\u3002\u7559\u7A7A\u4FDD\u5B58\u5373\u5220\u9664\u3002\u4EC5\u672C\u90E8\u7F72\u7684\u673A\u5668\u4EBA\u53EF\u7F16\u8F91\u3002</p>
|
|
496
|
+
<textarea id="tf-modal-text" style="width:100%;min-height:200px;font:13px/1.5 ui-monospace,Menlo,monospace;padding:10px;box-sizing:border-box"></textarea>
|
|
497
|
+
<div style="display:flex;justify-content:flex-end;gap:8px;margin-top:12px">
|
|
498
|
+
<button id="tf-modal-cancel">\u53D6\u6D88</button><button id="tf-modal-save" class="primary">\u4FDD\u5B58</button>
|
|
499
|
+
</div>
|
|
500
|
+
</div>
|
|
501
|
+
</div>
|
|
502
|
+
</section>`}function Pn(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 Nn(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(l=>l.deployment.id===a.id);if(!r.length)continue;let i=a.id===Rt,m=i?"\u672C\u90E8\u7F72":a.stale?"\u8FDC\u7AEF\xB7\u79BB\u7EBF\uFF1F":"\u8FDC\u7AEF",k=e.kind==="local"&&!i?` <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>`:"",$=`${e.key}::${a.id}`,p=ve.has($),g=r.filter(l=>de(e.key).has(l.larkAppId)).length;if(s+=`<div class="tf-dep-h" data-dk="${d($)}" style="cursor:pointer;margin:10px 0 2px"><b>${p?"\u25BE":"\u25B8"} ${d(a.name)}</b> <span class="muted" style="font-size:12px">\uFF08${m}\uFF09\xB7 ${r.length} \u4E2A${g?`\uFF0C\u5DF2\u9009 ${g}`:""}</span>${k}</div>`,!!p){s+='<table style="width:100%;border-collapse:collapse;font-size:14px"><tbody>';for(let l of r){let I=d(l.larkAppId),L=de(e.key).has(l.larkAppId)?" checked":"",b=l.deployment.stale?"opacity:.55":"",f=i?`<input class="tf-cap" data-app="${I}" value="${d(l.capability||"")}" placeholder="\u80FD\u529B\u6807\u7B7E\u2026" style="width:92%;padding:3px 6px">`:l.capability?d(l.capability):'<span class="muted">\u2014</span>',T=i?`<button class="tf-role" data-app="${I}" data-name="${d(l.name)}">${l.hasTeamRole?"\u5DF2\u8BBE\xB7\u6539":"\u8BBE\u7F6E"}</button>`:l.hasTeamRole?"\u6709\u89D2\u8272":'<span class="muted">\u2014</span>';s+=`<tr style="${b}"><td style="padding:4px 8px"><input type="checkbox" class="tf-pick" data-tk="${d(e.key)}" data-app="${I}"${L}></td><td style="padding:4px 8px">${d(l.name)}</td><td style="padding:4px 8px" class="muted">${d(l.cliId)}</td><td style="padding:4px 8px">${f}</td><td style="padding:4px 8px">${T}</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)}" 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 ue(){let e=B("tf-teams"),t=De();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 i of t){let m=i.bots.filter(Pn);m.forEach(g=>s.add(g.larkAppId)),i.bots.forEach(g=>a.add(g.larkAppId));let k=new Set(m.map(g=>g.larkAppId));[...de(i.key)].forEach(g=>{k.has(g)||de(i.key).delete(g)});let $=!ye.has(i.key),p=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${d(i.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(i.key)}" style="cursor:pointer;display:flex;align-items:center;gap:8px;flex-wrap:wrap"><b style="font-size:15px">${$?"\u25B8":"\u25BE"} ${d(i.label)}</b>`+(i.sub?` <span class="muted" style="font-size:12px">${d(i.sub)}</span>`:"")+p+` <span class="muted" style="font-size:12px">\xB7 ${i.deployments.length} \u4E2A\u90E8\u7F72 \xB7 ${i.bots.length} \u4E2A\u673A\u5668\u4EBA</span></div>`,$||(n+=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>':Nn(i,m)),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}`,jn()}function jn(){let e=B("tf-teams");e.querySelectorAll(".tf-team-h").forEach(t=>{t.onclick=()=>{let n=t.dataset.tk;ye.has(n)?ye.delete(n):ye.add(n),ue()}}),e.querySelectorAll(".tf-dep-h").forEach(t=>{t.onclick=()=>{let n=t.dataset.dk;ve.has(n)?ve.delete(n):ve.add(n),ue()}}),e.querySelectorAll(".tf-pick").forEach(t=>{t.onchange=()=>{let n=de(t.dataset.tk);t.checked?n.add(t.dataset.app):n.delete(t.dataset.app)}}),e.querySelectorAll(".tf-cap").forEach(t=>{t.onchange=async()=>{let n=t.dataset.app,s=t.value;await Ht("/api/team/local-bots/"+encodeURIComponent(n)+"/capability",{capability:s}),De().forEach(a=>{let r=a.bots.find(i=>i.larkAppId===n);r&&(r.capability=s.trim()||null)})}}),e.querySelectorAll(".tf-role").forEach(t=>{t.onclick=()=>Fn(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 Re("DELETE",`/api/team/hosted/${encodeURIComponent(t.dataset.team)}/members/${encodeURIComponent(t.dataset.dep)}`),ge())}}),e.querySelectorAll(".tf-grp").forEach(t=>{t.onclick=async()=>{let n=t.dataset.tk,s=On(n);if(!s)return;let a=[...de(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 i=(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 m=s.kind==="local"?await ce("/api/team/federated-group",{name:i,larkAppIds:a,teamId:s.teamId}):await ce("/api/team/remote-group",{hubUrl:s.hubUrl,teamId:s.teamId,name:i,larkAppIds:a});Un(r,m.body,m.status),m.body?.ok&&(de(n).clear(),s.kind==="local"&&ge())}})}function Un(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>`:"",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>`:"",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}${i}${m}`}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 Fn(e,t){let n=await Ae("/api/team/local-bots/"+encodeURIComponent(e)+"/role");B("tf-modal-title").textContent="\u56E2\u961F\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 Bt(){let e=Array.from(new Set(De().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 ge(){let t=(await Ae("/api/team/hosted")).body;if(!t?.ok){Qe=[],ue();return}Rt=t.deployment.deploymentId,He=t.suggestedHubUrl||"",B("tf-owner").textContent=t.deployment.ownerName||(t.deployment.ownerUnionId?"\u5DF2\u7ED1\u5B9A":"\u672A\u7ED1\u5B9A"),Qe=(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||[]})),Bt(),ue()}async function _n(){At=((await Ae("/api/team/remote-roster")).body?.memberships||[]).map(n=>{let s=n.roster?.deployments||[],a=s.find(i=>i.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||[]}}),Bt(),ue()}function Ot(e){e.innerHTML=qn(),Xe.clear(),ye.clear(),ve.clear(),["tf-search","tf-cli","tf-fcap","tf-frole"].forEach(t=>{let n=B(t);n.oninput=ue,n.onchange=ue}),B("tf-modal-cancel").onclick=()=>{B("tf-modal").style.display="none"},B("tf-modal-save").onclick=async()=>{let t=B("tf-modal").dataset.app;await Ht("/api/team/local-bots/"+encodeURIComponent(t)+"/role",{role:B("tf-modal-text").value}),B("tf-modal").style.display="none",ge()},zn(),ge(),_n()}function Wn(){return`<section class="page">
|
|
503
|
+
<div class="page-heading"><div>
|
|
504
|
+
<p class="eyebrow">\u56E2\u961F</p><h1>\u56E2\u961F\u7BA1\u7406</h1>
|
|
505
|
+
<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>
|
|
506
|
+
</div></div>
|
|
507
|
+
${Dt("manage")}
|
|
508
|
+
<div class="card" style="margin-bottom:16px">
|
|
509
|
+
<h2 style="margin-top:0">\u6211\u6258\u7BA1\u7684\u56E2\u961F</h2>
|
|
510
|
+
<p style="display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin-bottom:6px">
|
|
511
|
+
<input id="tm-newname" placeholder="\u65B0\u56E2\u961F\u540D\u79F0" style="min-width:200px">
|
|
512
|
+
<button id="tm-create" class="primary">\u521B\u5EFA\u56E2\u961F</button>
|
|
513
|
+
<span class="muted tm-cout" style="font-size:13px"></span>
|
|
514
|
+
</p>
|
|
515
|
+
<div id="tm-list">\u52A0\u8F7D\u4E2D\u2026</div>
|
|
516
|
+
</div>
|
|
517
|
+
<div class="card">
|
|
518
|
+
<h2 style="margin-top:0">\u52A0\u5165\u522B\u4EBA\u7684\u56E2\u961F</h2>
|
|
519
|
+
<p style="display:flex;gap:8px;flex-wrap:wrap;align-items:center">
|
|
520
|
+
<input id="tm-hub" placeholder="Hub \u5730\u5740\uFF0C\u5982 http://10.0.0.5:7891" style="flex:1;min-width:240px">
|
|
521
|
+
<input id="tm-code" placeholder="\u9080\u8BF7\u7801" style="min-width:160px">
|
|
522
|
+
<button id="tm-join" class="primary">\u52A0\u5165</button>
|
|
523
|
+
</p>
|
|
524
|
+
<div id="tm-join-out" style="display:none;margin-top:6px"></div>
|
|
525
|
+
</div>
|
|
526
|
+
</section>`}async function Ze(){let t=(await Ae("/api/team/hosted")).body,n=B("tm-list");He=t?.suggestedHubUrl||He;let s=t?.teams||[];if(!s.length){n.innerHTML='<p class="muted">\u8FD8\u6CA1\u6709\u56E2\u961F\u3002</p>';return}n.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)">
|
|
527
|
+
<div style="display:flex;align-items:center;gap:8px;flex-wrap:wrap">
|
|
528
|
+
<b>${d(a.name)}</b>${a.isDefault?' <span class="muted" style="font-size:12px">\u9ED8\u8BA4</span>':""}
|
|
529
|
+
<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>
|
|
530
|
+
<span style="margin-left:auto;display:flex;gap:6px">
|
|
531
|
+
<button class="tm-invite ghost" data-team="${d(a.teamId)}" style="font-size:12px">\u751F\u6210\u9080\u8BF7\u7801</button>
|
|
532
|
+
${a.isDefault?"":`<button class="tm-del ghost" data-team="${d(a.teamId)}" data-name="${d(a.name)}" style="font-size:12px">\u5220\u9664</button>`}
|
|
533
|
+
</span>
|
|
534
|
+
</div>
|
|
535
|
+
<div class="tm-inv-out" data-team="${d(a.teamId)}" style="display:none;margin-top:6px;font-size:13px"></div></div>`}).join(""),n.querySelectorAll(".tm-invite").forEach(a=>{a.onclick=async()=>{let r=a.dataset.team,i=n.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 ce("/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>${d(He)}</code><br>\u9080\u8BF7\u7801\uFF1A<code style="font-size:15px">${d(m.body.code)}</code>`:i.innerHTML='<span class="err">\u751F\u6210\u5931\u8D25\u3002</span>'}}),n.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 Re("DELETE","/api/team/hosted/"+encodeURIComponent(a.dataset.team)),Ze())}})}function qt(e){e.innerHTML=Wn(),B("tm-create").onclick=async()=>{let t=B("tm-newname").value.trim(),n=e.querySelector(".tm-cout");if(!t){n.innerHTML='<span class="err">\u8BF7\u586B\u56E2\u961F\u540D\u79F0</span>';return}n.innerHTML='<span class="muted">\u521B\u5EFA\u4E2D\u2026</span>';let s=await ce("/api/team/hosted",{name:t});s.body?.ok?(n.innerHTML='<span class="ok">\u5DF2\u521B\u5EFA</span>',B("tm-newname").value="",Ze()):n.innerHTML=`<span class="err">\u521B\u5EFA\u5931\u8D25\uFF1A${d(String(s.body?.error||s.status))}</span>`},B("tm-join").onclick=async()=>{let t=B("tm-hub").value.trim(),n=B("tm-code").value.trim(),s=B("tm-join-out");if(s.style.display="",!t||!n){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 ce("/api/team/join-remote",{hubUrl:t,inviteCode:n});if(a.body?.ok)s.innerHTML=`<span class="ok">\u5DF2\u52A0\u5165\u300C${d(a.body.teamName||"")}\u300D\uFF0C\u53BB\u300C\u6211\u7684\u56E2\u961F\u300D\u67E5\u770B\u3002</span>`,B("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">${d(String(i))}</span>`}},Ze()}function zn(){B("tf-autobind").onclick=async()=>{let e=B("tf-bind-out");e.style.display="",e.innerHTML='<span class="muted">\u8BC6\u522B\u4E2D\u2026</span>';let n=(await ce("/api/team/identity/auto-bind")).body;if(n?.ok&&n.owner){e.innerHTML=`<span class="ok">\u5DF2\u7ED1\u5B9A\uFF1A${d(n.owner.name||n.owner.unionId)}</span>`,ge();return}if(n?.ok&&n.needChoice&&Array.isArray(n.candidates)){let s=n.candidates.map(a=>`<button class="tf-pickowner ghost" data-union="${d(a.unionId)}" style="margin:2px">${d(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 ce("/api/team/identity/auto-bind",{unionId:a.dataset.union})).body;i?.ok&&i.owner?(e.innerHTML=`<span class="ok">\u5DF2\u7ED1\u5B9A\uFF1A${d(i.owner.name||i.owner.unionId)}</span>`,ge()):e.innerHTML=`<span class="err">\u7ED1\u5B9A\u5931\u8D25\uFF1A${d(String(i?.error||"unknown"))}</span>`}});return}if(n?.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${d(String(n?.error||"unknown"))}</span>`}}async function Pt(e){let t=await fetch(e);return{status:t.status,body:await t.json().catch(()=>({}))}}async function tt(e,t,n){let s=await fetch(t,{method:e,headers:{"content-type":"application/json"},body:n?JSON.stringify(n):void 0});return{status:s.status,body:await s.json().catch(()=>({}))}}function _(e){return document.getElementById(e)}function K(e){return(_(e).value||"").trim()}var nt=[];function Jn(){return`<section class="page">
|
|
536
|
+
<div class="page-heading">
|
|
537
|
+
<div>
|
|
538
|
+
<p class="eyebrow">\u63A5\u5165\u70B9 \xB7 beta</p>
|
|
539
|
+
<h1>\u63A5\u5165\u70B9\uFF08Webhook\uFF09<span class="muted" style="font-size:14px;font-weight:400">beta</span></h1>
|
|
540
|
+
<p>\u8BA9\u5916\u90E8\u7CFB\u7EDF\uFF08\u76D1\u63A7\u544A\u8B66\u3001CI\u3001\u5DE5\u5355\u2026\uFF09\u901A\u8FC7\u4E00\u4E2A webhook \u89E6\u53D1\u673A\u5668\u4EBA\u5728\u7FA4\u91CC\u8BF4\u8BDD\u6216\u8DD1\u5DE5\u4F5C\u6D41\u3002<span class="muted">\uFF08beta\uFF1A\u5C1A\u672A\u5145\u5206\u6D4B\u8BD5\uFF0C\u6B22\u8FCE\u53CD\u9988\uFF09</span></p>
|
|
541
|
+
</div>
|
|
542
|
+
</div>
|
|
543
|
+
|
|
544
|
+
<div class="card" style="margin-bottom:16px">
|
|
545
|
+
<h2 style="margin-top:0">\u65B0\u5EFA\u63A5\u5165\u70B9</h2>
|
|
546
|
+
<div class="cn-form" style="display:grid;grid-template-columns:140px 1fr;gap:10px 14px;align-items:center;max-width:680px">
|
|
547
|
+
<label>\u540D\u79F0</label><input id="cn-name" placeholder="\u5982\uFF1A\u7EBF\u4E0A\u544A\u8B66">
|
|
548
|
+
<label>\u89E6\u53D1\u7684\u673A\u5668\u4EBA</label><select id="cn-bot"></select>
|
|
549
|
+
<label>\u89E6\u53D1\u65B9\u5F0F</label>
|
|
550
|
+
<select id="cn-kind"><option value="turn">\u5355\u8F6E\u5BF9\u8BDD\uFF08\u8BA9\u673A\u5668\u4EBA\u56DE\u5E94\u4E00\u6B21\uFF09</option><option value="workflow">\u5DE5\u4F5C\u6D41</option></select>
|
|
551
|
+
<label class="cn-wf" style="display:none">\u5DE5\u4F5C\u6D41 ID</label><input class="cn-wf" id="cn-wf" style="display:none" placeholder="workflowId">
|
|
552
|
+
<label>\u6295\u9012\u5230\u54EA\u4E2A\u7FA4</label>
|
|
553
|
+
<select id="cn-mode">
|
|
554
|
+
<option value="dynamic">\u7531\u8BF7\u6C42\u6307\u5B9A\uFF08\u7FA4 ID \u968F\u8BF7\u6C42\u4F20\u5165\uFF09</option>
|
|
555
|
+
<option value="fixed">\u56FA\u5B9A\u7FA4</option>
|
|
556
|
+
<option value="new-group">\u6BCF\u6B21\u65B0\u5EFA\u7FA4</option>
|
|
557
|
+
</select>
|
|
558
|
+
<label class="cn-fixed" style="display:none">\u7FA4 ID</label><input class="cn-fixed" id="cn-chat" style="display:none" placeholder="oc_\u2026">
|
|
559
|
+
<label class="cn-allow">\u5141\u8BB8\u7684\u7FA4<span class="muted" style="font-weight:400">\uFF08\u53EF\u9009\uFF09</span></label>
|
|
560
|
+
<input class="cn-allow" id="cn-allow" placeholder="oc_xxx,oc_yyy\uFF08\u9017\u53F7\u5206\u9694\uFF0C\u7559\u7A7A=\u4E0D\u9650\uFF09">
|
|
561
|
+
<label class="cn-life" style="display:none">\u53BB\u91CD\u5B57\u6BB5</label><input class="cn-life" id="cn-dedup" style="display:none" placeholder="\u5982 payload.alert.id">
|
|
562
|
+
<label class="cn-life" style="display:none">\u72B6\u6001\u5B57\u6BB5</label><input class="cn-life" id="cn-status" style="display:none" placeholder="\u5982 payload.status">
|
|
563
|
+
<label>\u7B7E\u540D\u5BC6\u94A5</label><input id="cn-secret" placeholder="\u7559\u7A7A\u81EA\u52A8\u751F\u6210\uFF08\u53EA\u663E\u793A\u4E00\u6B21\uFF09">
|
|
564
|
+
</div>
|
|
565
|
+
<div style="margin-top:14px"><button id="cn-create" class="primary">\u521B\u5EFA</button>
|
|
566
|
+
<span class="muted" id="cn-create-out" style="margin-left:10px;font-size:13px"></span></div>
|
|
567
|
+
<div id="cn-created" style="display:none;margin-top:12px"></div>
|
|
568
|
+
</div>
|
|
569
|
+
|
|
570
|
+
<div class="card">
|
|
571
|
+
<h2 style="margin-top:0">\u5DF2\u6709\u63A5\u5165\u70B9 <span class="muted" id="cn-count" style="font-size:13px"></span></h2>
|
|
572
|
+
<div id="cn-list">\u52A0\u8F7D\u4E2D\u2026</div>
|
|
573
|
+
</div>
|
|
574
|
+
</section>`}function et(){let e=_("cn-kind").value,t=_("cn-mode").value;document.querySelectorAll(".cn-wf").forEach(n=>{n.style.display=e==="workflow"?"":"none"}),document.querySelectorAll(".cn-fixed").forEach(n=>{n.style.display=t==="fixed"?"":"none"}),document.querySelectorAll(".cn-allow").forEach(n=>{n.style.display=t==="fixed"?"none":""}),document.querySelectorAll(".cn-life").forEach(n=>{n.style.display=t==="new-group"?"":"none"})}function Nt(e){return`${location.origin}/webhook/${encodeURIComponent(e)}`}function Gn(e){return e==="fixed"?"\u56FA\u5B9A\u7FA4":e==="new-group"?"\u6BCF\u6B21\u65B0\u5EFA\u7FA4":"\u8BF7\u6C42\u6307\u5B9A\u7FA4"}function Kn(e){return e==="workflow"?"\u5DE5\u4F5C\u6D41":"\u5355\u8F6E"}function Vn(e){let t=_("cn-list");if(_("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(n=>{let s=nt.find(r=>r.larkAppId===n.target.botId),a=Nt(n.id);return`<div class="card" style="margin:0 0 10px;padding:12px 14px;background:var(--bg-soft,#f6f7f9)">
|
|
575
|
+
<div style="display:flex;align-items:center;gap:8px;flex-wrap:wrap">
|
|
576
|
+
<b style="font-size:15px">${d(n.name)}</b>
|
|
577
|
+
<span class="${n.enabled?"ok":"muted"}" style="font-size:12px">${n.enabled?"\u5DF2\u542F\u7528":"\u5DF2\u505C\u7528"}</span>
|
|
578
|
+
<span class="muted" style="font-size:12px">\xB7 ${d(s?.botName||n.target.botId)} \xB7 ${Kn(n.target.kind)} \xB7 ${Gn(n.target.mode)}</span>
|
|
579
|
+
<span style="margin-left:auto;display:flex;gap:6px">
|
|
580
|
+
<button class="cn-toggle ghost" data-id="${d(n.id)}" data-on="${n.enabled}" style="font-size:12px">${n.enabled?"\u505C\u7528":"\u542F\u7528"}</button>
|
|
581
|
+
<button class="cn-del ghost" data-id="${d(n.id)}" style="font-size:12px">\u5220\u9664</button>
|
|
582
|
+
</span>
|
|
583
|
+
</div>
|
|
584
|
+
<div style="margin-top:6px;font-size:13px;display:flex;align-items:center;gap:8px;flex-wrap:wrap">
|
|
585
|
+
<span class="muted">Webhook URL\uFF1A</span><code style="font-size:12px;word-break:break-all">${d(a)}</code>
|
|
586
|
+
<button class="cn-copy ghost" data-url="${d(a)}" style="font-size:12px">\u590D\u5236</button>
|
|
587
|
+
</div></div>`}).join(""),t.querySelectorAll(".cn-copy").forEach(n=>{n.onclick=()=>{navigator.clipboard?.writeText(n.dataset.url),n.textContent="\u5DF2\u590D\u5236",setTimeout(()=>n.textContent="\u590D\u5236",1200)}}),t.querySelectorAll(".cn-toggle").forEach(n=>{n.onclick=async()=>{await tt("PATCH","/api/connectors/"+encodeURIComponent(n.dataset.id),{enabled:n.dataset.on!=="true"}),Be()}}),t.querySelectorAll(".cn-del").forEach(n=>{n.onclick=async()=>{confirm("\u5220\u9664\u8FD9\u4E2A\u63A5\u5165\u70B9\uFF1F\u5B83\u7684 webhook URL \u4F1A\u7ACB\u5373\u5931\u6548\u3002")&&(await tt("DELETE","/api/connectors/"+encodeURIComponent(n.dataset.id)),Be())}})}async function Be(){let[e,t]=await Promise.all([Pt("/api/bots"),Pt("/api/connectors")]);nt=(e.body?.bots||[]).map(a=>({larkAppId:a.larkAppId,botName:a.botName||a.larkAppId}));let n=_("cn-bot"),s=n.value;n.innerHTML=nt.map(a=>`<option value="${d(a.larkAppId)}">${d(a.botName)}</option>`).join("")||'<option value="">\uFF08\u6CA1\u6709\u5728\u7EBF\u673A\u5668\u4EBA\uFF09</option>',s&&(n.value=s),Vn(t.body?.connectors||[])}function jt(e){e.innerHTML=Jn(),_("cn-kind").onchange=et,_("cn-mode").onchange=et,et(),_("cn-create").onclick=async()=>{let t=_("cn-create-out"),n=K("cn-name"),s=_("cn-bot").value;if(!n){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=_("cn-kind").value,r=_("cn-mode").value,i={name:n,enabled:!0,target:{kind:a,mode:r,botId:s},promptEnvelope:{sourceName:n}};if(a==="workflow"){if(!K("cn-wf")){t.innerHTML='<span class="err">\u8BF7\u586B\u5DE5\u4F5C\u6D41 ID</span>';return}i.target.workflowId=K("cn-wf")}if(r==="fixed"){if(!K("cn-chat")){t.innerHTML='<span class="err">\u56FA\u5B9A\u7FA4\u9700\u8981\u586B\u7FA4 ID</span>';return}i.target.chatId=K("cn-chat")}else{let $=K("cn-allow");$&&(i.target.allowChats=$.split(",").map(p=>p.trim()).filter(Boolean))}if(r==="new-group"){if(!K("cn-dedup")||!K("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:K("cn-dedup"),status:K("cn-status")}}let m=K("cn-secret");m&&(i.secret=m),t.innerHTML='<span class="muted">\u521B\u5EFA\u4E2D\u2026</span>';let k=await tt("POST","/api/connectors",i);if(k.status===201&&k.body?.ok){t.innerHTML="";let $=_("cn-created");$.style.display="";let p=k.body.webhookUrl||Nt(k.body.connector.id),g=k.body.secret;$.innerHTML=`<div class="card" style="padding:12px 14px;background:var(--bg-soft,#f6f7f9)">
|
|
588
|
+
<p class="ok" style="margin:0 0 6px">\u5DF2\u521B\u5EFA\u300C${d(n)}\u300D</p>
|
|
589
|
+
<p style="margin:4px 0;font-size:13px"><span class="muted">Webhook URL\uFF1A</span><code style="word-break:break-all">${d(p)}</code></p>
|
|
590
|
+
${g?`<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>${d(g)}</code></p>`:""}
|
|
591
|
+
<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(l=>{_(l).value=""}),Be()}else{let $=k.body?.error||k.status;t.innerHTML=`<span class="err">\u521B\u5EFA\u5931\u8D25\uFF1A${d(String($))}</span>`}},Be()}function Yn(){let e=[["",o("workflow.filter.nonTerminal")],["all",o("workflow.filter.all")],["pending",ie("pending")],["running",ie("running")],["waiting",ie("waiting")],["succeeded",ie("succeeded")],["failed",ie("failed")],["cancelled",ie("cancelled")]];return`
|
|
464
592
|
<nav class="wf-subnav">
|
|
465
|
-
<a href="#/workflows" class="active" data-i18n="workflow.subnav.runs">${
|
|
466
|
-
<a href="#/workflows/catalog" data-i18n="workflow.subnav.catalog">${
|
|
593
|
+
<a href="#/workflows" class="active" data-i18n="workflow.subnav.runs">${c(o("workflow.subnav.runs"))}</a>
|
|
594
|
+
<a href="#/workflows/catalog" data-i18n="workflow.subnav.catalog">${c(o("workflow.subnav.catalog"))}</a>
|
|
467
595
|
</nav>
|
|
468
596
|
<form id="wf-filters" class="filters">
|
|
469
|
-
<input type="search" name="q" placeholder="${
|
|
597
|
+
<input type="search" name="q" placeholder="${c(o("workflow.searchPlaceholder"))}" />
|
|
470
598
|
<select name="status">
|
|
471
|
-
${e.map(([t,
|
|
599
|
+
${e.map(([t,n])=>`<option value="${c(t)}">${c(n)}</option>`).join("")}
|
|
472
600
|
</select>
|
|
473
601
|
<span id="wf-last-load" class="muted"></span>
|
|
474
602
|
</form>
|
|
475
603
|
<table>
|
|
476
604
|
<thead><tr>
|
|
477
|
-
<th>${
|
|
478
|
-
<th>${
|
|
479
|
-
<th>${
|
|
605
|
+
<th>${c(o("workflow.table.run"))}</th><th>${c(o("workflow.table.workflow"))}</th><th>${c(o("workflow.table.status"))}</th>
|
|
606
|
+
<th>${c(o("workflow.table.lastSeq"))}</th><th>${c(o("workflow.table.dangling"))}</th><th>${c(o("workflow.table.updated"))}</th>
|
|
607
|
+
<th>${c(o("workflow.table.chatApp"))}</th>
|
|
480
608
|
</tr></thead>
|
|
481
609
|
<tbody id="wf-tbody"></tbody>
|
|
482
610
|
</table>
|
|
483
|
-
`}var
|
|
484
|
-
<td><a href="#/workflows/${encodeURIComponent(
|
|
485
|
-
<td>${
|
|
486
|
-
<td>${
|
|
487
|
-
<td>${
|
|
488
|
-
<td class="${
|
|
489
|
-
<td title="${
|
|
490
|
-
<td>${
|
|
491
|
-
</tr>`}).join("")}function g(){
|
|
611
|
+
`}var Qn=5e3,Xn=2e3,ke=new Set(["succeeded","failed","cancelled"]);function c(e){return e.replace(/[&<>"']/g,t=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[t])}function Zn(e){let t=new Date(e),s=Date.now()-e;return s<6e4?o("time.secondsAgo",{value:Math.max(1,Math.floor(s/1e3))}):s<36e5?o("time.minutesAgo",{value:Math.floor(s/6e4)}):s<864e5?o("time.hoursAgo",{value:Math.floor(s/36e5)}):t.toISOString().slice(0,19).replace("T"," ")}function ne(e){return`<span class="${ke.has(e)?"wf-status terminal":"wf-status live"} wf-status-${c(e)}">${c(ie(e))}</span>`}function ie(e){let t=`workflow.status.${e}`,n=o(t);return n===t?e:n}function Wt(e){let t=location.hash.match(/^#\/workflows\/([^?#]+)(?:\?([^#]*))?$/);if(t){let n=new URLSearchParams(t[2]??"");return to(e,decodeURIComponent(t[1]),{focusAttemptId:n.get("attempt")??void 0})}return eo(e)}function eo(e){e.innerHTML=Yn();let t=e.querySelector("#wf-tbody"),n=e.querySelector("#wf-filters"),s=e.querySelector("#wf-last-load"),a=[],r=null,i=!1,m=null,k=!1;function $(b){let T=(new FormData(n).get("q")??"").trim().toLowerCase();return T?b.filter(S=>S.runId.toLowerCase().includes(T)||S.workflowId.toLowerCase().includes(T)||(S.chatId??"").toLowerCase().includes(T)):b}function p(){let b=$(a);if(b.length===0){t.innerHTML=`<tr><td colspan="7" class="empty">${m?c(o("workflow.list.failedLoad",{error:m})):a.length===0?c(o("workflow.list.noRuns")):c(o("workflow.list.noFilterMatch"))}</td></tr>`;return}t.innerHTML=b.map(f=>{let T=`${f.dEf}/${f.dAct}/${f.dWait}`,S=f.dEf+f.dAct+f.dWait>0?"wf-dangling has":"wf-dangling none",y=[];f.chatId&&y.push(c(f.chatId)),f.larkAppId&&y.push(`<span class="muted">${c(f.larkAppId)}</span>`);let x=y.length>0?y.join("<br/>"):"\u2014",u=oo(f);return`<tr data-runid="${c(f.runId)}">
|
|
612
|
+
<td><a href="#/workflows/${encodeURIComponent(f.runId)}"><code>${c(f.runId)}</code></a></td>
|
|
613
|
+
<td>${c(f.workflowId)}</td>
|
|
614
|
+
<td>${ne(f.status)}${f.failedNodeId?` <span class="muted">(${c(f.failedNodeId)})</span>`:""}${u}</td>
|
|
615
|
+
<td>${f.lastSeq}</td>
|
|
616
|
+
<td class="${S}">${T}</td>
|
|
617
|
+
<td title="${c(new Date(f.updatedAt).toISOString())}">${Zn(f.updatedAt)}</td>
|
|
618
|
+
<td>${x}</td>
|
|
619
|
+
</tr>`}).join("")}function g(){m?(s.textContent=o("workflow.list.error",{error:m}),s.classList.add("error")):(s.textContent=o("workflow.list.loaded",{count:a.length,time:new Date().toLocaleTimeString()}),s.classList.remove("error"))}async function l(){if(!(k||i)&&!document.hidden){i=!0;try{let b=n.elements.namedItem("status")?.value??"",f=new URLSearchParams;b==="all"?f.set("all","1"):b&&f.set("status",b);let T="/api/workflows/runs"+(f.toString()?`?${f}`:""),S=await fetch(T);S.ok?(a=(await S.json()).runs??[],m=null):(m=`HTTP ${S.status}`,a=[])}catch(b){m=b?.message??String(b),a=[]}finally{i=!1,k||(p(),g())}}}function I(){r!==null&&window.clearTimeout(r),r=window.setTimeout(async()=>{await l(),k||I()},Qn)}function L(){document.hidden||l()}return n.addEventListener("input",()=>{p()}),n.addEventListener("change",b=>{b.target.getAttribute("name")==="status"&&l()}),document.addEventListener("visibilitychange",L),l().then(()=>{k||I()}),()=>{k=!0,r!==null&&window.clearTimeout(r),document.removeEventListener("visibilitychange",L)}}function to(e,t,n={}){e.innerHTML=`
|
|
492
620
|
<div class="wf-detail-head">
|
|
493
|
-
<a class="btn-link" href="#/workflows">${
|
|
621
|
+
<a class="btn-link" href="#/workflows">${c(o("workflow.detail.back"))}</a>
|
|
494
622
|
<div>
|
|
495
|
-
<h2><code>${
|
|
496
|
-
<div id="wf-detail-subtitle" class="muted">${
|
|
623
|
+
<h2><code>${c(t)}</code></h2>
|
|
624
|
+
<div id="wf-detail-subtitle" class="muted">${c(o("workflow.detail.loading"))}</div>
|
|
497
625
|
</div>
|
|
498
|
-
<button id="wf-cancel-run" type="button" class="contrast" hidden>${
|
|
626
|
+
<button id="wf-cancel-run" type="button" class="contrast" hidden>${c(o("workflow.detail.cancel"))}</button>
|
|
499
627
|
<span id="wf-detail-refresh" class="muted"></span>
|
|
500
628
|
</div>
|
|
501
629
|
<section id="wf-detail-error" class="hint-warn" hidden></section>
|
|
@@ -504,20 +632,20 @@ ${u.join(`
|
|
|
504
632
|
<section id="wf-dangling-panel"></section>
|
|
505
633
|
<section class="wf-panel">
|
|
506
634
|
<div class="wf-panel-title">
|
|
507
|
-
<h3>${
|
|
635
|
+
<h3>${c(o("workflow.detail.parallel"))}</h3>
|
|
508
636
|
<span id="wf-parallel-meta" class="muted"></span>
|
|
509
637
|
</div>
|
|
510
638
|
<div id="wf-parallel-view"></div>
|
|
511
639
|
</section>
|
|
512
640
|
<section class="wf-panel">
|
|
513
641
|
<div class="wf-panel-title">
|
|
514
|
-
<h3>${
|
|
642
|
+
<h3>${c(o("workflow.detail.nodes"))}</h3>
|
|
515
643
|
</div>
|
|
516
644
|
<div class="wf-table-scroll">
|
|
517
645
|
<table>
|
|
518
646
|
<thead><tr>
|
|
519
|
-
<th>${
|
|
520
|
-
<th>${
|
|
647
|
+
<th>${c(o("workflow.detail.node"))}</th><th>${c(o("workflow.detail.nodeStatus"))}</th><th>${c(o("workflow.detail.activity"))}</th><th>${c(o("workflow.detail.activityStatus"))}</th>
|
|
648
|
+
<th>${c(o("workflow.detail.attempts"))}</th><th>${c(o("workflow.detail.current"))}</th><th>${c(o("workflow.detail.detail"))}</th>
|
|
521
649
|
</tr></thead>
|
|
522
650
|
<tbody id="wf-node-tbody"></tbody>
|
|
523
651
|
</table>
|
|
@@ -525,237 +653,237 @@ ${u.join(`
|
|
|
525
653
|
</section>
|
|
526
654
|
<section class="wf-panel">
|
|
527
655
|
<div class="wf-panel-title">
|
|
528
|
-
<h3>${
|
|
656
|
+
<h3>${c(o("workflow.detail.nodeIO"))}</h3>
|
|
529
657
|
</div>
|
|
530
658
|
<div id="wf-io-list" class="wf-io-list"></div>
|
|
531
659
|
</section>
|
|
532
660
|
<section class="wf-panel">
|
|
533
661
|
<div class="wf-panel-title">
|
|
534
|
-
<h3>${
|
|
535
|
-
<button id="wf-load-older" type="button" hidden>${
|
|
662
|
+
<h3>${c(o("workflow.detail.timeline"))}</h3>
|
|
663
|
+
<button id="wf-load-older" type="button" hidden>${c(o("workflow.detail.loadOlder"))}</button>
|
|
536
664
|
</div>
|
|
537
665
|
<div class="wf-table-scroll wf-timeline-scroll">
|
|
538
666
|
<table>
|
|
539
667
|
<thead><tr>
|
|
540
|
-
<th>${
|
|
668
|
+
<th>${c(o("workflow.detail.seq"))}</th><th>${c(o("workflow.detail.event"))}</th><th>${c(o("workflow.detail.actor"))}</th><th>${c(o("workflow.detail.node"))}</th><th>${c(o("workflow.detail.activity"))}</th><th>${c(o("workflow.detail.error"))}</th><th>${c(o("workflow.detail.time"))}</th>
|
|
541
669
|
</tr></thead>
|
|
542
670
|
<tbody id="wf-event-tbody"></tbody>
|
|
543
671
|
</table>
|
|
544
672
|
</div>
|
|
545
673
|
<div id="wf-event-meta" class="muted"></div>
|
|
546
674
|
</section>
|
|
547
|
-
`;let
|
|
548
|
-
<span class="muted error">${
|
|
549
|
-
</div>`}function
|
|
675
|
+
`;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"),k=e.querySelector("#wf-dangling-panel"),$=e.querySelector("#wf-parallel-view"),p=e.querySelector("#wf-parallel-meta"),g=e.querySelector("#wf-node-tbody"),l=e.querySelector("#wf-io-list"),I=e.querySelector(".wf-timeline-scroll"),L=e.querySelector("#wf-event-tbody"),b=e.querySelector("#wf-event-meta"),f=e.querySelector("#wf-cancel-run"),T=e.querySelector("#wf-load-older"),S=null,y=[],x=new Set,u=null,w=null,h=!1,E=0,M=null,A=!1,P=!1,O=!1,H=new Set,q=new Map,j=new Map,F=new Map,z=new Set,ee=new Map,te=new Set,he=new Map,rn=new Map,rt=0,it=n.focusAttemptId;function G(v){if(!v){r.hidden=!0,r.textContent="";return}r.hidden=!1,r.textContent=v}function lt(v){if(!v){i.hidden=!0,i.textContent="";return}i.hidden=!1,i.textContent=v}async function dt(){let v=await fetch(`/api/workflows/runs/${encodeURIComponent(t)}/snapshot`);if(v.status===404)throw new Error(o("workflow.detail.unknownRun"));if(!v.ok)throw new Error(o("workflow.detail.snapshotHttp",{status:v.status}));S=await v.json()}async function Te(v){let U=await fetch(`/api/workflows/runs/${encodeURIComponent(t)}/events?${v}`);if(U.status===404)throw new Error(o("workflow.detail.unknownRun"));if(!U.ok)throw new Error(o("workflow.detail.eventsHttp",{status:U.status}));return await U.json()}function Le(v,U){let D=v.filter(R=>x.has(R.eventId)?!1:(x.add(R.eventId),!0));D.length!==0&&(y=U==="prepend"?[...D,...y]:[...y,...D],y.sort((R,oe)=>$e(R.eventId)-$e(oe.eventId)))}async function ln(){await dt();let v=await Te(new URLSearchParams({tail:"100"}));y=[],x=new Set,Le(v.events,"append"),u=v.oldestSeq,w=v.newestSeq,h=v.hasOlder,E=v.totalCount,Y()}async function Ee(){if(!(A||P||document.hidden)){P=!0;try{if(await dt(),w!==null){let v=await Te(new URLSearchParams({afterSeq:String(w),limit:"200"}));Le(v.events,"append"),v.newestSeq!==null&&(w=v.newestSeq),u===null&&v.oldestSeq!==null&&(u=v.oldestSeq),E=v.totalCount}else{let v=await Te(new URLSearchParams({tail:"1"}));Le(v.events,"append"),u=v.oldestSeq,w=v.newestSeq,h=v.hasOlder,E=v.totalCount}G(null),Y()}catch(v){G(v?.message??String(v))}finally{P=!1}}}async function dn(){if(!(u===null||!h)){T.disabled=!0;try{let v=await Te(new URLSearchParams({beforeSeq:String(u),limit:"100"}));Le(v.events,"prepend"),v.oldestSeq!==null&&(u=v.oldestSeq),h=v.hasOlder,E=v.totalCount,G(null),Y()}catch(v){G(v?.message??String(v))}finally{T.disabled=!1}}}async function cn(){if(!S||ke.has(S.run.status)||O)return;if(!S.chatBinding?.larkAppId){G(o("workflow.detail.cancelUnavailable",{runId:t}));return}let v=ao(S),U=o("workflow.detail.cancelConfirm",{runId:t,...v});if(window.confirm(U)){O=!0,f.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(o("workflow.detail.writeAccessCancel"));let R=await D.json().catch(()=>({}));if(!D.ok||!R.ok)throw new Error(R.hint??R.error??o("workflow.detail.cancelHttp",{status:D.status}));lt(R.pending?o("workflow.detail.cancelPending"):null),G(null),await Ee()}catch(D){G(D?.message??String(D))}finally{O=!1,f.disabled=!1,Y()}}}async function un(v,U){if(!te.has(v)){te.add(v),he.delete(v),Y();try{let D=await fetch(`/api/workflows/runs/${encodeURIComponent(t)}/attempts/${encodeURIComponent(U)}/${encodeURIComponent(v)}/resume`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({})});if(D.status===401)throw new Error(o("workflow.detail.writeAccessResume"));let R=await D.json().catch(()=>({}));if(!D.ok||!R.ok||!R.resumeId||!R.url)throw new Error(R.hint??R.message??R.error??o("workflow.detail.resumeStartFailed",{status:D.status}));ee.set(v,{resumeId:R.resumeId,url:R.url})}catch(D){let R=D?.message??String(D);he.set(v,R)}finally{te.delete(v),Y()}}}async function pn(v,U){if(!te.has(v)){te.add(v),he.delete(v),Y();try{let D=await fetch(`/api/workflows/runs/${encodeURIComponent(t)}/attempts/${encodeURIComponent(U)}/${encodeURIComponent(v)}/resume/end`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({reason:"ended_by_dashboard"})});if(D.status===401)throw new Error(o("workflow.detail.writeAccessResume"));let R=await D.json().catch(()=>({}));if(!D.ok||!R.ok)if(R.error==="resume_not_running")ee.delete(v);else throw new Error(R.hint??R.message??R.error??o("workflow.detail.resumeEndFailed",{status:D.status}));else ee.delete(v)}catch(D){let R=D?.message??String(D);he.set(v,R)}finally{te.delete(v),Y()}}}async function fn(v,U){if(!z.has(v)){z.add(v),F.delete(v),Y();try{let D=j.get(v)?.trim()||void 0,R=await fetch(`/api/workflows/runs/${encodeURIComponent(t)}/${U}`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({comment:D})});if(R.status===401)throw new Error(o("workflow.detail.writeAccessApproval"));let oe=await R.json().catch(()=>({}));if(!R.ok||!oe.ok)throw new Error(oe.hint??oe.message??oe.error??o("workflow.detail.actionHttp",{action:U,status:R.status}));let Fe=U==="approve"?o("workflow.detail.approved"):o("workflow.detail.rejected");F.set(v,{kind:"ok",text:oe.alreadyTerminal?o("workflow.detail.alreadyTerminal",{label:Fe}):oe.pending?o("workflow.detail.workflowContinue",{label:Fe}):o("workflow.detail.workflowRefreshing",{label:Fe})}),G(null),await Ee()}catch(D){let R=D?.message??String(D);F.set(v,{kind:"error",text:R}),G(R)}finally{z.delete(v),Y()}}}function Y(){if(!S)return;rt=I.scrollTop;let v=S.run;ke.has(v.status)&<(null),s.innerHTML=`${c(v.workflowId??"?")} \xB7 ${ne(v.status)} \xB7 lastSeq ${S.lastSeq}`,a.textContent=o("workflow.detail.refreshed",{time:new Date().toLocaleTimeString()}),f.hidden=ke.has(v.status),f.disabled=O||!S.chatBinding?.larkAppId,f.textContent=S.chatBinding?.larkAppId?o("workflow.detail.cancel"):o("workflow.detail.cliCancelOnly"),f.title=S.chatBinding?.larkAppId?o("workflow.detail.cancelTitle"):o("workflow.detail.cliCancelTitle",{runId:t}),no(m,S),so(k,S),ro($,p,S,y),fo(g,S),mo(l,S,H,q,{comments:j,statuses:F,resolving:z,onResolve:fn},{sessions:ee,pending:te,errors:he,onStart:un,onEnd:pn},it,rn)&&(it=void 0),jo(L,y),I.scrollTop=rt,T.hidden=!h,b.textContent=o("workflow.detail.eventsLoaded",{loaded:y.length,total:E})}function Ue(){if(M!==null&&window.clearTimeout(M),S&&ke.has(S.run.status)){M=null;return}M=window.setTimeout(async()=>{await Ee(),A||Ue()},Xn)}function ct(){document.hidden||Ee().then(()=>{!A&&M===null&&Ue()})}return T.addEventListener("click",()=>{dn()}),f.addEventListener("click",()=>{cn()}),document.addEventListener("visibilitychange",ct),ln().then(()=>{G(null),A||Ue()}).catch(v=>{G(v?.message??String(v)),s.textContent=o("workflow.detail.loadFailed")}),()=>{A=!0,M!==null&&window.clearTimeout(M),document.removeEventListener("visibilitychange",ct)}}function no(e,t){let n=t.run,s=[[o("workflow.summary.workflow"),c(n.workflowId??"?")],[o("workflow.summary.status"),ne(n.status)],[o("workflow.summary.lastSeq"),String(t.lastSeq)],[o("workflow.summary.updated"),c(new Date(t.updatedAt).toLocaleString())],[o("workflow.summary.revision"),c(Pe(n.revisionId))],[o("workflow.summary.initiator"),c(n.initiator??"-")]];n.failedNodeId&&s.push([o("workflow.summary.failedNode"),c(n.failedNodeId)]),n.cancelOriginEventId&&s.push([o("workflow.summary.cancelOrigin"),c(n.cancelOriginEventId)]),t.chatBinding&&(s.push([o("workflow.summary.chat"),`<code>${c(t.chatBinding.chatId)}</code>`]),s.push([o("workflow.summary.app"),`<code>${c(t.chatBinding.larkAppId)}</code>`])),e.innerHTML=s.map(([a,r])=>`<div class="wf-summary-item"><span>${a}</span><strong>${r}</strong></div>`).join("")}function oo(e){if(!e.errorCode)return"";let t=e.errorMessage?` \u2014 ${Wo(e.errorMessage,96)}`:"";return`<div class="wf-run-error">
|
|
676
|
+
<span class="muted error">${c(e.errorCode)}</span>${c(t)}
|
|
677
|
+
</div>`}function ao(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 so(e,t){let n=t.dangling,s=[[o("workflow.dangling.activities"),n.activities],[o("workflow.dangling.effects"),n.effectAttempted],[o("workflow.dangling.waits"),n.waits],[o("workflow.dangling.cancels"),n.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>${c(o("workflow.detail.dangling"))}</h3></div><div class="muted">${c(o("workflow.detail.noDangling"))}</div>`;return}e.innerHTML=`<div class="wf-panel-title"><h3>${c(o("workflow.detail.dangling"))}</h3><span class="wf-dangling has">${a}</span></div>
|
|
550
678
|
<div class="wf-dangling-grid">
|
|
551
|
-
${
|
|
552
|
-
</div>`}function
|
|
553
|
-
<span title="${
|
|
554
|
-
<span title="${
|
|
679
|
+
${s.map(([r,i])=>`<div><strong>${r}</strong>${i.length===0?`<div class="muted">${c(o("workflow.detail.none"))}</div>`:`<ul>${i.map(m=>`<li><code>${c(m)}</code></li>`).join("")}</ul>`}</div>`).join("")}
|
|
680
|
+
</div>`}function ro(e,t,n,s){let a=io(s,n);if(a.length===0){t.textContent="",e.innerHTML=`<div class="empty">${c(o("workflow.detail.noParallelData"))}</div>`;return}let r=Date.now(),i=Math.min(...a.map(l=>l.startedAt)),m=Math.max(...a.map(l=>l.endedAt??r),i+1e3),k=Math.max(1,m-i),$=co(a,r),p=a.filter(l=>!l.endedAt&&(l.status==="running"||l.status==="effectAttempting")).length;t.textContent=o("workflow.detail.parallelMeta",{count:a.length,max:$,running:p});let g=a.sort((l,I)=>l.startedAt-I.startedAt||l.activityId.localeCompare(I.activityId)).map(l=>lo(l,i,k,r)).join("");e.innerHTML=`<div class="wf-parallel-axis">
|
|
681
|
+
<span title="${c(new Date(i).toISOString())}">${c(qe(i))}</span>
|
|
682
|
+
<span title="${c(new Date(m).toISOString())}">${c(qe(m))}</span>
|
|
555
683
|
</div>
|
|
556
|
-
<div class="wf-parallel-list">${g}</div>`}function
|
|
684
|
+
<div class="wf-parallel-list">${g}</div>`}function io(e,t){let n=new Map,s=new Map(t.activities.map(a=>[a.activityId,a.ownerNodeId]));for(let a of[...e].sort((r,i)=>$e(r.eventId)-$e(i.eventId))){let r=_o(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 k=n.get(m);if(a.type==="attemptCreated"){let $=typeof r.attemptNumber=="number"?r.attemptNumber:void 0;k={nodeId:typeof r.nodeId=="string"?r.nodeId:s.get(i),activityId:i,attemptId:m,attemptNumber:$,status:"pending",startedAt:a.timestamp},n.set(m,k);continue}k||(k={nodeId:s.get(i),activityId:i,attemptId:m,status:"pending",startedAt:a.timestamp},n.set(m,k)),a.type==="activityRunning"?(k.status="running",k.runningAt=a.timestamp):a.type==="effectAttempted"?k.status="effectAttempting":a.type==="activityWaiting"||a.type==="waitCreated"?k.status="waiting":uo(a.type)&&(k.status=po(a.type),k.endedAt=a.timestamp,k.endType=a.type)}return[...n.values()]}function lo(e,t,n,s){let a=e.endedAt??s,r=_t((e.startedAt-t)/n*100,0,100),i=_t((Math.max(a,e.startedAt+1)-e.startedAt)/n*100,.7,100-r),m=e.nodeId??e.activityId,k=e.attemptNumber!==void 0?`#${e.attemptNumber}`:Pe(e.attemptId),$=[`${m} ${e.status}`,`${new Date(e.startedAt).toISOString()} \u2192 ${e.endedAt?new Date(e.endedAt).toISOString():o("workflow.detail.parallelNow")}`,e.endType?`end: ${e.endType}`:void 0].filter(Boolean).join(`
|
|
557
685
|
`);return`<div class="wf-parallel-row">
|
|
558
686
|
<div class="wf-parallel-label">
|
|
559
|
-
<code>${
|
|
560
|
-
<span class="muted">${
|
|
687
|
+
<code>${c(m)}</code>
|
|
688
|
+
<span class="muted">${c(e.activityId)} \xB7 ${c(k)}</span>
|
|
561
689
|
</div>
|
|
562
690
|
<div class="wf-parallel-track">
|
|
563
|
-
<div class="wf-parallel-bar wf-parallel-${
|
|
564
|
-
<span>${
|
|
691
|
+
<div class="wf-parallel-bar wf-parallel-${c(e.status)}" style="left:${r.toFixed(3)}%;width:${i.toFixed(3)}%;" title="${c($)}">
|
|
692
|
+
<span>${c(ie(e.status))}</span>
|
|
565
693
|
</div>
|
|
566
694
|
</div>
|
|
567
|
-
</div>`}function
|
|
568
|
-
<td>${e?`<code>${
|
|
569
|
-
<td>${e?
|
|
570
|
-
<td>${t?`<code>${
|
|
571
|
-
<td>${t?
|
|
695
|
+
</div>`}function co(e,t){let n=[];for(let r of e)n.push({time:r.startedAt,delta:1}),n.push({time:r.endedAt??t,delta:-1});n.sort((r,i)=>r.time-i.time||i.delta-r.delta);let s=0,a=0;for(let r of n)s+=r.delta,a=Math.max(a,s);return a}function uo(e){return e==="activitySucceeded"||e==="activityFailed"||e==="activityTimedOut"||e==="activityCanceled"}function po(e){return e==="activitySucceeded"?"succeeded":e==="activityCanceled"?"cancelled":e==="activityTimedOut"?"timedOut":"failed"}function fo(e,t){let n=new Map(t.activities.map(r=>[r.activityId,r])),s=new Set,a=[];for(let r of t.nodes){let i=(r.activityId?n.get(r.activityId):void 0)??t.activities.find(m=>m.ownerNodeId===r.nodeId);i&&s.add(i.activityId),a.push(Ut(r,i))}for(let r of t.activities)s.has(r.activityId)||a.push(Ut(void 0,r));e.innerHTML=a.length>0?a.join(""):`<tr><td colspan="7" class="empty">${c(o("workflow.detail.noNodes"))}</td></tr>`}function Ut(e,t){let n=t?.attempts[t.attempts.length-1];return`<tr>
|
|
696
|
+
<td>${e?`<code>${c(e.nodeId)}</code>`:'<span class="muted">-</span>'}</td>
|
|
697
|
+
<td>${e?ne(e.status):'<span class="muted">-</span>'}</td>
|
|
698
|
+
<td>${t?`<code>${c(t.activityId)}</code>`:'<span class="muted">-</span>'}</td>
|
|
699
|
+
<td>${t?ne(t.status):'<span class="muted">-</span>'}</td>
|
|
572
700
|
<td>${t?.attempts.length??0}</td>
|
|
573
|
-
<td>${
|
|
574
|
-
<td>${
|
|
575
|
-
</tr>`}function
|
|
701
|
+
<td>${n?`<code>${c(n.attemptId)}</code>`:'<span class="muted">-</span>'}</td>
|
|
702
|
+
<td>${n?No(n):`<span class="muted">${c(o("workflow.detail.idle"))}</span>`}</td>
|
|
703
|
+
</tr>`}function mo(e,t,n,s,a,r,i,m){Ro(e,n,s),Co(e,a.comments);let k=!!(i&&t.attemptIO?.[i]?.terminal);k&&i&&n.add(ot(i,o("workflow.detail.liveTerminal")));let $=wo(t),p=new Set;if(m){for(let l of $){p.add(l.key);let I=m.get(l.key);I||(I=go(l.key),m.set(l.key,I),e.appendChild(I.article)),ho(I,l,n,a,r,i)}for(let[l,I]of Array.from(m))p.has(l)||(I.article.remove(),m.delete(l));if($.length===0){if(!e.querySelector(".wf-io-empty-placeholder")){let l=document.createElement("div");l.className="empty wf-io-empty-placeholder",l.textContent=o("workflow.detail.noNodeIO"),e.appendChild(l)}}else e.querySelector(".wf-io-empty-placeholder")?.remove()}else{let l=[];for(let I of $)l.push($o(I,n,a,r,i));e.innerHTML=l.length>0?l.join(""):`<div class="empty">${c(o("workflow.detail.noNodeIO"))}</div>`}Bo(e,s);let g=Ao(e,i);return Do(e,n),Oo(e,s),Ho(e,a),Zt(e,r),g&&k}function wo(e){let t=new Map(e.activities.map(a=>[a.activityId,a])),n=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}n.add(r.activityId),s.push({key:`activity:${r.activityId}`,node:a,activity:r,io:e.attemptIO?.[Oe(r)?.attemptId??""]})}for(let a of e.activities)n.has(a.activityId)||s.push({key:`activity:${a.activityId}`,activity:a,io:e.attemptIO?.[Oe(a)?.attemptId??""]});return s}function go(e){let t=document.createElement("article");t.className="wf-io-card",t.dataset.wfCardKey=e;let n=document.createElement("div");n.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(n),t.appendChild(s),t.appendChild(a),{article:t,head:n,terminalSlot:s,grid:a,currentTerminalUrl:null}}function ho(e,t,n,s,a,r){let i=Oe(t.activity),m=t.node?.nodeId??t.activity?.ownerNodeId??t.activity?.activityId??"unknown",k=!!(i&&i.attemptId===r);e.article.classList.toggle("is-focused",k),i?e.article.dataset.wfAttemptCard=i.attemptId:delete e.article.dataset.wfAttemptCard;let $=Xt(i,s);e.head.innerHTML=`
|
|
576
704
|
<header>
|
|
577
705
|
<div>
|
|
578
|
-
<strong><code>${
|
|
579
|
-
<span class="muted">${t.activity?
|
|
706
|
+
<strong><code>${c(m)}</code></strong>
|
|
707
|
+
<span class="muted">${t.activity?c(t.activity.activityId):c(o("workflow.detail.notDispatched"))}</span>
|
|
580
708
|
</div>
|
|
581
|
-
<div>${t.node?
|
|
709
|
+
<div>${t.node?ne(t.node.status):""} ${t.activity?ne(t.activity.status):""}</div>
|
|
582
710
|
</header>
|
|
583
711
|
<div class="wf-io-meta">
|
|
584
|
-
${
|
|
712
|
+
${i?`${c(o("workflow.detail.attempt"))} <code>${c(i.attemptId)}</code>`:c(o("workflow.detail.noAttempt"))}
|
|
585
713
|
</div>
|
|
586
|
-
${
|
|
587
|
-
`;let
|
|
588
|
-
${
|
|
589
|
-
${
|
|
590
|
-
${
|
|
591
|
-
${
|
|
592
|
-
${t.io?.waitPrompt?
|
|
593
|
-
`}function
|
|
594
|
-
<summary>${
|
|
714
|
+
${$}
|
|
715
|
+
`;let p=zt(i,t.activity,t.io?.terminal,a),g=p?.url??null;if(g!==e.currentTerminalUrl)p===null?e.terminalSlot.innerHTML="":e.terminalSlot.innerHTML=Jt(t.key,i,t.activity,t.io?.terminal,p,n,a),e.currentTerminalUrl=g;else if(p!==null&&t.io?.terminal){let I=e.terminalSlot.querySelector("details.wf-terminal-block > summary");if(I){let L=Gt(p.kind);I.innerHTML=`${c(L)} ${Qt(i,t.io.terminal)}`}i&&xo(e.terminalSlot,i,t.activity,t.io.terminal,p,a)}let l=i?.attemptId??t.activity?.activityId??t.node?.nodeId??"unknown";e.grid.innerHTML=`
|
|
716
|
+
${Z(l,o("workflow.detail.authoredInput"),t.io?.input,n)}
|
|
717
|
+
${Z(l,o("workflow.detail.resolvedInput"),t.io?.resolvedInput,n)}
|
|
718
|
+
${Z(l,o("workflow.detail.output"),t.io?.output,n)}
|
|
719
|
+
${Z(l,o("workflow.detail.executionLog"),t.io?.log,n)}
|
|
720
|
+
${t.io?.waitPrompt?Z(l,o("workflow.detail.waitPrompt"),t.io.waitPrompt,n):""}
|
|
721
|
+
`}function zt(e,t,n,s){if(!n||n.error)return null;if(So(e,n))return{kind:"live",url:To(n)};if(!e||!t||!Io(e,n))return null;let a=Eo();if(!a)return null;let r=s?.sessions.get(e.attemptId);return r?{kind:"resume",url:r.url,resumeId:r.resumeId,downloadUrl:Ft(a,t.activityId,e.attemptId)}:{kind:"replay",url:Lo(a,t.activityId,e.attemptId,!!n.hasPtyLog),downloadUrl:Ft(a,t.activityId,e.attemptId)}}function Jt(e,t,n,s,a,r,i){if(!s)return"";let m=Gt(a.kind),k=ot(e,m),$=Qt(t,s),p=bo(a.kind),g=a.kind==="replay"||a.kind==="resume"?`<a class="btn-link" href="${c(a.downloadUrl)}" download>${c(o("workflow.detail.downloadFullLog"))}</a>`:"",l=t?Vt(t,n,s,a,i):"",I=t?Yt(t.attemptId,i):"";return`<details class="wf-io-block wf-terminal-block" data-io-key="${c(k)}"${r.has(k)?" open":""}>
|
|
722
|
+
<summary>${c(m)} ${$}</summary>
|
|
595
723
|
<div class="wf-terminal-actions">
|
|
596
|
-
<a class="btn-link" href="${
|
|
724
|
+
<a class="btn-link" href="${c(a.url)}" target="_blank" rel="noreferrer">${c(p)}</a>
|
|
597
725
|
${g}
|
|
598
726
|
${l}
|
|
599
727
|
</div>
|
|
600
|
-
${
|
|
601
|
-
<iframe class="wf-terminal-frame" src="${
|
|
602
|
-
</details>`}function
|
|
728
|
+
${I}
|
|
729
|
+
<iframe class="wf-terminal-frame" src="${c(a.url)}" title="${c(m)}" loading="lazy"></iframe>
|
|
730
|
+
</details>`}function Gt(e){return e==="live"?o("workflow.detail.liveTerminal"):e==="resume"?o("workflow.detail.terminalResume"):o("workflow.detail.terminalReplay")}function bo(e){return e==="live"?o("workflow.detail.openTerminalNewTab"):e==="resume"?o("workflow.detail.openResumeNewTab"):o("workflow.detail.openReplayNewTab")}var Kt=new Set(["antigravity","codex-app","cursor","mira"]),yo=new Set(["aiden","coco","claude-code","codex","mtr","hermes"]);function vo(e){return!!e&&(yo.has(e)||Kt.has(e))}function ko(e){return!!e&&Kt.has(e)}function Vt(e,t,n,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="${c(e.attemptId)}" data-wf-resume-activity="${c(t.activityId)}"`;return r?`<button type="button" class="btn-link" data-wf-resume-button="1" data-wf-resume-action="end" ${m}${i?" disabled":""}>${c(i?o("workflow.detail.resumeEnding"):o("workflow.detail.endResumeSession"))}</button>`:vo(n.cliId)?ko(n.cliId)&&!n.cliSessionId?`<button type="button" class="btn-link" data-wf-resume-button="1" disabled title="${c(o("workflow.detail.resumeMissingCliSession"))}">${c(o("workflow.detail.resumeSession"))}</button>`:`<button type="button" class="btn-link" data-wf-resume-button="1" data-wf-resume-action="start" ${m}${i?" disabled":""}>${c(i?o("workflow.detail.resumeStarting"):o("workflow.detail.resumeSession"))}</button>`:`<button type="button" class="btn-link" data-wf-resume-button="1" disabled title="${c(o("workflow.detail.resumeUnsupportedCli",{cliId:n.cliId??"?"}))}">${c(o("workflow.detail.resumeSession"))}</button>`}function Yt(e,t){if(!t)return"";let n=t.errors.get(e);return n?`<div class="hint-warn wf-resume-status" data-wf-resume-status="${c(e)}">${c(n)}</div>`:""}function $o(e,t,n,s,a){let r=Oe(e.activity),i=e.node?.nodeId??e.activity?.ownerNodeId??e.activity?.activityId??"unknown",m=r?.attemptId??e.activity?.activityId??e.node?.nodeId??"unknown",k=Xt(r,n),$=r?.attemptId===a?" is-focused":"",p=r?` data-wf-attempt-card="${c(r.attemptId)}"`:"",g=zt(r,e.activity,e.io?.terminal,s),l=g?Jt(m,r,e.activity,e.io?.terminal,g,t,s):"";return`<article class="wf-io-card${$}" data-wf-card-key="${c(e.key)}"${p}>
|
|
603
731
|
<div class="wf-io-card-head">
|
|
604
732
|
<header>
|
|
605
733
|
<div>
|
|
606
|
-
<strong><code>${i
|
|
607
|
-
<span class="muted">${e.activity?
|
|
734
|
+
<strong><code>${c(i)}</code></strong>
|
|
735
|
+
<span class="muted">${e.activity?c(e.activity.activityId):c(o("workflow.detail.notDispatched"))}</span>
|
|
608
736
|
</div>
|
|
609
|
-
<div>${e.node?
|
|
737
|
+
<div>${e.node?ne(e.node.status):""} ${e.activity?ne(e.activity.status):""}</div>
|
|
610
738
|
</header>
|
|
611
739
|
<div class="wf-io-meta">
|
|
612
|
-
${
|
|
740
|
+
${r?`${c(o("workflow.detail.attempt"))} <code>${c(r.attemptId)}</code>`:c(o("workflow.detail.noAttempt"))}
|
|
613
741
|
</div>
|
|
614
|
-
${
|
|
742
|
+
${k}
|
|
615
743
|
</div>
|
|
616
744
|
<div class="wf-io-terminal-slot">${l}</div>
|
|
617
745
|
<div class="wf-io-grid">
|
|
618
|
-
${
|
|
619
|
-
${
|
|
620
|
-
${
|
|
621
|
-
${
|
|
622
|
-
${e.io?.waitPrompt?
|
|
746
|
+
${Z(m,o("workflow.detail.authoredInput"),e.io?.input,t)}
|
|
747
|
+
${Z(m,o("workflow.detail.resolvedInput"),e.io?.resolvedInput,t)}
|
|
748
|
+
${Z(m,o("workflow.detail.output"),e.io?.output,t)}
|
|
749
|
+
${Z(m,o("workflow.detail.executionLog"),e.io?.log,t)}
|
|
750
|
+
${e.io?.waitPrompt?Z(m,o("workflow.detail.waitPrompt"),e.io.waitPrompt,t):""}
|
|
623
751
|
</div>
|
|
624
|
-
</article>`}function
|
|
752
|
+
</article>`}function Oe(e){return e?.attempts[e.attempts.length-1]}function Qt(e,t){let n=[];return t.error?n.push(o("workflow.detail.error")):n.push(t.status==="live"?o("workflow.detail.terminalLive"):o("workflow.detail.terminalClosedShort")),e?.status&&n.push(e.status),t.webPort>0&&n.push(`:${t.webPort}`),`<span class="muted">${c(n.join(" \xB7 "))}</span>`}function So(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 To(e){return`http://${window.location.hostname||"127.0.0.1"}:${e.webPort}`}function Lo(e,t,n,s){let a=new URLSearchParams({runId:e,activityId:t,attemptId:n});return s&&a.set("hasPtyLog","1"),`/assets/terminal-replay.html?${a.toString()}`}function Ft(e,t,n){return`/api/workflows/runs/${encodeURIComponent(e)}/attempts/${encodeURIComponent(t)}/${encodeURIComponent(n)}/terminal-log/raw?download=1`}function Eo(){let e=window.location.hash.match(/^#\/workflows\/([^/?#]+)/);if(!e)return null;try{return decodeURIComponent(e[1])}catch{return null}}function Xt(e,t){if(!Mo(e))return"";let n=e.attemptId,s=t.comments.get(n)??"",a=t.resolving.has(n),r=t.statuses.get(n),i=r?.kind==="error"?"hint-warn":"hint-ok";return`<div class="wf-approval-box" data-wf-approval="${c(n)}">
|
|
625
753
|
<label>
|
|
626
|
-
<span>${
|
|
627
|
-
<textarea class="wf-approval-comment" data-wf-approval-comment="${
|
|
754
|
+
<span>${c(o("workflow.detail.approvalComment"))}</span>
|
|
755
|
+
<textarea class="wf-approval-comment" data-wf-approval-comment="${c(n)}" rows="2" placeholder="${c(o("workflow.detail.optionalComment"))}"${a?" disabled":""}>${c(s)}</textarea>
|
|
628
756
|
</label>
|
|
629
757
|
<div class="wf-approval-actions">
|
|
630
|
-
<button type="button" class="primary" data-wf-approval-action="approve" data-wf-attempt-id="${
|
|
631
|
-
<button type="button" data-wf-approval-action="reject" data-wf-attempt-id="${
|
|
632
|
-
${a?`<span class="muted">${
|
|
758
|
+
<button type="button" class="primary" data-wf-approval-action="approve" data-wf-attempt-id="${c(n)}"${a?" disabled":""}>${c(o("workflow.detail.approve"))}</button>
|
|
759
|
+
<button type="button" data-wf-approval-action="reject" data-wf-attempt-id="${c(n)}"${a?" disabled":""}>${c(o("workflow.detail.reject"))}</button>
|
|
760
|
+
${a?`<span class="muted">${c(o("workflow.detail.submitting"))}</span>`:""}
|
|
633
761
|
</div>
|
|
634
|
-
${
|
|
635
|
-
</div>`}function
|
|
636
|
-
<summary>${
|
|
637
|
-
${
|
|
638
|
-
</details>`}function
|
|
639
|
-
<td>${
|
|
640
|
-
<td><code>${
|
|
641
|
-
<td>${
|
|
642
|
-
<td>${t.nodeId?`<code>${
|
|
643
|
-
<td>${t.activityId?`<code>${
|
|
644
|
-
<td>${t.errorCode?`<span class="muted error">${
|
|
645
|
-
<td title="${
|
|
646
|
-
</tr>`}function
|
|
762
|
+
${r?`<div class="${i} wf-approval-status">${c(r.text)}</div>`:""}
|
|
763
|
+
</div>`}function Mo(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(n=>{let s=n.dataset.wfApprovalComment;s&&t.set(s,n.value)})}function Zt(e,t){e.querySelectorAll("button[data-wf-resume-action][data-wf-resume-attempt][data-wf-resume-activity]").forEach(n=>{n.dataset.wfResumeBound!=="1"&&(n.dataset.wfResumeBound="1",n.addEventListener("click",()=>{let s=n.dataset.wfResumeAttempt,a=n.dataset.wfResumeActivity,r=n.dataset.wfResumeAction;!s||!a||(r==="start"?t.onStart(s,a):r==="end"&&t.onEnd(s,a))}))})}function xo(e,t,n,s,a,r){let i=e.querySelector(".wf-terminal-actions");if(!i)return;let m=i.querySelector('button[data-wf-resume-button="1"]'),k=Vt(t,n,s,a,r);m?m.outerHTML=k:k&&i.insertAdjacentHTML("beforeend",k);let $=e.querySelector("details.wf-terminal-block");if($){let p=$.querySelector(".wf-resume-status"),g=Yt(t.attemptId,r);p?p.outerHTML=g:g&&i.insertAdjacentHTML("afterend",g)}Zt(e,r)}function Ho(e,t){e.querySelectorAll("textarea[data-wf-approval-comment]").forEach(n=>{let s=n.dataset.wfApprovalComment;s&&n.addEventListener("input",()=>{t.comments.set(s,n.value)})}),e.querySelectorAll("button[data-wf-approval-action][data-wf-attempt-id]").forEach(n=>{n.addEventListener("click",()=>{let s=n.dataset.wfAttemptId,a=n.dataset.wfApprovalAction;!s||a!=="approve"&&a!=="reject"||t.onResolve(s,a)})})}function Z(e,t,n,s){let a=ot(e,t);return`<details class="wf-io-block" data-io-key="${c(a)}"${s.has(a)?" open":""}>
|
|
764
|
+
<summary>${c(t)} ${qo(n)}</summary>
|
|
765
|
+
${Po(n)}
|
|
766
|
+
</details>`}function ot(e,t){return`${e}:${t}`}function Ao(e,t){if(!t)return!1;for(let n of e.querySelectorAll("[data-wf-attempt-card]"))if(n.dataset.wfAttemptCard===t)return n.scrollIntoView({block:"center"}),!0;return!1}function Ro(e,t,n){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&&n.set(a,r.scrollTop)})}function Do(e,t){e.querySelectorAll("details.wf-io-block[data-io-key]").forEach(n=>{n.dataset.ioToggleBound!=="1"&&(n.dataset.ioToggleBound="1",n.addEventListener("toggle",()=>{let s=n.dataset.ioKey;s&&(n.open?t.add(s):t.delete(s))}))})}function Bo(e,t){e.querySelectorAll("details.wf-io-block[data-io-key]").forEach(n=>{let s=n.dataset.ioKey;if(!s)return;let a=t.get(s);if(a===void 0)return;let r=n.querySelector(".wf-io-pre");r&&(r.scrollTop=a)})}function Oo(e,t){e.querySelectorAll("details.wf-io-block[data-io-key]").forEach(n=>{let s=n.dataset.ioKey;if(!s)return;let a=n.querySelector(".wf-io-pre");a&&a.dataset.ioScrollBound!=="1"&&(a.dataset.ioScrollBound="1",a.addEventListener("scroll",()=>{t.set(s,a.scrollTop)}))})}function qo(e){if(!e)return`<span class="muted">${c(o("workflow.detail.empty"))}</span>`;let t=[];return e.outputBytes!==void 0&&t.push(`${e.outputBytes}B`),e.truncated&&t.push(o("workflow.detail.truncated")),e.error&&t.push(o("workflow.detail.error")),e.outputHash&&t.push(Pe(e.outputHash)),t.length?`<span class="muted">${c(t.join(" \xB7 "))}</span>`:""}function Po(e){if(!e)return`<div class="muted wf-io-empty">${c(o("workflow.detail.noData"))}</div>`;let t=e.value!==void 0?JSON.stringify(e.value,null,2):e.text??"",n=e.error?`<div class="muted error">${c(e.error)}</div>`:"";return t?`${n}<pre class="wf-io-pre">${c(t)}</pre>`:`${n}<div class="muted wf-io-empty">${c(o("workflow.detail.noPreview"))}</div>`}function No(e){let t=[];if(e.effectAttempted&&t.push(`${c(o("workflow.detail.effect"))} ${c(e.effectAttempted.provider)}`),e.wait){let n=e.wait.resolution?`${e.wait.resolution.kind}${e.wait.resolution.resolution?":"+e.wait.resolution.resolution:""}`:o("workflow.detail.open");t.push(`${c(o("workflow.detail.wait"))} ${c(e.wait.waitKind)} ${c(n)}`),e.wait.deadlineAt!==void 0&&t.push(`${c(o("workflow.detail.deadline"))} ${c(qe(e.wait.deadlineAt))}`)}if(e.error){let n=`${e.error.errorCode}${e.error.errorClass?` \xB7 ${e.error.errorClass}`:""}`;t.push(`<span class="muted error">${c(n)}</span>`),e.error.errorMessage&&t.push(`<span class="error wf-error-msg">${c(e.error.errorMessage)}</span>`)}return e.output&&t.push(`${c(o("workflow.detail.output"))} ${c(Pe(e.output.outputHash))}`),e.runningMs!==void 0&&t.push(`${e.runningMs}ms`),t.length>0?t.join("<br/>"):'<span class="muted">-</span>'}function jo(e,t){e.innerHTML=t.length>0?t.map(Uo).join(""):`<tr><td colspan="7" class="empty">${c(o("workflow.detail.noEvents"))}</td></tr>`}function Uo(e){let t=Fo(e.payload);return`<tr>
|
|
767
|
+
<td>${$e(e.eventId)}</td>
|
|
768
|
+
<td><code>${c(e.type)}</code></td>
|
|
769
|
+
<td>${c(e.actor)}</td>
|
|
770
|
+
<td>${t.nodeId?`<code>${c(t.nodeId)}</code>`:"-"}</td>
|
|
771
|
+
<td>${t.activityId?`<code>${c(t.activityId)}</code>`:"-"}</td>
|
|
772
|
+
<td>${t.errorCode?`<span class="muted error">${c(t.errorCode)}</span>`:"-"}</td>
|
|
773
|
+
<td title="${c(new Date(e.timestamp).toISOString())}">${c(qe(e.timestamp))}</td>
|
|
774
|
+
</tr>`}function $e(e){let t=e.lastIndexOf("-");if(t<0)return 0;let n=Number(e.slice(t+1));return Number.isFinite(n)?n:0}function Fo(e){if(!e||typeof e!="object"||"ref"in e)return{};let t=e,n={};typeof t.nodeId=="string"&&(n.nodeId=t.nodeId),typeof t.activityId=="string"&&(n.activityId=t.activityId),typeof t.failedNodeId=="string"&&(n.nodeId=t.failedNodeId);let s=t.error;return s&&typeof s=="object"&&"errorCode"in s&&(n.errorCode=String(s.errorCode)),n}function _o(e){return!e.payload||typeof e.payload!="object"||"ref"in e.payload?null:e.payload}function _t(e,t,n){return Math.min(n,Math.max(t,e))}function Pe(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 qe(e){return new Date(e).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit",second:"2-digit"})}function C(e){return e.replace(/[&<>"']/g,t=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[t])}function en(e){return e?e.length>18?e.slice(0,10)+"..."+e.slice(-6):e:"-"}function tn(e){let t=location.hash.match(/^#\/(?:workflows\/catalog|workflows-catalog)\/([^/?#]+)$/);return t?Jo(e,decodeURIComponent(t[1])):zo(e)}function zo(e){e.innerHTML=`
|
|
647
775
|
<nav class="wf-subnav">
|
|
648
|
-
<a href="#/workflows" data-i18n="workflow.subnav.runs">${
|
|
649
|
-
<a href="#/workflows/catalog" class="active" data-i18n="workflow.subnav.catalog">${
|
|
776
|
+
<a href="#/workflows" data-i18n="workflow.subnav.runs">${C(o("workflow.subnav.runs"))}</a>
|
|
777
|
+
<a href="#/workflows/catalog" class="active" data-i18n="workflow.subnav.catalog">${C(o("workflow.subnav.catalog"))}</a>
|
|
650
778
|
</nav>
|
|
651
779
|
<section class="catalog-head">
|
|
652
780
|
<div>
|
|
653
|
-
<h2>${
|
|
654
|
-
<p class="muted">${
|
|
781
|
+
<h2>${C(o("catalog.title"))}</h2>
|
|
782
|
+
<p class="muted">${C(o("catalog.subtitle"))}</p>
|
|
655
783
|
</div>
|
|
656
|
-
<button id="catalog-refresh" type="button">${
|
|
784
|
+
<button id="catalog-refresh" type="button">${C(o("catalog.refresh"))}</button>
|
|
657
785
|
</section>
|
|
658
786
|
<form id="catalog-filters" class="filters">
|
|
659
|
-
<input type="search" name="q" placeholder="${
|
|
787
|
+
<input type="search" name="q" placeholder="${C(o("catalog.searchPlaceholder"))}" />
|
|
660
788
|
<span id="catalog-status" class="muted"></span>
|
|
661
789
|
</form>
|
|
662
790
|
<div class="wf-table-scroll">
|
|
663
791
|
<table>
|
|
664
792
|
<thead><tr>
|
|
665
|
-
<th>${
|
|
666
|
-
<th>${
|
|
667
|
-
<th>${
|
|
668
|
-
<th>${
|
|
669
|
-
<th>${
|
|
670
|
-
<th>${
|
|
793
|
+
<th>${C(o("catalog.table.workflow"))}</th>
|
|
794
|
+
<th>${C(o("catalog.table.version"))}</th>
|
|
795
|
+
<th>${C(o("catalog.table.params"))}</th>
|
|
796
|
+
<th>${C(o("catalog.table.nodes"))}</th>
|
|
797
|
+
<th>${C(o("catalog.table.revision"))}</th>
|
|
798
|
+
<th>${C(o("catalog.table.path"))}</th>
|
|
671
799
|
</tr></thead>
|
|
672
800
|
<tbody id="catalog-tbody"></tbody>
|
|
673
801
|
</table>
|
|
674
802
|
</div>
|
|
675
|
-
`;let t=e.querySelector("#catalog-tbody"),
|
|
803
|
+
`;let t=e.querySelector("#catalog-tbody"),n=e.querySelector("#catalog-status"),s=e.querySelector("#catalog-filters"),a=e.querySelector("#catalog-refresh"),r=[],i=null,m=!1;function k(){let g=(new FormData(s).get("q")??"").trim().toLowerCase();return g?r.filter(l=>l.workflowId.toLowerCase().includes(g)||l.path.toLowerCase().includes(g)):r}function $(){i?(n.textContent=o("catalog.loadFailed",{error:i}),n.classList.add("error")):(n.textContent=`${r.length}`,n.classList.remove("error"));let g=k();if(g.length===0){t.innerHTML=`<tr><td colspan="6" class="empty">${r.length===0?C(o("catalog.noDefinitions")):C(o("catalog.noFilterMatch"))}</td></tr>`;return}t.innerHTML=g.map(l=>`
|
|
676
804
|
<tr>
|
|
677
|
-
<td><a href="#/workflows/catalog/${encodeURIComponent(l.workflowId)}"><code>${
|
|
805
|
+
<td><a href="#/workflows/catalog/${encodeURIComponent(l.workflowId)}"><code>${C(l.workflowId)}</code></a></td>
|
|
678
806
|
<td>${l.version}</td>
|
|
679
|
-
<td>${
|
|
807
|
+
<td>${C(o("catalog.paramSummary",{required:l.requiredParamCount,total:l.paramCount}))}</td>
|
|
680
808
|
<td>${l.nodeCount}</td>
|
|
681
|
-
<td><code>${
|
|
682
|
-
<td><code>${
|
|
809
|
+
<td><code>${C(en(l.revisionId))}</code></td>
|
|
810
|
+
<td><code>${C(l.path)}</code></td>
|
|
683
811
|
</tr>
|
|
684
|
-
`).join("")}async function
|
|
812
|
+
`).join("")}async function p(){a.disabled=!0,n.textContent=o("catalog.loading");try{let g=await fetch("/api/workflows/definitions");if(!g.ok)throw new Error(`HTTP ${g.status}`);r=(await g.json()).definitions??[],i=null}catch(g){i=g?.message??String(g),r=[]}finally{a.disabled=!1,m||$()}}return s.addEventListener("input",$),a.addEventListener("click",()=>{p()}),p(),()=>{m=!0}}function Jo(e,t){e.innerHTML=`
|
|
685
813
|
<div class="catalog-detail-head">
|
|
686
|
-
<a class="btn-link" href="#/workflows/catalog">${
|
|
814
|
+
<a class="btn-link" href="#/workflows/catalog">${C(o("catalog.back"))}</a>
|
|
687
815
|
<div>
|
|
688
|
-
<h2><code>${
|
|
689
|
-
<div id="catalog-detail-subtitle" class="muted">${
|
|
816
|
+
<h2><code>${C(t)}</code></h2>
|
|
817
|
+
<div id="catalog-detail-subtitle" class="muted">${C(o("workflow.detail.loading"))}</div>
|
|
690
818
|
</div>
|
|
691
819
|
</div>
|
|
692
820
|
<section id="catalog-error" class="hint-warn" hidden></section>
|
|
693
821
|
<section id="catalog-run-status" class="hint-ok" hidden></section>
|
|
694
822
|
<div id="catalog-detail-body"></div>
|
|
695
|
-
`;let
|
|
823
|
+
`;let n=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,k=!1;function $(f){s.hidden=!f,s.textContent=f??""}function p(f){a.hidden=!f,a.textContent=f??""}function g(f){let T={};for(let[S,y]of Object.entries(f??{}))"default"in y&&(T[S]=y.default);return T}function l(){if(!i)return;let f=i.definition;n.textContent=`${o("catalog.revision")} ${en(i.revisionId)} \xB7 ${i.path}`;let T=JSON.stringify(g(f.params),null,2);r.innerHTML=`
|
|
696
824
|
<section class="wf-panel">
|
|
697
|
-
<div class="wf-panel-title"><h3>${
|
|
825
|
+
<div class="wf-panel-title"><h3>${C(o("catalog.summary"))}</h3></div>
|
|
698
826
|
<div class="wf-summary-grid">
|
|
699
|
-
<div class="wf-summary-item"><span>${
|
|
700
|
-
<div class="wf-summary-item"><span>${
|
|
701
|
-
<div class="wf-summary-item"><span>${
|
|
702
|
-
<div class="wf-summary-item"><span>${
|
|
827
|
+
<div class="wf-summary-item"><span>${C(o("catalog.table.workflow"))}</span><strong><code>${C(f.workflowId)}</code></strong></div>
|
|
828
|
+
<div class="wf-summary-item"><span>${C(o("catalog.table.version"))}</span><strong>${f.version}</strong></div>
|
|
829
|
+
<div class="wf-summary-item"><span>${C(o("catalog.nodeCount"))}</span><strong>${Object.keys(f.nodes).length}</strong></div>
|
|
830
|
+
<div class="wf-summary-item"><span>${C(o("catalog.path"))}</span><strong><code>${C(i.path)}</code></strong></div>
|
|
703
831
|
</div>
|
|
704
832
|
</section>
|
|
705
833
|
|
|
706
834
|
<section class="wf-panel">
|
|
707
|
-
<div class="wf-panel-title"><h3>${
|
|
835
|
+
<div class="wf-panel-title"><h3>${C(o("catalog.runPanel"))}</h3></div>
|
|
708
836
|
<form id="catalog-run-form" class="catalog-run-form">
|
|
709
837
|
<label>
|
|
710
|
-
<span>${
|
|
711
|
-
<textarea id="catalog-params" rows="8" spellcheck="false" placeholder="${
|
|
838
|
+
<span>${C(o("catalog.paramsJson"))}</span>
|
|
839
|
+
<textarea id="catalog-params" rows="8" spellcheck="false" placeholder="${C(o("catalog.paramsPlaceholder"))}">${C(T)}</textarea>
|
|
712
840
|
</label>
|
|
713
841
|
<div class="catalog-chat-grid">
|
|
714
842
|
<label>
|
|
715
|
-
<span>${
|
|
843
|
+
<span>${C(o("catalog.chatId"))}</span>
|
|
716
844
|
<input id="catalog-chat-id" type="text" autocomplete="off" />
|
|
717
845
|
</label>
|
|
718
846
|
<label>
|
|
719
|
-
<span>${
|
|
847
|
+
<span>${C(o("catalog.larkAppId"))}</span>
|
|
720
848
|
<input id="catalog-lark-app-id" type="text" autocomplete="off" />
|
|
721
849
|
</label>
|
|
722
850
|
</div>
|
|
723
|
-
<div class="muted">${
|
|
851
|
+
<div class="muted">${C(o("catalog.chatBindingHint"))}</div>
|
|
724
852
|
<div id="catalog-param-errors" class="catalog-param-errors" hidden></div>
|
|
725
|
-
<button id="catalog-run-btn" type="submit" class="primary">${
|
|
853
|
+
<button id="catalog-run-btn" type="submit" class="primary">${C(o("catalog.run"))}</button>
|
|
726
854
|
</form>
|
|
727
855
|
</section>
|
|
728
856
|
|
|
729
857
|
<section class="wf-panel">
|
|
730
|
-
<div class="wf-panel-title"><h3>${
|
|
731
|
-
${
|
|
858
|
+
<div class="wf-panel-title"><h3>${C(o("catalog.paramsSchema"))}</h3></div>
|
|
859
|
+
${Go(f.params)}
|
|
732
860
|
</section>
|
|
733
861
|
|
|
734
862
|
<section class="wf-panel">
|
|
735
|
-
<div class="wf-panel-title"><h3>${
|
|
736
|
-
<pre class="wf-io-pre">${
|
|
863
|
+
<div class="wf-panel-title"><h3>${C(o("catalog.definitionJson"))}</h3></div>
|
|
864
|
+
<pre class="wf-io-pre">${C(JSON.stringify(f,null,2))}</pre>
|
|
737
865
|
</section>
|
|
738
|
-
`,
|
|
866
|
+
`,L()}async function I(){if(!i||k)return;let f=r.querySelector("#catalog-params"),T=r.querySelector("#catalog-chat-id"),S=r.querySelector("#catalog-lark-app-id"),y=r.querySelector("#catalog-run-btn"),x=r.querySelector("#catalog-param-errors"),u;try{if(u=JSON.parse(f.value||"{}"),!u||typeof u!="object"||Array.isArray(u))throw new Error(o("catalog.badParamsJson"))}catch(w){x.hidden=!1,x.innerHTML=`<div class="muted error">${C(w?.message??String(w))}</div>`;return}k=!0,y.disabled=!0,y.textContent=o("catalog.running"),x.hidden=!0,$(null),p(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:T.value.trim(),larkAppId:S.value.trim()}})});if(w.status===401)throw new Error(o("catalog.writeAccess"));let h=await w.json().catch(()=>({}));if(!w.ok||!h.ok)throw h.issues?.length&&(x.hidden=!1,x.innerHTML=`<strong>${C(o("catalog.invalidParams"))}</strong><ul>${h.issues.map(E=>`<li>${C(o("catalog.issue",{path:E.path.length?E.path.join("."):"(root)",message:E.message}))}</li>`).join("")}</ul>`),new Error(h.hint??h.message??h.error??o("catalog.runHttp",{status:w.status}));p(o("catalog.runStarted")),h.runId&&(location.hash=`#/workflows/${encodeURIComponent(h.runId)}`)}catch(w){$(w?.message??String(w))}finally{k=!1,y.disabled=!1,y.textContent=o("catalog.run")}}function L(){r.querySelector("#catalog-run-form")?.addEventListener("submit",T=>{T.preventDefault(),I()})}async function b(){try{let f=await fetch(`/api/workflows/definitions/${encodeURIComponent(t)}`);if(f.status===404)throw new Error("unknown_workflow");if(!f.ok)throw new Error(`HTTP ${f.status}`);i=await f.json(),$(null),l()}catch(f){$(o("catalog.definitionLoadFailed",{error:f?.message??String(f)})),n.textContent=o("workflow.detail.loadFailed")}}return b().then(()=>{}),()=>{m=!0}}function Go(e){let t=Object.entries(e??{});return t.length===0?`<div class="muted">${C(o("catalog.noParams"))}</div>`:`<div class="catalog-param-list">${t.map(([n,s])=>`
|
|
739
867
|
<article class="catalog-param">
|
|
740
868
|
<header>
|
|
741
|
-
<code>${
|
|
742
|
-
<span class="wf-status">${
|
|
743
|
-
<span class="muted">${
|
|
869
|
+
<code>${C(n)}</code>
|
|
870
|
+
<span class="wf-status">${C(s.required?o("catalog.required"):o("catalog.optional"))}</span>
|
|
871
|
+
<span class="muted">${C(s.type)}${s.format?` \xB7 ${C(s.format)}`:""}</span>
|
|
744
872
|
</header>
|
|
745
|
-
${
|
|
746
|
-
${"default"in
|
|
873
|
+
${s.description?`<div class="muted">${C(o("catalog.description"))}: ${C(s.description)}</div>`:""}
|
|
874
|
+
${"default"in s?`<pre class="wf-io-pre">${C(`${o("catalog.default")}: ${JSON.stringify(s.default,null,2)}`)}</pre>`:""}
|
|
747
875
|
</article>
|
|
748
|
-
`).join("")}</div>`}var
|
|
749
|
-
<img class="qr-image" src="${e.qrDataUrl}" alt="${
|
|
750
|
-
${e.qrUrl?`<a class="onboarding-link" href="${e.qrUrl}" target="_blank" rel="noopener">${
|
|
751
|
-
</div>`:"",
|
|
876
|
+
`).join("")}</div>`}var pe=null,Ne=null;function je(){Ne!==null&&(window.clearInterval(Ne),Ne=null)}function nn(){return pe||(pe=document.createElement("dialog"),pe.className="onboarding-dialog",document.body.appendChild(pe),pe.addEventListener("close",je),pe)}function Ko(e){return e.status==="waiting_for_scan"?o("botOnboarding.waiting"):e.status==="verifying"?o("botOnboarding.verifying"):e.status==="completed"?o("botOnboarding.completed"):e.status==="failed"?`${o("botOnboarding.failed")}: ${e.message??e.error??"unknown"}`:o("botOnboarding.starting")}function Se(e){let t=nn(),n=e.qrDataUrl?`<div class="qr-card">
|
|
877
|
+
<img class="qr-image" src="${e.qrDataUrl}" alt="${o("botOnboarding.qrAlt")}">
|
|
878
|
+
${e.qrUrl?`<a class="onboarding-link" href="${e.qrUrl}" target="_blank" rel="noopener">${o("botOnboarding.openLink")}</a>`:""}
|
|
879
|
+
</div>`:"",s=e.appId?`<p><b>App ID:</b> <code>${e.appId}</code></p>`:"",a=e.status==="completed"?`<p class="hint-ok">${o("botOnboarding.restartHint")}</p>`:"";t.innerHTML=`<article>
|
|
752
880
|
<header>
|
|
753
|
-
<h3>${
|
|
754
|
-
<p>${
|
|
881
|
+
<h3>${o("botOnboarding.title")}</h3>
|
|
882
|
+
<p>${o("botOnboarding.intro")}</p>
|
|
755
883
|
</header>
|
|
756
|
-
<p class="onboarding-status status-${e.status}">${
|
|
757
|
-
${
|
|
758
|
-
${
|
|
884
|
+
<p class="onboarding-status status-${e.status}">${Ko(e)}</p>
|
|
885
|
+
${n}
|
|
886
|
+
${s}
|
|
759
887
|
${a}
|
|
760
|
-
<form method="dialog"><button>${
|
|
761
|
-
</article>`}async function
|
|
888
|
+
<form method="dialog"><button>${o("botOnboarding.close")}</button></form>
|
|
889
|
+
</article>`}async function Vo(e){let t=await fetch(`/api/bot-onboarding/${encodeURIComponent(e)}`),n=await t.json();if(!t.ok||!n?.job)throw new Error(n?.error??`http_${t.status}`);Se(n.job),(n.job.status==="completed"||n.job.status==="failed")&&je()}async function Yo(){je(),Se({id:"",status:"starting"});let e=nn();e.open||e.showModal();try{let t=await fetch("/api/bot-onboarding/start",{method:"POST"}),n=await t.json();if(!t.ok||!n?.job?.id)throw new Error(n?.error??`http_${t.status}`);Se(n.job),Ne=window.setInterval(()=>{Vo(n.job.id).catch(s=>{je(),Se({id:n.job.id,status:"failed",message:s instanceof Error?s.message:String(s)})})},1200)}catch(t){Se({id:"",status:"failed",message:t instanceof Error?t.message:String(t)})}}function on(){let e=document.getElementById("add-bot-btn");e&&(e.onclick=()=>{Yo()})}var V=document.getElementById("root"),Ie=null;function at(){Ie&&(Ie(),Ie=null);let e=location.hash||"#/";e.startsWith("#/workflows/catalog")||e.startsWith("#/workflows-catalog")?Ie=tn(V):e.startsWith("#/workflows")?Ie=Wt(V):e.startsWith("#/groups")?St(V):e.startsWith("#/bot-defaults")?Lt(V):e.startsWith("#/connectors")?jt(V):e.startsWith("#/team/manage")?qt(V):e.startsWith("#/team")?Ot(V):e.startsWith("#/roles")?xt(V):e.startsWith("#/schedules")?kt(V):e.startsWith("#/sessions")?yt(V):ht(V);for(let t of document.querySelectorAll(".sidebar-nav a")){let n=t.getAttribute("href")??"#/";t.classList.toggle("active",n===(e||"#/"))}}var st=document.getElementById("status");function sn(){st&&(st.textContent=N.online?o("status.live"):o("status.disconnected"),st.className="connection-status "+(N.online?"online":"offline"))}N.on(sn);function an(){document.querySelectorAll("[data-i18n]").forEach(e=>{e.textContent=o(e.dataset.i18n??"")}),document.querySelectorAll("[data-locale]").forEach(e=>{e.classList.toggle("active",e.dataset.locale===ae.locale)}),document.querySelectorAll("[data-theme-mode]").forEach(e=>{e.classList.toggle("active",e.dataset.themeMode===ae.themeMode)}),sn()}function Qo(){document.querySelectorAll("[data-locale]").forEach(e=>{e.onclick=()=>ae.setLocale(e.dataset.locale)}),document.querySelectorAll("[data-theme-mode]").forEach(e=>{e.onclick=()=>ae.setThemeMode(e.dataset.themeMode)})}(async()=>{ae.init(),Qo(),on(),ae.on(()=>{an(),at()}),an();try{await ut()}catch(e){console.error("botmux dashboard bootstrap failed",e),N.setOnline(!1)}window.addEventListener("hashchange",at),at()})();})();
|