botmux 3.11.0 → 3.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.runtime-build-id +1 -1
- package/dist/adapters/backend/riff-backend.d.ts.map +1 -1
- package/dist/adapters/backend/riff-backend.js +11 -2
- package/dist/adapters/backend/riff-backend.js.map +1 -1
- package/dist/adapters/cli/coco.js +3 -3
- package/dist/adapters/cli/coco.js.map +1 -1
- package/dist/adapters/cli/codex.js +2 -2
- package/dist/adapters/cli/codex.js.map +1 -1
- package/dist/cli/dashboard-command.d.ts +23 -0
- package/dist/cli/dashboard-command.d.ts.map +1 -0
- package/dist/cli/dashboard-command.js +61 -0
- package/dist/cli/dashboard-command.js.map +1 -0
- package/dist/cli/dashboard-endpoint.d.ts +3 -3
- package/dist/cli/dashboard-endpoint.d.ts.map +1 -1
- package/dist/cli/dashboard-endpoint.js +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +36 -22
- package/dist/cli.js.map +1 -1
- package/dist/codex-rpc-engine.d.ts +33 -3
- package/dist/codex-rpc-engine.d.ts.map +1 -1
- package/dist/codex-rpc-engine.js +182 -20
- package/dist/codex-rpc-engine.js.map +1 -1
- package/dist/codex-rpc-lifecycle.d.ts +42 -7
- package/dist/codex-rpc-lifecycle.d.ts.map +1 -1
- package/dist/codex-rpc-lifecycle.js +73 -7
- package/dist/codex-rpc-lifecycle.js.map +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.js +1 -1
- package/dist/core/command-handler.d.ts +3 -2
- package/dist/core/command-handler.d.ts.map +1 -1
- package/dist/core/command-handler.js +3 -2
- package/dist/core/command-handler.js.map +1 -1
- package/dist/core/dashboard-ipc-server.d.ts.map +1 -1
- package/dist/core/dashboard-ipc-server.js +15 -10
- package/dist/core/dashboard-ipc-server.js.map +1 -1
- package/dist/core/dispatch.d.ts +9 -10
- package/dist/core/dispatch.d.ts.map +1 -1
- package/dist/core/dispatch.js +17 -10
- package/dist/core/dispatch.js.map +1 -1
- package/dist/core/role-resolver.d.ts +5 -2
- package/dist/core/role-resolver.d.ts.map +1 -1
- package/dist/core/role-resolver.js +55 -23
- package/dist/core/role-resolver.js.map +1 -1
- package/dist/core/session-manager.d.ts +2 -0
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +20 -18
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/terminal-write-auth.d.ts +4 -4
- package/dist/core/terminal-write-auth.d.ts.map +1 -1
- package/dist/core/terminal-write-auth.js +18 -6
- package/dist/core/terminal-write-auth.js.map +1 -1
- package/dist/daemon.d.ts +1 -0
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +101 -12
- package/dist/daemon.js.map +1 -1
- package/dist/dashboard/auth.d.ts +21 -11
- package/dist/dashboard/auth.d.ts.map +1 -1
- package/dist/dashboard/auth.js +39 -25
- package/dist/dashboard/auth.js.map +1 -1
- package/dist/dashboard/bot-payload.d.ts +0 -1
- package/dist/dashboard/bot-payload.d.ts.map +1 -1
- package/dist/dashboard/bot-payload.js +0 -1
- package/dist/dashboard/bot-payload.js.map +1 -1
- package/dist/dashboard/dashboard-summary-endpoint.d.ts +39 -0
- package/dist/dashboard/dashboard-summary-endpoint.d.ts.map +1 -0
- package/dist/dashboard/dashboard-summary-endpoint.js +50 -0
- package/dist/dashboard/dashboard-summary-endpoint.js.map +1 -0
- package/dist/dashboard/dashboard-summary.d.ts +62 -0
- package/dist/dashboard/dashboard-summary.d.ts.map +1 -0
- package/dist/dashboard/dashboard-summary.js +106 -0
- package/dist/dashboard/dashboard-summary.js.map +1 -0
- package/dist/dashboard/web/feed-group-picker.d.ts +13 -0
- package/dist/dashboard/web/feed-group-picker.d.ts.map +1 -0
- package/dist/dashboard/web/feed-group-picker.js +45 -0
- package/dist/dashboard/web/feed-group-picker.js.map +1 -0
- package/dist/dashboard/web/groups-page.d.ts.map +1 -1
- package/dist/dashboard/web/groups-page.js +2 -5
- package/dist/dashboard/web/groups-page.js.map +1 -1
- package/dist/dashboard/web/i18n.d.ts.map +1 -1
- package/dist/dashboard/web/i18n.js +8 -14
- package/dist/dashboard/web/i18n.js.map +1 -1
- package/dist/dashboard/web/roles-page.d.ts.map +1 -1
- package/dist/dashboard/web/roles-page.js +36 -4
- package/dist/dashboard/web/roles-page.js.map +1 -1
- package/dist/dashboard/web/roles.d.ts +3 -1
- package/dist/dashboard/web/roles.d.ts.map +1 -1
- package/dist/dashboard/web/roles.js +10 -2
- package/dist/dashboard/web/roles.js.map +1 -1
- package/dist/dashboard/web/schedules-page.d.ts.map +1 -1
- package/dist/dashboard/web/schedules-page.js +8 -2
- package/dist/dashboard/web/schedules-page.js.map +1 -1
- package/dist/dashboard/web/sessions-page.d.ts.map +1 -1
- package/dist/dashboard/web/sessions-page.js +2 -2
- package/dist/dashboard/web/sessions-page.js.map +1 -1
- package/dist/dashboard-web/app.js +1 -1
- package/dist/dashboard-web/chunks/{bot-defaults-page-GED4FEMS.js → bot-defaults-page-USC6EZXP.js} +1 -1
- package/dist/dashboard-web/chunks/chunk-6ALR252E.js +1 -0
- package/dist/dashboard-web/chunks/{chunk-VBXJOJFH.js → chunk-A7VC4BXR.js} +1 -1
- package/dist/dashboard-web/chunks/{chunk-HK7NIBJG.js → chunk-KOPISPHN.js} +1 -1
- package/dist/dashboard-web/chunks/{chunk-6J5LS5AU.js → chunk-M6BHWQFO.js} +6 -6
- package/dist/dashboard-web/chunks/{chunk-KRHWZEXU.js → chunk-RXRJB5SO.js} +1 -1
- package/dist/dashboard-web/chunks/{connectors-page-EELM66G3.js → connectors-page-4Z76HWRH.js} +1 -1
- package/dist/dashboard-web/chunks/groups-page-C6NSDTIN.js +8 -0
- package/dist/dashboard-web/chunks/{insights-page-54UBPPOC.js → insights-page-UTOVYLLY.js} +1 -1
- package/dist/dashboard-web/chunks/{monitor-room-PORHVKRH.js → monitor-room-UKTJYTA2.js} +1 -1
- package/dist/dashboard-web/chunks/{monitoring-page-XGA7AXNL.js → monitoring-page-Q3CEZIVX.js} +1 -1
- package/dist/dashboard-web/chunks/{office-page-AO3AFG45.js → office-page-GYCZOOVJ.js} +1 -1
- package/dist/dashboard-web/chunks/{overview-page-F5BUTYR6.js → overview-page-W2VBJ6OH.js} +1 -1
- package/dist/dashboard-web/chunks/roles-page-U4SHER4K.js +1 -0
- package/dist/dashboard-web/chunks/schedules-page-TNPW3N3Q.js +1 -0
- package/dist/dashboard-web/chunks/{sessions-page-YFRPSTIL.js → sessions-page-YWWDDV57.js} +3 -3
- package/dist/dashboard-web/chunks/{settings-page-OJAZLZQT.js → settings-page-VKW5ZK5K.js} +1 -1
- package/dist/dashboard-web/chunks/{skills-page-EFZQPWV4.js → skills-page-ZCEDFGZZ.js} +1 -1
- package/dist/dashboard-web/chunks/{team-federation-page-ZJVAP7HW.js → team-federation-page-OMFGGBT2.js} +1 -1
- package/dist/dashboard-web/chunks/{v3-page-GYWTRGO2.js → v3-page-PLXWK4KO.js} +1 -1
- package/dist/dashboard-web/chunks/{whiteboards-page-MSDAG6P7.js → whiteboards-page-NJQOEO2P.js} +1 -1
- package/dist/dashboard-web/style.css +112 -0
- package/dist/dashboard.js +116 -31
- package/dist/dashboard.js.map +1 -1
- package/dist/desktop/main/ipc.js +1 -1
- package/dist/desktop/main/runtime-service.js +2 -2
- package/dist/desktop/main/runtime-service.js.map +1 -1
- package/dist/desktop/shared/types.d.ts +1 -1
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +3 -1
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/zh.d.ts.map +1 -1
- package/dist/i18n/zh.js +3 -1
- package/dist/i18n/zh.js.map +1 -1
- package/dist/services/adopt-input-sequence.d.ts +52 -0
- package/dist/services/adopt-input-sequence.d.ts.map +1 -0
- package/dist/services/adopt-input-sequence.js +50 -0
- package/dist/services/adopt-input-sequence.js.map +1 -0
- package/dist/services/bridge-fallback-gate.d.ts.map +1 -1
- package/dist/services/bridge-fallback-gate.js +34 -11
- package/dist/services/bridge-fallback-gate.js.map +1 -1
- package/dist/services/codex-bridge-queue.d.ts +103 -7
- package/dist/services/codex-bridge-queue.d.ts.map +1 -1
- package/dist/services/codex-bridge-queue.js +291 -17
- package/dist/services/codex-bridge-queue.js.map +1 -1
- package/dist/services/codex-transcript.d.ts +4 -3
- package/dist/services/codex-transcript.d.ts.map +1 -1
- package/dist/services/codex-transcript.js.map +1 -1
- package/dist/services/structured-bridge-clis.d.ts +8 -1
- package/dist/services/structured-bridge-clis.d.ts.map +1 -1
- package/dist/services/structured-bridge-clis.js +13 -0
- package/dist/services/structured-bridge-clis.js.map +1 -1
- package/dist/services/submit-confirmation.d.ts +50 -0
- package/dist/services/submit-confirmation.d.ts.map +1 -1
- package/dist/services/submit-confirmation.js +82 -0
- package/dist/services/submit-confirmation.js.map +1 -1
- package/dist/utils/async-serial-queue.d.ts +9 -0
- package/dist/utils/async-serial-queue.d.ts.map +1 -0
- package/dist/utils/async-serial-queue.js +13 -0
- package/dist/utils/async-serial-queue.js.map +1 -0
- package/dist/utils/runtime-screen-status.d.ts +52 -0
- package/dist/utils/runtime-screen-status.d.ts.map +1 -0
- package/dist/utils/runtime-screen-status.js +60 -0
- package/dist/utils/runtime-screen-status.js.map +1 -0
- package/dist/worker.js +1642 -452
- package/dist/worker.js.map +1 -1
- package/package.json +1 -1
- package/dist/dashboard-web/chunks/groups-page-WKUV6E42.js +0 -8
- package/dist/dashboard-web/chunks/roles-page-M5GIASJU.js +0 -1
- package/dist/dashboard-web/chunks/schedules-page-O232WQRR.js +0 -1
package/dist/dashboard-web/chunks/{whiteboards-page-MSDAG6P7.js → whiteboards-page-NJQOEO2P.js}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{c as B}from"./chunk-
|
|
1
|
+
import{c as B}from"./chunk-KOPISPHN.js";import{c as W,k as C}from"./chunk-5CJMHTT4.js";import"./chunk-M6BHWQFO.js";import{a as P}from"./chunk-SYC6D5XU.js";import{a as O,d as T}from"./chunk-5THX4DI2.js";import{c as R}from"./chunk-7SZRL6FY.js";var r=R(O(),1);var e=R(T(),1);function x(t){let a=Date.parse(t);if(!a)return t||"-";let o=Math.max(0,Math.floor((Date.now()-a)/1e3));if(o<60)return`${o}s ago`;let i=Math.floor(o/60);if(i<60)return`${i}m ago`;let n=Math.floor(i/60);return n<24?`${n}h ago`:`${Math.floor(n/24)}d ago`}function F(t){return t.chatId?.trim()||"__local__"}function G(t,a){if(t==="__local__")return"\u672A\u7ED1\u5B9A\u7FA4 / \u672C\u5730\u767D\u677F";let o=a.get(t);return o&&o!==t?`${o} (${t})`:t}function K(t,a){let o=new Map;for(let i of t){let n=F(i),c=o.get(n)??[];c.push(i),o.set(n,c)}return[...o.entries()].map(([i,n])=>({chatId:i,label:G(i,a),rows:[...n].sort((c,b)=>b.updatedAt.localeCompare(c.updatedAt))})).sort((i,n)=>i.label.localeCompare(n.label))}function z(){return decodeURIComponent((location.hash.match(/^#\/whiteboards\/([^/]+)/)?.[1]??"").trim())}async function E(t,a){let o=await fetch(`/api/whiteboards/${encodeURIComponent(t)}`),i=await o.json().catch(()=>({}));if(o.ok)return{id:t,content:String(i.content??""),row:a.find(n=>n.id===t)}}async function J(t){let a=new Map;if(!t?.ok)return a;let o=await t.json().catch(()=>({})),i=Array.isArray(o.chats)?o.chats:[];for(let n of i)n.chatId&&a.set(String(n.chatId),String(n.name||n.chatId));return a}function Q(t){let a=t.row,o=a.title||a.id;return(0,e.jsxs)("a",{className:`wb-item${t.active?" active":""}`,"data-whiteboard-id":a.id,href:`#/whiteboards/${encodeURIComponent(a.id)}`,onClick:i=>{i.preventDefault(),t.onSelect(a.id)},children:[(0,e.jsx)("div",{className:"wb-item-head",children:(0,e.jsxs)("div",{className:"wb-item-main",children:[(0,e.jsx)("strong",{children:(0,e.jsx)(C,{text:o,textClassName:"wb-title-scroll"})}),(0,e.jsx)("span",{className:"wb-id-text",title:a.id,children:a.id})]})}),(0,e.jsxs)("div",{className:"wb-item-foot",children:[(0,e.jsxs)("div",{className:"wb-item-meta",children:[(0,e.jsx)("span",{children:x(a.updatedAt)}),(0,e.jsx)("span",{children:"\xB7"}),(0,e.jsxs)("span",{children:["log ",a.logCount]})]}),(0,e.jsx)("span",{className:"wb-scope",title:a.scope,children:a.scope})]})]})}function p(t){let a=t.value||"-";return(0,e.jsxs)("div",{className:"wb-meta-card",title:a,children:[(0,e.jsx)("span",{children:t.label}),(0,e.jsx)("strong",{children:a})]})}function V(t){let a=t.selected;if(!a)return(0,e.jsx)("p",{className:"empty",children:"\u9009\u62E9\u5DE6\u4FA7\u767D\u677F\u67E5\u770B meta \u548C board.md\u3002"});let o=a.row,i=o?.chatId?G(o.chatId,t.groupNames):"\u672A\u7ED1\u5B9A\u7FA4 / \u672C\u5730\u767D\u677F",n=o?.title||a.id;return(0,e.jsxs)(e.Fragment,{children:[(0,e.jsxs)("div",{className:"wb-detail-head",children:[(0,e.jsxs)("div",{className:"wb-detail-title",children:[(0,e.jsx)("p",{className:"eyebrow",children:"WHITEBOARD"}),(0,e.jsx)("h2",{title:n,children:n}),(0,e.jsx)("code",{title:a.id,children:a.id})]}),(0,e.jsx)("button",{type:"button",className:"danger","data-delete-whiteboard":!0,onClick:t.onDelete,children:"\u5220\u9664\u767D\u677F"})]}),(0,e.jsxs)("div",{className:"wb-meta-grid",children:[(0,e.jsx)(p,{label:"\u6240\u5C5E\u7FA4",value:i}),(0,e.jsx)(p,{label:"\u8303\u56F4",value:o?.scope??"-"}),(0,e.jsx)(p,{label:"\u6700\u8FD1\u66F4\u65B0",value:o?.updatedAt?x(o.updatedAt):"-"}),(0,e.jsx)(p,{label:"\u6765\u6E90\u76EE\u5F55",value:o?.workingDir??"-"})]}),(0,e.jsxs)("details",{className:"wb-admin-info",children:[(0,e.jsx)("summary",{children:"\u7BA1\u7406\u4FE1\u606F / \u6587\u4EF6\u8DEF\u5F84"}),(0,e.jsx)("code",{children:o?.path??""})]}),(0,e.jsxs)("section",{className:"wb-board-panel",children:[(0,e.jsxs)("div",{className:"wb-board-panel-head",children:[(0,e.jsx)("strong",{children:"board.md"}),(0,e.jsx)("span",{children:"\u53EA\u8BFB\u9884\u89C8"})]}),(0,e.jsx)("pre",{children:a.content||"\uFF08\u6682\u65E0\u5185\u5BB9\uFF09"})]})]})}function X(t){let a=t.selected.row?.title||t.selected.id;return(0,e.jsx)("div",{className:"wb-delete-backdrop","data-delete-modal":!0,onClick:o=>{o.target===o.currentTarget&&t.onCancel()},children:(0,e.jsxs)("div",{className:"wb-delete-dialog",role:"dialog","aria-modal":"true","aria-labelledby":"wb-delete-title",children:[(0,e.jsxs)("div",{className:"wb-delete-body",children:[(0,e.jsx)("div",{className:"wb-delete-mark","aria-hidden":"true",children:"!"}),(0,e.jsxs)("div",{children:[(0,e.jsx)("h3",{id:"wb-delete-title",children:"\u5220\u9664\u767D\u677F\uFF1F"}),(0,e.jsxs)("p",{children:["\u5C06\u5220\u9664 ",(0,e.jsx)("strong",{children:a}),"\uFF08",(0,e.jsx)("code",{children:t.selected.id}),"\uFF09\u7684 board\u3001log\u3001meta\uFF0C\u5E76\u6E05\u7406\u9ED8\u8BA4\u7ED1\u5B9A\u548C\u4F1A\u8BDD\u5F15\u7528\u3002\u6B64\u64CD\u4F5C\u4E0D\u53EF\u6062\u590D\u3002"]})]})]}),(0,e.jsxs)("div",{className:"actions wb-delete-actions",children:[(0,e.jsx)("button",{type:"button","data-delete-cancel":!0,onClick:t.onCancel,disabled:t.deleting,children:"\u53D6\u6D88"}),(0,e.jsx)("button",{type:"button",className:"danger","data-delete-confirm":!0,onClick:t.onConfirm,disabled:t.deleting,children:t.deleting?"\u5220\u9664\u4E2D...":"\u786E\u8BA4\u5220\u9664"})]})]})})}function Y(){let t=B(),a=(0,r.useRef)(!1),o=(0,r.useRef)(0),[i,n]=(0,r.useState)(!1),[c,b]=(0,r.useState)([]),[g,_]=(0,r.useState)(()=>new Map),[m,f]=(0,r.useState)(),[H,S]=(0,r.useState)(!0),[k,M]=(0,r.useState)(null),[u,h]=(0,r.useState)(null),[L,D]=(0,r.useState)(!1),I=(0,r.useMemo)(()=>K(c,g),[c,g]);(0,r.useEffect)(()=>{a.current=!0;let s=z();async function d(){S(!0);try{let[l,y]=await Promise.all([fetch("/api/whiteboards"),fetch("/api/groups").catch(()=>null)]),w=await l.json().catch(()=>({}));if(!l.ok)throw new Error(w?.error??`HTTP ${l.status}`);let q=await J(y),$=Array.isArray(w.whiteboards)?w.whiteboards:[],A;if(s&&(A=await E(s,$)),!a.current)return;n(w.enabled===!0),b($),_(q),f(A),M(null)}catch(l){a.current&&M(l instanceof Error?l.message:String(l))}finally{a.current&&S(!1)}}return d(),()=>{a.current=!1,o.current+=1}},[]);async function U(s){let d=o.current+1;o.current=d;let l=await E(s,c);!a.current||o.current!==d||!l||(f(l),window.history.replaceState(null,"",`#/whiteboards/${encodeURIComponent(s)}`))}async function j(){if(u){D(!0);try{let s=await fetch(`/api/whiteboards/${encodeURIComponent(u.id)}`,{method:"DELETE"}),d=await s.json().catch(()=>({}));if(!s.ok||d.ok===!1)throw new Error(d?.error??`HTTP ${s.status}`);if(!a.current)return;b(l=>l.filter(y=>y.id!==u.id)),m?.id===u.id&&(f(void 0),window.history.replaceState(null,"","#/whiteboards")),h(null)}catch(s){a.current&&window.alert(`\u5220\u9664\u5931\u8D25\uFF1A${s instanceof Error?s.message:String(s)}`)}finally{a.current&&D(!1)}}}let v="\u767D\u677F\u80FD\u529B\u5F53\u524D\u5173\u95ED\uFF1A\u4E0D\u4F1A\u81EA\u52A8\u521B\u5EFA/\u7ED1\u5B9A\u767D\u677F\uFF0C\u4E5F\u4E0D\u4F1A\u6CE8\u5165\u5230 agent prompt\u3002\u5386\u53F2\u767D\u677F\u4EC5\u5728 dashboard \u4E2D\u53EA\u8BFB\u53EF\u89C1\uFF0C\u53EF\u5728\u6B64\u6E05\u7406\u3002",N=(s=!1)=>(0,e.jsx)("div",{className:"page-heading",children:(0,e.jsxs)("div",{children:[(0,e.jsx)("p",{className:"eyebrow",children:t("nav.whiteboards")}),(0,e.jsx)("h1",{children:t("nav.whiteboards")}),s?(0,e.jsx)("span",{className:"wb-disabled-pill",title:v,"aria-label":v,children:v}):null]})});return k?(0,e.jsxs)("section",{className:"page whiteboards-page",children:[N(),(0,e.jsxs)("p",{className:"hint-warn",children:["\u52A0\u8F7D\u767D\u677F\u5931\u8D25\uFF1A",k]})]}):H?(0,e.jsxs)("section",{className:"page whiteboards-page","data-whiteboards-host":!0,children:[N(),(0,e.jsx)(W,{label:t("common.loading")})]}):(0,e.jsxs)("section",{className:"page whiteboards-page",children:[N(!i),(0,e.jsxs)("div",{className:"wb-split",children:[(0,e.jsx)("section",{className:"overview-block wb-list-block",children:(0,e.jsx)("div",{className:"wb-list-panel",children:I.length===0?(0,e.jsx)("p",{className:"empty wb-empty",children:"\u6682\u65E0\u767D\u677F\u3002\u6253\u5F00\u80FD\u529B\u540E\uFF0C\u6BCF\u4E2A\u7FA4\u9996\u6B21\u9700\u8981\u767D\u677F\u65F6\u624D\u4F1A\u521B\u5EFA\u9ED8\u8BA4\u767D\u677F\u3002"}):I.map(s=>(0,e.jsxs)("details",{className:"wb-group",open:!0,children:[(0,e.jsxs)("summary",{children:[(0,e.jsx)("span",{className:"wb-group-title",children:(0,e.jsx)(C,{text:s.label,textClassName:"wb-group-title-scroll"})}),(0,e.jsx)("small",{children:s.rows.length})]}),(0,e.jsx)("div",{className:"wb-group-items",children:s.rows.map(d=>(0,e.jsx)(Q,{row:d,active:m?.id===d.id,onSelect:l=>{U(l)}},d.id))})]},s.chatId))})}),(0,e.jsx)("section",{className:"overview-block wb-detail-block",children:(0,e.jsx)("div",{className:"wb-detail-panel",id:"whiteboard-detail",children:(0,e.jsx)(V,{selected:m,groupNames:g,onDelete:()=>{m&&h(m)}})})})]}),u?(0,e.jsx)(X,{selected:u,deleting:L,onCancel:()=>h(null),onConfirm:()=>{j()}}):null]})}function ae(t){return P(t,(0,e.jsx)(Y,{}))}export{ae as renderWhiteboardsPage};
|
|
@@ -13640,6 +13640,16 @@ tbody tr[data-chat]:hover {
|
|
|
13640
13640
|
white-space: nowrap;
|
|
13641
13641
|
}
|
|
13642
13642
|
|
|
13643
|
+
.schedule-chip-strip span.schedule-chat-chip {
|
|
13644
|
+
display: inline-block;
|
|
13645
|
+
max-width: min(320px, 100%);
|
|
13646
|
+
overflow: hidden;
|
|
13647
|
+
text-overflow: ellipsis;
|
|
13648
|
+
white-space: nowrap;
|
|
13649
|
+
line-height: 22px;
|
|
13650
|
+
text-align: left;
|
|
13651
|
+
}
|
|
13652
|
+
|
|
13643
13653
|
.schedule-chip-strip span.schedule-error-chip {
|
|
13644
13654
|
border-color: color-mix(in srgb, var(--danger) 35%, var(--border-soft));
|
|
13645
13655
|
background: color-mix(in srgb, var(--danger) 10%, var(--surface));
|
|
@@ -26024,3 +26034,105 @@ button.ui-refresh-button.is-loading .ui-action-icon {
|
|
|
26024
26034
|
.cs-advanced-fields .cs-feed-group > legend {
|
|
26025
26035
|
padding: 0;
|
|
26026
26036
|
}
|
|
26037
|
+
|
|
26038
|
+
.feed-group-picker {
|
|
26039
|
+
position: relative;
|
|
26040
|
+
width: 100%;
|
|
26041
|
+
}
|
|
26042
|
+
|
|
26043
|
+
.feed-group-picker > summary {
|
|
26044
|
+
box-sizing: border-box;
|
|
26045
|
+
display: flex;
|
|
26046
|
+
align-items: center;
|
|
26047
|
+
min-height: var(--button-height);
|
|
26048
|
+
padding: 0 42px 0 var(--button-padding-x);
|
|
26049
|
+
border: 1px solid color-mix(in srgb, var(--accent) 38%, var(--border-soft));
|
|
26050
|
+
border-radius: var(--radius-full);
|
|
26051
|
+
background: var(--surface);
|
|
26052
|
+
color: var(--fg);
|
|
26053
|
+
cursor: pointer;
|
|
26054
|
+
list-style: none;
|
|
26055
|
+
border-color: color-mix(in srgb, var(--accent) 38%, var(--border-soft));
|
|
26056
|
+
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 10%, transparent);
|
|
26057
|
+
}
|
|
26058
|
+
|
|
26059
|
+
.feed-group-picker > summary::-webkit-details-marker { display: none; }
|
|
26060
|
+
|
|
26061
|
+
.feed-group-picker > summary > span,
|
|
26062
|
+
.feed-group-picker-pop > button {
|
|
26063
|
+
min-width: 0;
|
|
26064
|
+
overflow: hidden;
|
|
26065
|
+
text-overflow: ellipsis;
|
|
26066
|
+
white-space: nowrap;
|
|
26067
|
+
}
|
|
26068
|
+
|
|
26069
|
+
.feed-group-picker > summary::after {
|
|
26070
|
+
content: "";
|
|
26071
|
+
position: absolute;
|
|
26072
|
+
right: 18px;
|
|
26073
|
+
width: 8px;
|
|
26074
|
+
height: 8px;
|
|
26075
|
+
border-style: solid;
|
|
26076
|
+
border-width: 0 2px 2px 0;
|
|
26077
|
+
border-color: var(--accent-strong);
|
|
26078
|
+
transform: translateY(-2px) rotate(45deg);
|
|
26079
|
+
}
|
|
26080
|
+
|
|
26081
|
+
.feed-group-picker[open] > summary::after { transform: translateY(2px) rotate(225deg); }
|
|
26082
|
+
.feed-group-picker.is-disabled > summary { cursor: not-allowed; opacity: .58; }
|
|
26083
|
+
|
|
26084
|
+
.feed-group-picker-pop {
|
|
26085
|
+
position: absolute;
|
|
26086
|
+
z-index: 120;
|
|
26087
|
+
left: 0;
|
|
26088
|
+
right: 0;
|
|
26089
|
+
top: calc(100% + 6px);
|
|
26090
|
+
display: grid;
|
|
26091
|
+
max-height: min(420px, calc(100vh - 180px));
|
|
26092
|
+
overflow-y: auto;
|
|
26093
|
+
padding: 8px;
|
|
26094
|
+
border: 1px solid var(--border-soft);
|
|
26095
|
+
border-radius: 14px;
|
|
26096
|
+
background: var(--surface-elevated, var(--surface));
|
|
26097
|
+
box-shadow: 0 16px 40px rgb(0 0 0 / 28%);
|
|
26098
|
+
}
|
|
26099
|
+
|
|
26100
|
+
.feed-group-picker-new {
|
|
26101
|
+
position: sticky;
|
|
26102
|
+
z-index: 1;
|
|
26103
|
+
top: 0;
|
|
26104
|
+
display: grid;
|
|
26105
|
+
gap: 6px;
|
|
26106
|
+
padding: 4px 4px 10px;
|
|
26107
|
+
border-bottom: 1px solid var(--border-soft);
|
|
26108
|
+
background: var(--surface-elevated, var(--surface));
|
|
26109
|
+
}
|
|
26110
|
+
|
|
26111
|
+
.feed-group-picker-new > span { color: var(--muted); font-size: 12px; font-weight: 700; }
|
|
26112
|
+
.feed-group-picker-new > input {
|
|
26113
|
+
box-sizing: border-box;
|
|
26114
|
+
width: 100%;
|
|
26115
|
+
min-height: 38px;
|
|
26116
|
+
padding: 0 12px;
|
|
26117
|
+
border: 1px solid var(--border-soft);
|
|
26118
|
+
border-radius: 9px;
|
|
26119
|
+
background: var(--surface);
|
|
26120
|
+
color: var(--fg);
|
|
26121
|
+
}
|
|
26122
|
+
|
|
26123
|
+
.feed-group-picker-pop > button {
|
|
26124
|
+
min-height: 38px;
|
|
26125
|
+
padding: 0 12px;
|
|
26126
|
+
border: 0;
|
|
26127
|
+
border-radius: 8px;
|
|
26128
|
+
background: transparent;
|
|
26129
|
+
color: var(--fg);
|
|
26130
|
+
text-align: left;
|
|
26131
|
+
cursor: pointer;
|
|
26132
|
+
}
|
|
26133
|
+
|
|
26134
|
+
.feed-group-picker-pop > button:hover,
|
|
26135
|
+
.feed-group-picker-pop > button[aria-current="true"] {
|
|
26136
|
+
background: var(--accent-soft);
|
|
26137
|
+
color: var(--accent-strong);
|
|
26138
|
+
}
|
package/dist/dashboard.js
CHANGED
|
@@ -11,7 +11,7 @@ import { logger } from './utils/logger.js';
|
|
|
11
11
|
import { gracefulProcessExitCode } from './pm2-graceful-exit.js';
|
|
12
12
|
import { config, isWildcardBindHost } from './config.js';
|
|
13
13
|
import { listenWithProbe } from './utils/listen-with-probe.js';
|
|
14
|
-
import {
|
|
14
|
+
import { parseCookie, buildSetCookie, verifyHmac, cliAuthBind, decideDashboardAuth, loadPersistedToken, loadOrCreatePersistedToken, rotatePersistedToken, loadDashboardSecret, loadOrCreateDashboardSecret, } from './dashboard/auth.js';
|
|
15
15
|
import { DaemonRegistry, botsRosterSignature } from './dashboard/registry.js';
|
|
16
16
|
import { Aggregator, subscribeDaemon } from './dashboard/aggregator.js';
|
|
17
17
|
import { createSessionPresentationCoordinator } from './dashboard/session-presentation.js';
|
|
@@ -104,6 +104,8 @@ import { resolveEffectivePluginIds, updateBotPluginOverride } from './core/plugi
|
|
|
104
104
|
import { assertPluginBindingTransition, describePluginDependencyError } from './core/plugins/dependencies.js';
|
|
105
105
|
import { inspectGatewayEntry } from './core/plugins/mcp/gateway-installer.js';
|
|
106
106
|
import { fetchDaemonIpc } from './core/daemon-ipc-auth.js';
|
|
107
|
+
import { buildDashboardSummary, parseDashboardSummaryRows, } from './dashboard/dashboard-summary.js';
|
|
108
|
+
import { createDashboardSummaryEndpoint } from './dashboard/dashboard-summary-endpoint.js';
|
|
107
109
|
const SECRET_PATH = dashboardSecretPath();
|
|
108
110
|
const TOKEN_PATH = join(homedir(), '.botmux', '.dashboard-token');
|
|
109
111
|
/** Per-daemon budget for the cross-daemon insight overview fan-out — bounds
|
|
@@ -113,7 +115,8 @@ const BOTS_JSON_PATH = join(homedir(), '.botmux', 'bots.json');
|
|
|
113
115
|
const REGISTRY_DIR = join(resolveBotmuxDataDir(), 'dashboard-daemons');
|
|
114
116
|
// The dashboard probes upward if its configured port is busy (e.g. a second
|
|
115
117
|
// botmux instance on this host). The actually-bound port is persisted here so
|
|
116
|
-
// the `botmux dashboard` CLI can reach /__cli/
|
|
118
|
+
// the `botmux dashboard` CLI can reach /__cli/current, /__cli/ensure, and
|
|
119
|
+
// /__cli/rotate without guessing.
|
|
117
120
|
const PORT_PATH = join(homedir(), '.botmux', '.dashboard-port');
|
|
118
121
|
function loadOrCreateSecret() {
|
|
119
122
|
let existing;
|
|
@@ -137,15 +140,21 @@ function loadOrCreateSecret() {
|
|
|
137
140
|
process.exit(1);
|
|
138
141
|
}
|
|
139
142
|
}
|
|
140
|
-
// The
|
|
141
|
-
//
|
|
142
|
-
//
|
|
143
|
-
//
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
143
|
+
// The persisted file is the active-token authority. Reading it at each use
|
|
144
|
+
// keeps multiple dashboard processes coherent: first creation converges under
|
|
145
|
+
// a file lock, and an explicit rotation is observed by every process without a
|
|
146
|
+
// restart. `/__cli/current` remains a strictly read-only probe.
|
|
147
|
+
function currentDashboardToken() {
|
|
148
|
+
try {
|
|
149
|
+
return loadPersistedToken(TOKEN_PATH);
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
logger.warn(`[dashboard] Failed to read token from ${TOKEN_PATH}: ${error.message}`);
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
147
156
|
// The port we actually bound (may differ from config.dashboard.port after an
|
|
148
|
-
// EADDRINUSE probe). Used for
|
|
157
|
+
// EADDRINUSE probe). Used for token-bearing URLs and persisted for the CLI.
|
|
149
158
|
let boundDashboardPort = config.dashboard.port;
|
|
150
159
|
const SECRET = loadOrCreateSecret();
|
|
151
160
|
function tcpPortAvailable(host, port) {
|
|
@@ -220,7 +229,7 @@ aggregator.on(sessionPresentation.onEvent);
|
|
|
220
229
|
// 调试终端(owner-only 裸 bash)。默认工作目录取当前所有 session 的工作目录去重,
|
|
221
230
|
// 让 owner 从熟悉的目录起终端复现问题;都没有时模块内退回 homedir。
|
|
222
231
|
const debugTerminalManager = createDebugTerminalManager({
|
|
223
|
-
getActiveToken:
|
|
232
|
+
getActiveToken: currentDashboardToken,
|
|
224
233
|
defaultWorkingDirs: () => {
|
|
225
234
|
const dirs = new Set();
|
|
226
235
|
for (const s of aggregator.getSessions()) {
|
|
@@ -1032,9 +1041,9 @@ const groupsActionDeps = {
|
|
|
1032
1041
|
// ─── PR2 C8: Route B internal API (`/__daemon/*`) ───────────────────────────
|
|
1033
1042
|
// HMAC + loopback + ts ±60s + nonce TTL, signed-request envelope = full
|
|
1034
1043
|
// (ts, nonce, method, pathWithQuery, sha256(body)). Reuses `.dashboard-secret`
|
|
1035
|
-
// for the HMAC key — the same secret `/__cli
|
|
1036
|
-
// signing material is wider so a
|
|
1037
|
-
//
|
|
1044
|
+
// for the HMAC key — the same secret the `/__cli/*` protocol uses — but the
|
|
1045
|
+
// signing material is wider so a CLI signature cannot be replayed here and
|
|
1046
|
+
// vice versa (different protocols, same secret, no cross-replay).
|
|
1038
1047
|
//
|
|
1039
1048
|
// SECRET fail-closed: `loadOrCreateSecret()` returns a 32-byte base64url
|
|
1040
1049
|
// string and never empty; we still guard below at server-startup time.
|
|
@@ -1751,7 +1760,7 @@ async function handlePluginManagementApi(req, res, url) {
|
|
|
1751
1760
|
return false;
|
|
1752
1761
|
}
|
|
1753
1762
|
// ─── HTTP routing ────────────────────────────────────────────────────────────
|
|
1754
|
-
function authedToken(req, url) {
|
|
1763
|
+
function authedToken(req, url, activeToken) {
|
|
1755
1764
|
const q = url.searchParams.get('t');
|
|
1756
1765
|
if (q && q === activeToken)
|
|
1757
1766
|
return q;
|
|
@@ -2393,6 +2402,52 @@ function parseTerminalSessionId(pathname) {
|
|
|
2393
2402
|
const seg = pathname.slice(3).split('/')[0];
|
|
2394
2403
|
return seg || undefined;
|
|
2395
2404
|
}
|
|
2405
|
+
/**
|
|
2406
|
+
* Read every currently-online daemon directly for the public dashboard
|
|
2407
|
+
* summary. The regular aggregator intentionally retains offline
|
|
2408
|
+
* rows for operator history, so using it here would make an offline bot's old
|
|
2409
|
+
* sessions or schedules look live. A failed or malformed daemon snapshot
|
|
2410
|
+
* rejects the whole projection instead of silently turning missing data into
|
|
2411
|
+
* zeroes.
|
|
2412
|
+
*/
|
|
2413
|
+
async function liveDashboardSummary() {
|
|
2414
|
+
const daemons = registry.list();
|
|
2415
|
+
const configuredBots = loadBotConfigs();
|
|
2416
|
+
const snapshots = await Promise.all(daemons.map(async (daemon) => {
|
|
2417
|
+
const [sessionsResponse, schedulesResponse] = await Promise.all([
|
|
2418
|
+
fetchDaemonIpc(daemon.ipcPort, '/api/sessions', {
|
|
2419
|
+
signal: AbortSignal.timeout(2_000),
|
|
2420
|
+
}),
|
|
2421
|
+
fetchDaemonIpc(daemon.ipcPort, '/api/schedules', {
|
|
2422
|
+
signal: AbortSignal.timeout(2_000),
|
|
2423
|
+
}),
|
|
2424
|
+
]);
|
|
2425
|
+
if (!sessionsResponse.ok || !schedulesResponse.ok) {
|
|
2426
|
+
throw new Error('daemon_snapshot_http_error');
|
|
2427
|
+
}
|
|
2428
|
+
const [sessionsBody, schedulesBody] = await Promise.all([
|
|
2429
|
+
sessionsResponse.json(),
|
|
2430
|
+
schedulesResponse.json(),
|
|
2431
|
+
]);
|
|
2432
|
+
return parseDashboardSummaryRows({
|
|
2433
|
+
sessions: sessionsBody.sessions,
|
|
2434
|
+
schedules: schedulesBody.schedules,
|
|
2435
|
+
});
|
|
2436
|
+
}));
|
|
2437
|
+
return buildDashboardSummary({
|
|
2438
|
+
generatedAt: new Date(),
|
|
2439
|
+
configuredBotAppIds: configuredBots.map(bot => bot.larkAppId),
|
|
2440
|
+
onlineBotAppIds: daemons.map(daemon => daemon.larkAppId),
|
|
2441
|
+
sessions: snapshots.flatMap(snapshot => snapshot.sessions),
|
|
2442
|
+
schedules: snapshots.flatMap(snapshot => snapshot.schedules),
|
|
2443
|
+
});
|
|
2444
|
+
}
|
|
2445
|
+
const dashboardSummaryEndpoint = createDashboardSummaryEndpoint({
|
|
2446
|
+
load: liveDashboardSummary,
|
|
2447
|
+
onError: error => {
|
|
2448
|
+
logger.warn(`[dashboard-summary] live snapshot unavailable: ${error instanceof Error ? error.message : String(error)}`);
|
|
2449
|
+
},
|
|
2450
|
+
});
|
|
2396
2451
|
const server = createServer(async (req, res) => {
|
|
2397
2452
|
try {
|
|
2398
2453
|
const url = new URL(req.url ?? '/', `http://${req.headers.host ?? 'localhost'}`);
|
|
@@ -2411,7 +2466,8 @@ const server = createServer(async (req, res) => {
|
|
|
2411
2466
|
// outside the browser auth gate so packaged desktop apps can decide whether
|
|
2412
2467
|
// this runtime speaks their dashboard protocol before loading the SPA.
|
|
2413
2468
|
if (req.method === 'GET' && url.pathname === '/__desktop/compat') {
|
|
2414
|
-
const
|
|
2469
|
+
const activeToken = currentDashboardToken();
|
|
2470
|
+
const presentedToken = authedToken(req, url, activeToken);
|
|
2415
2471
|
const boundMachineId = activeToken && presentedToken === activeToken
|
|
2416
2472
|
? readPlatformBinding()?.machineId
|
|
2417
2473
|
: null;
|
|
@@ -2473,32 +2529,53 @@ const server = createServer(async (req, res) => {
|
|
|
2473
2529
|
if (await daemonInternalApi.handle(req, res, url)) {
|
|
2474
2530
|
return;
|
|
2475
2531
|
}
|
|
2476
|
-
// CLI rotate (HMAC + loopback only) — for `botmux dashboard`.
|
|
2477
|
-
// token
|
|
2532
|
+
// CLI rotate (HMAC + loopback only) — for `botmux dashboard rotate`.
|
|
2533
|
+
// Publish the new token only after a durable write succeeds.
|
|
2478
2534
|
if (req.method === 'POST' && url.pathname === '/__cli/rotate') {
|
|
2479
2535
|
const gate = verifyCliRequest(req, url.pathname);
|
|
2480
2536
|
if (!gate.ok)
|
|
2481
2537
|
return jsonRes(res, gate.status, gate.body);
|
|
2482
|
-
activeToken = generateToken();
|
|
2483
2538
|
try {
|
|
2484
|
-
|
|
2539
|
+
const token = rotatePersistedToken(TOKEN_PATH);
|
|
2540
|
+
return jsonRes(res, 200, dashboardUrlsFor(token));
|
|
2485
2541
|
}
|
|
2486
2542
|
catch (e) {
|
|
2487
2543
|
logger.warn(`[dashboard] Failed to persist token to ${TOKEN_PATH}: ${e.message}`);
|
|
2544
|
+
return jsonRes(res, 500, { error: 'token_persist_failed' });
|
|
2545
|
+
}
|
|
2546
|
+
}
|
|
2547
|
+
// CLI get-or-create URL (HMAC + loopback only). Existing links survive
|
|
2548
|
+
// untouched; the first caller atomically creates and persists a token.
|
|
2549
|
+
if (req.method === 'POST' && url.pathname === '/__cli/ensure') {
|
|
2550
|
+
const gate = verifyCliRequest(req, url.pathname);
|
|
2551
|
+
if (!gate.ok)
|
|
2552
|
+
return jsonRes(res, gate.status, gate.body);
|
|
2553
|
+
try {
|
|
2554
|
+
const token = loadOrCreatePersistedToken(TOKEN_PATH);
|
|
2555
|
+
return jsonRes(res, 200, dashboardUrlsFor(token));
|
|
2556
|
+
}
|
|
2557
|
+
catch (e) {
|
|
2558
|
+
logger.warn(`[dashboard] Failed to ensure token at ${TOKEN_PATH}: ${e.message}`);
|
|
2559
|
+
return jsonRes(res, 500, { error: 'token_persist_failed' });
|
|
2488
2560
|
}
|
|
2489
|
-
return jsonRes(res, 200, dashboardUrlsFor(activeToken));
|
|
2490
2561
|
}
|
|
2491
|
-
// CLI read current URL (HMAC + loopback only) — for
|
|
2492
|
-
//
|
|
2493
|
-
// dashboard link survives restart untouched. 404 → no token has ever been
|
|
2494
|
-
// minted (caller falls back to suggesting `botmux dashboard`).
|
|
2562
|
+
// CLI read current URL (HMAC + loopback only) — for start/restart hints and
|
|
2563
|
+
// safe port discovery. This never mints a token.
|
|
2495
2564
|
if (req.method === 'POST' && url.pathname === '/__cli/current') {
|
|
2496
2565
|
const gate = verifyCliRequest(req, url.pathname);
|
|
2497
2566
|
if (!gate.ok)
|
|
2498
2567
|
return jsonRes(res, gate.status, gate.body);
|
|
2499
|
-
|
|
2568
|
+
let token;
|
|
2569
|
+
try {
|
|
2570
|
+
token = loadPersistedToken(TOKEN_PATH);
|
|
2571
|
+
}
|
|
2572
|
+
catch (e) {
|
|
2573
|
+
logger.warn(`[dashboard] Failed to read token from ${TOKEN_PATH}: ${e.message}`);
|
|
2574
|
+
return jsonRes(res, 500, { error: 'token_unavailable' });
|
|
2575
|
+
}
|
|
2576
|
+
if (!token)
|
|
2500
2577
|
return jsonRes(res, 404, { error: 'no_active_token' });
|
|
2501
|
-
return jsonRes(res, 200, dashboardUrlsFor(
|
|
2578
|
+
return jsonRes(res, 200, dashboardUrlsFor(token));
|
|
2502
2579
|
}
|
|
2503
2580
|
// CLI 通知绑定变化(HMAC + loopback)——`botmux bind` 写完绑定后捅一下,立即重连平台,
|
|
2504
2581
|
// 无需重启 daemon,也不依赖 fs.watch。
|
|
@@ -2521,7 +2598,8 @@ const server = createServer(async (req, res) => {
|
|
|
2521
2598
|
startPlatformTunnelIfBound();
|
|
2522
2599
|
return jsonRes(res, 200, { ok: true });
|
|
2523
2600
|
}
|
|
2524
|
-
const
|
|
2601
|
+
const activeToken = currentDashboardToken();
|
|
2602
|
+
const presentedToken = authedToken(req, url, activeToken);
|
|
2525
2603
|
const globalDashboardConfig = readGlobalConfig().dashboard;
|
|
2526
2604
|
const decision = decideDashboardAuth({
|
|
2527
2605
|
method: req.method ?? 'GET',
|
|
@@ -2659,6 +2737,12 @@ const server = createServer(async (req, res) => {
|
|
|
2659
2737
|
return jsonRes(res, 200, startHd2dDownload());
|
|
2660
2738
|
}
|
|
2661
2739
|
// ─── Public API (cookie/token already validated above) ──────────────────
|
|
2740
|
+
if (req.method === 'GET' && url.pathname === '/api/dashboard/v1/summary') {
|
|
2741
|
+
const result = await dashboardSummaryEndpoint.get({ authenticated: authed });
|
|
2742
|
+
for (const [name, value] of Object.entries(result.headers))
|
|
2743
|
+
res.setHeader(name, value);
|
|
2744
|
+
return jsonRes(res, result.status, result.body);
|
|
2745
|
+
}
|
|
2662
2746
|
if (await handleResourceMonitorApi(req, res, url, resourceMonitor)) {
|
|
2663
2747
|
return;
|
|
2664
2748
|
}
|
|
@@ -5482,15 +5566,16 @@ function startPlatformTunnelIfBound() {
|
|
|
5482
5566
|
const binding = readPlatformBinding();
|
|
5483
5567
|
if (!binding)
|
|
5484
5568
|
return;
|
|
5485
|
-
|
|
5486
|
-
|
|
5569
|
+
const existingToken = currentDashboardToken();
|
|
5570
|
+
loadOrCreatePersistedToken(TOKEN_PATH);
|
|
5571
|
+
if (!existingToken) {
|
|
5487
5572
|
logger.info('[platform-tunnel] 已初始化 dashboard token');
|
|
5488
5573
|
}
|
|
5489
5574
|
const version = readBotmuxVersion();
|
|
5490
5575
|
platformTunnel = startPlatformTunnelClient({
|
|
5491
5576
|
binding,
|
|
5492
5577
|
getDashboardPort: () => boundDashboardPort,
|
|
5493
|
-
getDashboardToken:
|
|
5578
|
+
getDashboardToken: currentDashboardToken,
|
|
5494
5579
|
getVersion: () => version,
|
|
5495
5580
|
getBots: () => readPlatformBotsInfo(),
|
|
5496
5581
|
getTeamSyncRev: () => getPlatformTeamSyncRev(config.session.dataDir),
|